From b1f85d3f990fad4b6a6541d10a794a29ee86fdb1 Mon Sep 17 00:00:00 2001 From: Charles Herlin Date: Thu, 9 Nov 2017 15:45:42 +0100 Subject: [PATCH] Add sudo management in main and utils * perform readonly checks as a regular user * sudo -n is used for checks requiring root privileges * increase accountability by providing log of individual access to sensitive files --- README.md | 5 +++ bin/hardening.sh | 23 +++++++--- bin/hardening/12.10_find_suid_files.sh | 2 +- bin/hardening/12.11_find_sgid_files.sh | 2 +- .../12.7_find_world_writable_file.sh | 2 +- bin/hardening/12.8_find_unowned_files.sh | 2 +- bin/hardening/12.9_find_ungrouped_files.sh | 2 +- .../13.1_remove_empty_password_field.sh | 2 +- .../13.3_remove_legacy_shadow_entries.sh | 2 +- .../2.17_sticky_bit_world_writable_folder.sh | 2 +- bin/hardening/4.2_enable_nx_support.sh | 2 +- bin/hardening/6.15_mta_localhost.sh | 2 +- bin/hardening/8.1.10_record_dac_edit.sh | 8 +++- .../8.1.11_record_failed_access_file.sh | 8 +++- .../8.1.12_record_privileged_commands.sh | 11 ++++- .../8.1.13_record_successful_mount.sh | 8 +++- bin/hardening/8.1.14_record_file_deletions.sh | 8 +++- bin/hardening/8.1.15_record_sudoers_edit.sh | 8 +++- bin/hardening/8.1.16_record_sudo_usage.sh | 8 +++- bin/hardening/8.1.17_record_kernel_modules.sh | 8 +++- bin/hardening/8.1.18_freeze_auditd_conf.sh | 8 +++- bin/hardening/8.1.4_record_date_time_edit.sh | 8 +++- bin/hardening/8.1.5_record_user_group_edit.sh | 8 +++- bin/hardening/8.1.6_record_network_edit.sh | 8 +++- bin/hardening/8.1.7_record_mac_edit.sh | 8 +++- bin/hardening/8.1.8_record_login_logout.sh | 8 +++- bin/hardening/8.1.9_record_session_init.sh | 8 +++- cisharden.sudoers | 23 ++++++++++ lib/main.sh | 4 ++ lib/utils.sh | 42 +++++++++---------- 30 files changed, 187 insertions(+), 53 deletions(-) create mode 100644 cisharden.sudoers diff --git a/README.md b/README.md index 01ba138..a0b7731 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,11 @@ configuration. It will run all scripts in audit mode. If a script passes, it will automatically be enabled for future runs. Do NOT use this option if you have already started to customize your configuration. +``--sudo``: Audit your system as a normal user, but allow sudo escalation to read +specific root read-only files. You need to provide a sudoers file in /etc/sudoers.d/ +with NOPASWD option, since checks are executed with ``sudo -n`` option, that will +not prompt for a password. + ## Hacking **Getting the source** diff --git a/bin/hardening.sh b/bin/hardening.sh index eb0bd2f..914d964 100755 --- a/bin/hardening.sh +++ b/bin/hardening.sh @@ -22,6 +22,7 @@ AUDIT_ALL=0 AUDIT_ALL_ENABLE_PASSED=0 ALLOW_SERVICE_LIST=0 SET_HARDENING_LEVEL=0 +SUDO_MODE='' usage() { cat << EOF @@ -83,6 +84,13 @@ OPTIONS: The test number is the numbered prefix of the script, i.e. the test number of 1.2_script_name.sh is 1.2. + --sudo + This option lets you audit your system as a normal user, but allows sudo + escalation to gain read-only access to root files. Note that you need to + provide a sudoers file with NOPASSWD option in /etc/sudoers.d/ because + the '-n' option instructs sudo not to prompt for a password. + Finally note that '--sudo' mode only works for audit mode. + EOF exit 0 } @@ -124,6 +132,9 @@ while [[ $# > 0 ]]; do TEST_LIST[${#TEST_LIST[@]}]="$2" shift ;; + --sudo) + SUDO_MODE='--sudo' + ;; -h|--help) usage ;; @@ -197,14 +208,14 @@ for SCRIPT in $(ls $CIS_ROOT_DIR/bin/hardening/*.sh -v); do info "Treating $SCRIPT" if [ $AUDIT = 1 ]; then - debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit" - $SCRIPT --audit + debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit $SUDO_MODE" + $SCRIPT --audit $SUDO_MODE elif [ $AUDIT_ALL = 1 ]; then - debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all" - $SCRIPT --audit-all + debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE" + $SCRIPT --audit-all $SUDO_MODE elif [ $AUDIT_ALL_ENABLE_PASSED = 1 ]; then - debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all" - $SCRIPT --audit-all + debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT --audit-all $SUDO_MODE" + $SCRIPT --audit-all $SUDO_MODE elif [ $APPLY = 1 ]; then debug "$CIS_ROOT_DIR/bin/hardening/$SCRIPT" $SCRIPT diff --git a/bin/hardening/12.10_find_suid_files.sh b/bin/hardening/12.10_find_suid_files.sh index b9b4168..a361ca8 100755 --- a/bin/hardening/12.10_find_suid_files.sh +++ b/bin/hardening/12.10_find_suid_files.sh @@ -16,7 +16,7 @@ HARDENING_LEVEL=2 # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if there are suid files" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -4000 -print) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -4000 -print) for BINARY in $RESULT; do if grep -q $BINARY <<< "$EXCEPTIONS"; then debug "$BINARY is confirmed as an exception" diff --git a/bin/hardening/12.11_find_sgid_files.sh b/bin/hardening/12.11_find_sgid_files.sh index 59b61c1..9cc815c 100755 --- a/bin/hardening/12.11_find_sgid_files.sh +++ b/bin/hardening/12.11_find_sgid_files.sh @@ -16,7 +16,7 @@ HARDENING_LEVEL=2 # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if there are sgid files" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -2000 -print) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -2000 -print) for BINARY in $RESULT; do if grep -q $BINARY <<< "$EXCEPTIONS"; then debug "$BINARY is confirmed as an exception" diff --git a/bin/hardening/12.7_find_world_writable_file.sh b/bin/hardening/12.7_find_world_writable_file.sh index 5790714..6e24538 100755 --- a/bin/hardening/12.7_find_world_writable_file.sh +++ b/bin/hardening/12.7_find_world_writable_file.sh @@ -16,7 +16,7 @@ HARDENING_LEVEL=3 # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if there are world writable files" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type f -perm -0002 -print 2>/dev/null) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type f -perm -0002 -print 2>/dev/null) if [ ! -z "$RESULT" ]; then crit "Some world writable files are present" FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ') diff --git a/bin/hardening/12.8_find_unowned_files.sh b/bin/hardening/12.8_find_unowned_files.sh index 02e838f..8079917 100755 --- a/bin/hardening/12.8_find_unowned_files.sh +++ b/bin/hardening/12.8_find_unowned_files.sh @@ -18,7 +18,7 @@ USER='root' # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if there are unowned files" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nouser -print 2>/dev/null) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -nouser -print 2>/dev/null) if [ ! -z "$RESULT" ]; then crit "Some unowned files are present" FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ') diff --git a/bin/hardening/12.9_find_ungrouped_files.sh b/bin/hardening/12.9_find_ungrouped_files.sh index fbf65ee..57175e7 100755 --- a/bin/hardening/12.9_find_ungrouped_files.sh +++ b/bin/hardening/12.9_find_ungrouped_files.sh @@ -18,7 +18,7 @@ GROUP='root' # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if there are ungrouped files" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -nogroup -print 2>/dev/null) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -nogroup -print 2>/dev/null) if [ ! -z "$RESULT" ]; then crit "Some ungrouped files are present" FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ') diff --git a/bin/hardening/13.1_remove_empty_password_field.sh b/bin/hardening/13.1_remove_empty_password_field.sh index 2c33a0e..f86c395 100755 --- a/bin/hardening/13.1_remove_empty_password_field.sh +++ b/bin/hardening/13.1_remove_empty_password_field.sh @@ -18,7 +18,7 @@ FILE='/etc/shadow' # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if accounts have an empty password" - RESULT=$(cat $FILE | awk -F: '($2 == "" ) { print $1 }') + RESULT=$($SUDO_CMD cat $FILE | awk -F: '($2 == "" ) { print $1 }') if [ ! -z "$RESULT" ]; then crit "Some accounts have an empty password" crit $RESULT diff --git a/bin/hardening/13.3_remove_legacy_shadow_entries.sh b/bin/hardening/13.3_remove_legacy_shadow_entries.sh index 1ef2b44..f4d4b6f 100755 --- a/bin/hardening/13.3_remove_legacy_shadow_entries.sh +++ b/bin/hardening/13.3_remove_legacy_shadow_entries.sh @@ -19,7 +19,7 @@ RESULT='' # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if accounts have a legacy password entry" - if grep '^+:' $FILE -q; then + if $SUDO_CMD grep '^+:' $FILE -q; then RESULT=$(grep '^+:' $FILE) crit "Some accounts have a legacy password entry" crit $RESULT diff --git a/bin/hardening/2.17_sticky_bit_world_writable_folder.sh b/bin/hardening/2.17_sticky_bit_world_writable_folder.sh index 9f6b8fb..68ee59d 100755 --- a/bin/hardening/2.17_sticky_bit_world_writable_folder.sh +++ b/bin/hardening/2.17_sticky_bit_world_writable_folder.sh @@ -16,7 +16,7 @@ HARDENING_LEVEL=2 # This function will be called if the script status is on enabled / audit mode audit () { info "Checking if setuid is set on world writable Directories" - RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null) + RESULT=$(df --local -P | awk {'if (NR!=1) print $6'} | xargs -I '{}' $SUDO_CMD find '{}' -xdev -type d \( -perm -0002 -a ! -perm -1000 \) -print 2>/dev/null) if [ ! -z "$RESULT" ]; then crit "Some world writable directories are not on sticky bit mode!" FORMATTED_RESULT=$(sed "s/ /\n/g" <<< $RESULT | sort | uniq | tr '\n' ' ') diff --git a/bin/hardening/4.2_enable_nx_support.sh b/bin/hardening/4.2_enable_nx_support.sh index 81aeaa1..f41bb4e 100755 --- a/bin/hardening/4.2_enable_nx_support.sh +++ b/bin/hardening/4.2_enable_nx_support.sh @@ -19,7 +19,7 @@ PATTERN='NX[[:space:]]\(Execute[[:space:]]Disable\)[[:space:]]protection:[[:spac nx_supported_and_enabled() { if grep -q ' nx ' /proc/cpuinfo; then # NX supported, but if noexec=off specified, it's not enabled - if grep -qi 'noexec=off' /proc/cmdline; then + if $SUDO_CMD grep -qi 'noexec=off' /proc/cmdline; then FNRET=1 # supported but disabled else FNRET=0 # supported and enabled diff --git a/bin/hardening/6.15_mta_localhost.sh b/bin/hardening/6.15_mta_localhost.sh index f21a86c..1ef1d9c 100755 --- a/bin/hardening/6.15_mta_localhost.sh +++ b/bin/hardening/6.15_mta_localhost.sh @@ -17,7 +17,7 @@ HARDENING_EXCEPTION=mail # This function will be called if the script status is on enabled / audit mode audit () { info "Checking netport ports opened" - RESULT=$(netstat -an | grep LIST | grep ":25[[:space:]]") || : + RESULT=$($SUDO_CMD netstat -an | grep LIST | grep ":25[[:space:]]") || : RESULT=${RESULT:-} debug "Result is $RESULT" if [ -z "$RESULT" ]; then diff --git a/bin/hardening/8.1.10_record_dac_edit.sh b/bin/hardening/8.1.10_record_dac_edit.sh index 3559eb7..467c766 100755 --- a/bin/hardening/8.1.10_record_dac_edit.sh +++ b/bin/hardening/8.1.10_record_dac_edit.sh @@ -23,16 +23,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.11_record_failed_access_file.sh b/bin/hardening/8.1.11_record_failed_access_file.sh index f411283..aad2351 100755 --- a/bin/hardening/8.1.11_record_failed_access_file.sh +++ b/bin/hardening/8.1.11_record_failed_access_file.sh @@ -21,16 +21,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.12_record_privileged_commands.sh b/bin/hardening/8.1.12_record_privileged_commands.sh index 856e52c..95e03f4 100755 --- a/bin/hardening/8.1.12_record_privileged_commands.sh +++ b/bin/hardening/8.1.12_record_privileged_commands.sh @@ -14,23 +14,30 @@ set -u # One variable unset, it's over HARDENING_LEVEL=4 # Find all files with setuid or setgid set -AUDIT_PARAMS=$(find / -xdev \( -perm -4000 -o -perm -2000 \) -type f | awk '{print \ +SUDO_CMD='sudo -n' +AUDIT_PARAMS=$($SUDO_CMD find / -xdev \( -perm -4000 -o -perm -2000 \) -type f | awk '{print \ "-a always,exit -F path=" $1 " -F perm=x -F auid>=1000 -F auid!=4294967295 \ -k privileged" }') FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.13_record_successful_mount.sh b/bin/hardening/8.1.13_record_successful_mount.sh index c3e411b..d66c00d 100755 --- a/bin/hardening/8.1.13_record_successful_mount.sh +++ b/bin/hardening/8.1.13_record_successful_mount.sh @@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.14_record_file_deletions.sh b/bin/hardening/8.1.14_record_file_deletions.sh index 07d6acb..1889103 100755 --- a/bin/hardening/8.1.14_record_file_deletions.sh +++ b/bin/hardening/8.1.14_record_file_deletions.sh @@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.15_record_sudoers_edit.sh b/bin/hardening/8.1.15_record_sudoers_edit.sh index 6d22771..ea3ebd9 100755 --- a/bin/hardening/8.1.15_record_sudoers_edit.sh +++ b/bin/hardening/8.1.15_record_sudoers_edit.sh @@ -19,16 +19,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.16_record_sudo_usage.sh b/bin/hardening/8.1.16_record_sudo_usage.sh index 489602b..ede8754 100755 --- a/bin/hardening/8.1.16_record_sudo_usage.sh +++ b/bin/hardening/8.1.16_record_sudo_usage.sh @@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.17_record_kernel_modules.sh b/bin/hardening/8.1.17_record_kernel_modules.sh index d6f48f2..deb14f4 100755 --- a/bin/hardening/8.1.17_record_kernel_modules.sh +++ b/bin/hardening/8.1.17_record_kernel_modules.sh @@ -21,16 +21,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.18_freeze_auditd_conf.sh b/bin/hardening/8.1.18_freeze_auditd_conf.sh index 2342621..f6ce1ed 100755 --- a/bin/hardening/8.1.18_freeze_auditd_conf.sh +++ b/bin/hardening/8.1.18_freeze_auditd_conf.sh @@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.4_record_date_time_edit.sh b/bin/hardening/8.1.4_record_date_time_edit.sh index 41de3f5..3110963 100755 --- a/bin/hardening/8.1.4_record_date_time_edit.sh +++ b/bin/hardening/8.1.4_record_date_time_edit.sh @@ -22,16 +22,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.5_record_user_group_edit.sh b/bin/hardening/8.1.5_record_user_group_edit.sh index 1fa0777..a1762a7 100755 --- a/bin/hardening/8.1.5_record_user_group_edit.sh +++ b/bin/hardening/8.1.5_record_user_group_edit.sh @@ -22,16 +22,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.6_record_network_edit.sh b/bin/hardening/8.1.6_record_network_edit.sh index e1e2dc9..22e8533 100755 --- a/bin/hardening/8.1.6_record_network_edit.sh +++ b/bin/hardening/8.1.6_record_network_edit.sh @@ -23,16 +23,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.7_record_mac_edit.sh b/bin/hardening/8.1.7_record_mac_edit.sh index 756f45d..9a26de8 100755 --- a/bin/hardening/8.1.7_record_mac_edit.sh +++ b/bin/hardening/8.1.7_record_mac_edit.sh @@ -18,16 +18,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.8_record_login_logout.sh b/bin/hardening/8.1.8_record_login_logout.sh index 7ee224a..7f886d1 100755 --- a/bin/hardening/8.1.8_record_login_logout.sh +++ b/bin/hardening/8.1.8_record_login_logout.sh @@ -20,16 +20,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/bin/hardening/8.1.9_record_session_init.sh b/bin/hardening/8.1.9_record_session_init.sh index 807721d..9616a4b 100755 --- a/bin/hardening/8.1.9_record_session_init.sh +++ b/bin/hardening/8.1.9_record_session_init.sh @@ -20,16 +20,22 @@ FILE='/etc/audit/audit.rules' # This function will be called if the script status is on enabled / audit mode audit () { - IFS=$'\n' + # define custom IFS and save default one + d_IFS=$IFS + c_IFS=$'\n' + IFS=$c_IFS for AUDIT_VALUE in $AUDIT_PARAMS; do debug "$AUDIT_VALUE should be in file $FILE" + IFS=$d_IFS does_pattern_exist_in_file $FILE $AUDIT_VALUE + IFS=$c_IFS if [ $FNRET != 0 ]; then crit "$AUDIT_VALUE is not in file $FILE" else ok "$AUDIT_VALUE is present in $FILE" fi done + IFS=$d_IFS } # This function will be called if the script status is on enabled mode diff --git a/cisharden.sudoers b/cisharden.sudoers new file mode 100644 index 0000000..a66469f --- /dev/null +++ b/cisharden.sudoers @@ -0,0 +1,23 @@ +Cmnd_Alias SCL_CMD = /bin/grep ,\ + /bin/zgrep,\ + /bin/cat,\ + /usr/bin/stat,\ + /usr/bin/getent,\ + /usr/bin/[,\ + /bin/ls,\ + /usr/bin/find,\ + ! /usr/bin/find *-exec*, \ + ! /usr/bin/find *-delete*,\ + /usr/bin/apt-get update -y,\ + /usr/bin/apt-get upgrade -s,\ + /usr/bin/cut,\ + /sbin/iptables -nL,\ + /sbin/iptables -nL *,\ + /sbin/sysctl net.*,\ + /sbin/sysctl fs.*,\ + /sbin/sysctl kernel.*,\ + /sbin/sysctl -a,\ + /bin/dmesg "",\ + /bin/netstat + +cisharden ALL = (root) NOPASSWD: SCL_CMD diff --git a/lib/main.sh b/lib/main.sh index 3d21668..38d8e5e 100644 --- a/lib/main.sh +++ b/lib/main.sh @@ -4,6 +4,7 @@ SCRIPT_NAME=${LONG_SCRIPT_NAME%.sh} CRITICAL_ERRORS_NUMBER=0 # This will be used to see if a script failed, or passed status="" forcedstatus="" +SUDO_CMD="" [ -r $CIS_ROOT_DIR/lib/constants.sh ] && . $CIS_ROOT_DIR/lib/constants.sh [ -r $CIS_ROOT_DIR/etc/hardening.cfg ] && . $CIS_ROOT_DIR/etc/hardening.cfg @@ -31,6 +32,9 @@ while [[ $# > 0 ]]; do info "Audit argument passed but script is disabled" fi ;; + --sudo) + SUDO_CMD="sudo -n" + ;; *) debug "Unknown option passed" ;; diff --git a/lib/utils.sh b/lib/utils.sh index 71278aa..ab843fb 100644 --- a/lib/utils.sh +++ b/lib/utils.sh @@ -8,7 +8,7 @@ has_sysctl_param_expected_result() { local SYSCTL_PARAM=$1 local EXP_RESULT=$2 - if [ "$(sysctl $SYSCTL_PARAM 2>/dev/null)" = "$SYSCTL_PARAM = $EXP_RESULT" ]; then + if [ "$($SUDO_CMD sysctl $SYSCTL_PARAM 2>/dev/null)" = "$SYSCTL_PARAM = $EXP_RESULT" ]; then FNRET=0 elif [ $? = 255 ]; then debug "$SYSCTL_PARAM does not exist" @@ -21,7 +21,7 @@ has_sysctl_param_expected_result() { does_sysctl_param_exists() { local SYSCTL_PARAM=$1 - if [ "$(sysctl -a 2>/dev/null |grep "$SYSCTL_PARAM" -c)" = 0 ]; then + if [ "$($SUDO_CMD sysctl -a 2>/dev/null |grep "$SYSCTL_PARAM" -c)" = 0 ]; then FNRET=1 else FNRET=0 @@ -50,7 +50,7 @@ set_sysctl_param() { does_pattern_exist_in_dmesg() { local PATTERN=$1 - if $(dmesg | grep -qE "$PATTERN"); then + if $($SUDO_CMD dmesg | grep -qE "$PATTERN"); then FNRET=0 else FNRET=1 @@ -63,7 +63,7 @@ does_pattern_exist_in_dmesg() { does_file_exist() { local FILE=$1 - if [ -e $FILE ]; then + if $SUDO_CMD [ -e $FILE ]; then FNRET=0 else FNRET=1 @@ -76,8 +76,8 @@ has_file_correct_ownership() { local GROUP=$3 local USERID=$(id -u $USER) local GROUPID=$(getent group $GROUP | cut -d: -f3) - debug "stat -c '%u %g' $FILE" - if [ "$(stat -c "%u %g" $FILE)" = "$USERID $GROUPID" ]; then + debug "$SUDO_CMD stat -c '%u %g' $FILE" + if [ "$($SUDO_CMD stat -c "%u %g" $FILE)" = "$USERID $GROUPID" ]; then FNRET=0 else FNRET=1 @@ -88,7 +88,7 @@ has_file_correct_permissions() { local FILE=$1 local PERMISSIONS=$2 - if [ $(stat -L -c "%a" $1) = "$PERMISSIONS" ]; then + if [ $($SUDO_CMD stat -L -c "%a" $1) = "$PERMISSIONS" ]; then FNRET=0 else FNRET=1 @@ -100,9 +100,9 @@ does_pattern_exist_in_file() { local PATTERN=$2 debug "Checking if $PATTERN is present in $FILE" - if [ -r "$FILE" ] ; then - debug "grep -qE -- '$PATTERN' $FILE" - if $(grep -qE -- "$PATTERN" $FILE); then + if $SUDO_CMD [ -r "$FILE" ] ; then + debug "$SUDO_CMD grep -qE -- '$PATTERN' $FILE" + if $($SUDO_CMD grep -qE -- "$PATTERN" $FILE); then FNRET=0 else FNRET=1 @@ -189,7 +189,7 @@ does_group_exist() { is_service_enabled() { local SERVICE=$1 - if [ $(find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then + if [ $($SUDO_CMD find /etc/rc?.d/ -name "S*$SERVICE" -print | wc -l) -gt 0 ]; then debug "Service $SERVICE is enabled" FNRET=0 else @@ -209,10 +209,10 @@ is_kernel_option_enabled() { if [ $# -ge 2 ] ; then MODULE_NAME="$2" fi - if [ -r "/proc/config.gz" ] ; then - RESULT=$(zgrep "^$KERNEL_OPTION=" /proc/config.gz) || : - elif [ -r "/boot/config-$(uname -r)" ] ; then - RESULT=$(grep "^$KERNEL_OPTION=" "/boot/config-$(uname -r)") || : + if $SUDO_CMD [ -r "/proc/config.gz" ] ; then + RESULT=$($SUDO_CMD zgrep "^$KERNEL_OPTION=" /proc/config.gz) || : + elif $SUDO_CMD [ -r "/boot/config-$(uname -r)" ] ; then + RESULT=$($SUDO_CMD grep "^$KERNEL_OPTION=" "/boot/config-$(uname -r)") || : fi ANSWER=$(cut -d = -f 2 <<< "$RESULT") if [ "x$ANSWER" = "xy" ]; then @@ -226,13 +226,13 @@ is_kernel_option_enabled() { FNRET=2 # Not found fi - if [ "$FNRET" -ne 0 -a -n "$MODULE_NAME" -a -d "/lib/modules/$(uname -r)" ] ; then + if $SUDO_CMD [ "$FNRET" -ne 0 -a -n "$MODULE_NAME" -a -d "/lib/modules/$(uname -r)" ] ; then # also check in modules, because even if not =y, maybe # the admin compiled it separately later (or out-of-tree) # as a module (regardless of the fact that we have =m or not) debug "Checking if we have $MODULE_NAME.ko" - local modulefile=$(find "/lib/modules/$(uname -r)/" -type f -name "$MODULE_NAME.ko") - if [ -n "$modulefile" ] ; then + local modulefile=$($SUDO_CMD find "/lib/modules/$(uname -r)/" -type f -name "$MODULE_NAME.ko") + if $SUDO_CMD [ -n "$modulefile" ] ; then debug "We do have $modulefile!" # ... but wait, maybe it's blacklisted? check files in /etc/modprobe.d/ for "blacklist xyz" if grep -qRE "^\s*blacklist\s+$MODULE_NAME\s*$" /etc/modprobe.d/ ; then @@ -334,10 +334,10 @@ apt_update_if_needed() if [ $UPDATE_AGE -gt 21600 ] then # update too old, refresh database - apt-get update -y >/dev/null 2>/dev/null + $SUDO_CMD apt-get update -y >/dev/null 2>/dev/null fi else - apt-get update -y >/dev/null 2>/dev/null + $SUDO_CMD apt-get update -y >/dev/null 2>/dev/null fi } @@ -345,7 +345,7 @@ apt_check_updates() { local NAME="$1" local DETAILS="/dev/shm/${NAME}" - apt-get upgrade -s 2>/dev/null | grep -E "^Inst" > $DETAILS || : + $SUDO_CMD apt-get upgrade -s 2>/dev/null | grep -E "^Inst" > $DETAILS || : local COUNT=$(wc -l < "$DETAILS") FNRET=128 # Unknown function return result RESULT="" # Result output for upgrade