contains 101 rules |
System SettingsgroupContains rules that check correct system settings. |
contains 91 rules |
Installing and Maintaining SoftwaregroupThe following sections contain information on
security-relevant choices during the initial operating system
installation process and the setup of software
updates. |
contains 14 rules |
System and Software IntegritygroupSystem and software integrity can be gained by installing antivirus, increasing
system encryption strength with FIPS, verifying installed software, enabling SELinux,
installing an Intrusion Prevention System, etc. However, installing or enabling integrity
checking tools cannot prevent intrusions, but they can detect that an intrusion
may have occurred. Requirements for integrity checking may be highly dependent on
the environment in which the system will be used. Snapshot-based approaches such
as AIDE may induce considerable overhead in the presence of frequent software updates. |
contains 6 rules |
Software Integrity CheckinggroupBoth the AIDE (Advanced Intrusion Detection Environment)
software and the RPM package management system provide
mechanisms for verifying the integrity of installed software.
AIDE uses snapshots of file metadata (such as hashes) and compares these
to current system files in order to detect changes.
The RPM package management system can conduct integrity
checks by comparing information in its metadata database with
files installed on the system. |
contains 5 rules |
Verify Integrity with RPMgroupThe RPM package management system includes the ability
to verify the integrity of installed packages by comparing the
installed files with information about the files taken from the
package metadata stored in the RPM database. Although an attacker
could corrupt the RPM database (analogous to attacking the AIDE
database as described above), this check can still reveal
modification of important files. To list which files on the system differ from what is expected by the RPM database:
$ rpm -qVa
See the man page for rpm to see a complete explanation of each column. |
contains 2 rules |
Verify File Hashes with RPMruleWithout cryptographic integrity protections, system
executables and files can be altered by unauthorized users without
detection.
The RPM package management system can check the hashes of
installed software packages, including many that are important to system
security.
To verify that the cryptographic hash of system files and commands matches vendor
values, run the following command to list which files on the system
have hashes that differ from what is expected by the RPM database:
$ rpm -Va --noconfig | grep '^..5'
A "c" in the second column indicates that a file is a configuration file, which
may appropriately be expected to change. If the file was not expected to
change, investigate the cause of the change using audit logs or other means.
The package can then be reinstalled to restore the file.
Run the following command to determine which package owns the file:
$ rpm -qf FILENAME
The package can be reinstalled from a yum repository using the command:
$ sudo yum reinstall PACKAGENAME
Alternatively, the package can be reinstalled from trusted media using the command:
$ sudo rpm -Uvh PACKAGENAME Rationale:The hashes of important files like system executables should match the
information given by the RPM database. Executables with erroneous hashes could
be a sign of nefarious activity on the system. identifiers:
CCE-27157-7 references:
1.2.6, 11, 2, 3, 9, 5.10.4.1, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, 3.3.8, 3.4.1, CCI-000366, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, CM-6(d), CM-6(c), SI-7, SI-7(1), SI-7(6), AU-9(3), PR.DS-6, PR.DS-8, PR.IP-1, Req-11.5, SRG-OS-000480-GPOS-00227, RHEL-07-010020, SV-214799r603261_rule Remediation script:
# Find which files have incorrect hash (not in /etc, because of the system related config files) and then get files names
files_with_incorrect_hash="$(rpm -Va --noconfig | grep -E '^..5' | awk '{print $NF}' )"
# From files names get package names and change newline to space, because rpm writes each package to new line
packages_to_reinstall="$(rpm -qf $files_with_incorrect_hash | tr '\n' ' ')"
yum reinstall -y $packages_to_reinstall
Remediation script:- name: 'Set fact: Package manager reinstall command (dnf)'
set_fact:
package_manager_reinstall_cmd: dnf reinstall -y
when: ansible_distribution == "Fedora"
tags:
- CCE-27157-7
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010020
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_hashes
- name: 'Set fact: Package manager reinstall command (yum)'
set_fact:
package_manager_reinstall_cmd: yum reinstall -y
when: (ansible_distribution == "RedHat" or ansible_distribution == "OracleLinux")
tags:
- CCE-27157-7
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010020
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_hashes
- name: Read files with incorrect hash
command: rpm -Va --nodeps --nosize --nomtime --nordev --nocaps --nolinkto --nouser
--nogroup --nomode --noghost --noconfig
args:
warn: false
register: files_with_incorrect_hash
changed_when: false
failed_when: files_with_incorrect_hash.rc > 1
check_mode: false
when: (package_manager_reinstall_cmd is defined)
tags:
- CCE-27157-7
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010020
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_hashes
- name: Create list of packages
command: rpm -qf "{{ item }}"
args:
warn: false
with_items: '{{ files_with_incorrect_hash.stdout_lines | map(''regex_findall'',
''^[.]+[5]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'')
| list | unique }}'
register: list_of_packages
changed_when: false
check_mode: false
when:
- files_with_incorrect_hash.stdout_lines is defined
- (files_with_incorrect_hash.stdout_lines | length > 0)
tags:
- CCE-27157-7
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010020
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_hashes
- name: Reinstall packages of files with incorrect hash
command: '{{ package_manager_reinstall_cmd }} ''{{ item }}'''
args:
warn: false
with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list
| unique }}'
when:
- files_with_incorrect_hash.stdout_lines is defined
- (package_manager_reinstall_cmd is defined and (files_with_incorrect_hash.stdout_lines
| length > 0))
tags:
- CCE-27157-7
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010020
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_hashes
|
Verify and Correct File Permissions with RPMruleThe RPM package management system can check file access permissions
of installed software packages, including many that are important
to system security.
Verify that the file permissions of system files
and commands match vendor values. Check the file permissions
with the following command:
$ sudo rpm -Va | awk '{ if (substr($0,2,1)=="M") print $NF }'
Output indicates files that do not match vendor defaults.
After locating a file with incorrect permissions,
run the following command to determine which package owns it:
$ rpm -qf FILENAME
Next, run the following command to reset its permissions to
the correct values:
$ sudo rpm --setperms PACKAGENAME warning
Profiles may require that specific files have stricter file permissions than defined by the
vendor.
Such files will be reported as a finding and need to be evaluated according to your policy
and deployment environment. Rationale:Permissions on system binaries and configuration files that are too generous
could allow an unauthorized user to gain privileges that they should not have.
The permissions set by the vendor should be maintained. Any deviations from
this baseline should be investigated. identifiers:
CCE-27209-6 references:
1.7.1.4, 1.7.1.5, 1.7.1.6, 6.1.1, 6.1.2, 6.1.3, 6.1.4, 6.1.5, 6.1.6, 6.1.7, 6.1.8, 6.1.9, 1, 11, 12, 13, 14, 15, 16, 18, 3, 5, 6, 9, 5.10.4.1, APO01.06, APO11.04, BAI03.05, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.04, DSS05.07, DSS06.02, MEA02.01, 3.3.8, 3.4.1, CCI-001493, CCI-001494, CCI-001495, CCI-001496, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.2, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.5.1, A.12.6.2, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(d), CM-6(c), SI-7, SI-7(1), SI-7(6), AU-9(3), CM-6(a), PR.AC-4, PR.DS-5, PR.IP-1, PR.PT-1, Req-11.5, SRG-OS-000256-GPOS-00097, SRG-OS-000257-GPOS-00098, SRG-OS-000258-GPOS-00099, SRG-OS-000278-GPOS-00108, RHEL-07-010010, SV-204392r603261_rule Remediation script:
# Declare array to hold set of RPM packages we need to correct permissions for
declare -A SETPERMS_RPM_DICT
# Create a list of files on the system having permissions different from what
# is expected by the RPM database
readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,2,1)=="M") print $NF }')
for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}"
do
# NOTE: some files maybe controlled by more then one package
readarray -t RPM_PACKAGES < <(rpm -qf "${FILE_PATH}")
for RPM_PACKAGE in "${RPM_PACKAGES[@]}"
do
# Use an associative array to store packages as it's keys, not having to care about duplicates.
SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1
done
done
# For each of the RPM packages left in the list -- reset its permissions to the
# correct values
for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}"
do
rpm --restore "${RPM_PACKAGE}"
done
Remediation script:- name: Read list of files with incorrect permissions
command: rpm -Va --nodeps --nosignature --nofiledigest --nosize --nomtime --nordev
--nocaps --nolinkto --nouser --nogroup
args:
warn: false
register: files_with_incorrect_permissions
failed_when: files_with_incorrect_permissions.rc > 1
changed_when: false
check_mode: false
tags:
- CCE-27209-6
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010010
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_permissions
- name: Create list of packages
command: rpm -qf "{{ item }}"
args:
warn: false
with_items: '{{ files_with_incorrect_permissions.stdout_lines | map(''regex_findall'',
''^[.]+[M]+.* (\/.*)'', ''\1'') | map(''join'') | select(''match'', ''(\/.*)'')
| list | unique }}'
register: list_of_packages
changed_when: false
check_mode: false
when: (files_with_incorrect_permissions.stdout_lines | length > 0)
tags:
- CCE-27209-6
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010010
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_permissions
- name: Correct file permissions with RPM
command: rpm --setperms '{{ item }}'
args:
warn: false
with_items: '{{ list_of_packages.results | map(attribute=''stdout_lines'') | list
| unique }}'
when: (files_with_incorrect_permissions.stdout_lines | length > 0)
tags:
- CCE-27209-6
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-010010
- NIST-800-171-3.3.8
- NIST-800-171-3.4.1
- NIST-800-53-AU-9(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-6(c)
- NIST-800-53-CM-6(d)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- NIST-800-53-SI-7(6)
- PCI-DSS-Req-11.5
- high_complexity
- high_severity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- rpm_verify_permissions
|
Verify Integrity with AIDEgroupAIDE conducts integrity checks by comparing information about
files with previously-gathered information. Ideally, the AIDE database is
created immediately after initial system configuration, and then again after any
software update. AIDE is highly configurable, with further configuration
information located in /usr/share/doc/aide-VERSION . |
contains 3 rules |
Install AIDEruleThe aide package can be installed with the following command:
$ sudo yum install aide Rationale:The AIDE package must be installed if it is to be available for integrity checking. identifiers:
CCE-27096-7 references:
BP28(R51), 1.3.1, 1, 11, 12, 13, 14, 15, 16, 2, 3, 5, 7, 8, 9, 5.10.1.3, APO01.06, BAI01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS04.07, DSS05.02, DSS05.03, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 6.2, SR 7.6, 1034, 1288, 1341, 1417, A.11.2.4, A.12.1.2, A.12.2.1, A.12.4.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1, A.8.2.3, CM-6(a), DE.CM-1, DE.CM-7, PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, PR.IP-3, Req-11.5, SRG-OS-000363-GPOS-00150 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if ! rpm -q --quiet "aide" ; then
yum install -y "aide"
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure aide is installed
package:
name: aide
state: present
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27096-7
- CJIS-5.10.1.3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-11.5
- enable_strategy
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- package_aide_installed
Remediation script:include install_aide
class install_aide {
package { 'aide':
ensure => 'installed',
}
}
Remediation script:
package --add=aide
|
Configure Periodic Execution of AIDEruleAt a minimum, AIDE should be configured to run a weekly scan.
To implement a daily execution of AIDE at 4:05am using cron, add the following line to /etc/crontab :
05 4 * * * root /usr/sbin/aide --check
To implement a weekly execution of AIDE at 4:05am using cron, add the following line to /etc/crontab :
05 4 * * 0 root /usr/sbin/aide --check
AIDE can be executed periodically through other means; this is merely one example.
The usage of cron's special time codes, such as @daily and
@weekly is acceptable.Rationale:By default, AIDE does not install itself for periodic execution. Periodically
running AIDE is necessary to reveal unexpected changes in installed files.
Unauthorized changes to the baseline configuration could make the system vulnerable
to various attacks or allow unauthorized access to the operating system. Changes to
operating system configurations can have unintended side effects, some of which may
be relevant to security.
Detecting such changes and providing an automated response can help avoid unintended,
negative consequences that could ultimately affect the security state of the operating
system. The operating system's Information Management Officer (IMO)/Information System
Security Officer (ISSO) and System Administrators (SAs) must be notified via email and/or
monitoring system trap when there is an unauthorized modification of a configuration item. identifiers:
CCE-26952-2 references:
BP28(R51), 1.3.2, 1, 11, 12, 13, 14, 15, 16, 2, 3, 5, 7, 8, 9, 5.10.1.3, APO01.06, BAI01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS04.07, DSS05.02, DSS05.03, DSS05.05, DSS05.07, DSS06.02, DSS06.06, CCI-001744, CCI-002699, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 6.2, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.4.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1, A.8.2.3, SI-7, SI-7(1), CM-6(a), DE.CM-1, DE.CM-7, PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, PR.IP-3, Req-11.5, SRG-OS-000363-GPOS-00150, SRG-OS-000446-GPOS-00200, RHEL-07-020030, SV-204445r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if ! rpm -q --quiet "aide" ; then
yum install -y "aide"
fi
if ! grep -q "/usr/sbin/aide --check" /etc/crontab ; then
echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab
else
sed -i '/^.*\/usr\/sbin\/aide --check.*$/d' /etc/crontab
echo "05 4 * * * root /usr/sbin/aide --check" >> /etc/crontab
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure AIDE is installed
package:
name: '{{ item }}'
state: present
with_items:
- aide
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-26952-2
- CJIS-5.10.1.3
- DISA-STIG-RHEL-07-020030
- NIST-800-53-CM-6(a)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- PCI-DSS-Req-11.5
- aide_periodic_cron_checking
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Configure Periodic Execution of AIDE
cron:
name: run AIDE check
minute: 5
hour: 4
weekday: 0
user: root
job: /usr/sbin/aide --check
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-26952-2
- CJIS-5.10.1.3
- DISA-STIG-RHEL-07-020030
- NIST-800-53-CM-6(a)
- NIST-800-53-SI-7
- NIST-800-53-SI-7(1)
- PCI-DSS-Req-11.5
- aide_periodic_cron_checking
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Build and Test AIDE DatabaseruleRun the following command to generate a new database:
$ sudo /usr/sbin/aide --init
By default, the database will be written to the file /var/lib/aide/aide.db.new.gz .
Storing the database, the configuration file /etc/aide.conf , and the binary
/usr/sbin/aide (or hashes of these files), in a secure location (such as on read-only media) provides additional assurance about their integrity.
The newly-generated database can be installed as follows:
$ sudo cp /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
To initiate a manual check, run the following command:
$ sudo /usr/sbin/aide --check
If this check produces any unexpected output, investigate.Rationale:For AIDE to be effective, an initial database of "known-good" information about files
must be captured and it should be able to be verified against the installed files. identifiers:
CCE-27220-3 references:
BP28(R51), 1, 11, 12, 13, 14, 15, 16, 2, 3, 5, 7, 8, 9, 5.10.1.3, APO01.06, BAI01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS04.07, DSS05.02, DSS05.03, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 6.2, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.4.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.14.2.7, A.15.2.1, A.8.2.3, CM-6(a), DE.CM-1, DE.CM-7, PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, PR.IP-3, Req-11.5 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if ! rpm -q --quiet "aide" ; then
yum install -y "aide"
fi
/usr/sbin/aide --init
/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure AIDE is installed
package:
name: '{{ item }}'
state: present
with_items:
- aide
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27220-3
- CJIS-5.10.1.3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-11.5
- aide_build_database
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Build and Test AIDE Database
command: /usr/sbin/aide --init
changed_when: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27220-3
- CJIS-5.10.1.3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-11.5
- aide_build_database
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check whether the stock AIDE Database exists
stat:
path: /var/lib/aide/aide.db.new.gz
register: aide_database_stat
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27220-3
- CJIS-5.10.1.3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-11.5
- aide_build_database
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Stage AIDE Database
copy:
src: /var/lib/aide/aide.db.new.gz
dest: /var/lib/aide/aide.db.gz
backup: true
remote_src: true
when:
- (aide_database_stat.stat.exists is defined and aide_database_stat.stat.exists)
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27220-3
- CJIS-5.10.1.3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-11.5
- aide_build_database
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Disable PrelinkingruleThe prelinking feature changes binaries in an attempt to decrease their startup
time. In order to disable it, change or add the following line inside the file
/etc/sysconfig/prelink :
PRELINKING=no
Next, run the following command to return binaries to a normal, non-prelinked state:
$ sudo /usr/sbin/prelink -ua Rationale:Because the prelinking feature changes binaries, it can interfere with the
operation of certain software and/or modes such as AIDE, FIPS, etc. identifiers:
CCE-27078-5 references:
1.5.4, 11, 13, 14, 2, 3, 9, 5.10.1.3, APO01.06, BAI02.01, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS04.07, DSS05.03, DSS06.02, DSS06.06, 3.13.11, CCI-000803, CCI-002450, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 4.1, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.8.2.3, SC-13, CM-6(a), PR.DS-1, PR.DS-6, PR.DS-8, PR.IP-1, Req-11.5, SRG-OS-000120-VMM-000600, SRG-OS-000478-VMM-001980, SRG-OS-000396-VMM-001590 Remediation script:# prelink not installed
if test -e /etc/sysconfig/prelink -o -e /usr/sbin/prelink; then
if grep -q ^PRELINKING /etc/sysconfig/prelink
then
sed -i 's/^PRELINKING[:blank:]*=[:blank:]*[:alpha:]*/PRELINKING=no/' /etc/sysconfig/prelink
else
printf '\n' >> /etc/sysconfig/prelink
printf '%s\n' '# Set PRELINKING=no per security requirements' 'PRELINKING=no' >> /etc/sysconfig/prelink
fi
# Undo previous prelink changes to binaries if prelink is available.
if test -x /usr/sbin/prelink; then
/usr/sbin/prelink -ua
fi
fi
Remediation script:- name: Does prelink file exist
stat:
path: /etc/sysconfig/prelink
register: prelink_exists
tags:
- CCE-27078-5
- CJIS-5.10.1.3
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-13
- PCI-DSS-Req-11.5
- disable_prelink
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: disable prelinking
lineinfile:
path: /etc/sysconfig/prelink
regexp: ^PRELINKING=
line: PRELINKING=no
when: prelink_exists.stat.exists
tags:
- CCE-27078-5
- CJIS-5.10.1.3
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-13
- PCI-DSS-Req-11.5
- disable_prelink
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
GNOME Desktop EnvironmentgroupGNOME is a graphical desktop environment bundled with many Linux distributions that
allow users to easily interact with the operating system graphically rather than
textually. The GNOME Graphical Display Manager (GDM) provides login, logout, and user
switching contexts as well as display server management.
GNOME is developed by the GNOME Project and is considered the default
Red Hat Graphical environment.
For more information on GNOME and the GNOME Project, see https://www.gnome.org. |
contains 4 rules |
Configure GNOME Screen LockinggroupIn the default GNOME3 desktop, the screen can be locked
by selecting the user name in the far right corner of the main panel and
selecting Lock.
The following sections detail commands to enforce idle activation of the screensaver,
screen locking, a blank-screen screensaver, and an idle activation time.
Because users should be trained to lock the screen when they
step away from the computer, the automatic locking feature is only
meant as a backup.
The root account can be screen-locked; however, the root account should
never be used to log into an X Windows environment and should only
be used to for direct login via console in emergency circumstances.
For more information about enforcing preferences in the GNOME3 environment using the DConf
configuration system, see http://wiki.gnome.org/dconf and
the man page dconf(1) . |
contains 4 rules |
Enable GNOME3 Screensaver Idle ActivationruleTo activate the screensaver in the GNOME3 desktop after a period of inactivity,
add or set idle-activation-enabled to true in
/etc/dconf/db/local.d/00-security-settings . For example:
[org/gnome/desktop/screensaver]
idle-activation-enabled=true
Once the setting has been added, add a lock to
/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification.
For example:
/org/gnome/desktop/screensaver/idle-activation-enabled
After the settings have been set, run dconf update .Rationale:A session time-out lock is a temporary action taken when a user stops work and moves away from the immediate
physical vicinity of the information system but does not logout because of the temporary nature of the absence.
Rather than relying on the user to manually lock their operating system session prior to vacating the vicinity,
GNOME desktops can be configured to identify when a user's session has idled and take action to initiate the
session lock.
Enabling idle activation of the screensaver ensures the screensaver will
be activated after the idle delay. Applications requiring continuous,
real-time screen display (such as network management products) require the
login session does not have administrator rights and the display station is located in a
controlled-access area. identifiers:
CCE-80111-8 references:
1, 12, 15, 16, 5.5.5, DSS05.04, DSS05.10, DSS06.10, 3.1.10, CCI-000057, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, CM-6(a), AC-11(a), PR.AC-7, FMT_MOF_EXT.1, Req-8.1.8, SRG-OS-000029-GPOS-00010, RHEL-07-010100, SV-204402r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"
mkdir -p "${DBDIR}"
if [ "${#SETTINGSFILES[@]}" -eq 0 ]
then
[ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE}
printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
printf '%s=%s\n' "idle-activation-enabled" "true" >> ${DCONFFILE}
else
escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*idle-activation-enabled\\s*=" "${SETTINGSFILES[@]}"
then
sed -i "s/\\s*idle-activation-enabled\\s*=\\s*.*/idle-activation-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}"
else
sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\idle-activation-enabled=${escaped_value}" "${SETTINGSFILES[@]}"
fi
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"
mkdir -p "${LOCKSFOLDER}"
if [[ -z "${LOCKFILES}" ]]
then
echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-80111-8
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010100
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_activation_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Enable GNOME3 Screensaver Idle Activation
ini_file:
dest: /etc/dconf/db/local.d/00-security-settings
section: org/gnome/desktop/screensaver
option: idle_activation_enabled
value: 'true'
create: true
no_extra_spaces: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80111-8
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010100
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_activation_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Prevent user modification of GNOME idle_activation_enabled
lineinfile:
path: /etc/dconf/db/local.d/locks/00-security-settings-lock
regexp: ^/org/gnome/desktop/screensaver/idle-activation-enabled
line: /org/gnome/desktop/screensaver/idle-activation-enabled
create: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80111-8
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010100
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_activation_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Dconf Update
command: dconf update
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80111-8
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010100
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_activation_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
|
Set GNOME3 Screensaver Inactivity TimeoutruleThe idle time-out value for inactivity in the GNOME3 desktop is configured via the idle-delay
setting must be set under an appropriate configuration file(s) in the /etc/dconf/db/local.d directory
and locked in /etc/dconf/db/local.d/locks directory to prevent user modification.
For example, to configure the system for a 15 minute delay, add the following to
/etc/dconf/db/local.d/00-security-settings :
[org/gnome/desktop/session]
idle-delay=uint32 900
Once the setting has been added, add a lock to
/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification.
For example:
/org/gnome/desktop/session/idle-delay
After the settings have been set, run dconf update .Rationale:A session time-out lock is a temporary action taken when a user stops work and moves away from
the immediate physical vicinity of the information system but does not logout because of the
temporary nature of the absence. Rather than relying on the user to manually lock their operating
system session prior to vacating the vicinity, GNOME3 can be configured to identify when
a user's session has idled and take action to initiate a session lock. identifiers:
CCE-80110-0 references:
1, 12, 15, 16, 5.5.5, DSS05.04, DSS05.10, DSS06.10, 3.1.10, CCI-000057, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, AC-11(a), CM-6(a), PR.AC-7, FMT_MOF_EXT.1, Req-8.1.8, SRG-OS-000029-GPOS-00010, RHEL-07-010070, SV-204398r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
inactivity_timeout_value="1800"
# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"
mkdir -p "${DBDIR}"
if [ "${#SETTINGSFILES[@]}" -eq 0 ]
then
[ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE}
printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE}
printf '%s=%s\n' "idle-delay" "uint32 ${inactivity_timeout_value}" >> ${DCONFFILE}
else
escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")"
if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}"
then
sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${SETTINGSFILES[@]}"
else
sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${SETTINGSFILES[@]}"
fi
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"
mkdir -p "${LOCKSFOLDER}"
if [[ -z "${LOCKFILES}" ]]
then
echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-80110-0
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010070
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_delay
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: XCCDF Value inactivity_timeout_value # promote to variable
set_fact:
inactivity_timeout_value: !!str 1800
tags:
- always
- name: Set GNOME3 Screensaver Inactivity Timeout
ini_file:
dest: /etc/dconf/db/local.d/00-security-settings
section: org/gnome/desktop/screensaver
option: idle-delay
value: '{{ inactivity_timeout_value }}'
create: true
no_extra_spaces: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80110-0
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010070
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_delay
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Prevent user modification of GNOME idle-delay
lineinfile:
path: /etc/dconf/db/local.d/locks/00-security-settings-lock
regexp: ^/org/gnome/desktop/screensaver/idle-delay
line: /org/gnome/desktop/screensaver/idle-delay
create: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80110-0
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010070
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_delay
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Dconf Update
command: dconf update
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80110-0
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010070
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(a)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_idle_delay
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
|
Enable GNOME3 Screensaver Lock After Idle PeriodruleTo activate locking of the screensaver in the GNOME3 desktop when it is activated,
add or set lock-enabled to true in
/etc/dconf/db/local.d/00-security-settings . For example:
[org/gnome/desktop/screensaver]
lock-enabled=true
Once the settings have been added, add a lock to
/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification.
For example:
/org/gnome/desktop/screensaver/lock-enabled
After the settings have been set, run dconf update .Rationale:A session lock is a temporary action taken when a user stops work and moves away from the immediate physical vicinity
of the information system but does not want to logout because of the temporary nature of the absense. identifiers:
CCE-80112-6 references:
1, 12, 15, 16, 5.5.5, DSS05.04, DSS05.10, DSS06.10, 3.1.10, CCI-000056, CCI-000058, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, CM-6(a), PR.AC-7, FMT_MOF_EXT.1, Req-8.1.8, SRG-OS-000028-GPOS-00009, SRG-OS-000030-GPOS-00011, RHEL-07-010060, SV-204396r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"
mkdir -p "${DBDIR}"
if [ "${#SETTINGSFILES[@]}" -eq 0 ]
then
[ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE}
printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
printf '%s=%s\n' "lock-enabled" "true" >> ${DCONFFILE}
else
escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")"
if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}"
then
sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${SETTINGSFILES[@]}"
else
sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${SETTINGSFILES[@]}"
fi
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"
mkdir -p "${LOCKSFOLDER}"
if [[ -z "${LOCKFILES}" ]]
then
echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-80112-6
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010060
- NIST-800-171-3.1.10
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_lock_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Enable GNOME3 Screensaver Lock After Idle Period
ini_file:
dest: /etc/dconf/db/local.d/00-security-settings
section: org/gnome/desktop/screensaver
option: lock-enabled
value: 'true'
create: true
no_extra_spaces: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80112-6
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010060
- NIST-800-171-3.1.10
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_lock_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Prevent user modification of GNOME lock-enabled
lineinfile:
path: /etc/dconf/db/local.d/locks/00-security-settings-lock
regexp: ^/org/gnome/desktop/screensaver/lock-enabled
line: /org/gnome/desktop/screensaver/lock-enabled
create: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80112-6
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010060
- NIST-800-171-3.1.10
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_lock_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Dconf Update
command: dconf update
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80112-6
- CJIS-5.5.5
- DISA-STIG-RHEL-07-010060
- NIST-800-171-3.1.10
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_lock_enabled
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
|
Implement Blank ScreensaverruleTo set the screensaver mode in the GNOME3 desktop to a blank screen,
add or set picture-uri to string '' in
/etc/dconf/db/local.d/00-security-settings . For example:
[org/gnome/desktop/screensaver]
picture-uri=''
Once the settings have been added, add a lock to
/etc/dconf/db/local.d/locks/00-security-settings-lock to prevent user modification.
For example:
/org/gnome/desktop/screensaver/picture-uri
After the settings have been set, run dconf update .Rationale:Setting the screensaver mode to blank-only conceals the
contents of the display from passersby. identifiers:
CCE-80113-4 references:
1, 12, 15, 16, 5.5.5, DSS05.04, DSS05.10, DSS06.10, 3.1.10, CCI-000060, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, AC-11(1), CM-6(a), PR.AC-7, FMT_MOF_EXT.1, Req-8.1.8 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q gdm && [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Check for setting in any of the DConf db directories
# If files contain ibus or distro, ignore them.
# The assignment assumes that individual filenames don't contain :
readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
DCONFFILE="/etc/dconf/db/local.d/00-security-settings"
DBDIR="/etc/dconf/db/local.d"
mkdir -p "${DBDIR}"
if [ "${#SETTINGSFILES[@]}" -eq 0 ]
then
[ ! -z ${DCONFFILE} ] || echo "" >> ${DCONFFILE}
printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE}
printf '%s=%s\n' "picture-uri" "string ''" >> ${DCONFFILE}
else
escaped_value="$(sed -e 's/\\/\\\\/g' <<< "string ''")"
if grep -q "^\\s*picture-uri\\s*=" "${SETTINGSFILES[@]}"
then
sed -i "s/\\s*picture-uri\\s*=\\s*.*/picture-uri=${escaped_value}/g" "${SETTINGSFILES[@]}"
else
sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\picture-uri=${escaped_value}" "${SETTINGSFILES[@]}"
fi
fi
dconf update
# Check for setting in any of the DConf db directories
LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/picture-uri$" "/etc/dconf/db/" | grep -v 'distro\|ibus' | cut -d":" -f1)
LOCKSFOLDER="/etc/dconf/db/local.d/locks"
mkdir -p "${LOCKSFOLDER}"
if [[ -z "${LOCKFILES}" ]]
then
echo "/org/gnome/desktop/screensaver/picture-uri" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock"
fi
dconf update
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-80113-4
- CJIS-5.5.5
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_mode_blank
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Implement Blank Screensaver
ini_file:
dest: /etc/dconf/db/local.d/00-security-settings
section: org/gnome/desktop/screensaver
option: picture-uri
value: string ''
create: true
no_extra_spaces: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80113-4
- CJIS-5.5.5
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_mode_blank
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Prevent user modification of GNOME picture-uri
lineinfile:
path: /etc/dconf/db/local.d/locks/00-security-settings-lock
regexp: ^/org/gnome/desktop/screensaver/picture-uri
line: /org/gnome/desktop/screensaver/picture-uri
create: true
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80113-4
- CJIS-5.5.5
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_mode_blank
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
- name: Dconf Update
command: dconf update
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"gdm" in ansible_facts.packages'
tags:
- CCE-80113-4
- CJIS-5.5.5
- NIST-800-171-3.1.10
- NIST-800-53-AC-11(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.1.8
- dconf_gnome_screensaver_mode_blank
- low_complexity
- medium_disruption
- medium_severity
- no_reboot_needed
- unknown_strategy
|
Updating SoftwaregroupThe yum command line tool is used to install and
update software packages. The system also provides a graphical
software update tool in the System menu, in the Administration submenu,
called Software Update.
Red Hat Enterprise Linux 7 systems contain an installed software catalog called
the RPM database, which records metadata of installed packages. Consistently using
yum or the graphical Software Update for all software installation
allows for insight into the current inventory of installed software on the system.
|
contains 4 rules |
Ensure Red Hat GPG Key InstalledruleTo ensure the system can cryptographically verify base software packages
come from Red Hat (and to connect to the Red Hat Network to receive them),
the Red Hat GPG key must properly be installed. To install the Red Hat GPG
key, run:
$ sudo subscription-manager register
If the system is not connected to the Internet or an RHN Satellite, then
install the Red Hat GPG key from trusted media such as the Red Hat
installation CD-ROM or DVD. Assuming the disc is mounted in
/media/cdrom , use the following command as the root user to import
it into the keyring:
$ sudo rpm --import /media/cdrom/RPM-GPG-KEY
Alternatively, the key may be pre-loaded during the RHEL installation. In
such cases, the key can be installed by running the following command:
sudo rpm --import /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release Rationale:Changes to software components can have significant effects on the overall
security of the operating system. This requirement ensures the software has
not been tampered with and that it has been provided by a trusted vendor.
The Red Hat GPG key is necessary to cryptographically verify packages are
from Red Hat. identifiers:
CCE-26957-1 references:
BP28(R15), 1.2.3, 11, 2, 3, 9, 5.10.4.1, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650 Remediation script:# The two fingerprints below are retrieved from https://access.redhat.com/security/team/key
readonly REDHAT_RELEASE_FINGERPRINT="567E347AD0044ADE55BA8A5F199E2F91FD431D51"
readonly REDHAT_AUXILIARY_FINGERPRINT="43A6E49C4A38F4BE9ABF2A5345689C882FA658E0"
# Location of the key we would like to import (once it's integrity verified)
readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")")
# Verify /etc/pki/rpm-gpg directory permissions are safe
if [ "${RPM_GPG_DIR_PERMS}" -le "755" ]
then
# If they are safe, try to obtain fingerprints from the key file
# (to ensure there won't be e.g. CRC error).
readarray -t GPG_OUT < <(gpg --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep "^fpr" | cut -d ":" -f 10)
GPG_RESULT=$?
# No CRC error, safe to proceed
if [ "${GPG_RESULT}" -eq "0" ]
then
echo "${GPG_OUT[*]}" | grep -vE "${REDHAT_RELEASE_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || {
# If $REDHAT_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it
rpm --import "${REDHAT_RELEASE_KEY}"
}
fi
fi
Remediation script:- name: Read permission of GPG key directory
stat:
path: /etc/pki/rpm-gpg/
register: gpg_key_directory_permission
check_mode: false
tags:
- CCE-26957-1
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_redhat_gpgkey_installed
- high_severity
- medium_complexity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- name: Read signatures in GPG key
command: gpg --with-fingerprint --with-colons "/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release"
args:
warn: false
changed_when: false
register: gpg_fingerprints
check_mode: false
tags:
- CCE-26957-1
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_redhat_gpgkey_installed
- high_severity
- medium_complexity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- name: Set Fact - Installed GPG Fingerprints
set_fact:
gpg_installed_fingerprints: |-
{{ gpg_fingerprints.stdout | regex_findall('^pub.*
(?:^fpr[:]*)([0-9A-Fa-f]*)', '\1') | list }}
tags:
- CCE-26957-1
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_redhat_gpgkey_installed
- high_severity
- medium_complexity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- name: Set Fact - Valid fingerprints
set_fact:
gpg_valid_fingerprints: ("567E347AD0044ADE55BA8A5F199E2F91FD431D51" "43A6E49C4A38F4BE9ABF2A5345689C882FA658E0")
tags:
- CCE-26957-1
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_redhat_gpgkey_installed
- high_severity
- medium_complexity
- medium_disruption
- no_reboot_needed
- restrict_strategy
- name: Import RedHat GPG key
rpm_key:
state: present
key: /etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release
when:
- gpg_key_directory_permission.stat.mode <= '0755'
- (gpg_installed_fingerprints | difference(gpg_valid_fingerprints)) | length ==
0
- gpg_installed_fingerprints | length > 0
- ansible_distribution == "RedHat"
tags:
- CCE-26957-1
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_redhat_gpgkey_installed
- high_severity
- medium_complexity
- medium_disruption
- no_reboot_needed
- restrict_strategy
|
Ensure gpgcheck Enabled for All yum Package RepositoriesruleTo ensure signature checking is not disabled for
any repos, remove any lines from files in /etc/yum.repos.d of the form:
gpgcheck=0 Rationale:Verifying the authenticity of the software prior to installation validates
the integrity of the patch or upgrade received from a vendor. This ensures
the software has not been tampered with and that it has been provided by a
trusted vendor. Self-signed certificates are disallowed by this
requirement. Certificates used to verify the software must be from an
approved Certificate Authority (CA)." identifiers:
CCE-26876-3 references:
BP28(R15), 11, 2, 3, 9, 5.10.4.1, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SRG-OS-000366-GPOS-00153, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650 Remediation script:sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/*
Remediation script:- name: Grep for yum repo section names
shell: grep -HEr '^\[.+\]' -r /etc/yum.repos.d/
register: repo_grep_results
ignore_errors: true
changed_when: false
tags:
- CCE-26876-3
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- enable_strategy
- ensure_gpgcheck_never_disabled
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
- name: Set gpgcheck=1 for each yum repo
ini_file:
path: '{{ item[0] }}'
section: '{{ item[1] }}'
option: gpgcheck
value: '1'
no_extra_spaces: true
loop: '{{ repo_grep_results.stdout | regex_findall( ''(.+\.repo):\[(.+)\]\n?'' )
}}'
tags:
- CCE-26876-3
- CJIS-5.10.4.1
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- enable_strategy
- ensure_gpgcheck_never_disabled
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
|
Ensure gpgcheck Enabled In Main yum ConfigurationruleThe gpgcheck option controls whether
RPM packages' signatures are always checked prior to installation.
To configure yum to check package signatures before installing
them, ensure the following line appears in /etc/yum.conf in
the [main] section:
gpgcheck=1 Rationale:Changes to any software components can have significant effects on the
overall security of the operating system. This requirement ensures the
software has not been tampered with and that it has been provided by a
trusted vendor.
Accordingly, patches, service packs, device drivers, or operating system
components must be signed with a certificate recognized and approved by the
organization.
Verifying the authenticity of the software prior to installation
validates the integrity of the patch or upgrade received from a vendor.
This ensures the software has not been tampered with and that it has been
provided by a trusted vendor. Self-signed certificates are disallowed by
this requirement. Certificates used to verify the software must be from an
approved Certificate Authority (CA). identifiers:
CCE-26989-4 references:
BP28(R15), 1.2.2, 11, 2, 3, 9, 5.10.4.1, APO01.06, BAI03.05, BAI06.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS06.02, 3.4.8, CCI-001749, 164.308(a)(1)(ii)(D), 164.312(b), 164.312(c)(1), 164.312(c)(2), 164.312(e)(2)(i), 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.4, SR 3.1, SR 3.3, SR 3.4, SR 3.8, SR 7.6, A.11.2.4, A.12.1.2, A.12.2.1, A.12.5.1, A.12.6.2, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, CM-5(3), SI-7, SC-12, SC-12(3), CM-6(a), SA-12, SA-12(10), CM-11(a), CM-11(b), PR.DS-6, PR.DS-8, PR.IP-1, FPT_TUD_EXT.1, FPT_TUD_EXT.2, Req-6.2, SRG-OS-000366-GPOS-00153, RHEL-07-020050, SV-204447r603261_rule, SRG-OS-000366-VMM-001430, SRG-OS-000370-VMM-001460, SRG-OS-000404-VMM-001650 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q yum; then
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append "/etc/yum.conf" '^gpgcheck' '1' 'CCE-26989-4'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-26989-4
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-020050
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_gpgcheck_globally_activated
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
- unknown_strategy
- name: Check existence of yum on Fedora
stat:
path: /etc/yum.conf
register: yum_config_file
check_mode: false
when:
- ansible_distribution == "Fedora"
- '"yum" in ansible_facts.packages'
tags:
- CCE-26989-4
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-020050
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_gpgcheck_globally_activated
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
- unknown_strategy
- name: Ensure GPG check is globally activated (yum)
ini_file:
dest: /etc/yum.conf
section: main
option: gpgcheck
value: 1
no_extra_spaces: true
create: false
when:
- (ansible_distribution == "RedHat" or ansible_distribution == "CentOS" or ansible_distribution
== "Scientific" or yum_config_file.stat.exists)
- '"yum" in ansible_facts.packages'
tags:
- CCE-26989-4
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-020050
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_gpgcheck_globally_activated
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
- unknown_strategy
- name: Ensure GPG check is globally activated (dnf)
ini_file:
dest: /etc/dnf/dnf.conf
section: main
option: gpgcheck
value: 1
no_extra_spaces: true
create: false
when:
- ansible_distribution == "Fedora"
- '"yum" in ansible_facts.packages'
tags:
- CCE-26989-4
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-020050
- NIST-800-171-3.4.8
- NIST-800-53-CM-11(a)
- NIST-800-53-CM-11(b)
- NIST-800-53-CM-5(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-SA-12
- NIST-800-53-SA-12(10)
- NIST-800-53-SC-12
- NIST-800-53-SC-12(3)
- NIST-800-53-SI-7
- PCI-DSS-Req-6.2
- ensure_gpgcheck_globally_activated
- high_severity
- low_complexity
- medium_disruption
- no_reboot_needed
- unknown_strategy
|
Ensure Software Patches Installedrule
If the system is joined to the Red Hat Network, a Red Hat Satellite Server,
or a yum server, run the following command to install updates:
$ sudo yum update
If the system is not configured to use one of these sources, updates (in the form of RPM packages)
can be manually downloaded from the Red Hat Network and installed using rpm .
NOTE: U.S. Defense systems are required to be patched within 30 days or sooner as local policy
dictates.Rationale:Installing software updates is a fundamental mitigation against
the exploitation of publicly-known vulnerabilities. If the most
recent security patches and updates are not installed, unauthorized
users may take advantage of weaknesses in the unpatched software. The
lack of prompt attention to patching could result in a system compromise. identifiers:
CCE-26895-3 references:
BP28(R08), 1.8, 18, 20, 4, 5.10.4.1, APO12.01, APO12.02, APO12.03, APO12.04, BAI03.10, DSS05.01, DSS05.02, CCI-000366, 4.2.3, 4.2.3.12, 4.2.3.7, 4.2.3.9, A.12.6.1, A.14.2.3, A.16.1.3, A.18.2.2, A.18.2.3, SI-2(5), SI-2(c), CM-6(a), ID.RA-1, PR.IP-12, FMT_MOF_EXT.1, Req-6.2, SRG-OS-000480-GPOS-00227, RHEL-07-020260, SV-204459r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:
yum -y update
Remediation script:- name: Security patches are up to date
package:
name: '*'
state: latest
tags:
- CCE-26895-3
- CJIS-5.10.4.1
- DISA-STIG-RHEL-07-020260
- NIST-800-53-CM-6(a)
- NIST-800-53-SI-2(5)
- NIST-800-53-SI-2(c)
- PCI-DSS-Req-6.2
- high_disruption
- high_severity
- low_complexity
- patch_strategy
- reboot_required
- security_patches_up_to_date
- skip_ansible_lint
|
Account and Access ControlgroupIn traditional Unix security, if an attacker gains
shell access to a certain login account, they can perform any action
or access any file to which that account has access. Therefore,
making it more difficult for unauthorized people to gain shell
access to accounts, particularly to privileged accounts, is a
necessary part of securing a system. This section introduces
mechanisms for restricting access to accounts under
Red Hat Enterprise Linux 7. |
contains 13 rules |
Protect Accounts by Configuring PAMgroupPAM, or Pluggable Authentication Modules, is a system
which implements modular authentication for Linux programs. PAM provides
a flexible and configurable architecture for authentication, and it should be configured
to minimize exposure to unnecessary risk. This section contains
guidance on how to accomplish that.
PAM is implemented as a set of shared objects which are
loaded and invoked whenever an application wishes to authenticate a
user. Typically, the application must be running as root in order
to take advantage of PAM, because PAM's modules often need to be able
to access sensitive stores of account information, such as /etc/shadow.
Traditional privileged network listeners
(e.g. sshd) or SUID programs (e.g. sudo) already meet this
requirement. An SUID root application, userhelper, is provided so
that programs which are not SUID or privileged themselves can still
take advantage of PAM.
PAM looks in the directory /etc/pam.d for
application-specific configuration information. For instance, if
the program login attempts to authenticate a user, then PAM's
libraries follow the instructions in the file /etc/pam.d/login
to determine what actions should be taken.
One very important file in /etc/pam.d is
/etc/pam.d/system-auth . This file, which is included by
many other PAM configuration files, defines 'default' system authentication
measures. Modifying this file is a good way to make far-reaching
authentication changes, for instance when implementing a
centralized authentication service. warning
Be careful when making changes to PAM's configuration files.
The syntax for these files is complex, and modifications can
have unexpected consequences. The default configurations shipped
with applications should be sufficient for most users. |
contains 6 rules |
Set Password Quality RequirementsgroupThe default pam_pwquality PAM module provides strength
checking for passwords. It performs a number of checks, such as
making sure passwords are not similar to dictionary words, are of
at least a certain length, are not the previous password reversed,
and are not simply a change of case from the previous password. It
can also require passwords to be in certain character classes. The
pam_pwquality module is the preferred way of configuring
password requirements.
The man pages pam_pwquality(8)
provide information on the capabilities and configuration of
each. |
contains 2 rules |
Set Password Quality Requirements with pam_pwqualitygroupThe pam_pwquality PAM module can be configured to meet
requirements for a variety of policies.
For example, to configure pam_pwquality to require at least one uppercase
character, lowercase character, digit, and other (special)
character, make sure that pam_pwquality exists in /etc/pam.d/system-auth :
password requisite pam_pwquality.so try_first_pass local_users_only retry=3 authtok_type=
If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth .
Next, modify the settings in /etc/security/pwquality.conf to match the following:
difok = 4
minlen = 14
dcredit = -1
ucredit = -1
lcredit = -1
ocredit = -1
maxrepeat = 3
The arguments can be modified to ensure compliance with
your organization's security policy. Discussion of each parameter follows. |
contains 2 rules |
Ensure PAM Enforces Password Requirements - Minimum LengthruleThe pam_pwquality module's minlen parameter controls requirements for
minimum characters required in a password. Add minlen=12
after pam_pwquality to set minimum password length requirements. Rationale:The shorter the password, the lower the number of possible combinations
that need to be tested before the password is compromised.
Password complexity, or strength, is a measure of the effectiveness of a
password in resisting attempts at guessing and brute-force attacks.
Password length is one factor of several that helps to determine strength
and how long it takes to crack a password. Use of more characters in a password
helps to exponentially increase the time and/or resources required to
compromose the password. identifiers:
CCE-27293-0 references:
BP28(R18), 5.3.1, 1, 12, 15, 16, 5, 5.6.2.1.1, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, CCI-000205, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(c), IA-5(1)(a), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, FMT_MOF_EXT.1, Req-8.2.3, SRG-OS-000078-GPOS-00046, RHEL-07-010280, SV-204423r603261_rule, SRG-OS-000072-VMM-000390, SRG-OS-000078-VMM-000450 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
var_password_pam_minlen="12"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^minlen' $var_password_pam_minlen 'CCE-27293-0' '%s = %s'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-27293-0
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010280
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(a)
- NIST-800-53-IA-5(4)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.3
- accounts_password_pam_minlen
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: XCCDF Value var_password_pam_minlen # promote to variable
set_fact:
var_password_pam_minlen: !!str 12
tags:
- always
- name: Ensure PAM variable minlen is set accordingly
lineinfile:
create: true
dest: /etc/security/pwquality.conf
regexp: ^#?\s*minlen
line: minlen = {{ var_password_pam_minlen }}
when: '"pam" in ansible_facts.packages'
tags:
- CCE-27293-0
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010280
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(a)
- NIST-800-53-IA-5(4)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.3
- accounts_password_pam_minlen
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Ensure PAM Enforces Password Requirements - Minimum Different CharactersruleThe pam_pwquality module's difok parameter sets the number of characters
in a password that must not be present in and old password during a password change.
Modify the difok setting in /etc/security/pwquality.conf
to equal 6 to require differing characters
when changing passwords. Rationale:Use of a complex password helps to increase the time and resources
required to compromise the password. Password complexity, or strength,
is a measure of the effectiveness of a password in resisting attempts
at guessing and brute–force attacks.
Password complexity is one factor of several that determines how long
it takes to crack a password. The more complex the password, the
greater the number of possible combinations that need to be tested
before the password is compromised.
Requiring a minimum number of different characters during password changes ensures that
newly changed passwords should not resemble previously compromised ones.
Note that passwords which are changed on compromised systems will still be compromised, however. identifiers:
CCE-82020-9 references:
1, 12, 15, 16, 5, 5.6.2.1.1, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, CCI-000195, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(c), IA-5(1)(b), CM-6(a), IA-5(4), PR.AC-1, PR.AC-6, PR.AC-7, SRG-OS-000072-GPOS-00040, RHEL-07-010160, SV-204411r603261_rule, SRG-OS-000072-VMM-000390 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
var_password_pam_difok="6"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/security/pwquality.conf' '^difok' $var_password_pam_difok 'CCE-82020-9' '%s = %s'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-82020-9
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010160
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(b)
- NIST-800-53-IA-5(4)
- NIST-800-53-IA-5(c)
- accounts_password_pam_difok
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: XCCDF Value var_password_pam_difok # promote to variable
set_fact:
var_password_pam_difok: !!str 6
tags:
- always
- name: Ensure PAM variable difok is set accordingly
lineinfile:
create: true
dest: /etc/security/pwquality.conf
regexp: ^#?\s*difok
line: difok = {{ var_password_pam_difok }}
when: '"pam" in ansible_facts.packages'
tags:
- CCE-82020-9
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010160
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(b)
- NIST-800-53-IA-5(4)
- NIST-800-53-IA-5(c)
- accounts_password_pam_difok
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Set Password Hashing AlgorithmgroupThe system's default algorithm for storing password hashes in
/etc/shadow is SHA-512. This can be configured in several
locations. |
contains 3 rules |
Set Password Hashing Algorithm in /etc/login.defsruleIn /etc/login.defs , add or correct the following line to ensure
the system will use SHA-512 as the hashing algorithm:
ENCRYPT_METHOD SHA512 Rationale:Passwords need to be protected at all times, and encryption is the standard method for protecting passwords.
If passwords are not encrypted, they can be plainly read (i.e., clear text) and easily compromised. Passwords
that are encrypted with a weak algorithm are no more protected than if they are kept in plain text.
Using a stronger hashing algorithm makes password cracking attacks more difficult. identifiers:
CCE-82050-6 references:
BP28(R32), 6.3.1, 1, 12, 15, 16, 5, 5.6.2.2, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.13.11, CCI-000196, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 0418, 1055, 1402, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(c), IA-5(1)(c), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.1, SRG-OS-000073-GPOS-00041, RHEL-07-010210, SV-204416r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q shadow-utils; then
var_password_hashing_algorithm="SHA512"
if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then
sed -i "s/^ENCRYPT_METHOD .*/ENCRYPT_METHOD $var_password_hashing_algorithm/g" /etc/login.defs
else
echo "" >> /etc/login.defs
echo "ENCRYPT_METHOD $var_password_hashing_algorithm" >> /etc/login.defs
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-82050-6
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010210
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- set_password_hashing_algorithm_logindefs
- name: XCCDF Value var_password_hashing_algorithm # promote to variable
set_fact:
var_password_hashing_algorithm: !!str SHA512
tags:
- always
- name: Set Password Hashing Algorithm in /etc/login.defs
lineinfile:
dest: /etc/login.defs
regexp: ^#?ENCRYPT_METHOD
line: ENCRYPT_METHOD {{ var_password_hashing_algorithm }}
state: present
create: true
when: '"shadow-utils" in ansible_facts.packages'
tags:
- CCE-82050-6
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010210
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- set_password_hashing_algorithm_logindefs
|
Set Password Hashing Algorithm in /etc/libuser.confruleIn /etc/libuser.conf , add or correct the following line in its
[defaults] section to ensure the system will use the SHA-512
algorithm for password hashing:
crypt_style = sha512 Rationale:Passwords need to be protected at all times, and encryption is the standard
method for protecting passwords. If passwords are not encrypted, they can
be plainly read (i.e., clear text) and easily compromised. Passwords that
are encrypted with a weak algorithm are no more protected than if they are
kepy in plain text.
This setting ensures user and group account administration utilities are
configured to store only encrypted representations of passwords.
Additionally, the crypt_style configuration option ensures the use
of a strong hashing algorithm that makes password cracking attacks more
difficult. identifiers:
CCE-82038-1 references:
1, 12, 15, 16, 5, 5.6.2.2, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.13.11, CCI-000196, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 0418, 1055, 1402, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(c), IA-5(1)(c), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.1, SRG-OS-000073-GPOS-00041, RHEL-07-010220, SV-204417r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q libuser; then
LIBUSER_CONF="/etc/libuser.conf"
CRYPT_STYLE_REGEX='[[:space:]]*\[defaults](.*(\n)+)+?[[:space:]]*crypt_style[[:space:]]*'
# Try find crypt_style in [defaults] section. If it is here, then change algorithm to sha512.
# If it isn't here, then add it to [defaults] section.
if grep -qzosP $CRYPT_STYLE_REGEX $LIBUSER_CONF ; then
sed -i "s/\(crypt_style[[:space:]]*=[[:space:]]*\).*/\1sha512/g" $LIBUSER_CONF
elif grep -qs "\[defaults]" $LIBUSER_CONF ; then
sed -i "/[[:space:]]*\[defaults]/a crypt_style = sha512" $LIBUSER_CONF
else
echo -e "[defaults]\ncrypt_style = sha512" >> $LIBUSER_CONF
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-82038-1
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010220
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- set_password_hashing_algorithm_libuserconf
- name: Set Password Hashing Algorithm in /etc/libuser.conf
lineinfile:
dest: /etc/libuser.conf
insertafter: ^\s*\[defaults]
regexp: ^#?crypt_style
line: crypt_style = sha512
state: present
create: true
when: '"libuser" in ansible_facts.packages'
tags:
- CCE-82038-1
- CJIS-5.6.2.2
- DISA-STIG-RHEL-07-010220
- NIST-800-171-3.13.11
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.1
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- set_password_hashing_algorithm_libuserconf
|
Set PAM's Password Hashing AlgorithmruleThe PAM system service can be configured to only store encrypted
representations of passwords. In
/etc/pam.d/system-auth ,
the
password section of the file controls which PAM modules execute
during a password change. Set the pam_unix.so module in the
password section to include the argument sha512 , as shown
below:
password sufficient pam_unix.so sha512 other arguments...
This will help ensure when local users change their passwords, hashes for
the new passwords will be generated using the SHA-512 algorithm. This is
the default.Rationale:Passwords need to be protected at all times, and encryption is the standard
method for protecting passwords. If passwords are not encrypted, they can
be plainly read (i.e., clear text) and easily compromised. Passwords that
are encrypted with a weak algorithm are no more protected than if they are
kepy in plain text.
This setting ensures user and group account administration utilities are
configured to store only encrypted representations of passwords.
Additionally, the crypt_style configuration option ensures the use
of a strong hashing algorithm that makes password cracking attacks more
difficult. identifiers:
CCE-82043-1 references:
BP28(R32), 5.3.4, 1, 12, 15, 16, 5, 5.6.2.2, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.13.11, CCI-000196, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 0418, 1055, 1402, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(c), IA-5(1)(c), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.1, SRG-OS-000073-GPOS-00041, RHEL-07-010200, SV-204415r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
AUTH_FILES[0]="/etc/pam.d/system-auth"
AUTH_FILES[1]="/etc/pam.d/password-auth"
for pamFile in "${AUTH_FILES[@]}"
do
if ! grep -q "^password.*sufficient.*pam_unix.so.*sha512" $pamFile; then
sed -i --follow-symlinks "/^password.*sufficient.*pam_unix.so/ s/$/ sha512/" $pamFile
fi
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Ensure PAM Displays Last Logon/Access NotificationruleTo configure the system to notify users of last logon/access
using pam_lastlog , add or correct the pam_lastlog
settings in
/etc/pam.d/postlogin to read as follows:
session [success=1 default=ignore] pam_succeed_if.so service !~ gdm* service !~ su* quiet
session [default=1] pam_lastlog.so nowtmp showfailed
session optional pam_lastlog.so silent noupdate showfailed Rationale:Users need to be aware of activity that occurs regarding
their account. Providing users with information regarding the number
of unsuccessful attempts that were made to login to their account
allows the user to determine if any unauthorized activity has occurred
and gives them an opportunity to notify administrators. identifiers:
CCE-27275-7 references:
1, 12, 15, 16, 5.5.2, DSS05.04, DSS05.10, DSS06.10, CCI-000366, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, 0582, 0584, 05885, 0586, 0846, 0957, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, AC-9(1), CM-6(a), PR.AC-7, Req-10.2.4, SRG-OS-000480-GPOS-00227, RHEL-07-040530, SV-204605r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
if grep -q "^session.*pam_lastlog.so" /etc/pam.d/postlogin; then
sed -i --follow-symlinks "/pam_lastlog.so/d" /etc/pam.d/postlogin
fi
echo "session [default=1] pam_lastlog.so nowtmp showfailed" >> /etc/pam.d/postlogin
echo "session optional pam_lastlog.so silent noupdate showfailed" >> /etc/pam.d/postlogin
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Protect Accounts by Restricting Password-Based LogingroupConventionally, Unix shell accounts are accessed by
providing a username and password to a login program, which tests
these values for correctness using the /etc/passwd and
/etc/shadow files. Password-based login is vulnerable to
guessing of weak passwords, and to sniffing and man-in-the-middle
attacks against passwords entered over a network or at an insecure
console. Therefore, mechanisms for accessing accounts by entering
usernames and passwords should be restricted to those which are
operationally necessary. |
contains 6 rules |
Verify Proper Storage and Existence of Password
HashesgroupBy default, password hashes for local accounts are stored
in the second field (colon-separated) in
/etc/shadow . This file should be readable only by
processes running with root credentials, preventing users from
casually accessing others' password hashes and attempting
to crack them.
However, it remains possible to misconfigure the system
and store password hashes
in world-readable files such as /etc/passwd , or
to even store passwords themselves in plaintext on the system.
Using system-provided tools for password change/creation
should allow administrators to avoid such misconfiguration. |
contains 3 rules |
All GIDs referenced in /etc/passwd must be defined in /etc/groupruleAdd a group to the system for each GID referenced without a corresponding group. Rationale:If a user is assigned the Group Identifier (GID) of a group not existing on the system, and a group
with the Gruop Identifier (GID) is subsequently created, the user may have unintended rights to
any files associated with the group. identifiers:
CCE-27503-2 references:
1, 12, 15, 16, 5, 5.5.2, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, CCI-000764, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-2, CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.5.a, SRG-OS-000104-GPOS-00051, RHEL-07-020300, SV-204461r603261_rule |
Verify All Account Password Hashes are ShadowedruleIf any password hashes are stored in /etc/passwd (in the second field,
instead of an x or * ), the cause of this misconfiguration should be
investigated. The account should have its password reset and the hash should be
properly stored, or the account should be deleted entirely. Rationale:The hashes for all user account passwords should be stored in
the file /etc/shadow and never in /etc/passwd ,
which is readable by all users. identifiers:
CCE-27352-4 references:
1, 12, 15, 16, 5, 5.5.2, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.5.10, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 1410, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(h), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, Req-8.2.1 |
Prevent Login to Accounts With Empty PasswordruleIf an account is configured for password authentication
but does not have an assigned password, it may be possible to log
into the account without authentication. Remove any instances of the
nullok in
/etc/pam.d/system-auth
to prevent logins with empty passwords. Rationale:If an account has an empty password, anyone could log in and
run commands with the privileges of that account. Accounts with
empty passwords should never be used in operational environments. identifiers:
CCE-27286-4 references:
1, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2, APO01.06, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.10, 3.1.1, 3.1.5, CCI-000366, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, IA-5(1)(a), IA-5(c), CM-6(a), PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5, FIA_UAU.1, Req-8.2.3, SRG-OS-000480-GPOS-00227, RHEL-07-010290, SV-204424r603261_rule Remediation script:sed --follow-symlinks -i 's/\<nullok\>//g' /etc/pam.d/system-auth
sed --follow-symlinks -i 's/\<nullok\>//g' /etc/pam.d/password-auth
Remediation script:- name: Prevent Log In to Accounts With Empty Password - system-auth
replace:
dest: /etc/pam.d/system-auth
regexp: nullok
tags:
- CCE-27286-4
- CJIS-5.5.2
- DISA-STIG-RHEL-07-010290
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(a)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.3
- configure_strategy
- high_severity
- low_complexity
- medium_disruption
- no_empty_passwords
- no_reboot_needed
- name: Prevent Log In to Accounts With Empty Password - password-auth
replace:
dest: /etc/pam.d/password-auth
regexp: nullok
tags:
- CCE-27286-4
- CJIS-5.5.2
- DISA-STIG-RHEL-07-010290
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(a)
- NIST-800-53-IA-5(c)
- PCI-DSS-Req-8.2.3
- configure_strategy
- high_severity
- low_complexity
- medium_disruption
- no_empty_passwords
- no_reboot_needed
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
mode: 0644
path: /etc/pam.d/password-auth
overwrite: true
- contents:
source: data:,%23%20Generated%20by%20authselect%20on%20Sat%20Oct%2027%2014%3A59%3A36%202018%0A%23%20Do%20not%20modify%20this%20file%20manually.%0A%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_env.so%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_faildelay.so%20delay%3D2000000%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_fprintd.so%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet%0Aauth%20%20%20%20%20%20%20%20%5Bdefault%3D1%20ignore%3Dignore%20success%3Dok%5D%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20try_first_pass%0Aauth%20%20%20%20%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3E%3D%201000%20quiet_success%0Aauth%20%20%20%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20forward_pass%0Aauth%20%20%20%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_localuser.so%0Aaccount%20%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20uid%20%3C%201000%20quiet%0Aaccount%20%20%20%20%20%5Bdefault%3Dbad%20success%3Dok%20user_unknown%3Dignore%5D%20pam_sss.so%0Aaccount%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_permit.so%0A%0Apassword%20%20%20%20requisite%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_pwquality.so%20try_first_pass%20local_users_only%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%20sha512%20shadow%20try_first_pass%20use_authtok%0Apassword%20%20%20%20sufficient%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%20use_authtok%0Apassword%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_deny.so%0A%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_keyinit.so%20revoke%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_limits.so%0A-session%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_systemd.so%0Asession%20%20%20%20%20%5Bsuccess%3D1%20default%3Dignore%5D%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_succeed_if.so%20service%20in%20crond%20quiet%20use_uid%0Asession%20%20%20%20%20required%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_unix.so%0Asession%20%20%20%20%20optional%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20pam_sss.so%0A
mode: 0644
path: /etc/pam.d/system-auth
overwrite: true
|
Set Account Expiration ParametersgroupAccounts can be configured to be automatically disabled
after a certain time period,
meaning that they will require administrator interaction to become usable again.
Expiration of accounts after inactivity can be set for all accounts by default
and also on a per-account basis, such as for accounts that are known to be temporary.
To configure automatic expiration of an account following
the expiration of its password (that is, after the password has expired and not been changed),
run the following command, substituting NUM_DAYS and USER appropriately:
$ sudo chage -I NUM_DAYS USER
Accounts, such as temporary accounts, can also be configured to expire on an explicitly-set date with the
-E option.
The file /etc/default/useradd controls
default settings for all newly-created accounts created with the system's
normal command line utilities.warning
This will only apply to newly created accounts |
contains 2 rules |
Ensure All Accounts on the System Have Unique NamesruleEnsure accounts on the system have unique names.
To ensure all accounts have unique names, run the following command:
$ sudo getent passwd | awk -F: '{ print $1}' | uniq -d
If a username is returned, change or delete the username.Rationale:Unique usernames allow for accountability on the system. |
Set Account Expiration Following InactivityruleTo specify the number of days after a password expires (which
signifies inactivity) until an account is permanently disabled, add or correct
the following line in /etc/default/useradd :
INACTIVE=0
If a password is currently on the verge of expiration, then
0
day(s) remain(s) until the account is automatically
disabled. However, if the password will not expire for another 60 days, then 60
days plus 0 day(s) could
elapse until the account would be automatically disabled. See the
useradd man page for more information.Rationale:Disabling inactive accounts ensures that accounts which may not
have been responsibly removed are not available to attackers
who may have compromised their credentials. identifiers:
CCE-27355-7 references:
5.4.1.4, 1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8, 5.6.2.1.1, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.5.6, CCI-000017, CCI-000795, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2, A.12.4.1, A.12.4.3, A.18.1.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, IA-4(e), AC-2(3), CM-6(a), DE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, Req-8.1.4, SRG-OS-000118-GPOS-00060, RHEL-07-010310, SV-204426r603261_rule, SRG-OS-000003-VMM-000030, SRG-OS-000118-VMM-000590 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q shadow-utils; then
var_account_disable_post_pw_expiration="0"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/default/useradd' '^INACTIVE' "$var_account_disable_post_pw_expiration" 'CCE-27355-7' '%s=%s'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-27355-7
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010310
- NIST-800-171-3.5.6
- NIST-800-53-AC-2(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-4(e)
- PCI-DSS-Req-8.1.4
- account_disable_post_pw_expiration
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: XCCDF Value var_account_disable_post_pw_expiration # promote to variable
set_fact:
var_account_disable_post_pw_expiration: !!str 0
tags:
- always
- name: Set Account Expiration Following Inactivity
lineinfile:
create: true
dest: /etc/default/useradd
regexp: ^INACTIVE
line: INACTIVE={{ var_account_disable_post_pw_expiration }}
when: '"shadow-utils" in ansible_facts.packages'
tags:
- CCE-27355-7
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010310
- NIST-800-171-3.5.6
- NIST-800-53-AC-2(3)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-4(e)
- PCI-DSS-Req-8.1.4
- account_disable_post_pw_expiration
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Set Password Expiration ParametersgroupThe file /etc/login.defs controls several
password-related settings. Programs such as passwd ,
su , and
login consult /etc/login.defs to determine
behavior with regard to password aging, expiration warnings,
and length. See the man page login.defs(5) for more information.
Users should be forced to change their passwords, in order to
decrease the utility of compromised passwords. However, the need to
change passwords often should be balanced against the risk that
users will reuse or write down passwords if forced to change them
too often. Forcing password changes every 90-360 days, depending on
the environment, is recommended. Set the appropriate value as
PASS_MAX_DAYS and apply it to existing accounts with the
-M flag.
The PASS_MIN_DAYS (-m ) setting prevents password
changes for 7 days after the first change, to discourage password
cycling. If you use this setting, train users to contact an administrator
for an emergency password change in case a new password becomes
compromised. The PASS_WARN_AGE (-W ) setting gives
users 7 days of warnings at login time that their passwords are about to expire.
For example, for each existing human user USER, expiration parameters
could be adjusted to a 180 day maximum password age, 7 day minimum password
age, and 7 day warning period with the following command:
$ sudo chage -M 180 -m 7 -W 7 USER |
contains 1 rule |
Set Password Minimum AgeruleTo specify password minimum age for new accounts,
edit the file /etc/login.defs
and add or correct the following line:
PASS_MIN_DAYS -1
A value of 1 day is considered sufficient for many
environments. The DoD requirement is 1.
The profile requirement is -1 .Rationale:Enforcing a minimum password lifetime helps to prevent repeated password
changes to defeat the password reuse or history enforcement requirement. If
users are allowed to immediately and continually change their password,
then the password could be repeatedly changed in a short period of time to
defeat the organization's policy regarding password reuse.
Setting the minimum password age protects against users cycling back to a
favorite password after satisfying the password reuse requirement. identifiers:
CCE-82036-5 references:
5.4.1.2, 1, 12, 15, 16, 5, 5.6.2.1.1, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.5.8, CCI-000198, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, 0418, 1055, 1402, A.18.1.4, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, IA-5(f), IA-5(1)(d), CM-6(a), PR.AC-1, PR.AC-6, PR.AC-7, SRG-OS-000075-GPOS-00043, RHEL-07-010230, SV-204418r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q shadow-utils; then
var_accounts_minimum_age_login_defs="-1"
grep -q ^PASS_MIN_DAYS /etc/login.defs && \
sed -i "s/PASS_MIN_DAYS.*/PASS_MIN_DAYS $var_accounts_minimum_age_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_MIN_DAYS $var_accounts_minimum_age_login_defs" >> /etc/login.defs
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-82036-5
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010230
- NIST-800-171-3.5.8
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(d)
- NIST-800-53-IA-5(f)
- accounts_minimum_age_login_defs
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: XCCDF Value var_accounts_minimum_age_login_defs # promote to variable
set_fact:
var_accounts_minimum_age_login_defs: !!str -1
tags:
- always
- name: Set Password Minimum Age
lineinfile:
create: true
dest: /etc/login.defs
regexp: ^#?PASS_MIN_DAYS
line: PASS_MIN_DAYS {{ var_accounts_minimum_age_login_defs }}
when: '"shadow-utils" in ansible_facts.packages'
tags:
- CCE-82036-5
- CJIS-5.6.2.1.1
- DISA-STIG-RHEL-07-010230
- NIST-800-171-3.5.8
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(d)
- NIST-800-53-IA-5(f)
- accounts_minimum_age_login_defs
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Secure Session Configuration Files for Login AccountsgroupWhen a user logs into a Unix account, the system
configures the user's session by reading a number of files. Many of
these files are located in the user's home directory, and may have
weak permissions as a result of user error or misconfiguration. If
an attacker can modify or even read certain types of account
configuration information, they can often gain full access to the
affected user's account. Therefore, it is important to test and
correct configuration file permissions for interactive accounts,
particularly those of privileged users such as root or system
administrators. |
contains 1 rule |
Limit the Number of Concurrent Login Sessions Allowed Per UserruleLimiting the number of allowed users and sessions per user can limit risks related to Denial of
Service attacks. This addresses concurrent sessions for a single account and does not address
concurrent sessions by a single user via multiple accounts. To set the number of concurrent
sessions per user add the following line in /etc/security/limits.conf or
a file under /etc/security/limits.d/ :
* hard maxlogins 3 Rationale:Limiting simultaneous user logins can insulate the system from denial of service
problems caused by excessive logins. Automated login processes operating improperly or
maliciously may result in an exceptional number of simultaneous login sessions. identifiers:
CCE-82041-5 references:
14, 15, 18, 9, 5.5.2.2, DSS01.05, DSS05.02, CCI-000054, 4.3.3.4, SR 3.1, SR 3.8, A.13.1.1, A.13.1.3, A.13.2.1, A.14.1.2, A.14.1.3, AC-10, CM-6(a), PR.AC-5, SRG-OS-000027-GPOS-00008, RHEL-07-040000, SV-204576r603261_rule, SRG-OS-000027-VMM-000080 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q pam; then
var_accounts_max_concurrent_login_sessions="3"
if grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.d/*.conf; then
sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.d/*.conf
elif grep -q '^[^#]*\<maxlogins\>' /etc/security/limits.conf; then
sed -i "/^[^#]*\<maxlogins\>/ s/maxlogins.*/maxlogins $var_accounts_max_concurrent_login_sessions/" /etc/security/limits.conf
else
echo "* hard maxlogins $var_accounts_max_concurrent_login_sessions" >> /etc/security/limits.conf
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-82041-5
- CJIS-5.5.2.2
- DISA-STIG-RHEL-07-040000
- NIST-800-53-AC-10
- NIST-800-53-CM-6(a)
- accounts_max_concurrent_login_sessions
- low_complexity
- low_disruption
- low_severity
- no_reboot_needed
- restrict_strategy
- name: XCCDF Value var_accounts_max_concurrent_login_sessions # promote to variable
set_fact:
var_accounts_max_concurrent_login_sessions: !!str 3
tags:
- always
- name: Find /etc/security/limits.d files containing maxlogins configuration
find:
paths: /etc/security/limits.d
contains: ^[\s]*\*[\s]+(?:(?:hard)|(?:-))[\s]+maxlogins
patterns: '*.conf'
register: maxlogins
when: '"pam" in ansible_facts.packages'
tags:
- CCE-82041-5
- CJIS-5.5.2.2
- DISA-STIG-RHEL-07-040000
- NIST-800-53-AC-10
- NIST-800-53-CM-6(a)
- accounts_max_concurrent_login_sessions
- low_complexity
- low_disruption
- low_severity
- no_reboot_needed
- restrict_strategy
- name: Limit the Number of Concurrent Login Sessions Allowed Per User in files from
limits.d
replace:
dest: '{{ item.path }}'
regexp: ^#?\*.*maxlogins.*
replace: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions
}}'
with_items:
- '{{ maxlogins.files }}'
when: '"pam" in ansible_facts.packages'
tags:
- CCE-82041-5
- CJIS-5.5.2.2
- DISA-STIG-RHEL-07-040000
- NIST-800-53-AC-10
- NIST-800-53-CM-6(a)
- accounts_max_concurrent_login_sessions
- low_complexity
- low_disruption
- low_severity
- no_reboot_needed
- restrict_strategy
- name: Limit the Number of Concurrent Login Sessions Allowed Per User
lineinfile:
state: present
dest: /etc/security/limits.conf
insertbefore: ^# End of file
regexp: ^#?\*.*maxlogins
line: '* hard maxlogins {{ var_accounts_max_concurrent_login_sessions
}}'
create: true
when:
- maxlogins.matched == 0
- '"pam" in ansible_facts.packages'
tags:
- CCE-82041-5
- CJIS-5.5.2.2
- DISA-STIG-RHEL-07-040000
- NIST-800-53-AC-10
- NIST-800-53-CM-6(a)
- accounts_max_concurrent_login_sessions
- low_complexity
- low_disruption
- low_severity
- no_reboot_needed
- restrict_strategy
|
System Accounting with auditdgroupThe audit service provides substantial capabilities
for recording system activities. By default, the service audits about
SELinux AVC denials and certain types of security-relevant events
such as system logins, account modifications, and authentication
events performed by programs such as sudo.
Under its default configuration, auditd has modest disk space
requirements, and should not noticeably impact system performance.
NOTE: The Linux Audit daemon auditd can be configured to use
the augenrules program to read audit rules files (*.rules )
located in /etc/audit/rules.d location and compile them to create
the resulting form of the /etc/audit/audit.rules configuration file
during the daemon startup (default configuration). Alternatively, the auditd
daemon can use the auditctl utility to read audit rules from the
/etc/audit/audit.rules configuration file during daemon startup,
and load them into the kernel. The expected behavior is configured via the
appropriate ExecStartPost directive setting in the
/usr/lib/systemd/system/auditd.service configuration file.
To instruct the auditd daemon to use the augenrules program
to read audit rules (default configuration), use the following setting:
ExecStartPost=-/sbin/augenrules --load
in the /usr/lib/systemd/system/auditd.service configuration file.
In order to instruct the auditd daemon to use the auditctl
utility to read audit rules, use the following setting:
ExecStartPost=-/sbin/auditctl -R /etc/audit/audit.rules
in the /usr/lib/systemd/system/auditd.service configuration file.
Refer to [Service] section of the /usr/lib/systemd/system/auditd.service
configuration file for further details.
Government networks often have substantial auditing
requirements and auditd can be configured to meet these
requirements.
Examining some example audit records demonstrates how the Linux audit system
satisfies common requirements.
The following example from Fedora Documentation available at
https://access.redhat.com/documentation/en-US/Red_Hat_Enterprise_Linux/7/html/SELinux_Users_and_Administrators_Guide/sect-Security-Enhanced_Linux-Troubleshooting-Fixing_Problems.html#sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages
shows the substantial amount of information captured in a
two typical "raw" audit messages, followed by a breakdown of the most important
fields. In this example the message is SELinux-related and reports an AVC
denial (and the associated system call) that occurred when the Apache HTTP
Server attempted to access the /var/www/html/file1 file (labeled with
the samba_share_t type):
type=AVC msg=audit(1226874073.147:96): avc: denied { getattr } for pid=2465 comm="httpd"
path="/var/www/html/file1" dev=dm-0 ino=284133 scontext=unconfined_u:system_r:httpd_t:s0
tcontext=unconfined_u:object_r:samba_share_t:s0 tclass=file
type=SYSCALL msg=audit(1226874073.147:96): arch=40000003 syscall=196 success=no exit=-13
a0=b98df198 a1=bfec85dc a2=54dff4 a3=2008171 items=0 ppid=2463 pid=2465 auid=502 uid=48
gid=48 euid=48 suid=48 fsuid=48 egid=48 sgid=48 fsgid=48 tty=(none) ses=6 comm="httpd"
exe="/usr/sbin/httpd" subj=unconfined_u:system_r:httpd_t:s0 key=(null)
msg=audit(1226874073.147:96) - The number in parentheses is the unformatted time stamp (Epoch time)
for the event, which can be converted to standard time by using the
date command.
{ getattr } - The item in braces indicates the permission that was denied.
getattr
indicates the source process was trying to read the target file's status information.
This occurs before reading files. This action is denied due to the file being
accessed having the wrong label. Commonly seen permissions include getattr ,
read , and write .
comm="httpd" - The executable that launched the process. The full path of the executable is
found in the
exe= section of the system call (SYSCALL ) message,
which in this case, is exe="/usr/sbin/httpd" .
path="/var/www/html/file1" - The path to the object (target) the process attempted to access.
scontext="unconfined_u:system_r:httpd_t:s0" - The SELinux context of the process that attempted the denied action. In
this case, it is the SELinux context of the Apache HTTP Server, which is running
in the
httpd_t domain.
tcontext="unconfined_u:object_r:samba_share_t:s0" - The SELinux context of the object (target) the process attempted to access.
In this case, it is the SELinux context of
file1 . Note: the samba_share_t
type is not accessible to processes running in the httpd_t domain.
- From the system call (
SYSCALL ) message, two items are of interest:
success=no : indicates whether the denial (AVC) was enforced or not.
success=no indicates the system call was not successful (SELinux denied
access). success=yes indicates the system call was successful - this can
be seen for permissive domains or unconfined domains, such as initrc_t
and kernel_t .
exe="/usr/sbin/httpd" : the full path to the executable that launched
the process, which in this case, is exe="/usr/sbin/httpd" .
|
contains 41 rules |
Configure auditd Rules for Comprehensive AuditinggroupThe auditd program can perform comprehensive
monitoring of system activity. This section describes recommended
configuration settings for comprehensive auditing, but a full
description of the auditing system's capabilities is beyond the
scope of this guide. The mailing list linux-audit@redhat.com exists
to facilitate community discussion of the auditing system.
The audit subsystem supports extensive collection of events, including:
- Tracing of arbitrary system calls (identified by name or number)
on entry or exit.
- Filtering by PID, UID, call success, system call argument (with
some limitations), etc.
- Monitoring of specific files for modifications to the file's
contents or metadata.
Auditing rules at startup are controlled by the file /etc/audit/audit.rules .
Add rules to it to meet the auditing requirements for your organization.
Each line in /etc/audit/audit.rules represents a series of arguments
that can be passed to auditctl and can be individually tested
during runtime. See documentation in /usr/share/doc/audit-VERSION and
in the related man pages for more details.
If copying any example audit rulesets from /usr/share/doc/audit-VERSION ,
be sure to comment out the
lines containing arch= which are not appropriate for your system's
architecture. Then review and understand the following rules,
ensuring rules are activated as needed for the appropriate
architecture.
After reviewing all the rules, reading the following sections, and
editing as needed, the new rules can be activated as follows:
$ sudo service auditd restart |
contains 32 rules |
Record File Deletion Events by UsergroupAt a minimum, the audit system should collect file deletion events
for all users and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d , setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete |
contains 1 rule |
Ensure auditd Collects File Deletion Events by UserruleAt a minimum the audit system should collect file deletion events
for all users and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d , setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename,renameat -F auid>=1000 -F auid!=unset -F key=delete
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S rmdir,unlink,unlinkat,rename -S renameat -F auid>=1000 -F auid!=unset -F key=delete warning
This rule checks for multiple syscalls related to file deletion;
it was written with DISA STIG in mind. Other policies should use a
separate rule for each syscall that needs to be checked. For example:
audit_rules_file_deletion_events_rmdir audit_rules_file_deletion_events_unlink audit_rules_file_deletion_events_unlinkat
Rationale:Auditing file deletions will create an audit trail for files that are removed
from the system. The audit trail could aid in system troubleshooting, as well as, detecting
malicious processes that attempt to delete log files to conceal their presence. identifiers:
CCE-27206-2 references:
4.1.14, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000366, CCI-000172, CCI-002884, 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.2.7 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S .* -F auid>=1000 -F auid!=unset -k *"
# Use escaped BRE regex to specify rule group
GROUP="\(rmdir\|unlink\|rename\)"
FULL_RULE="-a always,exit -F arch=$ARCH -S rmdir -S unlink -S unlinkat -S rename -S renameat -F auid>=1000 -F auid!=unset -k delete"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Record Unauthorized Access Attempts Events to Files (unsuccessful)groupAt a minimum, the audit system should collect unauthorized file
accesses for all users and root. Note that the "-F arch=b32" lines should be
present even on a 64 bit system. These commands identify system calls for
auditing. Even if the system is 64 bit it can still execute 32 bit system
calls. Additionally, these rules can be configured in a number of ways while
still achieving the desired effect. An example of this is that the "-S" calls
could be split up and placed on separate lines, however, this is less efficient.
Add the following to /etc/audit/audit.rules :
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If your system is 64 bit then these lines should be duplicated and the
arch=b32 replaced with arch=b64 as follows:
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access |
contains 1 rule |
Ensure auditd Collects Unauthorized Access Attempts to Files (unsuccessful)ruleAt a minimum the audit system should collect unauthorized file
accesses for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following lines to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b32 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access
If the system is 64 bit then also add the following lines:
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=access
-a always,exit -F arch=b64 -S creat,open,openat,open_by_handle_at,truncate,ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=access warning
This rule checks for multiple syscalls related to unsuccessful file modification;
it was written with DISA STIG in mind. Other policies should use a
separate rule for each syscall that needs to be checked. For example:
audit_rules_unsuccessful_file_modification_open audit_rules_unsuccessful_file_modification_ftruncate audit_rules_unsuccessful_file_modification_creat
Rationale:Unsuccessful attempts to access files could be an indicator of malicious activity on a system. Auditing
these events could serve as evidence of potential system compromise. identifiers:
CCE-27347-4 references:
1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, CCI-002884, 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, 0582, 0584, 05885, 0586, 0846, 0957, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.2.4, Req-10.2.1 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
# First fix the -EACCES requirement
PATTERN="-a always,exit -F arch=$ARCH -S .* -F exit=-EACCES -F auid>=1000 -F auid!=unset -k *"
# Use escaped BRE regex to specify rule group
GROUP="\(creat\|open\|truncate\)"
FULL_RULE="-a always,exit -F arch=$ARCH -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EACCES -F auid>=1000 -F auid!=unset -k access"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
# Then fix the -EPERM requirement
PATTERN="-a always,exit -F arch=$ARCH -S .* -F exit=-EPERM -F auid>=1000 -F auid!=unset -k *"
# No need to change content of $GROUP variable - it's the same as for -EACCES case above
FULL_RULE="-a always,exit -F arch=$ARCH -S creat -S open -S openat -S open_by_handle_at -S truncate -S ftruncate -F exit=-EPERM -F auid>=1000 -F auid!=unset -k access"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Record Information on the Use of Privileged CommandsgroupAt a minimum, the audit system should collect the execution of
privileged commands for all users and root. |
contains 1 rule |
Ensure auditd Collects Information on the Use of Privileged CommandsruleThe audit system should collect information about usage of privileged
commands for all users and root. To find the relevant setuid /
setgid programs, run the following command for each local partition
PART:
$ sudo find PART -xdev -type f -perm -4000 -o -type f -perm -2000 2>/dev/null
If the auditd daemon is configured to use the augenrules
program to read audit rules during daemon startup (the default), add a line of
the following form to a file with suffix .rules in the directory
/etc/audit/rules.d for each setuid / setgid program on the system,
replacing the SETUID_PROG_PATH part with the full path of that setuid /
setgid program in the list:
-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add a line of the following
form to /etc/audit/audit.rules for each setuid / setgid program on the
system, replacing the SETUID_PROG_PATH part with the full path of that
setuid / setgid program in the list:
-a always,exit -F path=SETUID_PROG_PATH -F auid>=1000 -F auid!=unset -F key=privileged warning
This rule checks for multiple syscalls related to privileged commands;
it was written with DISA STIG in mind. Other policies should use a
separate rule for each syscall that needs to be checked. For example:
audit_rules_privileged_commands_su audit_rules_privileged_commands_umount audit_rules_privileged_commands_passwd
Rationale:Misuse of privileged functions, either intentionally or unintentionally by
authorized users, or by unauthorized external entities that have compromised system accounts,
is a serious and ongoing concern and can have significant adverse impacts on organizations.
Auditing the use of privileged functions is one way to detect such misuse and identify
the risk from insider and advanced persistent threats.
Privileged programs are subject to escalation-of-privilege attacks,
which attempt to subvert their normal role of providing some necessary but
limited capability. As such, motivation exists to monitor these programs for
unusual activity. identifiers:
CCE-27437-3 references:
4.1.12, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO08.04, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.05, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-002234, 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.5, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.3.4.5.9, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 3.9, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, 0582, 0584, 05885, 0586, 0846, 0957, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.1, A.16.1.2, A.16.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.3, A.6.2.1, A.6.2.2, AC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-2, DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, DE.DP-4, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, RS.CO-2, Req-10.2.2, SRG-OS-000327-GPOS-00127, RHEL-07-030360, SV-204516r603261_rule, SRG-OS-000471-VMM-001910 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to perform remediation for 'audit_rules_privileged_commands' rule
#
# Expects two arguments:
#
# audit_tool tool used to load audit rules
# One of 'auditctl' or 'augenrules'
#
# min_auid Minimum original ID the user logged in with
#
# Example Call(s):
#
# perform_audit_rules_privileged_commands_remediation "auditctl" "500"
# perform_audit_rules_privileged_commands_remediation "augenrules" "1000"
#
function perform_audit_rules_privileged_commands_remediation {
#
# Load function arguments into local variables
local tool="$1"
local min_auid="$2"
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: perform_audit_rules_privileged_commands_remediation 'auditctl | augenrules' '500 | 1000'"
echo "Aborting."
exit 1
fi
declare -a files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then:
# * add '/etc/audit/audit.rules'to the list of files to be inspected,
# * specify '/etc/audit/audit.rules' as the output audit file, where
# missing rules should be inserted
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect=("/etc/audit/audit.rules")
output_audit_file="/etc/audit/audit.rules"
#
# If the audit tool is 'augenrules', then:
# * add '/etc/audit/rules.d/*.rules' to the list of files to be inspected
# (split by newline),
# * specify /etc/audit/rules.d/privileged.rules' as the output file, where
# missing rules should be inserted
elif [ "$tool" == 'augenrules' ]
then
readarray -t files_to_inspect < <(find /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -print)
output_audit_file="/etc/audit/rules.d/privileged.rules"
fi
# Obtain the list of SUID/SGID binaries on the particular system (split by newline)
# into privileged_binaries array
privileged_binaries=()
readarray -t privileged_binaries < <(find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3 -o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs -o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null)
# Keep list of SUID/SGID binaries that have been already handled within some previous iteration
declare -a sbinaries_to_skip=()
# For each found sbinary in privileged_binaries list
for sbinary in "${privileged_binaries[@]}"
do
# Check if this sbinary wasn't already handled in some of the previous sbinary iterations
# Return match only if whole sbinary definition matched (not in the case just prefix matched!!!)
if [[ $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]]
then
# If so, don't process it second time & go to process next sbinary
continue
fi
# Reset the counter of inspected files when starting to check
# presence of existing audit rule for new sbinary
local count_of_inspected_files=0
# Define expected rule form for this binary
expected_rule="-a always,exit -F path=${sbinary} -F auid>=${min_auid} -F auid!=unset -F key=privileged"
# If list of audit rules files to be inspected is empty, just add new rule and move on to next binary
if [[ ${#files_to_inspect[@]} -eq 0 ]]; then
echo "$expected_rule" >> "$output_audit_file"
continue
fi
# Replace possible slash '/' character in sbinary definition so we could use it in sed expressions below
sbinary_esc=${sbinary//$'/'/$'\/'}
# For each audit rules file from the list of files to be inspected
for afile in "${files_to_inspect[@]}"
do
# Search current audit rules file's content for match. Match criteria:
# * existing rule is for the same SUID/SGID binary we are currently processing (but
# can contain multiple -F path= elements covering multiple SUID/SGID binaries)
# * existing rule contains all arguments from expected rule form (though can contain
# them in arbitrary order)
base_search=$(sed -e '/-a always,exit/!d' -e '/-F path='"${sbinary_esc}"'[^[:graph:]]/!d' \
-e '/-F path=[^[:space:]]\+/!d' \
-e '/-F auid>='"${min_auid}"'/!d' -e '/-F auid!=\(4294967295\|unset\)/!d' \
-e '/-k \|-F key=/!d' "$afile")
# Increase the count of inspected files for this sbinary
count_of_inspected_files=$((count_of_inspected_files + 1))
# Search current audit rules file's content for presence of rule pattern for this sbinary
if [[ $base_search ]]
then
# Current audit rules file already contains rule for this binary =>
# Store the exact form of found rule for this binary for further processing
concrete_rule=$base_search
# Select all other SUID/SGID binaries possibly also present in the found rule
readarray -t handled_sbinaries < <(grep -o -e "-F path=[^[:space:]]\+" <<< "$concrete_rule")
handled_sbinaries=("${handled_sbinaries[@]//-F path=/}")
# Merge the list of such SUID/SGID binaries found in this iteration with global list ignoring duplicates
readarray -t sbinaries_to_skip < <(for i in "${sbinaries_to_skip[@]}" "${handled_sbinaries[@]}"; do echo "$i"; done | sort -du)
# if there is a -F perm flag, remove it
if grep -q '.*-F\s\+perm=[rwxa]\+.*' <<< "$concrete_rule"; then
# Separate concrete_rule into three sections using hash '#'
# sign as a delimiter around rule's permission section borders
# note that the trailing space after perm flag is captured because there would be
# two consecutive spaces after joining remaining parts of the rule together
concrete_rule="$(echo "$concrete_rule" | sed -n "s/\(.*\)\+\(-F perm=[rwax]\+\ \?\)\+/\1#\2#/p")"
# Split concrete_rule into head, perm, and tail sections using hash '#' delimiter
rule_head=$(cut -d '#' -f 1 <<< "$concrete_rule")
rule_perm=$(cut -d '#' -f 2 <<< "$concrete_rule")
rule_tail=$(cut -d '#' -f 3 <<< "$concrete_rule")
# Remove permissions section from existing rule in the file
sed -i "s#${rule_head}\(.*\)${rule_tail}#${rule_head}${rule_tail}#" "$afile"
fi
# If the required audit rule for particular sbinary wasn't found yet, insert it under following conditions:
#
# * in the "auditctl" mode of operation insert particular rule each time
# (because in this mode there's only one file -- /etc/audit/audit.rules to be inspected for presence of this rule),
#
# * in the "augenrules" mode of operation insert particular rule only once and only in case we have already
# searched all of the files from /etc/audit/rules.d/*.rules location (since that audit rule can be defined
# in any of those files and if not, we want it to be inserted only once into /etc/audit/rules.d/privileged.rules file)
#
elif [ "$tool" == "auditctl" ] || [[ "$tool" == "augenrules" && $count_of_inspected_files -eq "${#files_to_inspect[@]}" ]]
then
# Check if this sbinary wasn't already handled in some of the previous afile iterations
# Return match only if whole sbinary definition matched (not in the case just prefix matched!!!)
if [[ ! $(sed -ne "\|${sbinary}|p" <<< "${sbinaries_to_skip[*]}") ]]
then
# Current audit rules file's content doesn't contain expected rule for this
# SUID/SGID binary yet => append it
echo "$expected_rule" >> "$output_audit_file"
fi
continue
fi
done
done
}
perform_audit_rules_privileged_commands_remediation "auditctl" "1000"
perform_audit_rules_privileged_commands_remediation "augenrules" "1000"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Search for privileged commands
shell: find / -not \( -fstype afs -o -fstype ceph -o -fstype cifs -o -fstype smb3
-o -fstype smbfs -o -fstype sshfs -o -fstype ncpfs -o -fstype ncp -o -fstype nfs
-o -fstype nfs4 -o -fstype gfs -o -fstype gfs2 -o -fstype glusterfs -o -fstype
gpfs -o -fstype pvfs2 -o -fstype ocfs2 -o -fstype lustre -o -fstype davfs -o -fstype
fuse.sshfs \) -type f \( -perm -4000 -o -perm -2000 \) 2> /dev/null
args:
warn: false
executable: /bin/bash
check_mode: false
register: find_result
changed_when: false
failed_when: false
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27437-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030360
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(4)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- audit_rules_privileged_commands
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for audit rule entries
find:
paths: /etc/audit/rules.d
recurse: false
contains: ^.*path={{ item }} .*$
patterns: '*.rules'
with_items:
- '{{ find_result.stdout_lines }}'
register: files_result
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27437-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030360
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(4)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- audit_rules_privileged_commands
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Overwrites the rule in rules.d
lineinfile:
path: '{{ item.1.path }}'
line: -a always,exit -F path={{ item.0.item }} -F auid>=1000 -F auid!=unset -F
key=privileged
create: false
regexp: ^.*path={{ item.0.item }} .*$
with_subelements:
- '{{ files_result.results }}'
- files
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27437-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030360
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(4)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- audit_rules_privileged_commands
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Adds the rule in rules.d
lineinfile:
path: /etc/audit/rules.d/privileged.rules
line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged
create: true
with_items:
- '{{ files_result.results }}'
when:
- files_result.results is defined and item.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27437-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030360
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(4)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- audit_rules_privileged_commands
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Inserts/replaces the rule in audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: -a always,exit -F path={{ item.item }} -F auid>=1000 -F auid!=unset -F key=privileged
create: true
regexp: ^.*path={{ item.item }} .*$
with_items:
- '{{ files_result.results }}'
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27437-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030360
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(4)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- audit_rules_privileged_commands
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Records Events that Modify Date and Time InformationgroupArbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time. All changes to the system
time should be audited. |
contains 5 rules |
Record Attempts to Alter Time Through stimeruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d for both 32 bit and 64 bit systems:
-a always,exit -F arch=b32 -S stime -F key=audit_time_rules
Since the 64 bit version of the "stime" system call is not defined in the audit
lookup table, the corresponding "-F arch=b64" form of this rule is not expected
to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule
form itself is sufficient for both 32 bit and 64 bit systems). If the
auditd daemon is configured to use the auditctl utility to
read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file for both 32 bit and 64 bit systems:
-a always,exit -F arch=b32 -S stime -F key=audit_time_rules
Since the 64 bit version of the "stime" system call is not defined in the audit
lookup table, the corresponding "-F arch=b64" form of this rule is not expected
to be defined on 64 bit systems (the aforementioned "-F arch=b32" stime rule
form itself is sufficient for both 32 bit and 64 bit systems). The -k option
allows for the specification of a key in string form that can be used for
better reporting capability through ausearch and aureport. Multiple system
calls can be defined on the same line to save space if desired, but is not
required. See an example of multiple combined system calls:
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules Rationale:Arbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time (such as sshd). All changes
to the system time should be audited. identifiers:
CCE-27299-7 references:
4.1.4, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-001487, CCI-000169, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.4.2.b Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
# Function to perform remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on RHEL, Fedora or OL systems.
# Remediation performed for both possible tools: 'auditctl' and 'augenrules'.
#
# Note: 'stime' system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore excluded from the list of time group system calls to be audited on this arch
#
# Example Call:
#
# perform_audit_adjtimex_settimeofday_stime_remediation
#
function perform_audit_adjtimex_settimeofday_stime_remediation {
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
# Create expected audit group and audit rule form for particular system call & architecture
if [ ${ARCH} = "b32" ]
then
# stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output)
# so append it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\|stime\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
elif [ ${ARCH} = "b64" ]
then
# stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore don't add it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
fi
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
perform_audit_adjtimex_settimeofday_stime_remediation
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Perform remediation of Audit rules for stime syscall for x86 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- stime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the
rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- stime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27299-7
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_stime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record Attempts to Alter Time Through clock_settimeruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S clock_settime -F a0=0x0 -F key=time-change
The -k option allows for the specification of a key in string form that can
be used for better reporting capability through ausearch and aureport.
Multiple system calls can be defined on the same line to save space if
desired, but is not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules Rationale:Arbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time (such as sshd). All changes
to the system time should be audited. identifiers:
CCE-27219-5 references:
4.1.4, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-001487, CCI-000169, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.4.2.b Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S clock_settime -F a0=.* \(-F key=\|-k \).*"
GROUP="clock_settime"
FULL_RULE="-a always,exit -F arch=$ARCH -S clock_settime -F a0=0x0 -k time-change"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27219-5
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_clock_settime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for clock_settime for x86 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- clock_settime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(?:-F[\s]+a0=0x0[\s]+)(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key time-change
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)time-change$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/time-change.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/time-change.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -F a0=0x0 -k time-change' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- clock_settime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(?:-F[\s]+a0=0x0[\s]+)(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -F a0=0x0 -k time-change' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27219-5
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_clock_settime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for clock_settime for x86_64 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- clock_settime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(?:-F[\s]+a0=0x0[\s]+)(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b64_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b64_rules_d="{{ audit_syscalls_found_b64_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key time-change
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)time-change$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/time-change.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/time-change.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -F a0=0x0 -k time-change' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- clock_settime
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(?:-F[\s]+a0=0x0[\s]+)(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b64_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b64_audit_rules="{{ audit_syscalls_found_b64_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -F a0=0x0 -k time-change' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_audit_rules < audit_syscalls_number_of_syscalls
when:
- audit_arch == "b64"
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27219-5
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_clock_settime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record attempts to alter time through settimeofdayruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S settimeofday -F key=audit_time_rules
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S settimeofday -F key=audit_time_rules
The -k option allows for the specification of a key in string form that can be
used for better reporting capability through ausearch and aureport. Multiple
system calls can be defined on the same line to save space if desired, but is
not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules Rationale:Arbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time (such as sshd). All changes
to the system time should be audited. identifiers:
CCE-27216-1 references:
4.1.4, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-001487, CCI-000169, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.4.2.b Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
# Function to perform remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on RHEL, Fedora or OL systems.
# Remediation performed for both possible tools: 'auditctl' and 'augenrules'.
#
# Note: 'stime' system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore excluded from the list of time group system calls to be audited on this arch
#
# Example Call:
#
# perform_audit_adjtimex_settimeofday_stime_remediation
#
function perform_audit_adjtimex_settimeofday_stime_remediation {
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
# Create expected audit group and audit rule form for particular system call & architecture
if [ ${ARCH} = "b32" ]
then
# stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output)
# so append it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\|stime\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
elif [ ${ARCH} = "b64" ]
then
# stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore don't add it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
fi
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
perform_audit_adjtimex_settimeofday_stime_remediation
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27216-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_settimeofday
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for settimeofday for x86 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- settimeofday
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the
rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- settimeofday
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27216-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_settimeofday
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for settimeofday for x86_64 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- settimeofday
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b64_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b64_rules_d="{{ audit_syscalls_found_b64_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the
rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- settimeofday
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b64_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b64_audit_rules="{{ audit_syscalls_found_b64_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_audit_rules < audit_syscalls_number_of_syscalls
when:
- audit_arch == "b64"
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27216-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_settimeofday
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record Attempts to Alter the localtime FileruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the default),
add the following line to a file with suffix .rules in the directory
/etc/audit/rules.d :
-w /etc/localtime -p wa -k audit_time_rules
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-w /etc/localtime -p wa -k audit_time_rules
The -k option allows for the specification of a key in string form that can
be used for better reporting capability through ausearch and aureport and
should always be used.Rationale:Arbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time (such as sshd). All changes
to the system time should be audited. identifiers:
CCE-27310-2 references:
4.1.4, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-001487, CCI-000169, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.4.2.b Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
fix_audit_watch_rule "augenrules" "/etc/localtime" "wa" "audit_time_rules"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Check if watch rule for /etc/localtime already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/localtime in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/localtime -p wa -k audit_time_rules
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/localtime already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/localtime\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/localtime in /etc/audit/audit.rules
lineinfile:
line: -w /etc/localtime -p wa -k audit_time_rules
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27310-2
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_watch_localtime
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record attempts to alter time through adjtimexruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S adjtimex -F key=audit_time_rules
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S adjtimex -F key=audit_time_rules
The -k option allows for the specification of a key in string form that can be
used for better reporting capability through ausearch and aureport. Multiple
system calls can be defined on the same line to save space if desired, but is
not required. See an example of multiple combined syscalls:
-a always,exit -F arch=b64 -S adjtimex,settimeofday -F key=audit_time_rules Rationale:Arbitrary changes to the system time can be used to obfuscate
nefarious activities in log files, as well as to confuse network services that
are highly dependent upon an accurate system time (such as sshd). All changes
to the system time should be audited. identifiers:
CCE-27290-6 references:
4.1.4, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-001487, CCI-000169, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.4.2.b Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
# Function to perform remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on RHEL, Fedora or OL systems.
# Remediation performed for both possible tools: 'auditctl' and 'augenrules'.
#
# Note: 'stime' system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore excluded from the list of time group system calls to be audited on this arch
#
# Example Call:
#
# perform_audit_adjtimex_settimeofday_stime_remediation
#
function perform_audit_adjtimex_settimeofday_stime_remediation {
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=${ARCH} -S .* -k *"
# Create expected audit group and audit rule form for particular system call & architecture
if [ ${ARCH} = "b32" ]
then
# stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output)
# so append it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\|stime\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -S stime -k audit_time_rules"
elif [ ${ARCH} = "b64" ]
then
# stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output)
# therefore don't add it to the list of time group system calls to be audited
GROUP="\(adjtimex\|settimeofday\)"
FULL_RULE="-a always,exit -F arch=${ARCH} -S adjtimex -S settimeofday -k audit_time_rules"
fi
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
perform_audit_adjtimex_settimeofday_stime_remediation
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27290-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_adjtimex
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for adjtimex for x86 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- adjtimex
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the
rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- adjtimex
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27290-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_adjtimex
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Perform remediation of Audit rules for adjtimex for x86_64 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- adjtimex
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b64_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b64_rules_d="{{ audit_syscalls_found_b64_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_time_rules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_time_rules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_time_rules.rules as the recipient for the
rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_time_rules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- adjtimex
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b64_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b64_audit_rules="{{ audit_syscalls_found_b64_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_time_rules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_audit_rules < audit_syscalls_number_of_syscalls
when:
- audit_arch == "b64"
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27290-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.4.2.b
- audit_rules_time_adjtimex
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record Attempts to Alter Logon and Logout EventsgroupThe audit system already collects login information for all users
and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d in order to watch for attempted manual
edits of files involved in storing logon events:
-w /var/log/tallylog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
-w /var/log/lastlog -p wa -k logins
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file in order to watch for unattempted manual
edits of files involved in storing logon events:
-w /var/log/tallylog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
-w /var/log/lastlog -p wa -k logins |
contains 1 rule |
Record Attempts to Alter Logon and Logout EventsruleThe audit system already collects login information for all users
and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d in order to watch for attempted manual
edits of files involved in storing logon events:
-w /var/log/tallylog -p wa -k logins
-w /var/run/faillock -p wa -k logins
-w /var/log/lastlog -p wa -k logins
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file in order to watch for unattempted manual
edits of files involved in storing logon events:
-w /var/log/tallylog -p wa -k logins
-w /var/run/faillock -p wa -k logins
-w /var/log/lastlog -p wa -k logins warning
This rule checks for multiple syscalls related to login events;
it was written with DISA STIG in mind. Other policies should use a
separate rule for each syscall that needs to be checked. For example:
audit_rules_login_events_tallylog audit_rules_login_events_faillock audit_rules_login_events_lastlog
Rationale:Manual editing of these files may indicate nefarious activity, such
as an attacker attempting to remove evidence of an intrusion. identifiers:
CCE-27204-7 references:
1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, CCI-002884, 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.2.3 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/log/tallylog" "wa" "logins"
fix_audit_watch_rule "augenrules" "/var/log/tallylog" "wa" "logins"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/run/faillock" "wa" "logins"
fix_audit_watch_rule "augenrules" "/var/run/faillock" "wa" "logins"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/log/lastlog" "wa" "logins"
fix_audit_watch_rule "augenrules" "/var/log/lastlog" "wa" "logins"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Record Events that Modify the System's Discretionary Access ControlsgroupAt a minimum, the audit system should collect file permission
changes for all users and root. Note that the "-F arch=b32" lines should be
present even on a 64 bit system. These commands identify system calls for
auditing. Even if the system is 64 bit it can still execute 32 bit system
calls. Additionally, these rules can be configured in a number of ways while
still achieving the desired effect. An example of this is that the "-S" calls
could be split up and placed on separate lines, however, this is less efficient.
Add the following to /etc/audit/audit.rules :
-a always,exit -F arch=b32 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b32 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If your system is 64 bit then these lines should be duplicated and the
arch=b32 replaced with arch=b64 as follows:
-a always,exit -F arch=b64 -S chmod,fchmod,fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S chown,fchown,fchownat,lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
-a always,exit -F arch=b64 -S setxattr,lsetxattr,fsetxattr,removexattr,lremovexattr,fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod |
contains 13 rules |
Record Events that Modify the System's Discretionary Access Controls - fchownatruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27387-0 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219, RHEL-07-030400, SV-204520r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fchownat.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fchownat tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fchownat
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fchownat.matched is defined and find_fchownat.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fchownat.files | map(attribute=''path'') | list | first }}'
when:
- find_fchownat.matched is defined and find_fchownat.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchownat rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchownat rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchownat rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchownat rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27387-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030400
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchownat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - fremovexattrruleAt a minimum, the audit system should collect file permission
changes for all users and root.
If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27353-2 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030480, SV-204528r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fremovexattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fremovexattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fremovexattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fremovexattr.matched is defined and find_fremovexattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fremovexattr.files | map(attribute=''path'') | list | first }}'
when:
- find_fremovexattr.matched is defined and find_fremovexattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fremovexattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fremovexattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fremovexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fremovexattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27353-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030480
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - fchmodruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured to
use the augenrules program to read audit rules during daemon startup
(the default), add the following line to a file with suffix .rules in
the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27393-8 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030420, SV-204522r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fchmod.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fchmod tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fchmod
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fchmod.matched is defined and find_fchmod.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fchmod.files | map(attribute=''path'') | list | first }}'
when:
- find_fchmod.matched is defined and find_fchmod.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmod rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmod rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmod rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmod rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27393-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030420
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - fchownruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27356-5 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219, RHEL-07-030380, SV-204518r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fchown.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fchown tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fchown
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fchown.matched is defined and find_fchown.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fchown.files | map(attribute=''path'') | list | first }}'
when:
- find_fchown.matched is defined and find_fchown.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchown rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchown rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchown rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27356-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030380
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - fsetxattrruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27389-6 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030450, SV-204525r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fsetxattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fsetxattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fsetxattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fsetxattr.matched is defined and find_fsetxattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fsetxattr.files | map(attribute=''path'') | list | first }}'
when:
- find_fsetxattr.matched is defined and find_fsetxattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fsetxattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fsetxattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fsetxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fsetxattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27389-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030450
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - chownruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured to
use the augenrules program to read audit rules during daemon startup
(the default), add the following line to a file with suffix .rules in
the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27364-9 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219, RHEL-07-030370, SV-204517r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S chown.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit chown tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_chown
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_chown.matched is defined and find_chown.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_chown.files | map(attribute=''path'') | list | first }}'
when:
- find_chown.matched is defined and find_chown.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chown rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chown rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chown rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27364-9
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030370
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - removexattrruleAt a minimum, the audit system should collect file permission
changes for all users and root.
If the auditd daemon is configured to use the augenrules
program to read audit rules during daemon startup (the default), add the
following line to a file with suffix .rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27367-2 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030470, SV-204527r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S removexattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S removexattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit removexattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_removexattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_removexattr.matched is defined and find_removexattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_removexattr.files | map(attribute=''path'') | list | first }}'
when:
- find_removexattr.matched is defined and find_removexattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the removexattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the removexattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the removexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the removexattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27367-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030470
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_removexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - lremovexattrruleAt a minimum, the audit system should collect file permission
changes for all users and root.
If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27410-0 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030490, SV-204529r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S lremovexattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S lremovexattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit lremovexattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_lremovexattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_lremovexattr.matched is defined and find_lremovexattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_lremovexattr.files | map(attribute=''path'') | list | first }}'
when:
- find_lremovexattr.matched is defined and find_lremovexattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lremovexattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lremovexattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lremovexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lremovexattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=unset
-F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27410-0
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030490
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lremovexattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - chmodruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured to
use the augenrules program to read audit rules during daemon startup
(the default), add the following line to a file with suffix .rules in
the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27339-1 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030410, SV-204521r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S chmod.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit chmod tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_chmod
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_chmod.matched is defined and find_chmod.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_chmod.files | map(attribute=''path'') | list | first }}'
when:
- find_chmod.matched is defined and find_chmod.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chmod rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chmod rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chmod rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the chmod rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27339-1
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030410
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_chmod
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - fchmodatruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured to
use the augenrules program to read audit rules during daemon startup
(the default), add the following line to a file with suffix .rules in
the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27388-8 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030430, SV-204523r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S fchmodat.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit fchmodat tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_fchmodat
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_fchmodat.matched is defined and find_fchmodat.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_fchmodat.files | map(attribute=''path'') | list | first }}'
when:
- find_fchmodat.matched is defined and find_fchmodat.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmodat rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmodat rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmodat rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the fchmodat rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27388-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030430
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_fchmodat
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - setxattrruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27213-8 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030440, SV-204524r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S setxattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit setxattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_setxattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_setxattr.matched is defined and find_setxattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_setxattr.files | map(attribute=''path'') | list | first }}'
when:
- find_setxattr.matched is defined and find_setxattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the setxattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the setxattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the setxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the setxattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27213-8
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030440
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_setxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - lchownruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27083-5 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, SRG-OS-000474-GPOS-00219, RHEL-07-030390, SV-204519r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S lchown.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit lchown tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_lchown
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_lchown.matched is defined and find_lchown.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_lchown.files | map(attribute=''path'') | list | first }}'
when:
- find_lchown.matched is defined and find_lchown.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lchown rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lchown rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lchown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lchown rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27083-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030390
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lchown
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Discretionary Access Controls - lsetxattrruleAt a minimum, the audit system should collect file permission
changes for all users and root. If the auditd daemon is configured
to use the augenrules program to read audit rules during daemon
startup (the default), add the following line to a file with suffix
.rules in the directory /etc/audit/rules.d :
-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod
If the system is 64 bit then also add the following line:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod warning
Note that these rules can be configured in a
number of ways while still achieving the desired effect. Here the system calls
have been placed independent of other system calls. Grouping these system
calls with others as identifying earlier in this guide is more efficient. Rationale:The changing of file permissions could indicate that a user is attempting to
gain access to information that would otherwise be disallowed. Auditing DAC modifications
can facilitate the identification of patterns of abuse among both authorized and
unauthorized users. identifiers:
CCE-27280-7 references:
4.1.10, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000172, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5, SRG-OS-000064-GPOS-00033, SRG-OS-000392-GPOS-00172, SRG-OS-000458-GPOS-00203, RHEL-07-030460, SV-204526r603261_rule, SRG-OS-000458-VMM-001810, SRG-OS-000474-VMM-001940 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S lsetxattr.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S lsetxattr -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit lsetxattr tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_lsetxattr
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_lsetxattr.matched is defined and find_lsetxattr.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_lsetxattr.files | map(attribute=''path'') | list | first }}'
when:
- find_lsetxattr.matched is defined and find_lsetxattr.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lsetxattr rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lsetxattr rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lsetxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the lsetxattr rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=unset -F
key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27280-7
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030460
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_dac_modification_lsetxattr
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Information on Kernel Modules Loading and UnloadinggroupTo capture kernel module loading and unloading events, use following lines, setting ARCH to
either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S init_module,delete_module -F key=modules
Place to add the lines depends on a way auditd daemon is configured. If it is configured
to use the augenrules program (the default), add the lines to a file with suffix
.rules in the directory /etc/audit/rules.d .
If the auditd daemon is configured to use the auditctl utility,
add the lines to file /etc/audit/audit.rules . |
contains 1 rule |
Ensure auditd Collects Information on Kernel Module Loading and UnloadingruleTo capture kernel module loading and unloading events, use following lines, setting ARCH to
either b32 for 32-bit system, or having two lines for both b32 and b64 in case your system is 64-bit:
-a always,exit -F arch=ARCH -S init_module,finit_module,delete_module -F key=modules
The place to add the lines depends on a way auditd daemon is configured. If it is configured
to use the augenrules program (the default), add the lines to a file with suffix
.rules in the directory /etc/audit/rules.d .
If the auditd daemon is configured to use the auditctl utility,
add the lines to file /etc/audit/audit.rules .Rationale:The addition/removal of kernel modules can be used to alter the behavior of
the kernel and potentially introduce malicious code into kernel space. It is important
to have an audit trail of modules that have been introduced into the kernel. identifiers:
CCE-27129-6 references:
4.1.17, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000172, 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.2.7 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit and 64-bit kernel syscall numbers not always line up =>
# it's required on a 64-bit system to check also for the presence
# of 32-bit's equivalent of the corresponding rule.
# (See `man 7 audit.rules` for details )
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
GROUP="modules"
PATTERN="-a always,exit -F arch=$ARCH -S init_module -S delete_module -S finit_module \(-F key=\|-k \).*"
FULL_RULE="-a always,exit -F arch=$ARCH -S init_module -S delete_module -S finit_module -k modules"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27129-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_kernel_module_loading
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Perform remediation of Audit rules for kernel module loading for x86 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- init_module
- delete_module
- finit_module
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key modules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)modules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/modules.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/modules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k modules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- init_module
- delete_module
- finit_module
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k modules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27129-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_kernel_module_loading
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Perform remediation of Audit rules for kernel module loading for x86_64 platform
block:
- name: Declare list of syscals
set_fact:
syscalls:
- init_module
- delete_module
- finit_module
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b64_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b64_rules_d="{{ audit_syscalls_found_b64_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key modules
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)modules$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/modules.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/modules.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k modules' }}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- init_module
- delete_module
- finit_module
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b64_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b64_audit_rules="{{ audit_syscalls_found_b64_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k modules' }}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_audit_rules < audit_syscalls_number_of_syscalls
when:
- audit_arch == "b64"
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27129-6
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_kernel_module_loading
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify User/Group InformationruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d , in order to capture events that modify
account changes:
-w /etc/group -p wa -k audit_rules_usergroup_modification
-w /etc/passwd -p wa -k audit_rules_usergroup_modification
-w /etc/gshadow -p wa -k audit_rules_usergroup_modification
-w /etc/shadow -p wa -k audit_rules_usergroup_modification
-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file, in order to capture events that modify
account changes:
-w /etc/group -p wa -k audit_rules_usergroup_modification
-w /etc/passwd -p wa -k audit_rules_usergroup_modification
-w /etc/gshadow -p wa -k audit_rules_usergroup_modification
-w /etc/shadow -p wa -k audit_rules_usergroup_modification
-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification warning
This rule checks for multiple syscalls related to account changes;
it was written with DISA STIG in mind. Other policies should use a
separate rule for each syscall that needs to be checked. For example:
audit_rules_usergroup_modification_group audit_rules_usergroup_modification_gshadow audit_rules_usergroup_modification_passwd
Rationale:In addition to auditing new user and group accounts, these watches
will alert the system administrator(s) to any modifications. Any unexpected
users, groups, or modifications should be investigated for legitimacy. identifiers:
CCE-27192-4 references:
5.2.5, 1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000018, CCI-000130, CCI-000172, CCI-001403, CCI-002130, 4.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, AC-2(4), AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.2.5, SRG-OS-000004-GPOS-00004, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000239-GPOS-00089, SRG-OS-000241-GPOS-00090, SRG-OS-000241-GPOS-00091, SRG-OS-000303-GPOS-00120, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, SRG-OS-000476-GPOS-00221, RHEL-07-030710, SV-204550r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/group" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "augenrules" "/etc/group" "wa" "audit_rules_usergroup_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/passwd" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "augenrules" "/etc/passwd" "wa" "audit_rules_usergroup_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/gshadow" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "augenrules" "/etc/gshadow" "wa" "audit_rules_usergroup_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/shadow" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "augenrules" "/etc/shadow" "wa" "audit_rules_usergroup_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/security/opasswd" "wa" "audit_rules_usergroup_modification"
fix_audit_watch_rule "augenrules" "/etc/security/opasswd" "wa" "audit_rules_usergroup_modification"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Record Events that Modify the System's Mandatory Access ControlsruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d :
-w /etc/selinux/ -p wa -k MAC-policy
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-w /etc/selinux/ -p wa -k MAC-policy Rationale:The system's mandatory access policy (SELinux) should not be
arbitrarily changed by anything other than administrator action. All changes to
MAC policy should be audited. identifiers:
CCE-27168-4 references:
4.1.7, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.8, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.5 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/selinux/" "wa" "MAC-policy"
fix_audit_watch_rule "augenrules" "/etc/selinux/" "wa" "MAC-policy"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key MAC-policy
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)MAC-policy$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use /etc/audit/rules.d/MAC-policy.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/MAC-policy.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /etc/selinux/ in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/selinux/ -p wa -k MAC-policy
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /etc/selinux/ already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/selinux/\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /etc/selinux/ in /etc/audit/audit.rules
lineinfile:
line: -w /etc/selinux/ -p wa -k MAC-policy
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27168-4
- CJIS-5.4.1.1
- NIST-800-171-3.1.8
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_mac_modification
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Record Events that Modify the System's Network EnvironmentruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d , setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification
-w /etc/issue -p wa -k audit_rules_networkconfig_modification
-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification
-w /etc/hosts -p wa -k audit_rules_networkconfig_modification
-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S sethostname,setdomainname -F key=audit_rules_networkconfig_modification
-w /etc/issue -p wa -k audit_rules_networkconfig_modification
-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification
-w /etc/hosts -p wa -k audit_rules_networkconfig_modification
-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification Rationale:The network environment should not be modified by anything other
than administrator action. Any change to network parameters should be
audited. identifiers:
CCE-27076-9 references:
4.1.6, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.5.5 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S .* -k *"
# Use escaped BRE regex to specify rule group
GROUP="set\(host\|domain\)name"
FULL_RULE="-a always,exit -F arch=$ARCH -S sethostname -S setdomainname -k audit_rules_networkconfig_modification"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
# Then perform the remediations for the watch rules
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/issue" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "augenrules" "/etc/issue" "wa" "audit_rules_networkconfig_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/issue.net" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "augenrules" "/etc/issue.net" "wa" "audit_rules_networkconfig_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/hosts" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "augenrules" "/etc/hosts" "wa" "audit_rules_networkconfig_modification"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/sysconfig/network" "wa" "audit_rules_networkconfig_modification"
fix_audit_watch_rule "augenrules" "/etc/sysconfig/network" "wa" "audit_rules_networkconfig_modification"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Remediate audit rules for network configuration for x86
block:
- name: Declare list of syscals
set_fact:
syscalls:
- sethostname
- setdomainname
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b32_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b32_rules_d="{{ audit_syscalls_found_b32_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as
the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_rules_networkconfig_modification'
}}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- sethostname
- setdomainname
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b32 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b32[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b32_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b32 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b32_audit_rules="{{ audit_syscalls_found_b32_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b32"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b32_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_rules_networkconfig_modification'
}}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b32_audit_rules < audit_syscalls_number_of_syscalls
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Remediate audit rules for network configuration for x86_64
block:
- name: Declare list of syscals
set_fact:
syscalls:
- sethostname
- setdomainname
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: '*.rules'
register: audit_syscalls_found_b64_rules_d
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/rules.d/
set_fact: audit_syscalls_matched_b64_rules_d="{{ audit_syscalls_found_b64_rules_d.results|sum(attribute='matched')|int
}}"
- name: Search /etc/audit/rules.d for other rules with the key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_syscalls_files
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as
the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
== 0
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_syscalls_files.files | map(attribute=''path'') | list | first
}}'
when: find_syscalls_files.matched is defined and find_syscalls_files.matched
> 0
- name: Insert the syscall rule in {{ all_files[0] }}
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_rules_d.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_rules_networkconfig_modification'
}}"
- name: Insert the line in {{ all_files[0] }}
lineinfile:
path: '{{ all_files[0] }}'
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_rules_d < audit_syscalls_number_of_syscalls
- name: Declare list of syscals
set_fact:
syscalls:
- sethostname
- setdomainname
- name: Declare number of syscalls
set_fact: audit_syscalls_number_of_syscalls="{{ syscalls|length|int }}"
- name: Check existence of syscalls for architecture b64 in /etc/audit/audit.rules
find:
paths: /etc/audit
contains: ^[\s]*-a[\s]+always,exit[\s]+(?:.*-F[\s]+arch=b64[\s]+)(?:.*(-S[\s]+{{
item }}[\s]+|([\s]+|[,]){{ item }}([\s]+|[,]))).*(-k[\s]+|-F[\s]+key=)[\S]+[\s]*$
patterns: audit.rules
register: audit_syscalls_found_b64_audit_rules
loop: '{{ syscalls }}'
- name: Get number of matched syscalls for architecture b64 in /etc/audit/audit.rules
set_fact: audit_syscalls_matched_b64_audit_rules="{{ audit_syscalls_found_b64_audit_rules.results|sum(attribute='matched')|int
}}"
- name: Insert the syscall rule in /etc/audit/audit.rules
block:
- name: 'Construct rule: add rule list, action and arch'
set_fact: tmpline="-a always,exit -F arch=b64"
- name: 'Construct rule: add syscalls'
set_fact: tmpline="{{ tmpline + ' -S ' + item.item }}"
loop: '{{ audit_syscalls_found_b64_audit_rules.results }}'
when: item.matched is defined and item.matched == 0
- name: 'Construct rule: add fields and key'
set_fact: tmpline="{{ tmpline + ' -k audit_rules_networkconfig_modification'
}}"
- name: Insert the line in /etc/audit/audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: '{{ tmpline }}'
create: true
state: present
when: audit_syscalls_matched_b64_audit_rules < audit_syscalls_number_of_syscalls
when:
- audit_arch == "b64"
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/issue already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the
recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/issue in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/issue already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/issue\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/issue in /etc/audit/audit.rules
lineinfile:
line: -w /etc/issue -p wa -k audit_rules_networkconfig_modification
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the
recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/issue.net in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/issue.net already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/issue.net\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/issue.net in /etc/audit/audit.rules
lineinfile:
line: -w /etc/issue.net -p wa -k audit_rules_networkconfig_modification
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/hosts already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the
recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/hosts in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/hosts already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/hosts\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/hosts in /etc/audit/audit.rules
lineinfile:
line: -w /etc/hosts -p wa -k audit_rules_networkconfig_modification
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key audit_rules_networkconfig_modification
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)audit_rules_networkconfig_modification$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/audit_rules_networkconfig_modification.rules as the
recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/audit_rules_networkconfig_modification.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/sysconfig/network in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Check if watch rule for /etc/sysconfig/network already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/etc/sysconfig/network\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Add watch rule for /etc/sysconfig/network in /etc/audit/audit.rules
lineinfile:
line: -w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27076-9
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.5
- audit_rules_networkconfig_modification
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Make the auditd Configuration ImmutableruleIf the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following line to a file with suffix .rules in the
directory /etc/audit/rules.d in order to make the auditd configuration
immutable:
-e 2
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file in order to make the auditd configuration
immutable:
-e 2
With this setting, a reboot will be required to change any audit rules.Rationale:Making the audit configuration immutable prevents accidental as
well as malicious modification of the audit rules, although it may be
problematic if legitimate changes are needed during system
operation identifiers:
CCE-27097-5 references:
4.1.18, 1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO01.06, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.3.1, 3.4.3, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.310(a)(2)(iv), 164.312(d), 164.310(d)(2)(iii), 164.312(b), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, ID.SC-4, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.5.2 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Traverse all of:
#
# /etc/audit/audit.rules, (for auditctl case)
# /etc/audit/rules.d/*.rules (for augenrules case)
#
# files to check if '-e .*' setting is present in that '*.rules' file already.
# If found, delete such occurrence since auditctl(8) manual page instructs the
# '-e 2' rule should be placed as the last rule in the configuration
find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-e[[:space:]]\+.*/d' {} ';'
# Append '-e 2' requirement at the end of both:
# * /etc/audit/audit.rules file (for auditctl case)
# * /etc/audit/rules.d/immutable.rules (for augenrules case)
for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules"
do
echo '' >> $AUDIT_FILE
echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_FILE
echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_FILE
echo '-e 2' >> $AUDIT_FILE
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Collect all files from /etc/audit/rules.d with .rules extension
find:
paths: /etc/audit/rules.d/
patterns: '*.rules'
register: find_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27097-5
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-171-3.4.3
- NIST-800-53-AC-6(9)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.2
- audit_rules_immutable
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Remove the -e option from all Audit config files
lineinfile:
path: '{{ item }}'
regexp: ^\s*(?:-e)\s+.*$
state: absent
loop: '{{ find_rules_d.files | map(attribute=''path'') | list + [''/etc/audit/audit.rules'']
}}'
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27097-5
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-171-3.4.3
- NIST-800-53-AC-6(9)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.2
- audit_rules_immutable
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add Audit -e option into /etc/audit/rules.d/immutable.rules and /etc/audit/audit.rules
lineinfile:
path: '{{ item }}'
create: true
line: -e 2
loop:
- /etc/audit/audit.rules
- /etc/audit/rules.d/immutable.rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27097-5
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-171-3.4.3
- NIST-800-53-AC-6(9)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.2
- audit_rules_immutable
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
System Audit Logs Must Have Mode 0640 or Less PermissiveruleIf log_group in /etc/audit/auditd.conf is set to a group other than the root
group account, change the mode of the audit log files with the following command:
$ sudo chmod 0640 audit_file
Otherwise, change the mode of the audit log files with the following command:
$ sudo chmod 0600 audit_file Rationale:If users can write to audit logs, audit trails can be modified or destroyed. identifiers:
CCE-27205-4 references:
1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01, 3.3.1, CCI-000162, CCI-000163, CCI-000164, CCI-001314, 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), AU-9(4), DE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.5, SRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084, RHEL-07-910055, SV-228564r606407_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then
GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ')
if ! [ "${GROUP}" == 'root' ] ; then
chmod 0640 /var/log/audit/audit.log
chmod 0440 /var/log/audit/audit.log.*
else
chmod 0600 /var/log/audit/audit.log
chmod 0400 /var/log/audit/audit.log.*
fi
else
chmod 0600 /var/log/audit/audit.log
chmod 0400 /var/log/audit/audit.log.*
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Ensure auditd Collects System Administrator ActionsruleAt a minimum, the audit system should collect administrator actions
for all users and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the default),
add the following line to a file with suffix .rules in the directory
/etc/audit/rules.d :
-w /etc/sudoers -p wa -k actions
-w /etc/sudoers.d/ -p wa -k actions
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file:
-w /etc/sudoers -p wa -k actions
-w /etc/sudoers.d/ -p wa -k actions Rationale:The actions taken by system administrators should be audited to keep a record
of what was executed on the system, as well as, for accountability purposes. identifiers:
CCE-27461-3 references:
4.1.15, 1, 11, 12, 13, 14, 15, 16, 18, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS06.03, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000126, CCI-000130, CCI-000135, CCI-000172, CCI-002884, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.8, 4.3.3.6.6, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.1, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, AC-2(7)(b), AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.2.2, Req-10.2.5.b, SRG-OS-000037-GPOS-00015, SRG-OS-000042-GPOS-00020, SRG-OS-000392-GPOS-00172, SRG-OS-000462-GPOS-00206, SRG-OS-000471-GPOS-00215, RHEL-07-030700, SV-204549r603261_rule, SRG-OS-000462-VMM-001840, SRG-OS-000471-VMM-001910 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/sudoers" "wa" "actions"
fix_audit_watch_rule "augenrules" "/etc/sudoers" "wa" "actions"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/etc/sudoers.d/" "wa" "actions"
fix_audit_watch_rule "augenrules" "/etc/sudoers.d/" "wa" "actions"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Search /etc/audit/rules.d for audit rule entries for sysadmin actions
find:
paths: /etc/audit/rules.d
recurse: false
contains: ^.*/etc/sudoers.*$
patterns: '*.rules'
register: find_audit_sysadmin_actions
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use /etc/audit/rules.d/actions.rules as the recipient for the rule
set_fact:
all_sysadmin_actions_files:
- /etc/audit/rules.d/actions.rules
when:
- find_audit_sysadmin_actions.matched is defined and find_audit_sysadmin_actions.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_sysadmin_actions_files:
- '{{ find_audit_sysadmin_actions.files | map(attribute=''path'') | list | first
}}'
when:
- find_audit_sysadmin_actions.matched is defined and find_audit_sysadmin_actions.matched
> 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Inserts/replaces audit rule for /etc/sudoers rule in rules.d
lineinfile:
path: '{{ all_sysadmin_actions_files[0] }}'
line: -w /etc/sudoers -p wa -k actions
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Inserts/replaces audit rule for /etc/sudoers.d rule in rules.d
lineinfile:
path: '{{ all_sysadmin_actions_files[0] }}'
line: -w /etc/sudoers.d/ -p wa -k actions
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Inserts/replaces audit rule for /etc/sudoers in audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: -w /etc/sudoers -p wa -k actions
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- name: Inserts/replaces audit rule for /etc/sudoers.d in audit.rules
lineinfile:
path: /etc/audit/audit.rules
line: -w /etc/sudoers.d/ -p wa -k actions
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27461-3
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030700
- NIST-800-171-3.1.7
- NIST-800-53-AC-2(7)(b)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.2
- PCI-DSS-Req-10.2.5.b
- audit_rules_sysadmin_actions
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Record Attempts to Alter Process and Session Initiation InformationruleThe audit system already collects process information for all
users and root. If the auditd daemon is configured to use the
augenrules program to read audit rules during daemon startup (the
default), add the following lines to a file with suffix .rules in the
directory /etc/audit/rules.d in order to watch for attempted manual
edits of files involved in storing such process information:
-w /var/run/utmp -p wa -k session
-w /var/log/btmp -p wa -k session
-w /var/log/wtmp -p wa -k session
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following lines to
/etc/audit/audit.rules file in order to watch for attempted manual
edits of files involved in storing such process information:
-w /var/run/utmp -p wa -k session
-w /var/log/btmp -p wa -k session
-w /var/log/wtmp -p wa -k session Rationale:Manual editing of these files may indicate nefarious activity, such
as an attacker attempting to remove evidence of an intrusion. identifiers:
CCE-27301-1 references:
4.1.9, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, 0582, 0584, 05885, 0586, 0846, 0957, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.2.3 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Perform the remediation
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/run/utmp" "wa" "session"
fix_audit_watch_rule "augenrules" "/var/run/utmp" "wa" "session"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/log/btmp" "wa" "session"
fix_audit_watch_rule "augenrules" "/var/log/btmp" "wa" "session"
# Function to fix audit file system object watch rule for given path:
# * if rule exists, also verifies the -w bits match the requirements
# * if rule doesn't exist yet, appends expected rule form to $files_to_inspect
# audit rules file, depending on the tool which was used to load audit rules
#
# Expects four arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules'
# * path value of -w audit rule's argument
# * required access bits value of -p audit rule's argument
# * key value of -k audit rule's argument
#
# Example call:
#
# fix_audit_watch_rule "auditctl" "/etc/localtime" "wa" "audit_time_rules"
#
function fix_audit_watch_rule {
# Load function arguments into local variables
local tool="$1"
local path="$2"
local required_access_bits="$3"
local key="$4"
# Check sanity of the input
if [ $# -ne "4" ]
then
echo "Usage: fix_audit_watch_rule 'tool' 'path' 'bits' 'key'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
declare -a files_to_inspect
files_to_inspect=()
# Check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
exit 1
# If the audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# into the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules')
# If the audit is 'augenrules', then check if rule is already defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection.
# If rule isn't defined, add '/etc/audit/rules.d/$key.rules' to list of files for inspection.
elif [ "$tool" == 'augenrules' ]
then
readarray -t matches < <(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules)
# For each of the matched entries
for match in "${matches[@]}"
do
# Extract filepath from the match
rulesd_audit_file=$(echo $match | cut -f1 -d ':')
# Append that path into list of files for inspection
files_to_inspect+=("$rulesd_audit_file")
done
# Case when particular audit rule isn't defined yet
if [ "${#files_to_inspect[@]}" -eq "0" ]
then
# Append '/etc/audit/rules.d/$key.rules' into list of files for inspection
local key_rule_file="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$key_rule_file" ]
then
touch "$key_rule_file"
chmod 0640 "$key_rule_file"
fi
files_to_inspect+=("$key_rule_file")
fi
fi
# Finally perform the inspection and possible subsequent audit rule
# correction for each of the files previously identified for inspection
for audit_rules_file in "${files_to_inspect[@]}"
do
# Check if audit watch file system object rule for given path already present
if grep -q -P -- "[\s]*-w[\s]+$path" "$audit_rules_file"
then
# Rule is found => verify yet if existing rule definition contains
# all of the required access type bits
# Escape slashes in path for use in sed pattern below
local esc_path=${path//$'/'/$'\/'}
# Define BRE whitespace class shortcut
local sp="[[:space:]]"
# Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule
current_access_bits=$(sed -ne "s/$sp*-w$sp\+$esc_path$sp\+-p$sp\+\([rxwa]\{1,4\}\).*/\1/p" "$audit_rules_file")
# Split required access bits string into characters array
# (to check bit's presence for one bit at a time)
for access_bit in $(echo "$required_access_bits" | grep -o .)
do
# For each from the required access bits (e.g. 'w', 'a') check
# if they are already present in current access bits for rule.
# If not, append that bit at the end
if ! grep -q "$access_bit" <<< "$current_access_bits"
then
# Concatenate the existing mask with the missing bit
current_access_bits="$current_access_bits$access_bit"
fi
done
# Propagate the updated rule's access bits (original + the required
# ones) back into the /etc/audit/audit.rules file for that rule
sed -i "s/\($sp*-w$sp\+$esc_path$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)/\1$current_access_bits\3/" "$audit_rules_file"
else
# Rule isn't present yet. Append it at the end of $audit_rules_file file
# with proper key
echo "-w $path -p $required_access_bits -k $key" >> "$audit_rules_file"
fi
done
}
fix_audit_watch_rule "auditctl" "/var/log/wtmp" "wa" "session"
fix_audit_watch_rule "augenrules" "/var/log/wtmp" "wa" "session"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key session
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)session$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/session.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/run/utmp in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /var/run/utmp -p wa -k session
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/run/utmp already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/var/run/utmp\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/run/utmp in /etc/audit/audit.rules
lineinfile:
line: -w /var/run/utmp -p wa -k session
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key session
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)session$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/session.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/btmp in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /var/log/btmp -p wa -k session
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/btmp already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/var/log/btmp\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/btmp in /etc/audit/audit.rules
lineinfile:
line: -w /var/log/btmp -p wa -k session
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/rules.d/
find:
paths: /etc/audit/rules.d
contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+
patterns: '*.rules'
register: find_existing_watch_rules_d
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other rules with specified key session
find:
paths: /etc/audit/rules.d
contains: ^.*(?:-F key=|-k\s+)session$
patterns: '*.rules'
register: find_watch_key
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use /etc/audit/rules.d/session.rules as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/session.rules
when:
- find_watch_key.matched is defined and find_watch_key.matched == 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_watch_key.files | map(attribute=''path'') | list | first }}'
when:
- find_watch_key.matched is defined and find_watch_key.matched > 0 and find_existing_watch_rules_d.matched
is defined and find_existing_watch_rules_d.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/wtmp in /etc/audit/rules.d/
lineinfile:
path: '{{ all_files[0] }}'
line: -w /var/log/wtmp -p wa -k session
create: true
when:
- find_existing_watch_rules_d.matched is defined and find_existing_watch_rules_d.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Check if watch rule for /var/log/wtmp already exists in /etc/audit/audit.rules
find:
paths: /etc/audit/
contains: ^\s*-w\s+/var/log/wtmp\s+-p\s+wa(\s|$)+
patterns: audit.rules
register: find_existing_watch_audit_rules
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Add watch rule for /var/log/wtmp in /etc/audit/audit.rules
lineinfile:
line: -w /var/log/wtmp -p wa -k session
state: present
dest: /etc/audit/audit.rules
create: true
when:
- find_existing_watch_audit_rules.matched is defined and find_existing_watch_audit_rules.matched
== 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27301-1
- CJIS-5.4.1.1
- NIST-800-171-3.1.7
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.3
- audit_rules_session_events
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
Ensure auditd Collects Information on Exporting to Media (successful)ruleAt a minimum, the audit system should collect media exportation
events for all users and root. If the auditd daemon is configured to
use the augenrules program to read audit rules during daemon startup
(the default), add the following line to a file with suffix .rules in
the directory /etc/audit/rules.d , setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export
If the auditd daemon is configured to use the auditctl
utility to read audit rules during daemon startup, add the following line to
/etc/audit/audit.rules file, setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S mount -F auid>=1000 -F auid!=unset -F key=export Rationale:The unauthorized exportation of data to external media could result in an information leak
where classified information, Privacy Act information, and intellectual property could be lost. An audit
trail should be created each time a filesystem is mounted to help identify and guard against information
loss. identifiers:
CCE-27447-2 references:
4.1.13, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.1.7, CCI-000135, CCI-002884, 164.308(a)(1)(ii)(D), 164.308(a)(3)(ii)(A), 164.308(a)(5)(ii)(C), 164.312(a)(2)(i), 164.312(b), 164.312(d), 164.312(e), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AU-2(d), AU-12(c), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.2.7, SRG-OS-000042-GPOS-00020, SRG-OS-000392-GPOS-00172, RHEL-07-030740, SV-204552r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
[ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64")
for ARCH in "${RULE_ARCHS[@]}"
do
PATTERN="-a always,exit -F arch=$ARCH -S mount.*"
GROUP="perm_mod"
FULL_RULE="-a always,exit -F arch=$ARCH -S mount -F auid>=1000 -F auid!=unset -F key=perm_mod"
# Perform the remediation for both possible tools: 'auditctl' and 'augenrules'
# Function to fix syscall audit rule for given system call. It is
# based on example audit syscall rule definitions as outlined in
# /usr/share/doc/audit-2.3.7/stig.rules file provided with the audit
# package. It will combine multiple system calls belonging to the same
# syscall group into one audit rule (rather than to create audit rule per
# different system call) to avoid audit infrastructure performance penalty
# in the case of 'one-audit-rule-definition-per-one-system-call'. See:
#
# https://www.redhat.com/archives/linux-audit/2014-November/msg00009.html
#
# for further details.
#
# Expects five arguments (each of them is required) in the form of:
# * audit tool tool used to load audit rules,
# either 'auditctl', or 'augenrules
# * audit rules' pattern audit rule skeleton for same syscall
# * syscall group greatest common string this rule shares
# with other rules from the same group
# * architecture architecture this rule is intended for
# * full form of new rule to add expected full form of audit rule as to be
# added into audit.rules file
#
# Note: The 2-th up to 4-th arguments are used to determine how many existing
# audit rules will be inspected for resemblance with the new audit rule
# (5-th argument) the function is going to add. The rule's similarity check
# is performed to optimize audit.rules definition (merge syscalls of the same
# group into one rule) to avoid the "single-syscall-per-audit-rule" performance
# penalty.
#
# Example call:
#
# See e.g. 'audit_rules_file_deletion_events.sh' remediation script
#
function fix_audit_syscall_rule {
# Load function arguments into local variables
local tool="$1"
local pattern="$2"
local group="$3"
local arch="$4"
local full_rule="$5"
# Check sanity of the input
if [ $# -ne "5" ]
then
echo "Usage: fix_audit_syscall_rule 'tool' 'pattern' 'group' 'arch' 'full rule'"
echo "Aborting."
exit 1
fi
# Create a list of audit *.rules files that should be inspected for presence and correctness
# of a particular audit rule. The scheme is as follows:
#
# -----------------------------------------------------------------------------------------
# Tool used to load audit rules | Rule already defined | Audit rules file to inspect |
# -----------------------------------------------------------------------------------------
# auditctl | Doesn't matter | /etc/audit/audit.rules |
# -----------------------------------------------------------------------------------------
# augenrules | Yes | /etc/audit/rules.d/*.rules |
# augenrules | No | /etc/audit/rules.d/$key.rules |
# -----------------------------------------------------------------------------------------
#
declare -a files_to_inspect
retval=0
# First check sanity of the specified audit tool
if [ "$tool" != 'auditctl' ] && [ "$tool" != 'augenrules' ]
then
echo "Unknown audit rules loading tool: $1. Aborting."
echo "Use either 'auditctl' or 'augenrules'!"
return 1
# If audit tool is 'auditctl', then add '/etc/audit/audit.rules'
# file to the list of files to be inspected
elif [ "$tool" == 'auditctl' ]
then
files_to_inspect+=('/etc/audit/audit.rules' )
# If audit tool is 'augenrules', then check if the audit rule is defined
# If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection
# If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection
elif [ "$tool" == 'augenrules' ]
then
# Extract audit $key from audit rule so we can use it later
matches=()
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)' '|' "$full_rule" : '.*-F[[:space:]]key=\([^[:space:]]\+\)')
readarray -t matches < <(sed -s -n -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d;F" /etc/audit/rules.d/*.rules)
if [ $? -ne 0 ]
then
retval=1
fi
for match in "${matches[@]}"
do
files_to_inspect+=("${match}")
done
# Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet
if [ ${#files_to_inspect[@]} -eq "0" ]
then
file_to_inspect="/etc/audit/rules.d/$key.rules"
files_to_inspect=("$file_to_inspect")
if [ ! -e "$file_to_inspect" ]
then
touch "$file_to_inspect"
chmod 0640 "$file_to_inspect"
fi
fi
fi
#
# Indicator that we want to append $full_rule into $audit_file by default
local append_expected_rule=0
for audit_file in "${files_to_inspect[@]}"
do
# Filter existing $audit_file rules' definitions to select those that:
# * follow the rule pattern, and
# * meet the hardware architecture requirement, and
# * are current syscall group specific
readarray -t existing_rules < <(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file")
if [ $? -ne 0 ]
then
retval=1
fi
# Process rules found case-by-case
for rule in "${existing_rules[@]}"
do
# Found rule is for same arch & key, but differs (e.g. in count of -S arguments)
if [ "${rule}" != "${full_rule}" ]
then
# If so, isolate just '(-S \w)+' substring of that rule
rule_syscalls=$(echo "$rule" | grep -o -P '(-S \w+ )+')
# Check if list of '-S syscall' arguments of that rule is subset
# of '-S syscall' list of expected $full_rule
if grep -q -- "$rule_syscalls" <<< "$full_rule"
then
# Rule is covered (i.e. the list of -S syscalls for this rule is
# subset of -S syscalls of $full_rule => existing rule can be deleted
# Thus delete the rule from audit.rules & our array
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
existing_rules=("${existing_rules[@]//$rule/}")
else
# Rule isn't covered by $full_rule - it besides -S syscall arguments
# for this group contains also -S syscall arguments for other syscall
# group. Example: '-S lchown -S fchmod -S fchownat' => group='chown'
# since 'lchown' & 'fchownat' share 'chown' substring
# Therefore:
# * 1) delete the original rule from audit.rules
# (original '-S lchown -S fchmod -S fchownat' rule would be deleted)
# * 2) delete the -S syscall arguments for this syscall group, but
# keep those not belonging to this syscall group
# (original '-S lchown -S fchmod -S fchownat' would become '-S fchmod'
# * 3) append the modified (filtered) rule again into audit.rules
# if the same rule not already present
#
# 1) Delete the original rule
sed -i -e "\;${rule};d" "$audit_file"
if [ $? -ne 0 ]
then
retval=1
fi
# 2) Delete syscalls for this group, but keep those from other groups
# Convert current rule syscall's string into array splitting by '-S' delimiter
IFS_BKP="$IFS"
IFS=$'-S'
read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
IFS="$IFS_BKP"
# Splitting by "-S" can't be replaced by the readarray functionality easily
# Declare new empty string to hold '-S syscall' arguments from other groups
new_syscalls_for_rule=''
# Walk through existing '-S syscall' arguments
for syscall_arg in "${rule_syscalls_as_array[@]}"
do
# Skip empty $syscall_arg values
if [ "$syscall_arg" == '' ]
then
continue
fi
# If the '-S syscall' doesn't belong to current group add it to the new list
# (together with adding '-S' delimiter back for each of such item found)
if grep -q -v -- "$group" <<< "$syscall_arg"
then
new_syscalls_for_rule="$new_syscalls_for_rule -S $syscall_arg"
fi
done
# Replace original '-S syscall' list with the new one for this rule
updated_rule=${rule//$rule_syscalls/$new_syscalls_for_rule}
# Squeeze repeated whitespace characters in rule definition (if any) into one
updated_rule=$(echo "$updated_rule" | tr -s '[:space:]')
# 3) Append the modified / filtered rule again into audit.rules
# (but only in case it's not present yet to prevent duplicate definitions)
if ! grep -q -- "$updated_rule" "$audit_file"
then
echo "$updated_rule" >> "$audit_file"
fi
fi
else
# $audit_file already contains the expected rule form for this
# architecture & key => don't insert it second time
append_expected_rule=1
fi
done
# We deleted all rules that were subset of the expected one for this arch & key.
# Also isolated rules containing system calls not from this system calls group.
# Now append the expected rule if it's not present in $audit_file yet
if [[ ${append_expected_rule} -eq "0" ]]
then
echo "$full_rule" >> "$audit_file"
fi
done
return $retval
}
fix_audit_syscall_rule "augenrules" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set architecture for audit mount tasks
set_fact:
audit_arch: b{{ ansible_architecture | regex_replace('.*(\d\d$)','\1') }}
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: /etc/audit/rules.d
recurse: false
contains: -F key=perm_mod$
patterns: '*.rules'
register: find_mount
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: If existing DAC ruleset not found, use /etc/audit/rules.d/privileged.rules
as the recipient for the rule
set_fact:
all_files:
- /etc/audit/rules.d/privileged.rules
when:
- find_mount.matched is defined and find_mount.matched == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Use matched file as the recipient for the rule
set_fact:
all_files:
- '{{ find_mount.files | map(attribute=''path'') | list | first }}'
when:
- find_mount.matched is defined and find_mount.matched > 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the mount rule in rules.d when on x86
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the mount rule in rules.d when on x86_64
lineinfile:
path: '{{ all_files[0] }}'
line: -a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -F key=perm_mod
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the mount rule in /etc/audit/audit.rules when on x86
lineinfile:
line: -a always,exit -F arch=b32 -S mount -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
- name: Inserts/replaces the mount rule in audit.rules when on x86_64
lineinfile:
line: -a always,exit -F arch=b64 -S mount -F auid>=1000 -F auid!=unset -F key=perm_mod
state: present
dest: /etc/audit/audit.rules
create: true
when:
- audit_arch is defined and audit_arch == 'b64'
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27447-2
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030740
- NIST-800-171-3.1.7
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.2.7
- audit_rules_media_export
- low_complexity
- low_disruption
- medium_severity
- reboot_required
- restrict_strategy
|
System Audit Logs Must Be Owned By RootruleAll audit logs must be owned by root user and group. By default, the path for audit log is /var/log/audit/ .
To properly set the owner of /var/log/audit , run the command:
$ sudo chown root /var/log/audit
To properly set the owner of /var/log/audit/* , run the command:
$ sudo chown root /var/log/audit/* Rationale:Unauthorized disclosure of audit records can reveal system and configuration data to
attackers, thus compromising its confidentiality. identifiers:
CCE-80125-8 references:
1, 11, 12, 13, 14, 15, 16, 18, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO01.06, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, DSS06.02, MEA02.01, 3.3.1, CCI-000162, CCI-000163, CCI-000164, CCI-001314, 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.7.3, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 5.2, SR 6.1, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.16.1.4, A.16.1.5, A.16.1.7, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), AU-9(4), DE.AE-3, DE.AE-5, PR.AC-4, PR.DS-5, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.5.1, SRG-OS-000057-GPOS-00027, SRG-OS-000058-GPOS-00028, SRG-OS-000059-GPOS-00029, SRG-OS-000206-GPOS-00084, RHEL-07-910055, SV-228564r606407_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then
GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ')
if ! [ "${GROUP}" == 'root' ] ; then
chown root.${GROUP} /var/log/audit
chown root.${GROUP} /var/log/audit/audit.log*
else
chown root.root /var/log/audit
chown root.root /var/log/audit/audit.log*
fi
else
chown root.root /var/log/audit
chown root.root /var/log/audit/audit.log*
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
|
Configure auditd Data RetentiongroupThe audit system writes data to /var/log/audit/audit.log . By default,
auditd rotates 5 logs by size (6MB), retaining a maximum of 30MB of
data in total, and refuses to write entries when the disk is too
full. This minimizes the risk of audit data filling its partition
and impacting other services. This also minimizes the risk of the audit
daemon temporarily disabling the system if it cannot write audit log (which
it can be configured to do).
For a busy
system or a system which is thoroughly auditing system activity, the default settings
for data retention may be
insufficient. The log file size needed will depend heavily on what types
of events are being audited. First configure auditing to log all the events of
interest. Then monitor the log size manually for awhile to determine what file
size will allow you to keep the required data for the correct time period.
Using a dedicated partition for /var/log/audit prevents the
auditd logs from disrupting system functionality if they fill, and,
more importantly, prevents other activity in /var from filling the
partition and stopping the audit trail. (The audit logs are size-limited and
therefore unlikely to grow without bound unless configured to do so.) Some
machines may have requirements that no actions occur which cannot be audited.
If this is the case, then auditd can be configured to halt the machine
if it runs out of space. Note: Since older logs are rotated,
configuring auditd this way does not prevent older logs from being
rotated away before they can be viewed.
If your system is configured to halt when logging cannot be performed, make
sure this can never happen under normal circumstances! Ensure that
/var/log/audit is on its own partition, and that this partition is
larger than the maximum amount of data auditd will retain
normally. |
contains 7 rules |
Configure auditd mail_acct Action on Low Disk SpaceruleThe auditd service can be configured to send email to
a designated account in certain situations. Add or correct the following line
in /etc/audit/auditd.conf to ensure that administrators are notified
via email for those situations:
action_mail_acct = root Rationale:Email sent to the root account is typically aliased to the
administrators of the system, who can take appropriate action. identifiers:
CCE-27394-6 references:
4.1.1.2, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 3.3.1, CCI-000139, CCI-001855, 164.312(a)(2)(ii), 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2, A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1, IA-5(1), AU-5(a), AU-5(2), CM-6(a), DE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7.a, SRG-OS-000343-GPOS-00134, RHEL-07-030350, SV-204515r603261_rule, SRG-OS-000046-VMM-000210, SRG-OS-000343-VMM-001240 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_action_mail_acct="root"
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^action_mail_acct' "$var_auditd_action_mail_acct" "CCE-27394-6"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_action_mail_acct # promote to variable
set_fact:
var_auditd_action_mail_acct: !!str root
tags:
- always
- name: Configure auditd mail_acct Action on Low Disk Space
lineinfile:
dest: /etc/audit/auditd.conf
line: action_mail_acct = {{ var_auditd_action_mail_acct }}
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27394-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030350
- NIST-800-171-3.3.1
- NIST-800-53-AU-5(2)
- NIST-800-53-AU-5(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)
- PCI-DSS-Req-10.7.a
- auditd_data_retention_action_mail_acct
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Configure auditd Max Log File SizeruleDetermine the amount of audit data (in megabytes)
which should be retained in each log file. Edit the file
/etc/audit/auditd.conf . Add or modify the following line, substituting
the correct value of 6 for STOREMB:
max_log_file = STOREMB
Set the value to 6 (MB) or higher for general-purpose systems.
Larger values, of course,
support retention of even more audit data.Rationale:The total storage for audit log files must be large enough to retain
log information over the period required. This is a function of the maximum
log file size and the number of logs retained. identifiers:
CCE-27319-3 references:
4.1.1.1, 1, 11, 12, 13, 14, 15, 16, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, AU-11, CM-6(a), DE.AE-3, DE.AE-5, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_max_log_file="6"
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^max_log_file' "$var_auditd_max_log_file" "CCE-27319-3"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_max_log_file # promote to variable
set_fact:
var_auditd_max_log_file: !!str 6
tags:
- always
- name: Configure auditd Max Log File Size
lineinfile:
dest: /etc/audit/auditd.conf
regexp: ^\s*max_log_file\s*=\s*.*$
line: max_log_file = {{ var_auditd_max_log_file }}
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27319-3
- CJIS-5.4.1.1
- NIST-800-53-AU-11
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.7
- auditd_data_retention_max_log_file
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Configure auditd Number of Logs RetainedruleDetermine how many log files
auditd should retain when it rotates logs.
Edit the file /etc/audit/auditd.conf . Add or modify the following
line, substituting NUMLOGS with the correct value of 5:
num_logs = NUMLOGS
Set the value to 5 for general-purpose systems.
Note that values less than 2 result in no log rotation.Rationale:The total storage for audit log files must be large enough to retain
log information over the period required. This is a function of the maximum log
file size and the number of logs retained. identifiers:
CCE-27348-2 references:
1, 11, 12, 13, 14, 15, 16, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 3.3.1, 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, AU-11, CM-6(a), DE.AE-3, DE.AE-5, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_num_logs="5"
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^num_logs' "$var_auditd_num_logs" "CCE-27348-2"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_num_logs # promote to variable
set_fact:
var_auditd_num_logs: !!str 5
tags:
- always
- name: Configure auditd Number of Logs Retained
lineinfile:
dest: /etc/audit/auditd.conf
line: num_logs = {{ var_auditd_num_logs }}
regexp: ^\s*num_logs\s*=\s*.*$
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27348-2
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AU-11
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.7
- auditd_data_retention_num_logs
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Configure auditd admin_space_left Action on Low Disk SpaceruleThe auditd service can be configured to take an action
when disk space is running low but prior to running out of space completely.
Edit the file /etc/audit/auditd.conf . Add or modify the following line,
substituting ACTION appropriately:
admin_space_left_action = ACTION
Set this value to single to cause the system to switch to single user
mode for corrective action. Acceptable values also include suspend and
halt . For certain systems, the need for availability
outweighs the need to log all actions, and a different setting should be
determined. Details regarding all possible values for ACTION are described in the
auditd.conf man page.Rationale:Administrators should be made aware of an inability to record
audit records. If a separate partition or logical volume of adequate size
is used, running low on space for audit records should never occur. identifiers:
CCE-27370-6 references:
4.1.1.2, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 3.3.1, CCI-000140, CCI-001343, CCI-001855, 164.312(a)(2)(ii), 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2, A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1, AU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a), DE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7, SRG-OS-000343-GPOS-00134, RHEL-07-030340, SV-204514r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_admin_space_left_action="single"
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^admin_space_left_action' "$var_auditd_admin_space_left_action" "CCE-27370-6"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_admin_space_left_action # promote to variable
set_fact:
var_auditd_admin_space_left_action: !!str single
tags:
- always
- name: Configure auditd admin_space_left Action on Low Disk Space
lineinfile:
dest: /etc/audit/auditd.conf
line: admin_space_left_action = {{ var_auditd_admin_space_left_action }}
regexp: ^\s*admin_space_left_action\s*=\s*.*$
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27370-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030340
- NIST-800-171-3.3.1
- NIST-800-53-AU-5(1)
- NIST-800-53-AU-5(2)
- NIST-800-53-AU-5(4)
- NIST-800-53-AU-5(b)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.7
- auditd_data_retention_admin_space_left_action
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Configure auditd max_log_file_action Upon Reaching Maximum Log SizeruleThe default action to take when the logs reach their maximum size
is to rotate the log files, discarding the oldest one. To configure the action taken
by auditd , add or correct the line in /etc/audit/auditd.conf :
max_log_file_action = ACTION
Possible values for ACTION are described in the auditd.conf man
page. These include:
syslog suspend rotate keep_logs
Set the ACTION to rotate to ensure log rotation
occurs. This is the default. The setting is case-insensitive.Rationale:Automatically rotating logs (by setting this to rotate )
minimizes the chances of the system unexpectedly running out of disk space by
being overwhelmed with log data. However, for systems that must never discard
log data, or which use external processes to transfer it and reclaim space,
keep_logs can be employed. identifiers:
CCE-27231-0 references:
4.1.1.3, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 164.312(a)(2)(ii), 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2, A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1, AU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a), DE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7, SRG-OS-000047-GPOS-00023 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_max_log_file_action="rotate"
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^max_log_file_action' "$var_auditd_max_log_file_action" "CCE-27231-0"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_max_log_file_action # promote to variable
set_fact:
var_auditd_max_log_file_action: !!str rotate
tags:
- always
- name: Configure auditd max_log_file_action Upon Reaching Maximum Log Size
lineinfile:
dest: /etc/audit/auditd.conf
line: max_log_file_action = {{ var_auditd_max_log_file_action }}
regexp: ^\s*max_log_file_action\s*=\s*.*$
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27231-0
- CJIS-5.4.1.1
- NIST-800-53-AU-5(1)
- NIST-800-53-AU-5(2)
- NIST-800-53-AU-5(4)
- NIST-800-53-AU-5(b)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.7
- auditd_data_retention_max_log_file_action
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Configure auditd to use audispd's syslog pluginruleTo configure the auditd service to use the
syslog plug-in of the audispd audit event multiplexor, set
the active line in /etc/audisp/plugins.d/syslog.conf to yes .
Restart the auditd service:
$ sudo service auditd restart Rationale:The auditd service does not include the ability to send audit
records to a centralized server for management directly. It does, however,
include a plug-in for audit event multiplexor (audispd) to pass audit records
to the local syslog server identifiers:
CCE-27341-7 references:
1, 11, 12, 13, 14, 15, 16, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, BAI03.05, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 3.3.1, CCI-000136, 164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(B), 164.308(a)(5)(ii)(C), 164.308(a)(6)(ii), 164.308(a)(8), 164.310(d)(2)(iii), 164.312(b), 164.314(a)(2)(i)(C), 164.314(a)(2)(iii), 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, AU-4(1), CM-6(a), DE.AE-3, DE.AE-5, PR.PT-1, RS.AN-1, RS.AN-4, FAU_GEN.1.1.c, Req-10.5.3, SRG-OS-000479-GPOS-00224, SRG-OS-000342-GPOS-00133, SRG-OS-000051-VMM-000230, SRG-OS-000058-VMM-000270, SRG-OS-000059-VMM-000280, SRG-OS-000479-VMM-001990, SRG-OS-000479-VMM-001990 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_syslog_active="yes"
AUDISP_SYSLOGCONFIG=/etc/audisp/plugins.d/syslog.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDISP_SYSLOGCONFIG '^active' "$var_syslog_active" "CCE-27341-7"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: enable syslog plugin
lineinfile:
dest: /etc/audisp/plugins.d/syslog.conf
regexp: ^active
line: active = yes
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27341-7
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AU-4(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.5.3
- auditd_audispd_syslog_plugin_activated
- configure_strategy
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Configure auditd space_left Action on Low Disk SpaceruleThe auditd service can be configured to take an action
when disk space starts to run low.
Edit the file /etc/audit/auditd.conf . Modify the following line,
substituting ACTION appropriately:
space_left_action = ACTION
Possible values for ACTION are described in the auditd.conf man page.
These include:
syslog email exec suspend single halt
Set this to email (instead of the default,
which is suspend ) as it is more likely to get prompt attention. Acceptable values
also include suspend , single , and halt .Rationale:Notifying administrators of an impending disk space problem may
allow them to take corrective action prior to any disruption. identifiers:
CCE-27375-5 references:
4.1.1.2, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO11.04, APO12.06, APO13.01, BAI03.05, BAI04.04, BAI08.02, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.04, DSS05.07, MEA02.01, 3.3.1, CCI-001855, 164.312(a)(2)(ii), 4.2.3.10, 4.3.3.3.9, 4.3.3.5.8, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 2.10, SR 2.11, SR 2.12, SR 2.8, SR 2.9, SR 6.1, SR 7.1, SR 7.2, A.12.1.3, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.16.1.4, A.16.1.5, A.16.1.7, A.17.2.1, AU-5(b), AU-5(2), AU-5(1), AU-5(4), CM-6(a), DE.AE-3, DE.AE-5, PR.DS-4, PR.PT-1, RS.AN-1, RS.AN-4, Req-10.7, SRG-OS-000343-GPOS-00134, RHEL-07-030340, SV-204514r603261_rule, SRG-OS-000343-VMM-001240 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
var_auditd_space_left_action="email"
#
# If space_left_action present in /etc/audit/auditd.conf, change value
# to var_auditd_space_left_action, else
# add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf
#
AUDITCONFIG=/etc/audit/auditd.conf
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append $AUDITCONFIG '^space_left_action' "$var_auditd_space_left_action" "CCE-27375-5"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value var_auditd_space_left_action # promote to variable
set_fact:
var_auditd_space_left_action: !!str email
tags:
- always
- name: Configure auditd space_left Action on Low Disk Space
lineinfile:
dest: /etc/audit/auditd.conf
line: space_left_action = {{ var_auditd_space_left_action }}
regexp: ^\s*space_left_action\s*=\s*.*$
state: present
create: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27375-5
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030340
- NIST-800-171-3.3.1
- NIST-800-53-AU-5(1)
- NIST-800-53-AU-5(2)
- NIST-800-53-AU-5(4)
- NIST-800-53-AU-5(b)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.7
- auditd_data_retention_space_left_action
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
|
Enable auditd ServiceruleThe auditd service is an essential userspace component of
the Linux Auditing System, as it is responsible for writing audit records to
disk.
The auditd service can be enabled with the following command:
$ sudo systemctl enable auditd.service Rationale:Without establishing what type of events occurred, it would be difficult
to establish, correlate, and investigate the events leading up to an outage or attack.
Ensuring the auditd service is active ensures audit records
generated by the kernel are appropriately recorded.
Additionally, a properly configured audit subsystem ensures that actions of
individual system users can be uniquely traced to those users so they
can be held accountable for their actions. identifiers:
CCE-27407-6 references:
4.1.2, 1, 11, 12, 13, 14, 15, 16, 19, 2, 3, 4, 5, 6, 7, 8, 9, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.03, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS03.05, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.3.1, 3.3.2, 3.3.6, CCI-000126, CCI-000130, CCI-000131, CCI-000132, CCI-000133, CCI-000134, CCI-000134, CCI-000135, CCI-001464, CCI-001487, CCI-001814, 164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(C), 164.310(a)(2)(iv), 164.310(d)(2)(iii), 164.312(b), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 6.2, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.7, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AC-2(g), AU-3, AU-10, AU-2(d), AU-12(c), AU-14(1), AC-6(9), CM-6(a), DE.AE-3, DE.AE-5, DE.CM-1, DE.CM-3, DE.CM-7, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.1, SRG-OS-000037-GPOS-00015, SRG-OS-000038-GPOS-00016, SRG-OS-000039-GPOS-00017, SRG-OS-000040-GPOS-00018, SRG-OS-000042-GPOS-00021, SRG-OS-000254-GPOS-00095, SRG-OS-000255-GPOS-00096, SRG-OS-000365-GPOS-00152, RHEL-07-030000, SV-204503r603261_rule, SRG-OS-000037-VMM-000150, SRG-OS-000063-VMM-000310, SRG-OS-000038-VMM-000160, SRG-OS-000039-VMM-000170, SRG-OS-000040-VMM-000180, SRG-OS-000041-VMM-000190 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'auditd.service'
"$SYSTEMCTL_EXEC" enable 'auditd.service'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Enable service auditd
block:
- name: Gather the package facts
package_facts:
manager: auto
- name: Enable service auditd
service:
name: auditd
enabled: 'yes'
state: started
when:
- '"audit" in ansible_facts.packages'
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27407-6
- CJIS-5.4.1.1
- DISA-STIG-RHEL-07-030000
- NIST-800-171-3.3.1
- NIST-800-171-3.3.2
- NIST-800-171-3.3.6
- NIST-800-53-AC-2(g)
- NIST-800-53-AC-6(9)
- NIST-800-53-AU-10
- NIST-800-53-AU-12(c)
- NIST-800-53-AU-14(1)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-3
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-10.1
- enable_strategy
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- service_auditd_enabled
Remediation script:include enable_auditd
class enable_auditd {
service {'auditd':
enable => true,
ensure => 'running',
}
}
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
systemd:
units:
- name: auditd.service
enabled: true
|
Enable Auditing for Processes Which Start Prior to the Audit DaemonruleTo ensure all processes can be audited, even those which start
prior to the audit daemon, add the argument audit=1 to the default
GRUB 2 command line for the Linux operating system in
/etc/default/grub , so that the line looks similar to
GRUB_CMDLINE_LINUX="... audit=1 ..."
In case the GRUB_DISABLE_RECOVERY is set to true, then the parameter should be added to the GRUB_CMDLINE_LINUX_DEFAULT instead.warning
The GRUB 2 configuration file, grub.cfg ,
is automatically updated each time a new kernel is installed. Note that any
changes to /etc/default/grub require rebuilding the grub.cfg
file. To update the GRUB 2 configuration file manually, use the
grub2-mkconfig -o command as follows:
Rationale:Each process on the system carries an "auditable" flag which indicates whether
its activities can be audited. Although auditd takes care of enabling
this for all processes which launch after it does, adding the kernel argument
ensures it is set for every process during boot. identifiers:
CCE-27212-0 references:
4.1.3, 1, 11, 12, 13, 14, 15, 16, 19, 3, 4, 5, 6, 7, 8, 5.4.1.1, APO10.01, APO10.03, APO10.04, APO10.05, APO11.04, APO12.06, APO13.01, BAI03.05, BAI08.02, DSS01.04, DSS02.02, DSS02.04, DSS02.07, DSS03.01, DSS05.02, DSS05.03, DSS05.04, DSS05.07, MEA01.01, MEA01.02, MEA01.03, MEA01.04, MEA01.05, MEA02.01, 3.3.1, CCI-001464, CCI-000130, 164.308(a)(1)(ii)(D), 164.308(a)(5)(ii)(C), 164.310(a)(2)(iv), 164.310(d)(2)(iii), 164.312(b), 4.2.3.10, 4.3.2.6.7, 4.3.3.3.9, 4.3.3.5.8, 4.3.3.6.6, 4.3.4.4.7, 4.3.4.5.6, 4.3.4.5.7, 4.3.4.5.8, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.13, SR 2.10, SR 2.11, SR 2.12, SR 2.6, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.1, SR 7.1, SR 7.6, A.11.2.6, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.15.2.1, A.15.2.2, A.16.1.4, A.16.1.5, A.16.1.7, A.6.2.1, A.6.2.2, AC-17(1), AU-14(1), AU-10, CM-6(a), IR-5(1), DE.AE-3, DE.AE-5, ID.SC-4, PR.AC-3, PR.PT-1, PR.PT-4, RS.AN-1, RS.AN-4, Req-10.3, SRG-OS-000254-GPOS-00095, SRG-OS-000254-VMM-000880 Remediation script:# Remediation is applicable only in certain platforms
if rpm --quiet -q grub2-common && [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Correct the form of default kernel command line in GRUB
if grep -q '^GRUB_CMDLINE_LINUX=.*audit=.*"' '/etc/default/grub' ; then
# modify the GRUB command-line if an audit= arg already exists
sed -i 's/\(^GRUB_CMDLINE_LINUX=".*\)audit=[^[:space:]]*\(.*"\)/\1 audit=1 \2/' '/etc/default/grub'
else
# no audit=arg is present, append it
sed -i 's/\(^GRUB_CMDLINE_LINUX=".*\)"/\1 audit=1"/' '/etc/default/grub'
fi
# Correct the form of kernel command line for each installed kernel in the bootloader
grubby --update-kernel=ALL --args="audit=1"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- package_facts:
manager: auto
name: Gather the package facts
tags:
- CCE-27212-0
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-10
- NIST-800-53-AU-14(1)
- NIST-800-53-CM-6(a)
- NIST-800-53-IR-5(1)
- PCI-DSS-Req-10.3
- grub2_audit_argument
- low_disruption
- medium_complexity
- medium_severity
- reboot_required
- restrict_strategy
- name: check audit argument exists
command: grep 'GRUB_CMDLINE_LINUX.*audit=' /etc/default/grub
failed_when: false
register: argcheck
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"grub2-common" in ansible_facts.packages'
tags:
- CCE-27212-0
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-10
- NIST-800-53-AU-14(1)
- NIST-800-53-CM-6(a)
- NIST-800-53-IR-5(1)
- PCI-DSS-Req-10.3
- grub2_audit_argument
- low_disruption
- medium_complexity
- medium_severity
- reboot_required
- restrict_strategy
- name: replace existing audit argument
replace:
path: /etc/default/grub
regexp: audit=\w+
replace: audit=1
when:
- argcheck.rc == 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"grub2-common" in ansible_facts.packages'
tags:
- CCE-27212-0
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-10
- NIST-800-53-AU-14(1)
- NIST-800-53-CM-6(a)
- NIST-800-53-IR-5(1)
- PCI-DSS-Req-10.3
- grub2_audit_argument
- low_disruption
- medium_complexity
- medium_severity
- reboot_required
- restrict_strategy
- name: add audit argument
replace:
path: /etc/default/grub
regexp: (GRUB_CMDLINE_LINUX=.*)"
replace: \1 audit=1"
when:
- argcheck.rc != 0
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"grub2-common" in ansible_facts.packages'
tags:
- CCE-27212-0
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-10
- NIST-800-53-AU-14(1)
- NIST-800-53-CM-6(a)
- NIST-800-53-IR-5(1)
- PCI-DSS-Req-10.3
- grub2_audit_argument
- low_disruption
- medium_complexity
- medium_severity
- reboot_required
- restrict_strategy
- name: update bootloader menu
command: /sbin/grubby --update-kernel=ALL --args="audit=1"
when:
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
- '"grub2-common" in ansible_facts.packages'
tags:
- CCE-27212-0
- CJIS-5.4.1.1
- NIST-800-171-3.3.1
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-10
- NIST-800-53-AU-14(1)
- NIST-800-53-CM-6(a)
- NIST-800-53-IR-5(1)
- PCI-DSS-Req-10.3
- grub2_audit_argument
- low_disruption
- medium_complexity
- medium_severity
- reboot_required
- restrict_strategy
|
File Permissions and MasksgroupTraditional Unix security relies heavily on file and
directory permissions to prevent unauthorized users from reading or
modifying files to which they should not have access.
Several of the commands in this section search filesystems
for files or directories with certain characteristics, and are
intended to be run on every local partition on a given system.
When the variable PART appears in one of the commands below,
it means that the command is intended to be run repeatedly, with the
name of each local partition substituted for PART in turn.
The following command prints a list of all xfs partitions on the local
system, which is the default filesystem for Red Hat Enterprise Linux 7
installations:
$ mount -t xfs | awk '{print $3}'
For any systems that use a different
local filesystem type, modify this command as appropriate. |
contains 9 rules |
Verify Permissions on Important Files and
DirectoriesgroupPermissions for many files on a system must be set
restrictively to ensure sensitive information is properly protected.
This section discusses important
permission restrictions which can be verified
to ensure that no harmful discrepancies have
arisen. |
contains 9 rules |
Verify Permissions on Files with Local Account Information and CredentialsgroupThe default restrictive permissions for files which act as
important security databases such as passwd , shadow ,
group , and gshadow files must be maintained. Many utilities
need read access to the passwd file in order to function properly, but
read access to the shadow file allows malicious attacks against system
passwords, and should never be enabled. |
contains 9 rules |
Verify User Who Owns shadow Filerule To properly set the owner of /etc/shadow , run the command: $ sudo chown root /etc/shadow Rationale:The /etc/shadow file contains the list of local
system accounts and stores password hashes. Protection of this file is
critical for system security. Failure to give ownership of this file
to root provides the designated owner with access to sensitive information
which could weaken the system security posture. identifiers:
CCE-82022-5 references:
BP28(R36), 6.1.3, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chown 0 /etc/shadow
Remediation script:- name: Test for existence /etc/shadow
stat:
path: /etc/shadow
register: file_exists
tags:
- CCE-82022-5
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure owner 0 on /etc/shadow
file:
path: /etc/shadow
owner: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82022-5
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Permissions on passwd Filerule
To properly set the permissions of /etc/passwd , run the command:
$ sudo chmod 0644 /etc/passwd Rationale:If the /etc/passwd file is writable by a group-owner or the
world the risk of its compromise is increased. The file contains the list of
accounts on the system and associated information, and protection of this file
is critical for system security. identifiers:
CCE-82029-0 references:
BP28(R36), 6.1.2, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chmod 0644 /etc/passwd
Remediation script:- name: Test for existence /etc/passwd
stat:
path: /etc/passwd
register: file_exists
tags:
- CCE-82029-0
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure permission 0644 on /etc/passwd
file:
path: /etc/passwd
mode: '0644'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82029-0
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify User Who Owns passwd Filerule To properly set the owner of /etc/passwd , run the command: $ sudo chown root /etc/passwd Rationale:The /etc/passwd file contains information about the users that are configured on
the system. Protection of this file is critical for system security. identifiers:
CCE-82052-2 references:
6.1.2, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chown 0 /etc/passwd
Remediation script:- name: Test for existence /etc/passwd
stat:
path: /etc/passwd
register: file_exists
tags:
- CCE-82052-2
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure owner 0 on /etc/passwd
file:
path: /etc/passwd
owner: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82052-2
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Permissions on shadow Filerule
To properly set the permissions of /etc/shadow , run the command:
$ sudo chmod 0000 /etc/shadow Rationale:The /etc/shadow file contains the list of local
system accounts and stores password hashes. Protection of this file is
critical for system security. Failure to give ownership of this file
to root provides the designated owner with access to sensitive information
which could weaken the system security posture. identifiers:
CCE-82042-3 references:
BP28(R36), 6.1.3, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chmod 0000 /etc/shadow
Remediation script:- name: Test for existence /etc/shadow
stat:
path: /etc/shadow
register: file_exists
tags:
- CCE-82042-3
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure permission 0000 on /etc/shadow
file:
path: /etc/shadow
mode: '0000'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82042-3
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Group Who Owns shadow Filerule To properly set the group owner of /etc/shadow , run the command: $ sudo chgrp root /etc/shadow Rationale:The /etc/shadow file stores password hashes. Protection of this file is
critical for system security. identifiers:
CCE-82051-4 references:
6.1.3, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chgrp 0 /etc/shadow
Remediation script:- name: Test for existence /etc/shadow
stat:
path: /etc/shadow
register: file_exists
tags:
- CCE-82051-4
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure group owner 0 on /etc/shadow
file:
path: /etc/shadow
group: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82051-4
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_shadow
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify User Who Owns group Filerule To properly set the owner of /etc/group , run the command: $ sudo chown root /etc/group Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. identifiers:
CCE-82031-6 references:
6.1.4, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chown 0 /etc/group
Remediation script:- name: Test for existence /etc/group
stat:
path: /etc/group
register: file_exists
tags:
- CCE-82031-6
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure owner 0 on /etc/group
file:
path: /etc/group
owner: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82031-6
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_owner_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Permissions on group Filerule
To properly set the permissions of /etc/passwd , run the command:
$ sudo chmod 0644 /etc/passwd Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. identifiers:
CCE-82032-4 references:
BP28(R36), 6.1.4, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chmod 0644 /etc/group
Remediation script:- name: Test for existence /etc/group
stat:
path: /etc/group
register: file_exists
tags:
- CCE-82032-4
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure permission 0644 on /etc/group
file:
path: /etc/group
mode: '0644'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82032-4
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_permissions_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Group Who Owns group Filerule To properly set the group owner of /etc/group , run the command: $ sudo chgrp root /etc/group Rationale:The /etc/group file contains information regarding groups that are configured
on the system. Protection of this file is important for system security. identifiers:
CCE-82037-3 references:
6.1.4, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chgrp 0 /etc/group
Remediation script:- name: Test for existence /etc/group
stat:
path: /etc/group
register: file_exists
tags:
- CCE-82037-3
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure group owner 0 on /etc/group
file:
path: /etc/group
group: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-82037-3
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_group
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify Group Who Owns passwd Filerule To properly set the group owner of /etc/passwd , run the command: $ sudo chgrp root /etc/passwd Rationale:The /etc/passwd file contains information about the users that are configured on
the system. Protection of this file is critical for system security. identifiers:
CCE-26639-5 references:
6.1.2, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-8.7.c Remediation script:
chgrp 0 /etc/passwd
Remediation script:- name: Test for existence /etc/passwd
stat:
path: /etc/passwd
register: file_exists
tags:
- CCE-26639-5
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure group owner 0 on /etc/passwd
file:
path: /etc/passwd
group: '0'
when: file_exists.stat is defined and file_exists.stat.exists
tags:
- CCE-26639-5
- CJIS-5.5.2.2
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-8.7.c
- configure_strategy
- file_groupowner_etc_passwd
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Network Configuration and FirewallsgroupMost systems must be connected to a network of some
sort, and this brings with it the substantial risk of network
attack. This section discusses the security impact of decisions
about networking which must be made when configuring a system.
This section also discusses firewalls, network access
controls, and other network security frameworks, which allow
system-level rules to be written that can limit an attackers' ability
to connect to your system. These rules can specify that network
traffic should be allowed or denied from certain IP addresses,
hosts, and networks. The rules can also specify which of the
system's network services are available to particular hosts or
networks. |
contains 12 rules |
firewalldgroupThe dynamic firewall daemon firewalld provides a
dynamically managed firewall with support for network “zones” to assign
a level of trust to a network and its associated connections and interfaces.
It has support for IPv4 and IPv6 firewall settings. It supports Ethernet
bridges and has a separation of runtime and permanent configuration options.
It also has an interface for services or applications to add firewall rules
directly.
A graphical configuration tool, firewall-config , is used to configure
firewalld , which in turn uses iptables tool to communicate
with Netfilter in the kernel which implements packet filtering.
The firewall service provided by firewalld is dynamic rather than
static because changes to the configuration can be made at anytime and are
immediately implemented. There is no need to save or apply the changes. No
unintended disruption of existing network connections occurs as no part of
the firewall has to be reloaded. |
contains 2 rules |
Inspect and Activate Default firewalld RulesgroupFirewalls can be used to separate networks into different zones
based on the level of trust the user has decided to place on the devices and
traffic within that network. NetworkManager informs firewalld to which
zone an interface belongs. An interface's assigned zone can be changed by
NetworkManager or via the firewall-config tool.
The zone settings in /etc/firewalld/ are a range of preset settings
which can be quickly applied to a network interface. These are the zones
provided by firewalld sorted according to the default trust level of the
zones from untrusted to trusted:
drop
Any incoming network packets are dropped, there is no
reply. Only outgoing network connections are possible. block
Any incoming network connections are rejected with an
icmp-host-prohibited message for IPv4 and icmp6-adm-prohibited
for IPv6. Only network connections initiated from within the system are
possible. public
For use in public areas. You do not trust the other
computers on the network to not harm your computer. Only selected incoming
connections are accepted. external
For use on external networks with masquerading enabled
especially for routers. You do not trust the other computers on the network to
not harm your computer. Only selected incoming connections are accepted. dmz
For computers in your demilitarized zone that are
publicly-accessible with limited access to your internal network. Only selected
incoming connections are accepted. work
For use in work areas. You mostly trust the other computers
on networks to not harm your computer. Only selected incoming connections are
accepted. home
For use in home areas. You mostly trust the other computers
on networks to not harm your computer. Only selected incoming connections are
accepted. internal
For use on internal networks. You mostly trust the
other computers on the networks to not harm your computer. Only selected
incoming connections are accepted. trusted
All network connections are accepted.
It is possible to designate one of these zones to be the default zone. When
interface connections are added to NetworkManager , they are assigned
to the default zone. On installation, the default zone in firewalld is set to
be the public zone.
To find out all the settings of a zone, for example the public zone,
enter the following command as root:
# firewall-cmd --zone=public --list-all
Example output of this command might look like the following:
# firewall-cmd --zone=public --list-all
public
interfaces:
services: mdns dhcpv6-client ssh
ports:
forward-ports:
icmp-blocks: source-quench
To view the network zones currently active, enter the following command as root:
# firewall-cmd --get-service
The following listing displays the result of this command
on common Red Hat Enterprise Linux 7 system:
# firewall-cmd --get-service
amanda-client bacula bacula-client dhcp dhcpv6 dhcpv6-client dns ftp
high-availability http https imaps ipp ipp-client ipsec kerberos kpasswd
ldap ldaps libvirt libvirt-tls mdns mountd ms-wbt mysql nfs ntp openvpn
pmcd pmproxy pmwebapi pmwebapis pop3s postgresql proxy-dhcp radius rpc-bind
samba samba-client smtp ssh telnet tftp tftp-client transmission-client
vnc-server wbem-https
Finally to view the network zones that will be active after the next firewalld
service reload, enter the following command as root:
# firewall-cmd --get-service --permanent |
contains 1 rule |
Verify firewalld Enabledrule The firewalld service can be enabled with the following command: $ sudo systemctl enable firewalld.service Rationale:Access control methods provide the ability to enhance system security posture
by restricting services and known good IP addresses and address ranges. This
prevents connections from unknown hosts and protocols. identifiers:
CCE-80998-8 references:
11, 3, 9, BAI10.01, BAI10.02, BAI10.03, BAI10.05, 3.1.3, 3.4.7, CCI-000366, 4.3.4.3.2, 4.3.4.3.3, SR 7.6, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, AC-4, CM-7(b), CA-3(5), SC-7(21), CM-6(a), PR.IP-1, FMT_MOF_EXT.1, SRG-OS-000480-GPOS-00227, SRG-OS-000480-GPOS-00231, SRG-OS-000480-GPOS-00232, RHEL-07-040520, SV-204604r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
SYSTEMCTL_EXEC='/usr/bin/systemctl'
"$SYSTEMCTL_EXEC" start 'firewalld.service'
"$SYSTEMCTL_EXEC" enable 'firewalld.service'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Enable service firewalld
block:
- name: Gather the package facts
package_facts:
manager: auto
- name: Enable service firewalld
service:
name: firewalld
enabled: 'yes'
state: started
when:
- '"firewalld" in ansible_facts.packages'
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80998-8
- DISA-STIG-RHEL-07-040520
- NIST-800-171-3.1.3
- NIST-800-171-3.4.7
- NIST-800-53-AC-4
- NIST-800-53-CA-3(5)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-7(21)
- enable_strategy
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- service_firewalld_enabled
Remediation script:include enable_firewalld
class enable_firewalld {
service {'firewalld':
enable => true,
ensure => 'running',
}
}
|
Strengthen the Default RulesetgroupThe default rules can be strengthened. The system
scripts that activate the firewall rules expect them to be defined
in configuration files under the /etc/firewalld/services
and /etc/firewalld/zones directories.
The following recommendations describe how to strengthen the
default ruleset configuration file. An alternative to editing this
configuration file is to create a shell script that makes calls to
the firewall-cmd program to load in rules under the /etc/firewalld/services
and /etc/firewalld/zones directories.
Instructions apply to both unless otherwise noted. Language and address
conventions for regular firewalld rules are used throughout this section. warning
The program firewall-config
allows additional services to penetrate the default firewall rules
and automatically adjusts the firewalld ruleset(s). |
contains 1 rule |
Set Default firewalld Zone for Incoming PacketsruleTo set the default zone to drop for
the built-in default zone which processes incoming IPv4 and IPv6 packets,
modify the following line in
/etc/firewalld/firewalld.conf to be:
DefaultZone=drop warning
To prevent denying any access to the system, automatic remediation
of this control is not available. Remediation must be automated as
a component of machine provisioning, or followed manually as outlined
above. Rationale:In firewalld the default zone is applied only after all
the applicable rules in the table are examined for a match. Setting the
default zone to drop implements proper design for a firewall, i.e.
any packets which are not explicitly permitted should not be
accepted. identifiers:
CCE-27349-0 references:
11, 14, 3, 9, 5.10.1, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06, 3.1.3, 3.4.7, 3.13.6, CCI-000366, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6, 1416, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2, CA-3(5), CM-7(b), SC-7(23), CM-6(a), PR.IP-1, PR.PT-3, FMT_MOF_EXT.1, SRG-OS-000480-GPOS-00227, RHEL-07-040810, SV-204628r603261_rule, SRG-OS-000480-VMM-002000 |
Wireless NetworkinggroupWireless networking, such as 802.11
(WiFi) and Bluetooth, can present a security risk to sensitive or
classified systems and networks. Wireless networking hardware is
much more likely to be included in laptop or portable systems than
in desktops or servers.
Removal of hardware provides the greatest assurance that the wireless
capability remains disabled. Acquisition policies often include provisions to
prevent the purchase of equipment that will be used in sensitive spaces and
includes wireless capabilities. If it is impractical to remove the wireless
hardware, and policy permits the device to enter sensitive spaces as long
as wireless is disabled, efforts should instead focus on disabling wireless capability
via software. |
contains 1 rule |
Disable Wireless Through Software ConfigurationgroupIf it is impossible to remove the wireless hardware
from the device in question, disable as much of it as possible
through software. The following methods can disable software
support for wireless networking, but note that these methods do not
prevent malicious software or careless users from re-activating the
devices. |
contains 1 rule |
Disable Bluetooth Kernel ModuleruleThe kernel's module loading system can be configured to prevent
loading of the Bluetooth module. Add the following to
the appropriate /etc/modprobe.d configuration file
to prevent the loading of the Bluetooth module:
install bluetooth /bin/true Rationale:If Bluetooth functionality must be disabled, preventing the kernel
from loading the kernel module provides an additional safeguard against its
activation. identifiers:
CCE-27327-6 references:
11, 12, 14, 15, 3, 8, 9, 5.13.1.3, APO13.01, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.05, DSS06.06, 3.1.16, CCI-000085, CCI-001551, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6, A.11.2.6, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.2.1, A.6.2.2, A.9.1.2, AC-18(a), AC-18(3), CM-7(a), CM-7(b), CM-6(a), MP-7, PR.AC-3, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000095-GPOS-00049 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if LC_ALL=C grep -q -m 1 "^install bluetooth" /etc/modprobe.d/bluetooth.conf ; then
sed -i 's/^install bluetooth.*/install bluetooth /bin/true/g' /etc/modprobe.d/bluetooth.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/bluetooth.conf
echo "install bluetooth /bin/true" >> /etc/modprobe.d/bluetooth.conf
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure kernel module 'bluetooth' is disabled
lineinfile:
create: true
dest: /etc/modprobe.d/bluetooth.conf
regexp: bluetooth
line: install bluetooth /bin/true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27327-6
- CJIS-5.13.1.3
- NIST-800-171-3.1.16
- NIST-800-53-AC-18(3)
- NIST-800-53-AC-18(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-MP-7
- disable_strategy
- kernel_module_bluetooth_disabled
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,install%20bluetooth%20/bin/true%0A
mode: 0644
path: /etc/modprobe.d/75-kernel_module_bluetooth_disabled.conf
overwrite: true
|
Kernel Parameters Which Affect NetworkinggroupThe sysctl utility is used to set
parameters which affect the operation of the Linux kernel. Kernel parameters
which affect networking and have security implications are described here. |
contains 7 rules |
Network Parameters for Hosts OnlygroupIf the system is not going to be used as a router, then setting certain
kernel parameters ensure that the host will not perform routing
of network traffic. |
contains 2 rules |
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 InterfacesruleTo set the runtime status of the net.ipv4.conf.all.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.send_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.conf.all.send_redirects = 0 Rationale:ICMP redirect messages are used by routers to inform hosts that a more
direct route exists for a particular destination. These messages contain information
from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers. identifiers:
CCE-80156-3 references:
BP28(R22), 3.1.2, 1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 3.1.20, CCI-000366, 4.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), SC-5, CM-6(a), SC-7(a), DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000480-GPOS-00227, RHEL-07-040660, SV-204617r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
#
# Set runtime for net.ipv4.conf.all.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects="0"
#
# If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0"
# else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.send_redirects' "0" 'CCE-80156-3'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure sysctl net.ipv4.conf.all.send_redirects is set to 0
sysctl:
name: net.ipv4.conf.all.send_redirects
value: '0'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80156-3
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040660
- NIST-800-171-3.1.20
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-5
- NIST-800-53-SC-7(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_conf_all_send_redirects
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.conf.all.send_redirects%3D0%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_send_redirects.conf
overwrite: true
|
Disable Kernel Parameter for Sending ICMP Redirects on all IPv4 Interfaces by DefaultruleTo set the runtime status of the net.ipv4.conf.default.send_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.send_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.conf.default.send_redirects = 0 Rationale:ICMP redirect messages are used by routers to inform hosts that a more
direct route exists for a particular destination. These messages contain information
from the system's route table possibly revealing portions of the network topology.
The ability to send ICMP redirects is only appropriate for systems acting as routers. identifiers:
CCE-80999-6 references:
BP28(R22), 3.1.2, 1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 3.1.20, CCI-000366, 4.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), SC-5, CM-6(a), SC-7(a), DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000480-GPOS-00227, RHEL-07-040650, SV-204616r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
#
# Set runtime for net.ipv4.conf.default.send_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects="0"
#
# If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0"
# else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.send_redirects' "0" 'CCE-80999-6'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure sysctl net.ipv4.conf.default.send_redirects is set to 0
sysctl:
name: net.ipv4.conf.default.send_redirects
value: '0'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80999-6
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040650
- NIST-800-171-3.1.20
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-5
- NIST-800-53-SC-7(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_conf_default_send_redirects
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.conf.default.send_redirects%3D0%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_send_redirects.conf
overwrite: true
|
Network Related Kernel Runtime Parameters for Hosts and RoutersgroupCertain kernel parameters should be set for systems which are
acting as either hosts or routers to improve the system's ability defend
against certain types of IPv4 protocol attacks. |
contains 5 rules |
Disable Accepting ICMP Redirects for All IPv4 InterfacesruleTo set the runtime status of the net.ipv4.conf.all.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.all.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.conf.all.accept_redirects = 0 Rationale:ICMP redirect messages are used by routers to inform hosts that a more
direct route exists for a particular destination. These messages modify the
host's route table and are unauthenticated. An illicit ICMP redirect
message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should be
disabled unless absolutely required." identifiers:
CCE-80158-9 references:
BP28(R22), 3.2.2, 1, 11, 12, 13, 14, 15, 16, 2, 3, 7, 8, 9, 5.10.1.1, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS03.05, DSS05.02, DSS05.05, DSS05.07, DSS06.06, 3.1.20, CCI-000366, CCI-001503, CCI-001551, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.9.1.2, CM-7(a), CM-7(b), CM-6(a), SC-7(a), DE.CM-1, PR.DS-4, PR.IP-1, PR.PT-3, SRG-OS-000480-GPOS-00227, RHEL-07-040641, SV-204615r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sysctl_net_ipv4_conf_all_accept_redirects_value="0"
#
# Set runtime for net.ipv4.conf.all.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_redirects="$sysctl_net_ipv4_conf_all_accept_redirects_value"
#
# If net.ipv4.conf.all.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.all.accept_redirects = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.accept_redirects' "$sysctl_net_ipv4_conf_all_accept_redirects_value" 'CCE-80158-9'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_accept_redirects_value: !!str 0
tags:
- always
- name: Ensure sysctl net.ipv4.conf.all.accept_redirects is set
sysctl:
name: net.ipv4.conf.all.accept_redirects
value: '{{ sysctl_net_ipv4_conf_all_accept_redirects_value }}'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80158-9
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040641
- NIST-800-171-3.1.20
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-7(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_conf_all_accept_redirects
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.conf.all.accept_redirects%3D0%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_all_accept_redirects.conf
overwrite: true
|
Enable Kernel Parameter to Use TCP Syncookies on IPv4 InterfacesruleTo set the runtime status of the net.ipv4.tcp_syncookies kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.tcp_syncookies=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.tcp_syncookies = 1 Rationale:A TCP SYN flood attack can cause a denial of service by filling a
system's TCP connection table with connections in the SYN_RCVD state.
Syncookies can be used to track a connection when a subsequent ACK is received,
verifying the initiator is attempting a valid connection and is not a flood
source. This feature is activated when a flood condition is detected, and
enables the system to continue servicing valid connection requests. identifiers:
CCE-27495-1 references:
BP28(R22), 3.2.8, 1, 12, 13, 14, 15, 16, 18, 2, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.07, DSS06.02, 3.1.20, CCI-000366, 4.2.3.4, 4.3.3.4, 4.4.3.3, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), SC-5(1), SC-5(2), SC-5(3)(a), CM-6(a), DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.PT-4, SRG-OS-000480-GPOS-00227, SRG-OS-000420-GPOS-00186, SRG-OS-000142-GPOS-00071 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sysctl_net_ipv4_tcp_syncookies_value="1"
#
# Set runtime for net.ipv4.tcp_syncookies
#
/sbin/sysctl -q -n -w net.ipv4.tcp_syncookies="$sysctl_net_ipv4_tcp_syncookies_value"
#
# If net.ipv4.tcp_syncookies present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.tcp_syncookies = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.tcp_syncookies' "$sysctl_net_ipv4_tcp_syncookies_value" 'CCE-27495-1'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sysctl_net_ipv4_tcp_syncookies_value # promote to variable
set_fact:
sysctl_net_ipv4_tcp_syncookies_value: !!str 1
tags:
- always
- name: Ensure sysctl net.ipv4.tcp_syncookies is set
sysctl:
name: net.ipv4.tcp_syncookies
value: '{{ sysctl_net_ipv4_tcp_syncookies_value }}'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27495-1
- CJIS-5.10.1.1
- NIST-800-171-3.1.20
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-5(1)
- NIST-800-53-SC-5(2)
- NIST-800-53-SC-5(3)(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_tcp_syncookies
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.tcp_syncookies%3D1%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_tcp_syncookies.conf
overwrite: true
|
Enable Kernel Parameter to Ignore ICMP Broadcast Echo Requests on IPv4 InterfacesruleTo set the runtime status of the net.ipv4.icmp_echo_ignore_broadcasts kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.icmp_echo_ignore_broadcasts=1
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.icmp_echo_ignore_broadcasts = 1 Rationale:Responding to broadcast (ICMP) echoes facilitates network mapping
and provides a vector for amplification attacks.
Ignoring ICMP echo requests (pings) sent to broadcast or multicast
addresses makes the system slightly more difficult to enumerate on the network. identifiers:
CCE-80165-4 references:
3.2.5, 1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 3.1.20, CCI-000366, 4.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), SC-5, DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000480-GPOS-00227, RHEL-07-040630, SV-204613r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value="1"
#
# Set runtime for net.ipv4.icmp_echo_ignore_broadcasts
#
/sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts="$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value"
#
# If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.icmp_echo_ignore_broadcasts' "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" 'CCE-80165-4'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable
set_fact:
sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: !!str 1
tags:
- always
- name: Ensure sysctl net.ipv4.icmp_echo_ignore_broadcasts is set
sysctl:
name: net.ipv4.icmp_echo_ignore_broadcasts
value: '{{ sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value }}'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80165-4
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040630
- NIST-800-171-3.1.20
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-5
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_icmp_echo_ignore_broadcasts
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.icmp_echo_ignore_broadcasts%3D1%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_icmp_echo_ignore_broadcasts.conf
overwrite: true
|
Disable Kernel Parameter for Accepting Source-Routed Packets on IPv4 Interfaces by DefaultruleTo set the runtime status of the net.ipv4.conf.default.accept_source_route kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_source_route=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.conf.default.accept_source_route = 0 Rationale:Source-routed packets allow the source of the packet to suggest routers
forward the packet along a different path than configured on the router,
which can be used to bypass network security measures.
Accepting source-routed packets in the IPv4 protocol has few legitimate
uses. It should be disabled unless it is absolutely required, such as when
IPv4 forwarding is enabled and the system is legitimately functioning as a
router. identifiers:
CCE-80162-1 references:
BP28(R22), 3.2.1, 1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 3.1.20, CCI-000366, CCI-001551, 4.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), SC-5, SC-7(a), DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000480-GPOS-00227, RHEL-07-040620, SV-204612r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sysctl_net_ipv4_conf_default_accept_source_route_value="0"
#
# Set runtime for net.ipv4.conf.default.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_source_route="$sysctl_net_ipv4_conf_default_accept_source_route_value"
#
# If net.ipv4.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.default.accept_source_route = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.accept_source_route' "$sysctl_net_ipv4_conf_default_accept_source_route_value" 'CCE-80162-1'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_source_route_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_default_accept_source_route_value: !!str 0
tags:
- always
- name: Ensure sysctl net.ipv4.conf.default.accept_source_route is set
sysctl:
name: net.ipv4.conf.default.accept_source_route
value: '{{ sysctl_net_ipv4_conf_default_accept_source_route_value }}'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80162-1
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040620
- NIST-800-171-3.1.20
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-5
- NIST-800-53-SC-7(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_conf_default_accept_source_route
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.conf.default.accept_source_route%3D0%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_source_route.conf
overwrite: true
|
Disable Kernel Parameter for Accepting ICMP Redirects by Default on IPv4 InterfacesruleTo set the runtime status of the net.ipv4.conf.default.accept_redirects kernel parameter, run the following command: $ sudo sysctl -w net.ipv4.conf.default.accept_redirects=0
To make sure that the setting is persistent, add the following line to a file in the directory /etc/sysctl.d : net.ipv4.conf.default.accept_redirects = 0 Rationale:ICMP redirect messages are used by routers to inform hosts that a more
direct route exists for a particular destination. These messages modify the
host's route table and are unauthenticated. An illicit ICMP redirect
message could result in a man-in-the-middle attack.
This feature of the IPv4 protocol has few legitimate uses. It should
be disabled unless absolutely required. identifiers:
CCE-80163-9 references:
BP28(R22), 3.2.2, 1, 11, 12, 13, 14, 15, 16, 18, 2, 3, 4, 6, 7, 8, 9, 5.10.1.1, APO01.06, APO13.01, BAI04.04, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.03, DSS01.05, DSS03.01, DSS03.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.06, 3.1.20, CCI-000366, CCI-001551, 4.2.3.4, 4.3.3.4, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 6.2, SR 7.1, SR 7.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.1, A.12.1.2, A.12.1.3, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.2, A.13.1.3, A.13.2.1, A.13.2.2, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.17.2.1, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-7(a), CM-7(b), CM-6(a), SC-7(a), DE.AE-1, DE.CM-1, ID.AM-3, PR.AC-5, PR.DS-4, PR.DS-5, PR.IP-1, PR.PT-3, PR.PT-4, SRG-OS-000480-GPOS-00227, RHEL-07-040640, SV-204614r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sysctl_net_ipv4_conf_default_accept_redirects_value="0"
#
# Set runtime for net.ipv4.conf.default.accept_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects="$sysctl_net_ipv4_conf_default_accept_redirects_value"
#
# If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.accept_redirects' "$sysctl_net_ipv4_conf_default_accept_redirects_value" 'CCE-80163-9'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_default_accept_redirects_value: !!str 0
tags:
- always
- name: Ensure sysctl net.ipv4.conf.default.accept_redirects is set
sysctl:
name: net.ipv4.conf.default.accept_redirects
value: '{{ sysctl_net_ipv4_conf_default_accept_redirects_value }}'
state: present
reload: true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80163-9
- CJIS-5.10.1.1
- DISA-STIG-RHEL-07-040640
- NIST-800-171-3.1.20
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-SC-7(a)
- disable_strategy
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
- sysctl_net_ipv4_conf_default_accept_redirects
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,net.ipv4.conf.default.accept_redirects%3D0%0A
mode: 0644
path: /etc/sysctl.d/75-sysctl_net_ipv4_conf_default_accept_redirects.conf
overwrite: true
|
Uncommon Network ProtocolsgroupThe system includes support for several network protocols which are not commonly used.
Although security vulnerabilities in kernel networking code are not frequently discovered,
the consequences can be dramatic. Ensuring uncommon network protocols are disabled
reduces the system's risk to attacks targeted at its implementation of those protocols. warning
Although these protocols are not commonly used, avoid disruption
in your network environment by ensuring they are not needed
prior to disabling them. |
contains 2 rules |
Disable SCTP SupportruleThe Stream Control Transmission Protocol (SCTP) is a
transport layer protocol, designed to support the idea of
message-oriented communication, with several streams of messages
within one connection.
To configure the system to prevent the sctp
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install sctp /bin/true Rationale:Disabling SCTP protects
the system against exploitation of any flaws in its implementation. identifiers:
CCE-82044-9 references:
3.5.2, 11, 14, 3, 9, 5.10.1, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06, 3.4.6, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2, CM-7(a), CM-7(b), CM-6(a), PR.IP-1, PR.PT-3, SRG-OS-000095-GPOS-00049 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if LC_ALL=C grep -q -m 1 "^install sctp" /etc/modprobe.d/sctp.conf ; then
sed -i 's/^install sctp.*/install sctp /bin/true/g' /etc/modprobe.d/sctp.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf
echo "install sctp /bin/true" >> /etc/modprobe.d/sctp.conf
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure kernel module 'sctp' is disabled
lineinfile:
create: true
dest: /etc/modprobe.d/sctp.conf
regexp: sctp
line: install sctp /bin/true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82044-9
- CJIS-5.10.1
- NIST-800-171-3.4.6
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- disable_strategy
- kernel_module_sctp_disabled
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,install%20sctp%20/bin/true%0A
mode: 0644
path: /etc/modprobe.d/75-kernel_module_sctp_disabled.conf
overwrite: true
|
Disable DCCP SupportruleThe Datagram Congestion Control Protocol (DCCP) is a
relatively new transport layer protocol, designed to support
streaming media and telephony.
To configure the system to prevent the dccp
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install dccp /bin/true Rationale:Disabling DCCP protects
the system against exploitation of any flaws in its implementation. identifiers:
CCE-82024-1 references:
3.5.1, 11, 14, 3, 9, 5.10.1, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.05, DSS06.06, 3.4.6, CCI-001958, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.9.1.2, CM-7(a), CM-7(b), CM-6(a), PR.IP-1, PR.PT-3, SRG-OS-000096-GPOS-00050, SRG-OS-000378-GPOS-00163, RHEL-07-020101, SV-204450r603261_rule Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if LC_ALL=C grep -q -m 1 "^install dccp" /etc/modprobe.d/dccp.conf ; then
sed -i 's/^install dccp.*/install dccp /bin/true/g' /etc/modprobe.d/dccp.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/dccp.conf
echo "install dccp /bin/true" >> /etc/modprobe.d/dccp.conf
fi
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Ensure kernel module 'dccp' is disabled
lineinfile:
create: true
dest: /etc/modprobe.d/dccp.conf
regexp: dccp
line: install dccp /bin/true
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82024-1
- CJIS-5.10.1
- DISA-STIG-RHEL-07-020101
- NIST-800-171-3.4.6
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- disable_strategy
- kernel_module_dccp_disabled
- low_complexity
- medium_disruption
- medium_severity
- reboot_required
|
GRUB2 bootloader configurationgroupDuring the boot process, the boot loader is
responsible for starting the execution of the kernel and passing
options to it. The boot loader allows for the selection of
different kernels - possibly on different partitions or media.
The default Red Hat Enterprise Linux 7 boot loader for x86 systems is called GRUB2.
Options it can pass to the kernel include single-user mode, which
provides root access without any authentication, and the ability to
disable SELinux. To prevent local users from modifying the boot
parameters and endangering security, protect the boot loader configuration
with a password and ensure its configuration file's permissions
are set properly. |
contains 2 rules |
Non-UEFI GRUB2 bootloader configurationgroupNon-UEFI GRUB2 bootloader configuration |
contains 2 rules |
Verify /boot/grub2/grub.cfg User OwnershipruleThe file /boot/grub2/grub.cfg should
be owned by the root user to prevent destruction
or modification of the file.
To properly set the owner of /boot/grub2/grub.cfg , run the command:
$ sudo chown root /boot/grub2/grub.cfg Rationale:Only root should be able to modify important boot parameters. identifiers:
CCE-82026-6 references:
1.4.1, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 3.4.5, CCI-000225, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-7.1 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
chown 0 /boot/grub2/grub.cfg
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Test for existence /boot/grub2/grub.cfg
stat:
path: /boot/grub2/grub.cfg
register: file_exists
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82026-6
- CJIS-5.5.2.2
- NIST-800-171-3.4.5
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-7.1
- configure_strategy
- file_owner_grub2_cfg
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure owner 0 on /boot/grub2/grub.cfg
file:
path: /boot/grub2/grub.cfg
owner: '0'
when:
- file_exists.stat is defined and file_exists.stat.exists
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82026-6
- CJIS-5.5.2.2
- NIST-800-171-3.4.5
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-7.1
- configure_strategy
- file_owner_grub2_cfg
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Verify /boot/grub2/grub.cfg Group OwnershipruleThe file /boot/grub2/grub.cfg should
be group-owned by the root group to prevent
destruction or modification of the file.
To properly set the group owner of /boot/grub2/grub.cfg , run the command:
$ sudo chgrp root /boot/grub2/grub.cfg Rationale:The root group is a highly-privileged group. Furthermore, the group-owner of this
file should not have any access privileges anyway. identifiers:
CCE-82023-3 references:
1.4.1, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.2.2, APO01.06, DSS05.04, DSS05.07, DSS06.02, 3.4.5, CCI-000225, 164.308(a)(1)(ii)(B), 164.308(a)(7)(i), 164.308(a)(7)(ii)(A), 164.310(a)(1), 164.310(a)(2)(i), 164.310(a)(2)(ii), 164.310(a)(2)(iii), 164.310(b), 164.310(c), 164.310(d)(1), 164.310(d)(2)(iii), 4.3.3.7.3, SR 2.1, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, CM-6(a), AC-6(1), PR.AC-4, PR.DS-5, Req-7.1 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
chgrp 0 /boot/grub2/grub.cfg
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Test for existence /boot/grub2/grub.cfg
stat:
path: /boot/grub2/grub.cfg
register: file_exists
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82023-3
- CJIS-5.5.2.2
- NIST-800-171-3.4.5
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-7.1
- configure_strategy
- file_groupowner_grub2_cfg
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Ensure group owner 0 on /boot/grub2/grub.cfg
file:
path: /boot/grub2/grub.cfg
group: '0'
when:
- file_exists.stat is defined and file_exists.stat.exists
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-82023-3
- CJIS-5.5.2.2
- NIST-800-171-3.4.5
- NIST-800-53-AC-6(1)
- NIST-800-53-CM-6(a)
- PCI-DSS-Req-7.1
- configure_strategy
- file_groupowner_grub2_cfg
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
ServicesgroupThe best protection against vulnerable software is running less software. This section describes how to review
the software which Red Hat Enterprise Linux 7 installs on a system and disable software which is not needed. It
then enumerates the software packages installed on a default Red Hat Enterprise Linux 7 system and provides guidance about which
ones can be safely disabled.
Red Hat Enterprise Linux 7 provides a convenient minimal install option that essentially installs the bare necessities for a functional
system. When building Red Hat Enterprise Linux 7 systems, it is highly recommended to select the minimal packages and then build up
the system from there. |
contains 10 rules |
SSH ServergroupThe SSH protocol is recommended for remote login and
remote file transfer. SSH provides confidentiality and integrity
for data exchanged between two systems, as well as server
authentication, through the use of public key cryptography. The
implementation included with the system is called OpenSSH, and more
detailed documentation is available from its website,
https://www.openssh.com.
Its server program is called sshd and provided by the RPM package
openssh-server . |
contains 10 rules |
Configure OpenSSH Server if NecessarygroupIf the system needs to act as an SSH server, then
certain changes should be made to the OpenSSH daemon configuration
file /etc/ssh/sshd_config . The following recommendations can be
applied to this file. See the sshd_config(5) man page for more
detailed information. |
contains 10 rules |
Enable SSH Warning BannerruleTo enable the warning banner and ensure it is consistent
across the system, add or correct the following line in /etc/ssh/sshd_config :
Banner /etc/issue
Another section contains information on how to create an
appropriate system-wide warning banner.Rationale:The warning message reinforces policy awareness during the logon process and
facilitates possible legal action against attackers. Alternatively, systems
whose ownership should not be obvious should ensure usage of a banner that does
not provide easy attribution. identifiers:
CCE-27314-4 references:
5.2.15, 1, 12, 15, 16, 5.5.6, DSS05.04, DSS05.10, DSS06.10, 3.1.9, CCI-000048, CCI-000050, CCI-001384, CCI-001385, CCI-001386, CCI-001387, CCI-001388, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, SR 1.1, SR 1.10, SR 1.2, SR 1.5, SR 1.7, SR 1.8, SR 1.9, A.18.1.4, A.9.2.1, A.9.2.4, A.9.3.1, A.9.4.2, A.9.4.3, AC-8(a), AC-8(c), AC-17(a), CM-6(a), PR.AC-7, FTA_TAB.1, SRG-OS-000023-GPOS-00006, SRG-OS-000024-GPOS-00007, SRG-OS-000228-GPOS-00088, RHEL-07-040170, SV-204580r603261_rule, SRG-OS-000023-VMM-000060, SRG-OS-000024-VMM-000070 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "Banner /etc/issue" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Enable SSH Warning Banner
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*Banner\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: Banner /etc/issue
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27314-4
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040170
- NIST-800-171-3.1.9
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-8(a)
- NIST-800-53-AC-8(c)
- NIST-800-53-CM-6(a)
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_enable_warning_banner
|
Disable SSH Access via Empty PasswordsruleTo explicitly disallow SSH login from accounts with
empty passwords, add or correct the following line in /etc/ssh/sshd_config :
PermitEmptyPasswords no
Any accounts with empty passwords should be disabled immediately, and PAM configuration
should prevent users from being able to assign themselves empty passwords.Rationale:Configuring this setting for the SSH daemon provides additional assurance
that remote login via SSH will require a password, even in the event of
misconfiguration elsewhere. identifiers:
CCE-27471-2 references:
NT007(R17), 5.2.9, 11, 12, 13, 14, 15, 16, 18, 3, 5, 9, 5.5.6, APO01.06, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.02, DSS06.03, DSS06.06, 3.1.1, 3.1.5, CCI-000366, CCI-000766, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, SR 7.6, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.12.1.2, A.12.5.1, A.12.6.2, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, AC-17(a), CM-7(a), CM-7(b), CM-6(a), PR.AC-4, PR.AC-6, PR.DS-5, PR.IP-1, PR.PT-3, FIA_UAU.1, SRG-OS-000106-GPOS-00053, SRG-OS-000480-GPOS-00229, RHEL-07-010300, SV-204425r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "PermitEmptyPasswords no" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Disable SSH Access via Empty Passwords
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*PermitEmptyPasswords\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: PermitEmptyPasswords no
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27471-2
- CJIS-5.5.6
- DISA-STIG-RHEL-07-010300
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- high_severity
- low_complexity
- low_disruption
- no_reboot_needed
- restrict_strategy
- sshd_disable_empty_passwords
|
Enable SSH Server firewalld Firewall ExceptionruleBy default, inbound connections to SSH's port are allowed. If
the SSH server is being used but denied by the firewall, this exception should
be added to the firewall configuration.
To configure firewalld to allow access, run the following command(s):
firewall-cmd --permanent --add-service=ssh Rationale:If inbound SSH connections are expected, adding a firewall rule exception
will allow remote access through the SSH port. Remediation script:- name: Ensure firewalld is installed
package:
name: '{{ item }}'
state: present
with_items:
- firewalld
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80361-9
- NIST-800-171-3.1.12
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(b)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- configure_strategy
- firewalld_sshd_port_enabled
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: XCCDF Value sshd_listening_port # promote to variable
set_fact:
sshd_listening_port: !!str 22
tags:
- always
- name: Enable SSHD in firewalld (custom port)
firewalld:
port: '{{ sshd_listening_port }}/tcp'
permanent: true
state: enabled
when:
- sshd_listening_port != 22
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80361-9
- NIST-800-171-3.1.12
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(b)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- configure_strategy
- firewalld_sshd_port_enabled
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- name: Enable SSHD in firewalld (default port)
firewalld:
service: ssh
permanent: true
state: enabled
when:
- sshd_listening_port == 22
- ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-80361-9
- NIST-800-171-3.1.12
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(b)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- configure_strategy
- firewalld_sshd_port_enabled
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
|
Do Not Allow SSH Environment OptionsruleTo ensure users are not able to override environment
variables of the SSH daemon, add or correct the following line
in /etc/ssh/sshd_config :
PermitUserEnvironment no Rationale:SSH environment options potentially allow users to bypass
access restriction in some configurations. identifiers:
CCE-27363-1 references:
5.2.10, 11, 3, 9, 5.5.6, BAI10.01, BAI10.02, BAI10.03, BAI10.05, 3.1.12, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.4.3.2, 4.3.4.3.3, SR 7.6, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, AC-17(a), CM-7(a), CM-7(b), CM-6(a), PR.IP-1, SRG-OS-000480-GPOS-00229, RHEL-07-010460, SV-204434r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "PermitUserEnvironment no" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Do Not Allow SSH Environment Options
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*PermitUserEnvironment\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: PermitUserEnvironment no
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27363-1
- CJIS-5.5.6
- DISA-STIG-RHEL-07-010460
- NIST-800-171-3.1.12
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_do_not_permit_user_env
|
Allow Only SSH Protocol 2ruleOnly SSH protocol version 2 connections should be
permitted. The default setting in
/etc/ssh/sshd_config is correct, and can be
verified by ensuring that the following
line appears:
Protocol 2 warning
As of openssh-server version 7.4 and above, the only protocol
supported is version 2, and line Protocol 2 in
/etc/ssh/sshd_config is not necessary. Rationale:SSH protocol version 1 is an insecure implementation of the SSH protocol and
has many well-known vulnerability exploits. Exploits of the SSH daemon could provide
immediate root access to the system. identifiers:
CCE-27320-1 references:
NT007(R1), 5.2.2, 1, 12, 15, 16, 5, 8, 5.5.6, APO13.01, DSS01.04, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.1.13, 3.5.4, CCI-000197, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.6, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6, 0487, 1449, 1506, A.11.2.6, A.13.1.1, A.13.2.1, A.14.1.3, A.18.1.4, A.6.2.1, A.6.2.2, A.7.1.1, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.2, A.9.4.3, CM-6(a), AC-17(a), AC-17(2), IA-5(1)(c), SC-13, MA-4(6), PR.AC-1, PR.AC-3, PR.AC-6, PR.AC-7, PR.PT-4, SRG-OS-000074-GPOS-00042, SRG-OS-000480-GPOS-00227, RHEL-07-040390, SV-204594r603261_rule, SRG-OS-000033-VMM-000140 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/ssh/sshd_config' '^Protocol' '2' 'CCE-27320-1' '%s %s'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Allow Only SSH Protocol 2
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*Protocol\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: Protocol 2
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27320-1
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040390
- NIST-800-171-3.1.13
- NIST-800-171-3.5.4
- NIST-800-53-AC-17(2)
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-MA-4(6)
- NIST-800-53-SC-13
- high_severity
- low_complexity
- low_disruption
- no_reboot_needed
- restrict_strategy
- sshd_allow_only_protocol2
|
Use Only FIPS 140-2 Validated CiphersruleLimit the ciphers to those algorithms which are FIPS-approved.
Counter (CTR) mode is also preferred over cipher-block chaining (CBC) mode.
The following line in /etc/ssh/sshd_config
demonstrates use of FIPS-approved ciphers:
Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
The man page sshd_config(5) contains a list of supported ciphers.
Only the following ciphers are FIPS 140-2 certified on Red Hat Enterprise Linux 7:
- aes128-ctr
- aes192-ctr
- aes256-ctr
- aes128-cbc
- aes192-cbc
- aes256-cbc
- 3des-cbc
- rijndael-cbc@lysator.liu.se
Any combination of the above ciphers will pass this check.
Official FIPS 140-2 paperwork for Red Hat Enterprise Linux 7 can be found at
http://csrc.nist.gov/groups/STM/cmvp/documents/140-1/140sp/140sp2630.pdf
The rule is parametrized to use the following ciphers: aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se .warning
The system needs to be rebooted for these changes to take effect. warning
System Crypto Modules must be provided by a vendor that undergoes
FIPS-140 certifications.
FIPS-140 is applicable to all Federal agencies that use
cryptographic-based security systems to protect sensitive information
in computer and telecommunication systems (including voice systems) as
defined in Section 5131 of the Information Technology Management Reform
Act of 1996, Public Law 104-106. This standard shall be used in
designing and implementing cryptographic modules that Federal
departments and agencies operate or are operated for them under
contract. See https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.140-2.pdf
To meet this, the system has to have cryptographic software provided by
a vendor that has undergone this certification. This means providing
documentation, test results, design information, and independent third
party review by an accredited lab. While open source software is
capable of meeting this, it does not meet FIPS-140 unless the vendor
submits to this process. Rationale:Unapproved mechanisms that are used for authentication to the cryptographic module are not verified and therefore
cannot be relied upon to provide confidentiality or integrity, and system data may be compromised.
Operating systems utilizing encryption are required to use FIPS-compliant mechanisms for authenticating to
cryptographic modules.
FIPS 140-2 is the current standard for validating that mechanisms used to access cryptographic modules
utilize authentication that meets industry and government requirements. For government systems, this allows
Security Levels 1, 2, 3, or 4 for use on Red Hat Enterprise Linux 7. identifiers:
CCE-27295-5 references:
5.2.10, 1, 11, 12, 14, 15, 16, 18, 3, 5, 6, 8, 9, 5.5.6, APO11.04, APO13.01, BAI03.05, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS01.04, DSS05.02, DSS05.03, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.06, DSS06.10, MEA02.01, 3.1.13, 3.13.11, 3.13.8, CCI-000068, CCI-000366, CCI-000803, CCI-000877, CCI-002890, CCI-003123, 164.308(b)(1), 164.308(b)(2), 164.312(e)(1), 164.312(e)(2)(i), 164.312(e)(2)(ii), 164.314(b)(2)(i), 4.3.3.2.2, 4.3.3.3.9, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, 4.3.4.4.7, 4.4.2.1, 4.4.2.2, 4.4.2.4, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.10, SR 2.11, SR 2.12, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 2.8, SR 2.9, SR 3.1, SR 3.5, SR 3.8, SR 4.1, SR 4.3, SR 5.1, SR 5.2, SR 5.3, SR 7.1, SR 7.6, A.11.2.6, A.12.1.2, A.12.4.1, A.12.4.2, A.12.4.3, A.12.4.4, A.12.5.1, A.12.6.2, A.12.7.1, A.13.1.1, A.13.2.1, A.14.1.3, A.14.2.2, A.14.2.3, A.14.2.4, A.18.1.4, A.6.1.2, A.6.2.1, A.6.2.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, CM-6(a), AC-17(a), AC-17(2), SC-13, MA-4(6), IA-5(1)(c), SC-12(2), SC-12(3), PR.AC-1, PR.AC-3, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-1, PR.PT-1, PR.PT-3, PR.PT-4, SRG-OS-000033-GPOS-00014, SRG-OS-000120-GPOS-00061, SRG-OS-000125-GPOS-00065, SRG-OS-000250-GPOS-00093, SRG-OS-000393-GPOS-00173, SRG-OS-000394-GPOS-00174, SRG-OS-000033-VMM-000140, SRG-OS-000120-VMM-000600, SRG-OS-000478-VMM-001980, SRG-OS-000396-VMM-001590 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sshd_approved_ciphers="aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects arguments:
#
# config_file: Configuration file that will be modified
# key: Configuration option to change
# value: Value of the configuration option to change
# cce: The CCE identifier or '@CCENUM@' if no CCE identifier exists
# format: The printf-like format string that will be given stripped key and value as arguments,
# so e.g. '%s=%s' will result in key=value subsitution (i.e. without spaces around =)
#
# Optional arugments:
#
# format: Optional argument to specify the format of how key/value should be
# modified/appended in the configuration file. The default is key = value.
#
# Example Call(s):
#
# With default format of 'key = value':
# replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' '2' '@CCENUM@'
#
# With custom key/value format:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' 'disabled' '@CCENUM@' '%s=%s'
#
# With a variable:
# replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state '@CCENUM@' '%s=%s'
#
function replace_or_append {
local default_format='%s = %s' case_insensitive_mode=yes sed_case_insensitive_option='' grep_case_insensitive_option=''
local config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
if [ "$case_insensitive_mode" = yes ]; then
sed_case_insensitive_option="i"
grep_case_insensitive_option="-i"
fi
[ -n "$format" ] || format="$default_format"
# Check sanity of the input
[ $# -ge "3" ] || { echo "Usage: replace_or_append <config_file_location> <key_to_search> <new_value> [<CCE number or literal '@CCENUM@' if unknown>] [printf-like format, default is '$default_format']" >&2; exit 1; }
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
sed_command=('sed' '-i')
if test -L "$config_file"; then
sed_command+=('--follow-symlinks')
fi
# Test that the cce arg is not empty or does not equal @CCENUM@.
# If @CCENUM@ exists, it means that there is no CCE assigned.
if [ -n "$cce" ] && [ "$cce" != '@CCENUM@' ]; then
cce="${cce}"
else
cce="CCE"
fi
# Strip any search characters in the key arg so that the key can be replaced without
# adding any search characters to the config file.
stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "$key")
# shellcheck disable=SC2059
printf -v formatted_output "$format" "$stripped_key" "$value"
# If the key exists, change it. Otherwise, add it to the config_file.
# We search for the key string followed by a word boundary (matched by \>),
# so if we search for 'setting', 'setting2' won't match.
if LC_ALL=C grep -q -m 1 $grep_case_insensitive_option -e "${key}\\>" "$config_file"; then
"${sed_command[@]}" "s/${key}\\>.*/$formatted_output/g$sed_case_insensitive_option" "$config_file"
else
# \n is precaution for case where file ends without trailing newline
printf '\n# Per %s: Set %s in %s\n' "$cce" "$formatted_output" "$config_file" >> "$config_file"
printf '%s\n' "$formatted_output" >> "$config_file"
fi
}
replace_or_append '/etc/ssh/sshd_config' '^Ciphers' "$sshd_approved_ciphers" 'CCE-27295-5' '%s %s'
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sshd_approved_ciphers # promote to variable
set_fact:
sshd_approved_ciphers: !!str aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc,rijndael-cbc@lysator.liu.se
tags:
- always
- name: Use Only FIPS 140-2 Validated Ciphers
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*Ciphers\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: Ciphers {{ sshd_approved_ciphers }}
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27295-5
- CJIS-5.5.6
- NIST-800-171-3.1.13
- NIST-800-171-3.13.11
- NIST-800-171-3.13.8
- NIST-800-53-AC-17(2)
- NIST-800-53-AC-17(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-MA-4(6)
- NIST-800-53-SC-12(2)
- NIST-800-53-SC-12(3)
- NIST-800-53-SC-13
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_use_approved_ciphers
|
Set SSH Client Alive Count Max to zeroruleThe SSH server sends at most ClientAliveCountMax messages
during a SSH session and waits for a response from the SSH client.
The option ClientAliveInterval configures timeout after
each ClientAliveCountMax message. If the SSH server does not
receive a response from the client, then the connection is considered idle
and terminated.
To ensure the SSH idle timeout occurs precisely when the
ClientAliveInterval is set, set the ClientAliveCountMax to
value of 0 . Rationale:This ensures a user login will be terminated as soon as the ClientAliveInterval
is reached. identifiers:
CCE-83399-6 references:
5.2.12, 1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8, 5.5.6, APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.1.11, CCI-000879, CCI-001133, CCI-002361, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2, A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, AC-2(5), AC-12, AC-17(a), SC-10, CM-6(a), DE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2, Req-8.1.8, SRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109, RHEL-07-040340, SV-204589r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "ClientAliveCountMax 0" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Set SSH Client Alive Count Max to zero
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*ClientAliveCountMax\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: ClientAliveCountMax 0
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-83399-6
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040340
- NIST-800-171-3.1.11
- NIST-800-53-AC-12
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-2(5)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-10
- PCI-DSS-Req-8.1.8
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_set_keepalive_0
|
Set SSH Idle Timeout IntervalruleSSH allows administrators to set an idle timeout interval. After this interval
has passed, the idle user will be automatically logged out.
To set an idle timeout interval, edit the following line in /etc/ssh/sshd_config as
follows:
ClientAliveInterval 1800
The timeout interval is given in seconds. For example, have a timeout
of 10 minutes, set interval to 600.
If a shorter timeout has already been set for the login shell, that value will
preempt any SSH setting made in /etc/ssh/sshd_config . Keep in mind that
some processes may stop SSH from correctly detecting that the user is idle.warning
SSH disconnecting idle clients will not have desired effect without also
configuring ClientAliveCountMax in the SSH service configuration. warning
Following conditions may prevent the SSH session to time out:
- Remote processes on the remote machine generates output. As the output has to be transferred over the network to the client, the timeout is reset every time such transfer happens.
- Any
scp or sftp activity by the same user to the host resets the timeout.
Rationale:Terminating an idle ssh session within a short time period reduces the window of
opportunity for unauthorized personnel to take control of a management session
enabled on the console or console port that has been let unattended. identifiers:
CCE-27433-2 references:
BP28(R29), 5.2.12, 1, 12, 13, 14, 15, 16, 18, 3, 5, 7, 8, 5.5.6, APO13.01, BAI03.01, BAI03.02, BAI03.03, DSS01.03, DSS03.05, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.03, DSS06.10, 3.1.11, CCI-000879, CCI-001133, CCI-002361, 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 6.2, A.12.4.1, A.12.4.3, A.14.1.1, A.14.2.1, A.14.2.5, A.18.1.4, A.6.1.2, A.6.1.5, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, CM-6(a), AC-17(a), AC-2(5), AC-12, AC-17(a), SC-10, CM-6(a), DE.CM-1, DE.CM-3, PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.IP-2, Req-8.1.8, SRG-OS-000126-GPOS-00066, SRG-OS-000163-GPOS-00072, SRG-OS-000279-GPOS-00109, SRG-OS-000395-GPOS-00175, RHEL-07-040320, SV-204587r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
sshd_idle_timeout_value="1800"
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: XCCDF Value sshd_idle_timeout_value # promote to variable
set_fact:
sshd_idle_timeout_value: !!str 1800
tags:
- always
- name: Set SSH Idle Timeout Interval
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*ClientAliveInterval\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: ClientAliveInterval {{ sshd_idle_timeout_value }}
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27433-2
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040320
- NIST-800-171-3.1.11
- NIST-800-53-AC-12
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-2(5)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-6(a)
- NIST-800-53-SC-10
- PCI-DSS-Req-8.1.8
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_set_idle_timeout
|
Disable SSH Root LoginruleThe root user should never be allowed to login to a
system directly over a network.
To disable root login via SSH, add or correct the following line
in /etc/ssh/sshd_config :
PermitRootLogin no Rationale:Even though the communications channel may be encrypted, an additional layer of
security is gained by extending the policy of not logging directly on as root.
In addition, logging in with a user-specific account provides individual
accountability of actions performed on the system and also helps to minimize
direct attack attempts on root's password. identifiers:
CCE-27445-6 references:
BP28(R19), NT007(R21), 5.2.8, 1, 11, 12, 13, 14, 15, 16, 18, 3, 5, 5.5.6, APO01.06, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS05.10, DSS06.02, DSS06.03, DSS06.06, DSS06.10, 3.1.1, 3.1.5, CCI-000366, CCI-000770, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 5.2, A.10.1.1, A.11.1.4, A.11.1.5, A.11.2.1, A.13.1.1, A.13.1.3, A.13.2.1, A.13.2.3, A.13.2.4, A.14.1.2, A.14.1.3, A.18.1.4, A.6.1.2, A.7.1.1, A.7.1.2, A.7.3.1, A.8.2.2, A.8.2.3, A.9.1.1, A.9.1.2, A.9.2.1, A.9.2.2, A.9.2.3, A.9.2.4, A.9.2.6, A.9.3.1, A.9.4.1, A.9.4.2, A.9.4.3, A.9.4.4, A.9.4.5, AC-6(2), AC-17(a), IA-2, IA-2(5), CM-7(a), CM-7(b), CM-6(a), PR.AC-1, PR.AC-4, PR.AC-6, PR.AC-7, PR.DS-5, PR.PT-3, FIA_UAU.1, SRG-OS-000109-GPOS-00056, SRG-OS-000480-GPOS-00227, RHEL-07-040370, SV-204592r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "PermitRootLogin no" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Disable SSH Root Login
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*PermitRootLogin\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: PermitRootLogin no
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27445-6
- CJIS-5.5.6
- DISA-STIG-RHEL-07-040370
- NIST-800-171-3.1.1
- NIST-800-171-3.1.5
- NIST-800-53-
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-6(2)
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- NIST-800-53-IA-2
- NIST-800-53-IA-2(5)
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
- sshd_disable_root_login
|
Disable Host-Based AuthenticationruleSSH's cryptographic host-based authentication is
more secure than .rhosts authentication. However, it is
not recommended that hosts unilaterally trust one another, even
within an organization.
To disable host-based authentication, add or correct the
following line in /etc/ssh/sshd_config :
HostbasedAuthentication no Rationale:SSH trust relationships mean a compromise on one host
can allow an attacker to move trivially to other hosts. identifiers:
CCE-27413-4 references:
5.2.7, 11, 12, 14, 15, 16, 18, 3, 5, 9, 5.5.6, BAI10.01, BAI10.02, BAI10.03, BAI10.05, DSS05.02, DSS05.04, DSS05.05, DSS05.07, DSS06.03, DSS06.06, 3.1.12, CCI-000366, 164.308(a)(4)(i), 164.308(b)(1), 164.308(b)(3), 164.310(b), 164.312(e)(1), 164.312(e)(2)(ii), 4.3.3.2.2, 4.3.3.5.1, 4.3.3.5.2, 4.3.3.5.3, 4.3.3.5.4, 4.3.3.5.5, 4.3.3.5.6, 4.3.3.5.7, 4.3.3.5.8, 4.3.3.6.1, 4.3.3.6.2, 4.3.3.6.3, 4.3.3.6.4, 4.3.3.6.5, 4.3.3.6.6, 4.3.3.6.7, 4.3.3.6.8, 4.3.3.6.9, 4.3.3.7.1, 4.3.3.7.2, 4.3.3.7.3, 4.3.3.7.4, 4.3.4.3.2, 4.3.4.3.3, SR 1.1, SR 1.10, SR 1.11, SR 1.12, SR 1.13, SR 1.2, SR 1.3, SR 1.4, SR 1.5, SR 1.6, SR 1.7, SR 1.8, SR 1.9, SR 2.1, SR 2.2, SR 2.3, SR 2.4, SR 2.5, SR 2.6, SR 2.7, SR 7.6, 0421, 0422, 0431, 0974, 1173, 1401, 1504, 1505, 1546, 1557, 1558, 1559, 1560, 1561, A.12.1.2, A.12.5.1, A.12.6.2, A.14.2.2, A.14.2.3, A.14.2.4, A.6.1.2, A.7.1.1, A.9.1.2, A.9.2.1, A.9.2.3, A.9.4.1, A.9.4.4, A.9.4.5, AC-3, AC-17(a), CM-7(a), CM-7(b), CM-6(a), PR.AC-4, PR.AC-6, PR.IP-1, PR.PT-3, FIA_UAU.1, SRG-OS-000480-GPOS-00229, RHEL-07-010470, SV-204435r603261_rule, SRG-OS-000480-VMM-002000 Remediation script:# Remediation is applicable only in certain platforms
if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then
if [ -e "/etc/ssh/sshd_config" ] ; then
LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config"
else
touch "/etc/ssh/sshd_config"
fi
cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak"
# Insert before the line matching the regex '^Match'.
line_number="$(LC_ALL=C grep -n "^Match" "/etc/ssh/sshd_config.bak" | LC_ALL=C sed 's/:.*//g')"
if [ -z "$line_number" ]; then
# There was no match of '^Match', insert at
# the end of the file.
printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config"
else
head -n "$(( line_number - 1 ))" "/etc/ssh/sshd_config.bak" > "/etc/ssh/sshd_config"
printf '%s\n' "HostbasedAuthentication no" >> "/etc/ssh/sshd_config"
tail -n "+$(( line_number ))" "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config"
fi
# Clean up after ourselves.
rm "/etc/ssh/sshd_config.bak"
else
>&2 echo 'Remediation is not applicable, nothing was done'
fi
Remediation script:- name: Disable Host-Based Authentication
block:
- name: Deduplicate values from /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: false
regexp: (?i)^\s*HostbasedAuthentication\s+
state: absent
- name: Insert correct line to /etc/ssh/sshd_config
lineinfile:
path: /etc/ssh/sshd_config
create: true
line: HostbasedAuthentication no
state: present
insertbefore: ^[#\s]*Match
validate: /usr/sbin/sshd -t -f %s
when: ansible_virtualization_type not in ["docker", "lxc", "openvz", "podman", "container"]
tags:
- CCE-27413-4
- CJIS-5.5.6
- DISA-STIG-RHEL-07-010470
- NIST-800-171-3.1.12
- NIST-800-53-AC-17(a)
- NIST-800-53-AC-3
- NIST-800-53-CM-6(a)
- NIST-800-53-CM-7(a)
- NIST-800-53-CM-7(b)
- disable_host_auth
- low_complexity
- low_disruption
- medium_severity
- no_reboot_needed
- restrict_strategy
Remediation script:apiVersion: machineconfiguration.openshift.io/v1
kind: MachineConfig
spec:
config:
ignition:
version: 3.1.0
storage:
files:
- contents:
source: data:,%23%09%24OpenBSD%3A%20sshd_config%2Cv%201.103%202018%2F04%2F09%2020%3A41%3A22%20tj%20Exp%20%24%0A%0A%23%20This%20is%20the%20sshd%20server%20system-wide%20configuration%20file.%20%20See%0A%23%20sshd_config%285%29%20for%20more%20information.%0A%0A%23%20This%20sshd%20was%20compiled%20with%20PATH%3D%2Fusr%2Flocal%2Fbin%3A%2Fusr%2Fbin%3A%2Fusr%2Flocal%2Fsbin%3A%2Fusr%2Fsbin%0A%0A%23%20The%20strategy%20used%20for%20options%20in%20the%20default%20sshd_config%20shipped%20with%0A%23%20OpenSSH%20is%20to%20specify%20options%20with%20their%20default%20value%20where%0A%23%20possible%2C%20but%20leave%20them%20commented.%20%20Uncommented%20options%20override%20the%0A%23%20default%20value.%0A%0A%23%20If%20you%20want%20to%20change%20the%20port%20on%20a%20SELinux%20system%2C%20you%20have%20to%20tell%0A%23%20SELinux%20about%20this%20change.%0A%23%20semanage%20port%20-a%20-t%20ssh_port_t%20-p%20tcp%20%23PORTNUMBER%0A%23%0A%23Port%2022%0A%23AddressFamily%20any%0A%23ListenAddress%200.0.0.0%0A%23ListenAddress%20%3A%3A%0A%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_rsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ecdsa_key%0AHostKey%20%2Fetc%2Fssh%2Fssh_host_ed25519_key%0A%0A%23%20Ciphers%20and%20keying%0ARekeyLimit%20512M%201h%0A%0A%23%20System-wide%20Crypto%20policy%3A%0A%23%20This%20system%20is%20following%20system-wide%20crypto%20policy.%20The%20changes%20to%0A%23%20Ciphers%2C%20MACs%2C%20KexAlgoritms%20and%20GSSAPIKexAlgorithsm%20will%20not%20have%20any%0A%23%20effect%20here.%20They%20will%20be%20overridden%20by%20command-line%20options%20passed%20on%0A%23%20the%20server%20start%20up.%0A%23%20To%20opt%20out%2C%20uncomment%20a%20line%20with%20redefinition%20of%20%20CRYPTO_POLICY%3D%0A%23%20variable%20in%20%20%2Fetc%2Fsysconfig%2Fsshd%20%20to%20overwrite%20the%20policy.%0A%23%20For%20more%20information%2C%20see%20manual%20page%20for%20update-crypto-policies%288%29.%0A%0A%23%20Logging%0A%23SyslogFacility%20AUTH%0ASyslogFacility%20AUTHPRIV%0A%23LogLevel%20INFO%0A%0A%23%20Authentication%3A%0A%0A%23LoginGraceTime%202m%0APermitRootLogin%20no%0AStrictModes%20yes%0A%23MaxAuthTries%206%0A%23MaxSessions%2010%0A%0APubkeyAuthentication%20yes%0A%0A%23%20The%20default%20is%20to%20check%20both%20.ssh%2Fauthorized_keys%20and%20.ssh%2Fauthorized_keys2%0A%23%20but%20this%20is%20overridden%20so%20installations%20will%20only%20check%20.ssh%2Fauthorized_keys%0AAuthorizedKeysFile%09.ssh%2Fauthorized_keys%0A%0A%23AuthorizedPrincipalsFile%20none%0A%0A%23AuthorizedKeysCommand%20none%0A%23AuthorizedKeysCommandUser%20nobody%0A%0A%23%20For%20this%20to%20work%20you%20will%20also%20need%20host%20keys%20in%20%2Fetc%2Fssh%2Fssh_known_hosts%0AHostbasedAuthentication%20no%0A%23%20Change%20to%20yes%20if%20you%20don%27t%20trust%20~%2F.ssh%2Fknown_hosts%20for%0A%23%20HostbasedAuthentication%0AIgnoreUserKnownHosts%20yes%0A%23%20Don%27t%20read%20the%20user%27s%20~%2F.rhosts%20and%20~%2F.shosts%20files%0AIgnoreRhosts%20yes%0A%0A%23%20To%20disable%20tunneled%20clear%20text%20passwords%2C%20change%20to%20no%20here%21%0A%23PasswordAuthentication%20yes%0APermitEmptyPasswords%20no%0APasswordAuthentication%20no%0A%0A%23%20Change%20to%20no%20to%20disable%20s%2Fkey%20passwords%0A%23ChallengeResponseAuthentication%20yes%0AChallengeResponseAuthentication%20no%0A%0A%23%20Kerberos%20options%0AKerberosAuthentication%20no%0A%23KerberosOrLocalPasswd%20yes%0A%23KerberosTicketCleanup%20yes%0A%23KerberosGetAFSToken%20no%0A%23KerberosUseKuserok%20yes%0A%0A%23%20GSSAPI%20options%0AGSSAPIAuthentication%20no%0AGSSAPICleanupCredentials%20no%0A%23GSSAPIStrictAcceptorCheck%20yes%0A%23GSSAPIKeyExchange%20no%0A%23GSSAPIEnablek5users%20no%0A%0A%23%20Set%20this%20to%20%27yes%27%20to%20enable%20PAM%20authentication%2C%20account%20processing%2C%0A%23%20and%20session%20processing.%20If%20this%20is%20enabled%2C%20PAM%20authentication%20will%0A%23%20be%20allowed%20through%20the%20ChallengeResponseAuthentication%20and%0A%23%20PasswordAuthentication.%20%20Depending%20on%20your%20PAM%20configuration%2C%0A%23%20PAM%20authentication%20via%20ChallengeResponseAuthentication%20may%20bypass%0A%23%20the%20setting%20of%20%22PermitRootLogin%20without-password%22.%0A%23%20If%20you%20just%20want%20the%20PAM%20account%20and%20session%20checks%20to%20run%20without%0A%23%20PAM%20authentication%2C%20then%20enable%20this%20but%20set%20PasswordAuthentication%0A%23%20and%20ChallengeResponseAuthentication%20to%20%27no%27.%0A%23%20WARNING%3A%20%27UsePAM%20no%27%20is%20not%20supported%20in%20Fedora%20and%20may%20cause%20several%0A%23%20problems.%0AUsePAM%20yes%0A%0A%23AllowAgentForwarding%20yes%0A%23AllowTcpForwarding%20yes%0A%23GatewayPorts%20no%0AX11Forwarding%20yes%0A%23X11DisplayOffset%2010%0A%23X11UseLocalhost%20yes%0A%23PermitTTY%20yes%0A%0A%23%20It%20is%20recommended%20to%20use%20pam_motd%20in%20%2Fetc%2Fpam.d%2Fsshd%20instead%20of%20PrintMotd%2C%0A%23%20as%20it%20is%20more%20configurable%20and%20versatile%20than%20the%20built-in%20version.%0APrintMotd%20no%0A%0APrintLastLog%20yes%0A%23TCPKeepAlive%20yes%0APermitUserEnvironment%20no%0ACompression%20no%0AClientAliveInterval%20600%0AClientAliveCountMax%200%0A%23UseDNS%20no%0A%23PidFile%20%2Fvar%2Frun%2Fsshd.pid%0A%23MaxStartups%2010%3A30%3A100%0A%23PermitTunnel%20no%0A%23ChrootDirectory%20none%0A%23VersionAddendum%20none%0A%0A%23%20no%20default%20banner%20path%0ABanner%20%2Fetc%2Fissue%0A%0A%23%20Accept%20locale-related%20environment%20variables%0AAcceptEnv%20LANG%20LC_CTYPE%20LC_NUMERIC%20LC_TIME%20LC_COLLATE%20LC_MONETARY%20LC_MESSAGES%0AAcceptEnv%20LC_PAPER%20LC_NAME%20LC_ADDRESS%20LC_TELEPHONE%20LC_MEASUREMENT%0AAcceptEnv%20LC_IDENTIFICATION%20LC_ALL%20LANGUAGE%0AAcceptEnv%20XMODIFIERS%0A%0A%23%20override%20default%20of%20no%20subsystems%0ASubsystem%09sftp%09%2Fusr%2Flibexec%2Fopenssh%2Fsftp-server%0A%0A%23%20Example%20of%20overriding%20settings%20on%20a%20per-user%20basis%0A%23Match%20User%20anoncvs%0A%23%09X11Forwarding%20no%0A%23%09AllowTcpForwarding%20no%0A%23%09PermitTTY%20no%0A%23%09ForceCommand%20cvs%20server%0A%0AUsePrivilegeSeparation%20sandbox
mode: 0600
path: /etc/ssh/sshd_config
overwrite: true
|