contains 215 rules |
System Settings
[ref]groupContains rules that check correct system settings. |
contains 153 rules |
Installing and Maintaining Software
[ref]groupThe following sections contain information on
security-relevant choices during the initial operating system
installation process and the setup of software
updates. |
contains 11 rules |
Disk Partitioning
[ref]groupTo ensure separation and protection of data, there
are top-level system directories which should be placed on their
own physical partition or logical volume. The installer's default
partitioning scheme creates separate logical volumes for
/ , /boot , and swap .
- If starting with any of the default layouts, check the box to
"Review and modify partitioning." This allows for the easy creation
of additional logical volumes inside the volume group already
created, though it may require making
/ 's logical volume smaller to
create space. In general, using logical volumes is preferable to
using partitions because they can be more easily adjusted
later. - If creating a custom layout, create the partitions mentioned in
the previous paragraph (which the installer will require anyway),
as well as separate ones described in the following sections.
If a system has already been installed, and the default
partitioning scheme was used, it is possible but nontrivial to
modify it to create separate logical volumes for the directories
listed above. The Logical Volume Manager (LVM) makes this possible.
See the LVM HOWTO at http://tldp.org/HOWTO/LVM-HOWTO/ for more
detailed information on LVM. |
contains 1 rule |
Ensure /var/log/audit Located On Separate Partition
[ref]rule
Audit logs are stored in the /var/log/audit directory. Ensure that it
has its own partition or logical volume at installation time, or migrate it
later using LVM. Make absolutely certain that it is large enough to store all
audit logs that will be created by the auditing daemon.
Rationale:
Placing /var/log/audit in its own partition
enables better separation between audit files
and other files, and helps ensure that
auditing cannot be halted due to the partition running out
of space.
|
Software Integrity Checking
[ref]group
Both 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.
Integrity checking cannot prevent intrusions,
but can detect that they have occurred. Requirements
for software 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 5 rules |
Verify Integrity with AIDE
[ref]groupAIDE 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 2 rules |
Install AIDE
[ref]rule
Install the AIDE package with the command:
$ sudo yum install aide
Rationale:
The AIDE package must be installed if it is to be available for integrity checking.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command install aide
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Ensure aide is installed
package:
name="{{item}}"
state=present
with_items:
- aide
tags:
- package_aide_installed
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27024-9
- NIST-800-53-CM-3(d)
- NIST-800-53-CM-3(e)
- NIST-800-53-CM-6(d)
- NIST-800-53-SC-28
- NIST-800-53-SI-7
- PCI-DSS-Req-11.5
- DISA-STIG-RHEL-06-000016
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
include install_aide
class install_aide {
package { 'aide':
ensure => 'installed',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
package --add=aide
|
Build and Test AIDE Database
[ref]ruleRun 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.
References:
CM-3(d), CM-3(e), CM-6(d), SC-28, SI-7, CCI-000374, CCI-000416, CCI-001069, CCI-001263, CCI-001297, CCI-001589, Req-11.5, SV-65601r1_rule, SRG-OS-000232 Remediation Shell script: (show)
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command install aide
/usr/sbin/aide --init
/bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Ensure AIDE is installed"
package:
name="{{item}}"
state=present
with_items:
- aide
tags:
- aide_build_database
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27135-3
- NIST-800-53-CM-3(d)
- NIST-800-53-CM-3(e)
- NIST-800-53-CM-6(d)
- NIST-800-53-SC-28
- NIST-800-53-SI-7
- PCI-DSS-Req-11.5
- DISA-STIG-RHEL-06-000018
- name: "Build and Test AIDE Database"
shell: /usr/sbin/aide --init
tags:
- aide_build_database
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27135-3
- NIST-800-53-CM-3(d)
- NIST-800-53-CM-3(e)
- NIST-800-53-CM-6(d)
- NIST-800-53-SC-28
- NIST-800-53-SI-7
- PCI-DSS-Req-11.5
- DISA-STIG-RHEL-06-000018
- name: Stage AIDE Database"
copy:
src: /var/lib/aide/aide.db.new.gz
dest: /var/lib/aide/aide.db.gz
backup: yes
remote_src: yes
tags:
- aide_build_database
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27135-3
- NIST-800-53-CM-3(d)
- NIST-800-53-CM-3(e)
- NIST-800-53-CM-6(d)
- NIST-800-53-SC-28
- NIST-800-53-SI-7
- PCI-DSS-Req-11.5
- DISA-STIG-RHEL-06-000018
|
Additional Security Software
[ref]group
Additional security software that is not provided or supported
by Red Hat can be installed to provide complementary or duplicative
security capabilities to those provided by the base platform. Add-on
software may not be appropriate for some specialized systems.
|
contains 2 rules |
Install Intrusion Detection Software
[ref]rule
The base Red Hat platform already includes a sophisticated auditing system that
can detect intruder activity, as well as SELinux, which provides host-based
intrusion prevention capabilities by confining privileged programs and user
sessions which may become compromised.
In DoD environments, supplemental intrusion detection tools, such as, the McAfee
Host-based Security System, are available to integrate with existing infrastructure.
When these supplemental tools interfere with the proper functioning of SELinux, SELinux
takes precedence.
Rationale:
Host-based intrusion detection tools provide a system-level defense when an
intruder gains access to a system or network.
|
Install Virus Scanning Software
[ref]rule
Install virus scanning software, which uses signatures to search for the
presence of viruses on the filesystem.
The McAfee VirusScan Enterprise for Linux virus scanning tool is provided for DoD systems.
Ensure virus definition files are no older than 7 days, or their last release.
Configure the virus scanning software to perform scans dynamically on all
accessed files. If this is not possible, configure the
system to scan all altered files on the system on a daily
basis. If the system processes inbound SMTP mail, configure the virus scanner
to scan all received mail.
Rationale:
Virus scanning software can be used to detect if a system has been compromised by
computer viruses, as well as to limit their spread to other systems.
|
Disable Prelinking
[ref]rule
The 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.
Remediation Shell script: (show)
#
# Disable prelinking altogether
#
if grep -q ^PRELINKING /etc/sysconfig/prelink
then
sed -i 's/PRELINKING.*/PRELINKING=no/g' /etc/sysconfig/prelink
else
echo -e "\n# Set PRELINKING=no per security requirements" >> /etc/sysconfig/prelink
echo "PRELINKING=no" >> /etc/sysconfig/prelink
fi
#
# Undo previous prelink changes to binaries
#
/usr/sbin/prelink -ua
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: disable prelinking
lineinfile:
path: /etc/sysconfig/prelink
regexp: '^PRELINKING='
line: 'PRELINKING=no'
tags:
- disable_prelink
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27221-1
- NIST-800-53-CM-6(d)
- NIST-800-53-SC-28
- NIST-800-53-SI-7
- PCI-DSS-Req-11.5
|
GNOME Desktop Environment
[ref]group
GNOME 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 5 rules |
Configure GNOME Screen Locking
[ref]groupIn the default GNOME desktop, the screen can be locked
by choosing Lock Screen from the System menu.
The gconftool-2 program can be used to enforce mandatory
screen locking settings for the default GNOME environment.
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 Lock Screen icon from the System menu can
also be dragged to the taskbar in order to facilitate even more
convenient screen-locking.
The root account cannot be screen-locked, but this should
have no practical effect as 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 configuring GNOME screensaver, see
http://live.gnome.org/GnomeScreensaver. For more information about
enforcing preferences in the GNOME environment using the GConf
configuration system, see http://projects.gnome.org/gconf and
the man page gconftool-2(1) . |
contains 3 rules |
GNOME Desktop Screensaver Mandatory Use
[ref]rule
Run the following command to activate the screensaver
in the GNOME desktop after a period of inactivity:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /apps/gnome-screensaver/idle_activation_enabled true
Rationale:
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.
Remediation Shell script: (show)
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
yum -y install GConf2
fi
# Set the screensaver activation in the GNOME desktop after a period of inactivity
gconftool-2 --direct \
--config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
--type bool \
--set /apps/gnome-screensaver/idle_activation_enabled true
|
Enable Screen Lock Activation After Idle Period
[ref]rule
Run the following command to activate locking of the screensaver
in the GNOME desktop when it is activated:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /apps/gnome-screensaver/lock_enabled true
Rationale:
Enabling the activation of the screen lock after an idle period
ensures password entry will be required in order to
access the system, preventing access by passersby.
Remediation Shell script: (show)
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
yum -y install GConf2
fi
# Set the screensaver locking activation in the GNOME desktop when the
# screensaver is activated
gconftool-2 --direct \
--config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
--type bool \
--set /apps/gnome-screensaver/lock_enabled true
|
Implement Blank Screensaver
[ref]rule
Run the following command to set the screensaver mode
in the GNOME desktop to a blank screen:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type string \
--set /apps/gnome-screensaver/mode blank-only
Rationale:
Setting the screensaver mode to blank-only conceals the
contents of the display from passersby.
Remediation Shell script: (show)
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
yum -y install GConf2
fi
# Set the screensaver mode in the GNOME desktop to a blank screen
gconftool-2 --direct \
--config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
--type string \
--set /apps/gnome-screensaver/mode blank-only
|
GNOME Media Settings
[ref]group
GNOME media settings that apply to the graphical interface.
|
contains 2 rules |
Disable GNOME Automounting
[ref]ruleThe system's default desktop environment, GNOME, will mount
devices and removable media (such as DVDs, CDs and USB flash drives) whenever
they are inserted into the system. Disable automount and autorun within GNOME
by running the following:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /apps/nautilus/preferences/media_automount false
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /apps/nautilus/preferences/media_autorun_never true
Rationale:Disabling automatic mounting in GNOME can prevent
the introduction of malware via removable media.
It will, however, also prevent desktop users from legitimate use
of removable media.
|
Disable All GNOME Thumbnailers
[ref]ruleThe system's default desktop environment, GNOME, uses
a number of different thumbnailer programs to generate thumbnails
for any new or modified content in an opened folder. The following
command can disable the execution of these thumbnail applications:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /desktop/gnome/thumbnailers/disable_all true
This effectively prevents an attacker from gaining access to a
system through a flaw in GNOME's Nautilus thumbnail creators.
Rationale:An attacker with knowledge of a flaw in a GNOME3 thumbnailer application could craft a malicious
file to exploit this flaw. Assuming the attacker could place the malicious file on the local filesystem
(via a web upload for example) and assuming a user browses the same location using Nautilus, the
malicious file would exploit the thumbnailer with the potential for malicious code execution. It
is best to disable these thumbnailer applications unless they are explicitly required. |
File Permissions and Masks
[ref]groupTraditional 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 CentOS 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 37 rules |
Restrict Partition Mount Options
[ref]groupSystem partitions can be mounted with certain options
that limit what files on those partitions can do. These options
are set in the /etc/fstab configuration file, and can be
used to make certain types of malicious behavior more difficult. |
contains 6 rules |
Add nodev Option to Non-Root Local Partitions
[ref]ruleThe nodev mount option prevents files from being
interpreted as character or block devices.
Legitimate character and block devices should exist only in
the /dev directory on the root partition or within chroot
jails built for system services.
Add the nodev option to the fourth column of
/etc/fstab for the line which controls mounting of
any non-root local partitions.
Rationale:The nodev mount option prevents files from being
interpreted as character or block devices. The only legitimate location
for device files is the /dev directory located on the root partition.
The only exception to this is chroot jails, for which it is not advised
to set nodev on these filesystems. Remediation Shell script: (show)
# NOTE: Run-time reconfiguration of partitions' mount options is not possible.
# After performing this remediation be sure to also subsequently reboot the
# system as soon as possible for the remediation to take the effect!
# Shortened ID for frequently used character class
SP="[:space:]"
# Load /etc/fstab's content with LABEL= and UUID= tags expanded to real
# device names into FSTAB_REAL_DEVICES array splitting items by newline
IFS=$'\n' FSTAB_REAL_DEVICES=($(findmnt --fstab --evaluate --noheadings))
for line in ${FSTAB_REAL_DEVICES[@]}
do
# For each line:
# * squeeze multiple space characters into one,
# * split line content info four columns (target, source, fstype, and
# mount options) by space delimiter
IFS=$' ' read TARGET SOURCE FSTYPE MOUNT_OPTIONS <<< "$(echo $line | tr -s ' ')"
# Filter the targets according to the following criteria:
# * don't include record for root partition,
# * include the target only if it has the form of '/word.*' (not to include
# special entries like e.g swap),
# * include the target only if its source has the form of '/dev.*'
# (to process only local partitions)
if [[ ! $TARGET =~ ^\/$ ]] && # Don't include root partition
[[ $TARGET =~ ^\/[A-Za-z0-9_] ]] && # Include if target =~ '/word.*'
[[ $SOURCE =~ ^\/dev ]] # Include if source =~ '/dev.*'
then
# Check the mount options column if it doesn't contain 'nodev' keyword yet
if ! grep -q "nodev" <<< "$MOUNT_OPTIONS"
then
# Check if current mount options is empty string ('') meaning
# particular /etc/fstab row contain just 'defaults' keyword
if [[ ${#MOUNT_OPTIONS} == "0" ]]
then
# If so, add 'defaults' back and append 'nodev' keyword
MOUNT_OPTIONS="defaults,nodev"
else
# Otherwise append just 'nodev' keyword
MOUNT_OPTIONS="$MOUNT_OPTIONS,nodev"
fi
# Escape possible slash ('/') characters in target for use as sed
# expression below
TARGET_ESCAPED=${TARGET//$'/'/$'\/'}
# This target doesn't contain 'nodev' in mount options yet (and meets
# the above filtering criteria). Therefore obtain particular /etc/fstab's
# row into FSTAB_TARGET_ROW variable separating the mount options field with
# hash '#' character
FSTAB_TARGET_ROW=$(sed -n "s/\(.*$TARGET_ESCAPED[$SP]\+$FSTYPE[$SP]\+\)\([^$SP]\+\)/\1#\2#/p" /etc/fstab)
# Split the retrieved value by the hash '#' delimiter to get the
# row's head & tail (i.e. columns other than mount options) which won't
# get modified
IFS=$'#' read TARGET_HEAD TARGET_OPTS TARGET_TAIL <<< "$FSTAB_TARGET_ROW"
# Replace old mount options for particular /etc/fstab's row (for this target
# and fstype) with new mount options
sed -i "s#${TARGET_HEAD}\(.*\)${TARGET_TAIL}#${TARGET_HEAD}${MOUNT_OPTIONS}${TARGET_TAIL}#" /etc/fstab
fi
fi
done
|
Add nodev Option to /tmp
[ref]rule
The nodev mount option can be used to prevent device files from
being created in /tmp .
Legitimate character and block devices should not exist
within temporary directories like /tmp .
Add the nodev option to the fourth column of
/etc/fstab for the line which controls mounting of
/tmp .
Rationale:The only legitimate location for device files is the /dev directory
located on the root partition. The only exception to this is chroot jails. Remediation Shell script: (show)
function include_mount_options_functions {
:
}
# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt" ) -eq 0 ]; then
_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
fi
}
# $1: mount point
function get_mount_point_regexp {
printf "[[:space:]]%s[[:space:]]" "$1"
}
# $1: mount point
function assert_mount_point_in_fstab {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
grep "$_mount_point_match_regexp" -q /etc/fstab \
|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}
# $1: mount point
function remove_defaults_from_fstab_if_overriden {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,") -gt 0 ]
then
sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
fi
}
# $1: mount point
function ensure_partition_is_mounted {
local _mount_point="$1"
mkdir -p "$_mount_point" || return 1
if mountpoint -q "$_mount_point"; then
mount -o remount --target "$_mount_point"
else
mount --target "$_mount_point"
fi
}
include_mount_options_functions
# test "$mount_has_to_exist" = 'yes'
test "yes" = 'yes' && assert_mount_point_in_fstab /tmp \
|| { echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; exit 1; }
ensure_mount_option_in_fstab "/tmp" "nodev"
ensure_partition_is_mounted "/tmp"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | configure |
---|
- name: get back device associated to mountpoint
shell: mount | grep ' /tmp ' |cut -d ' ' -f 1
register: device_name
check_mode: no
tags:
- mount_option_tmp_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26499-4
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device previous mount option
shell: mount | grep ' /tmp ' | sed -re 's:.*\((.*)\):\1:'
register: device_cur_mountoption
check_mode: no
tags:
- mount_option_tmp_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26499-4
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device fstype
shell: mount | grep ' /tmp ' | cut -d ' ' -f 5
register: device_fstype
check_mode: no
tags:
- mount_option_tmp_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26499-4
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: Ensure permission nodev are set on /tmp
mount:
path: "/tmp"
src: "{{device_name.stdout}}"
opts: "{{device_cur_mountoption.stdout}},nodev"
state: "mounted"
fstype: "{{device_fstype.stdout}}"
tags:
- mount_option_tmp_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26499-4
- NIST-800-53-CM-7
- NIST-800-53-MP-2
|
Add nosuid Option to /tmp
[ref]ruleThe nosuid mount option can be used to prevent
execution of setuid programs in /tmp . The SUID and SGID permissions
should not be required in these world-writable directories.
Add the nosuid option to the fourth column of
/etc/fstab for the line which controls mounting of
/tmp .
Rationale:The presence of SUID and SGID executables should be tightly controlled. Users
should not be able to execute SUID or SGID binaries from temporary storage partitions. Remediation Shell script: (show)
function include_mount_options_functions {
:
}
# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt" ) -eq 0 ]; then
_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
fi
}
# $1: mount point
function get_mount_point_regexp {
printf "[[:space:]]%s[[:space:]]" "$1"
}
# $1: mount point
function assert_mount_point_in_fstab {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
grep "$_mount_point_match_regexp" -q /etc/fstab \
|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}
# $1: mount point
function remove_defaults_from_fstab_if_overriden {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,") -gt 0 ]
then
sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
fi
}
# $1: mount point
function ensure_partition_is_mounted {
local _mount_point="$1"
mkdir -p "$_mount_point" || return 1
if mountpoint -q "$_mount_point"; then
mount -o remount --target "$_mount_point"
else
mount --target "$_mount_point"
fi
}
include_mount_options_functions
# test "$mount_has_to_exist" = 'yes'
test "yes" = 'yes' && assert_mount_point_in_fstab /tmp \
|| { echo "Not remediating, because there is no record of /tmp in /etc/fstab" >&2; exit 1; }
ensure_mount_option_in_fstab "/tmp" "nosuid"
ensure_partition_is_mounted "/tmp"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | configure |
---|
- name: get back device associated to mountpoint
shell: mount | grep ' /tmp ' |cut -d ' ' -f 1
register: device_name
check_mode: no
tags:
- mount_option_tmp_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26762-5
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device previous mount option
shell: mount | grep ' /tmp ' | sed -re 's:.*\((.*)\):\1:'
register: device_cur_mountoption
check_mode: no
tags:
- mount_option_tmp_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26762-5
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device fstype
shell: mount | grep ' /tmp ' | cut -d ' ' -f 5
register: device_fstype
check_mode: no
tags:
- mount_option_tmp_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26762-5
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: Ensure permission nosuid are set on /tmp
mount:
path: "/tmp"
src: "{{device_name.stdout}}"
opts: "{{device_cur_mountoption.stdout}},nosuid"
state: "mounted"
fstype: "{{device_fstype.stdout}}"
tags:
- mount_option_tmp_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26762-5
- NIST-800-53-CM-7
- NIST-800-53-MP-2
|
Add nodev Option to /dev/shm
[ref]ruleThe nodev mount option can be used to prevent creation
of device files in /dev/shm .
Legitimate character and block devices should not exist
within temporary directories like /dev/shm .
Add the nodev option to the fourth column of
/etc/fstab for the line which controls mounting of
/dev/shm .
Rationale:The only legitimate location for device files is the /dev directory
located on the root partition. The only exception to this is chroot jails. Remediation Shell script: (show)
function include_mount_options_functions {
:
}
# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt" ) -eq 0 ]; then
_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
fi
}
# $1: mount point
function get_mount_point_regexp {
printf "[[:space:]]%s[[:space:]]" "$1"
}
# $1: mount point
function assert_mount_point_in_fstab {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
grep "$_mount_point_match_regexp" -q /etc/fstab \
|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}
# $1: mount point
function remove_defaults_from_fstab_if_overriden {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,") -gt 0 ]
then
sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
fi
}
# $1: mount point
function ensure_partition_is_mounted {
local _mount_point="$1"
mkdir -p "$_mount_point" || return 1
if mountpoint -q "$_mount_point"; then
mount -o remount --target "$_mount_point"
else
mount --target "$_mount_point"
fi
}
include_mount_options_functions
# test "$mount_has_to_exist" = 'yes'
test "yes" = 'yes' && assert_mount_point_in_fstab /dev/shm \
|| { echo "Not remediating, because there is no record of /dev/shm in /etc/fstab" >&2; exit 1; }
ensure_mount_option_in_fstab "/dev/shm" "nodev"
ensure_partition_is_mounted "/dev/shm"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | configure |
---|
- name: get back device associated to mountpoint
shell: mount | grep ' /dev/shm ' |cut -d ' ' -f 1
register: device_name
check_mode: no
tags:
- mount_option_dev_shm_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26778-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device previous mount option
shell: mount | grep ' /dev/shm ' | sed -re 's:.*\((.*)\):\1:'
register: device_cur_mountoption
check_mode: no
tags:
- mount_option_dev_shm_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26778-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device fstype
shell: mount | grep ' /dev/shm ' | cut -d ' ' -f 5
register: device_fstype
check_mode: no
tags:
- mount_option_dev_shm_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26778-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: Ensure permission nodev are set on /dev/shm
mount:
path: "/dev/shm"
src: "{{device_name.stdout}}"
opts: "{{device_cur_mountoption.stdout}},nodev"
state: "mounted"
fstype: "{{device_fstype.stdout}}"
tags:
- mount_option_dev_shm_nodev
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26778-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
|
Add noexec Option to /dev/shm
[ref]ruleThe noexec mount option can be used to prevent binaries
from being executed out of /dev/shm .
It can be dangerous to allow the execution of binaries
from world-writable temporary storage directories such as /dev/shm .
Add the noexec option to the fourth column of
/etc/fstab for the line which controls mounting of
/dev/shm .
Rationale:Allowing users to execute binaries from world-writable directories
such as /dev/shm can expose the system to potential compromise. Remediation Shell script: (show)
function include_mount_options_functions {
:
}
# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt" ) -eq 0 ]; then
_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
fi
}
# $1: mount point
function get_mount_point_regexp {
printf "[[:space:]]%s[[:space:]]" "$1"
}
# $1: mount point
function assert_mount_point_in_fstab {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
grep "$_mount_point_match_regexp" -q /etc/fstab \
|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}
# $1: mount point
function remove_defaults_from_fstab_if_overriden {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,") -gt 0 ]
then
sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
fi
}
# $1: mount point
function ensure_partition_is_mounted {
local _mount_point="$1"
mkdir -p "$_mount_point" || return 1
if mountpoint -q "$_mount_point"; then
mount -o remount --target "$_mount_point"
else
mount --target "$_mount_point"
fi
}
include_mount_options_functions
# test "$mount_has_to_exist" = 'yes'
test "yes" = 'yes' && assert_mount_point_in_fstab /dev/shm \
|| { echo "Not remediating, because there is no record of /dev/shm in /etc/fstab" >&2; exit 1; }
ensure_mount_option_in_fstab "/dev/shm" "noexec"
ensure_partition_is_mounted "/dev/shm"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | configure |
---|
- name: get back device associated to mountpoint
shell: mount | grep ' /dev/shm ' |cut -d ' ' -f 1
register: device_name
check_mode: no
tags:
- mount_option_dev_shm_noexec
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26622-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device previous mount option
shell: mount | grep ' /dev/shm ' | sed -re 's:.*\((.*)\):\1:'
register: device_cur_mountoption
check_mode: no
tags:
- mount_option_dev_shm_noexec
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26622-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device fstype
shell: mount | grep ' /dev/shm ' | cut -d ' ' -f 5
register: device_fstype
check_mode: no
tags:
- mount_option_dev_shm_noexec
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26622-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: Ensure permission noexec are set on /dev/shm
mount:
path: "/dev/shm"
src: "{{device_name.stdout}}"
opts: "{{device_cur_mountoption.stdout}},noexec"
state: "mounted"
fstype: "{{device_fstype.stdout}}"
tags:
- mount_option_dev_shm_noexec
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26622-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
|
Add nosuid Option to /dev/shm
[ref]ruleThe nosuid mount option can be used to prevent execution
of setuid programs in /dev/shm . The SUID and SGID permissions should not
be required in these world-writable directories.
Add the nosuid option to the fourth column of
/etc/fstab for the line which controls mounting of
/dev/shm .
Rationale:The presence of SUID and SGID executables should be tightly controlled. Users
should not be able to execute SUID or SGID binaries from temporary storage partitions. Remediation Shell script: (show)
function include_mount_options_functions {
:
}
# $1: mount point
# $2: new mount point option
function ensure_mount_option_in_fstab {
local _mount_point="$1" _new_opt="$2" _mount_point_match_regexp="" _previous_mount_opts=""
_mount_point_match_regexp="$(get_mount_point_regexp "$_mount_point")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -c "$_new_opt" ) -eq 0 ]; then
_previous_mount_opts=$(grep "$_mount_point_match_regexp" /etc/fstab | awk '{print $4}')
sed -i "s|\(${_mount_point_match_regexp}.*${_previous_mount_opts}\)|\1,${_new_opt}|" /etc/fstab
fi
}
# $1: mount point
function get_mount_point_regexp {
printf "[[:space:]]%s[[:space:]]" "$1"
}
# $1: mount point
function assert_mount_point_in_fstab {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
grep "$_mount_point_match_regexp" -q /etc/fstab \
|| { echo "The mount point '$1' is not even in /etc/fstab, so we can't set up mount options" >&2; return 1; }
}
# $1: mount point
function remove_defaults_from_fstab_if_overriden {
local _mount_point_match_regexp
_mount_point_match_regexp="$(get_mount_point_regexp "$1")"
if [ $(grep "$_mount_point_match_regexp" /etc/fstab | grep -q "defaults,") -gt 0 ]
then
sed -i "s|\(${_mount_point_match_regexp}.*\)defaults,|\1|" /etc/fstab
fi
}
# $1: mount point
function ensure_partition_is_mounted {
local _mount_point="$1"
mkdir -p "$_mount_point" || return 1
if mountpoint -q "$_mount_point"; then
mount -o remount --target "$_mount_point"
else
mount --target "$_mount_point"
fi
}
include_mount_options_functions
# test "$mount_has_to_exist" = 'yes'
test "yes" = 'yes' && assert_mount_point_in_fstab /dev/shm \
|| { echo "Not remediating, because there is no record of /dev/shm in /etc/fstab" >&2; exit 1; }
ensure_mount_option_in_fstab "/dev/shm" "nosuid"
ensure_partition_is_mounted "/dev/shm"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | high |
---|
Strategy: | configure |
---|
- name: get back device associated to mountpoint
shell: mount | grep ' /dev/shm ' |cut -d ' ' -f 1
register: device_name
check_mode: no
tags:
- mount_option_dev_shm_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26486-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device previous mount option
shell: mount | grep ' /dev/shm ' | sed -re 's:.*\((.*)\):\1:'
register: device_cur_mountoption
check_mode: no
tags:
- mount_option_dev_shm_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26486-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: get back device fstype
shell: mount | grep ' /dev/shm ' | cut -d ' ' -f 5
register: device_fstype
check_mode: no
tags:
- mount_option_dev_shm_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26486-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
- name: Ensure permission nosuid are set on /dev/shm
mount:
path: "/dev/shm"
src: "{{device_name.stdout}}"
opts: "{{device_cur_mountoption.stdout}},nosuid"
state: "mounted"
fstype: "{{device_fstype.stdout}}"
tags:
- mount_option_dev_shm_nosuid
- low_severity
- configure_strategy
- low_complexity
- high_disruption
- CCE-26486-1
- NIST-800-53-CM-7
- NIST-800-53-MP-2
|
Restrict Dynamic Mounting and Unmounting of
Filesystems
[ref]groupLinux includes a number of facilities for the automated addition
and removal of filesystems on a running system. These facilities may be
necessary in many environments, but this capability also carries some risk -- whether direct
risk from allowing users to introduce arbitrary filesystems,
or risk that software flaws in the automated mount facility itself could
allow an attacker to compromise the system.
This command can be used to list the types of filesystems that are
available to the currently executing kernel:
$ find /lib/modules/`uname -r`/kernel/fs -type f -name '*.ko'
If these filesystems are not required then they can be explicitly disabled
in a configuratio file in /etc/modprobe.d .
|
contains 9 rules |
Disable Modprobe Loading of USB Storage Driver
[ref]rule
To prevent USB storage devices from being used, configure the kernel module loading system
to prevent automatic loading of the USB storage driver.
To configure the system to prevent the usb-storage
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install usb-storage /bin/true
This will prevent the modprobe program from loading the usb-storage
module, but will not prevent an administrator (or another program) from using the
insmod program to load the module manually.Rationale:USB storage devices such as thumb drives can be used to introduce
malicious software. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install usb-storage" /etc/modprobe.d/usb-storage.conf ; then
sed -i 's/^install usb-storage.*/install usb-storage /bin/true/g' /etc/modprobe.d/usb-storage.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/usb-storage.conf
echo "install usb-storage /bin/true" >> /etc/modprobe.d/usb-storage.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'usb-storage' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- usb-storage
tags:
- kernel_module_usb-storage_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27016-5
- NIST-800-53-AC-19(a)
- NIST-800-53-AC-19(d)
- NIST-800-53-AC-19(e)
- DISA-STIG-RHEL-06-000503
|
Disable Booting from USB Devices in Boot Firmware
[ref]ruleConfigure the system boot firmware (historically called BIOS on PC
systems) to disallow booting from USB drives.
Rationale:Booting a system from a USB device would allow an attacker to
circumvent any security measures provided by the operating system. Attackers
could mount partitions and modify the configuration of the OS. |
Disable the Automounter
[ref]ruleThe autofs daemon mounts and unmounts filesystems, such as user
home directories shared via NFS, on demand. In addition, autofs can be used to handle
removable media, and the default configuration provides the cdrom device as /misc/cd .
However, this method of providing access to removable media is not common, so autofs
can almost always be disabled if NFS is not in use. Even if NFS is required, it may be
possible to configure filesystem mounts statically by editing /etc/fstab
rather than relying on the automounter.
The autofs service can be disabled with the following command:
$ sudo chkconfig autofs off
Rationale:Disabling the automounter permits the administrator to
statically control filesystem mounting through /etc/fstab .
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable autofs
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service autofs
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- autofs
tags:
- service_autofs_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26976-1
- NIST-800-53-AC-19(a)
- NIST-800-53-AC-19(d)
- NIST-800-53-AC-19(e)
- DISA-STIG-RHEL-06-000526
|
Disable Mounting of cramfs
[ref]rule
To configure the system to prevent the cramfs
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install cramfs /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install cramfs" /etc/modprobe.d/cramfs.conf ; then
sed -i 's/^install cramfs.*/install cramfs /bin/true/g' /etc/modprobe.d/cramfs.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/cramfs.conf
echo "install cramfs /bin/true" >> /etc/modprobe.d/cramfs.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'cramfs' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- cramfs
tags:
- kernel_module_cramfs_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26340-0
- NIST-800-53-CM-7
|
Disable Mounting of freevxfs
[ref]rule
To configure the system to prevent the freevxfs
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install freevxfs /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install freevxfs" /etc/modprobe.d/freevxfs.conf ; then
sed -i 's/^install freevxfs.*/install freevxfs /bin/true/g' /etc/modprobe.d/freevxfs.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/freevxfs.conf
echo "install freevxfs /bin/true" >> /etc/modprobe.d/freevxfs.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'freevxfs' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- freevxfs
tags:
- kernel_module_freevxfs_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26544-7
- NIST-800-53-CM-7
|
Disable Mounting of jffs2
[ref]rule
To configure the system to prevent the jffs2
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install jffs2 /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install jffs2" /etc/modprobe.d/jffs2.conf ; then
sed -i 's/^install jffs2.*/install jffs2 /bin/true/g' /etc/modprobe.d/jffs2.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/jffs2.conf
echo "install jffs2 /bin/true" >> /etc/modprobe.d/jffs2.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'jffs2' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- jffs2
tags:
- kernel_module_jffs2_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26670-0
- NIST-800-53-CM-7
|
Disable Mounting of hfs
[ref]rule
To configure the system to prevent the hfs
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install hfs /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install hfs" /etc/modprobe.d/hfs.conf ; then
sed -i 's/^install hfs.*/install hfs /bin/true/g' /etc/modprobe.d/hfs.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfs.conf
echo "install hfs /bin/true" >> /etc/modprobe.d/hfs.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'hfs' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- hfs
tags:
- kernel_module_hfs_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26800-3
- NIST-800-53-CM-7
|
Disable Mounting of hfsplus
[ref]rule
To configure the system to prevent the hfsplus
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install hfsplus /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install hfsplus" /etc/modprobe.d/hfsplus.conf ; then
sed -i 's/^install hfsplus.*/install hfsplus /bin/true/g' /etc/modprobe.d/hfsplus.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/hfsplus.conf
echo "install hfsplus /bin/true" >> /etc/modprobe.d/hfsplus.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'hfsplus' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- hfsplus
tags:
- kernel_module_hfsplus_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26361-6
- NIST-800-53-CM-7
|
Disable Mounting of squashfs
[ref]rule
To configure the system to prevent the squashfs
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install squashfs /bin/true
This effectively prevents usage of this uncommon filesystem.
Rationale:Linux kernel modules which implement filesystems that are not needed by the
local system should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install squashfs" /etc/modprobe.d/squashfs.conf ; then
sed -i 's/^install squashfs.*/install squashfs /bin/true/g' /etc/modprobe.d/squashfs.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/squashfs.conf
echo "install squashfs /bin/true" >> /etc/modprobe.d/squashfs.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'squashfs' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- squashfs
tags:
- kernel_module_squashfs_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26404-4
- NIST-800-53-CM-7
|
Verify Permissions on Important Files and
Directories
[ref]groupPermissions 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 16 rules |
Verify Permissions on Files with Local Account Information and Credentials
[ref]groupThe 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 12 rules |
Verify User Who Owns shadow File
[ref]rule
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. Remediation Shell script: (show)
|
Verify Group Who Owns shadow File
[ref]rule
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. Remediation Shell script: (show)
|
Verify Permissions on shadow File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0000 /etc/shadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Ensure permission 0000 on /etc/shadow
file:
path="{{item}}"
mode=0000
with_items:
- /etc/shadow
tags:
- file_permissions_etc_shadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26992-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000035
|
Verify User Who Owns group File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_owner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26822-7
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000042
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26822-7
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000042
|
Verify Group Who Owns group File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_groupowner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26930-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000043
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26930-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000043
|
Verify Permissions on group File
[ref]rule
To properly set the permissions of /etc/group , run the command:
$ sudo chmod 644 /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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0644 /etc/group
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/group file(s)
find:
paths: "{{ '/etc/group' | dirname }}"
patterns: "{{ '/etc/group' | basename }}"
register: files_found
tags:
- file_permissions_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26954-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000044
- name: Set permissions
file:
path: "{{ item.path }}"
mode: 0644
with_items:
- "{{ files_found.files }}"
tags:
- file_permissions_etc_group
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26954-8
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000044
|
Verify User Who Owns gshadow File
[ref]rule
To properly set the owner of /etc/gshadow , run the command:
$ sudo chown root /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/gshadow file(s)
find:
paths: "{{ '/etc/gshadow' | dirname }}"
patterns: "{{ '/etc/gshadow' | basename }}"
register: files_found
tags:
- file_owner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27026-4
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000036
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27026-4
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000036
|
Verify Group Who Owns gshadow File
[ref]rule
To properly set the group owner of /etc/gshadow , run the command:
$ sudo chgrp root /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/gshadow file(s)
find:
paths: "{{ '/etc/gshadow' | dirname }}"
patterns: "{{ '/etc/gshadow' | basename }}"
register: files_found
tags:
- file_groupowner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26975-3
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000037
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26975-3
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000037
|
Verify Permissions on gshadow File
[ref]rule
To properly set the permissions of /etc/gshadow , run the command:
$ sudo chmod 0000 /etc/gshadow
Rationale:The /etc/gshadow file contains group password hashes. Protection of this file
is critical for system security. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0000 /etc/gshadow
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Ensure permission 0000 on /etc/gshadow
file:
path="{{item}}"
mode=0000
with_items:
- /etc/gshadow
tags:
- file_permissions_etc_gshadow
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26951-4
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000038
|
Verify User Who Owns passwd File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chown root /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/passwd file(s)
find:
paths: "{{ '/etc/passwd' | dirname }}"
patterns: "{{ '/etc/passwd' | basename }}"
register: files_found
tags:
- file_owner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26953-0
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000039
- name: Set user ownership to root
file:
path: "{{ item.path }}"
owner: root
with_items:
- "{{ files_found.files }}"
tags:
- file_owner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26953-0
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000039
|
Verify Group Who Owns passwd File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chgrp root /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/passwd file(s)
find:
paths: "{{ '/etc/passwd' | dirname }}"
patterns: "{{ '/etc/passwd' | basename }}"
register: files_found
tags:
- file_groupowner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26856-5
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000040
- name: Set group ownership to root
file:
path: "{{ item.path }}"
group: root
with_items:
- "{{ files_found.files }}"
tags:
- file_groupowner_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26856-5
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000040
|
Verify Permissions on passwd File
[ref]rule
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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0644 /etc/passwd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Ensure permission 0644 on /etc/passwd
file:
path="{{item}}"
mode=0644
with_items:
- /etc/passwd
tags:
- file_permissions_etc_passwd
- medium_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-26868-0
- NIST-800-53-AC-6
- PCI-DSS-Req-8.7.c
- DISA-STIG-RHEL-06-000041
|
Verify File Permissions Within Some Important Directories
[ref]groupSome directories contain files whose confidentiality or integrity
is notably important and may also be susceptible to misconfiguration over time, particularly if
unpackaged software is installed. As such,
an argument exists to verify that files' permissions within these directories remain
configured correctly and restrictively.
|
contains 3 rules |
Verify that Shared Library Files Have Root Ownership
[ref]ruleSystem-wide shared library files, which are linked to executables
during process load time or run time, are stored in the following directories
by default:
/lib
/lib64
/usr/lib
/usr/lib64
Kernel modules, which can be added to the kernel during runtime, are also
stored in /lib/modules . All files in these directories should be
owned by the root user. If the directory, or any file in these
directories, is found to be owned by a user other than root correct its
ownership with the following command:
$ sudo chown root FILE
Rationale:Files from shared library directories are loaded into the address
space of processes (including privileged ones) or of the kernel itself at
runtime. Proper ownership is necessary to protect the integrity of the system.
Remediation Shell script: (show)
for LIBDIR in /usr/lib /usr/lib64 /lib /lib64
do
if [ -d $LIBDIR ]
then
find -L $LIBDIR \! -user root -exec chown root {} \;
fi
done
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list libraries without root ownership"
shell: "find -L /usr/lib /usr/lib64 /lib /lib64 \\! -user root"
register: libraries_not_owned_by_root
changed_when: False
failed_when: False
check_mode: no
tags:
- file_ownership_library_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27424-1
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000046
- name: "Set ownership of system libraries to root"
file:
path: "{{item}}"
owner: "root"
with_items: "{{ libraries_not_owned_by_root.stdout_lines }}"
when: libraries_not_owned_by_root | length > 0
tags:
- file_ownership_library_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27424-1
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000046
|
Verify that System Executables Have Restrictive Permissions
[ref]rule
System executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should not be group-writable or world-writable.
If any file FILE in these directories is found
to be group-writable or world-writable, correct its permission with the
following command:
$ sudo chmod go-w FILE
Rationale:System binaries are executed by privileged users, as well as system services,
and restrictive permissions are necessary to ensure execution of these programs
cannot be co-opted.
Remediation Shell script: (show)
DIRS="/bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec"
for dirPath in $DIRS; do
find "$dirPath" -perm /022 -exec chmod go-w '{}' \;
done
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list of world and group writable system executables"
shell: "find /bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec -perm /022 -type f"
register: world_writable_library_files
changed_when: False
failed_when: False
check_mode: no
tags:
- file_permissions_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27289-8
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000047
- name: "Remove world/group writability of system executables"
file:
path: "{{item}}"
mode: "go-w"
with_items: "{{ world_writable_library_files.stdout_lines }}"
when: world_writable_library_files.stdout_lines | length > 0
tags:
- file_permissions_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27289-8
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000047
|
Verify that System Executables Have Root Ownership
[ref]rule
System executables are stored in the following directories by default:
/bin
/sbin
/usr/bin
/usr/libexec
/usr/local/bin
/usr/local/sbin
/usr/sbin
All files in these directories should be owned by the root user.
If any file FILE in these directories is found
to be owned by a user other than root, correct its ownership with the
following command:
$ sudo chown root FILE
Rationale:System binaries are executed by privileged users as well as system services,
and restrictive permissions are necessary to ensure that their
execution of these programs cannot be co-opted.
Remediation Shell script: (show)
find /bin/ \
/usr/bin/ \
/usr/local/bin/ \
/sbin/ \
/usr/sbin/ \
/usr/local/sbin/ \
/usr/libexec \
\! -user root -execdir chown root {} \;
Remediation Ansible snippet: (show)
Complexity: | medium |
---|
Disruption: | medium |
---|
Strategy: | restrict |
---|
- name: "Read list of system executables without root ownership"
shell: "find /bin/ /usr/bin/ /usr/local/bin/ /sbin/ /usr/sbin/ /usr/local/sbin/ /usr/libexec \\! -user root"
register: no_root_system_executables
changed_when: False
failed_when: False
check_mode: no
tags:
- file_ownership_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27623-8
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000048
- name: "Set ownership to root of system executables"
file:
path: "{{item}}"
owner: "root"
with_items: "{{ no_root_system_executables.stdout_lines }}"
when: no_root_system_executables.stdout_lines | length > 0
tags:
- file_ownership_binary_dirs
- medium_severity
- restrict_strategy
- medium_complexity
- medium_disruption
- CCE-27623-8
- NIST-800-53-AC-6
- DISA-STIG-RHEL-06-000048
|
Ensure All SGID Executables Are Authorized
[ref]ruleThe SGID (set group id) bit should be set only on files that were
installed via authorized means. A straightforward means of identifying
unauthorized SGID files is determine if any were not installed as part of an
RPM package, which is cryptographically verified. Investigate the origin
of any unpackaged SGID files.
Rationale:Executable files with the SGID permission run with the privileges of
the owner of the file. SGID files of uncertain provenance could allow for
unprivileged users to elevate privileges. The presence of these files should be
strictly controlled on the system. |
Restrict Programs from Dangerous Execution Patterns
[ref]groupThe recommendations in this section are designed to
ensure that the system's features to protect against potentially
dangerous program execution are activated.
These protections are applied at the system initialization or
kernel level, and defend against certain types of badly-configured
or compromised programs. |
contains 6 rules |
Daemon Umask
[ref]groupThe umask is a per-process setting which limits
the default permissions for creation of new files and directories.
The system includes initialization scripts which set the default umask
for system daemons.
|
contains 1 rule |
Set Daemon Umask
[ref]ruleThe file /etc/init.d/functions includes initialization
parameters for most or all daemons started at boot time. The default umask of
022 prevents creation of group- or world-writable files. To set the default
umask for daemons, edit the following line, inserting 022 or 027 for
umask appropriately:
umask 022
Setting the umask to too restrictive a setting can cause serious errors at
runtime. Many daemons on the system already individually restrict themselves to
a umask of 077 in their own init scripts.
Rationale:The umask influences the permissions assigned to files created by a
process at run time. An unnecessarily permissive umask could result in files
being created with insecure permissions. Remediation Shell script: (show)
var_umask_for_daemons="022"
grep -q ^umask /etc/init.d/functions && \
sed -i "s/umask.*/umask $var_umask_for_daemons/g" /etc/init.d/functions
if ! [ $? -eq 0 ]; then
echo "umask $var_umask_for_daemons" >> /etc/init.d/functions
fi
|
Disable Core Dumps
[ref]groupA core dump file is the memory image of an executable
program when it was terminated by the operating system due to
errant behavior. In most cases, only software developers
legitimately need to access these files. The core dump files may
also contain sensitive information, or unnecessarily occupy large
amounts of disk space.
Once a hard limit is set in /etc/security/limits.conf , a
user cannot increase that limit within his or her own session. If access
to core dumps is required, consider restricting them to only
certain users or groups. See the limits.conf man page for more
information.
The core dumps of setuid programs are further protected. The
sysctl variable fs.suid_dumpable controls whether
the kernel allows core dumps from these programs at all. The default
value of 0 is recommended. |
contains 2 rules |
Disable Core Dumps for All Users
[ref]ruleTo disable core dumps for all users, add the following line to
/etc/security/limits.conf :
* hard core 0
Rationale:A core dump includes a memory image taken at the time the operating system
terminates an application. The memory image could contain sensitive data and is generally useful
only for developers trying to debug problems. Remediation Shell script: (show)
echo "* hard core 0" >> /etc/security/limits.conf
|
Disable Core Dumps for SUID programs
[ref]rule
To set the runtime status of the fs.suid_dumpable kernel parameter,
run the following command:
$ sudo sysctl -w fs.suid_dumpable=0
If this is not the system's default value, add the following line to /etc/sysctl.conf :
fs.suid_dumpable = 0
Rationale:The core dump of a setuid program is more likely to contain
sensitive data, as the program itself runs with greater privileges than the
user who initiated execution of the program. Disabling the ability for any
setuid program to write a core file decreases the risk of unauthorized access
of such data. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# Set runtime for fs.suid_dumpable
#
/sbin/sysctl -q -n -w fs.suid_dumpable=0
#
# If fs.suid_dumpable present in /etc/sysctl.conf, change value to "0"
# else, add "fs.suid_dumpable = 0" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^fs.suid_dumpable' "0" 'CCE-27044-7'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure sysctl fs.suid_dumpable is set to 0
sysctl:
name: fs.suid_dumpable
value: 0
state: present
reload: yes
tags:
- sysctl_fs_suid_dumpable
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27044-7
- NIST-800-53-SI-11
|
Enable ExecShield
[ref]groupExecShield describes kernel features that provide
protection against exploitation of memory corruption errors such as buffer
overflows. These features include random placement of the stack and other
memory regions, prevention of execution in memory that should only hold data,
and special handling of text buffers. These protections are enabled by default and
controlled through sysctl variables kernel.exec-shield and
kernel.randomize_va_space .
|
contains 2 rules |
Enable ExecShield
[ref]rule
To set the runtime status of the kernel.exec-shield kernel parameter,
run the following command:
$ sudo sysctl -w kernel.exec-shield=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
kernel.exec-shield = 1
Rationale:ExecShield uses the segmentation feature on all x86 systems
to prevent execution in memory higher than a certain address. It
writes an address as a limit in the code segment descriptor, to
control where code can be executed, on a per-process basis. When
the kernel places a process's memory regions such as the stack and
heap higher than this address, the hardware prevents execution in that
address range. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# Set runtime for kernel.exec-shield
#
/sbin/sysctl -q -n -w kernel.exec-shield=1
#
# If kernel.exec-shield present in /etc/sysctl.conf, change value to "1"
# else, add "kernel.exec-shield = 1" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^kernel.exec-shield' "1" 'CCE-27007-4'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure sysctl kernel.exec-shield is set to 1
sysctl:
name: kernel.exec-shield
value: 1
state: present
reload: yes
tags:
- sysctl_kernel_exec_shield
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27007-4
- NIST-800-53-SC-39
- DISA-STIG-RHEL-06-000079
|
Enable Randomized Layout of Virtual Address Space
[ref]rule
To set the runtime status of the kernel.randomize_va_space kernel parameter,
run the following command:
$ sudo sysctl -w kernel.randomize_va_space=2
If this is not the system's default value, add the following line to /etc/sysctl.conf :
kernel.randomize_va_space = 2
Rationale: Address space layout randomization (ASLR) makes it more difficult
for an attacker to predict the location of attack code they have introduced
into a process's address space during an attempt at exploitation. Additionally, ASLR
makes it more difficult for an attacker to know the location of existing code
in order to re-purpose it using return oriented programming (ROP) techniques.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# Set runtime for kernel.randomize_va_space
#
/sbin/sysctl -q -n -w kernel.randomize_va_space=2
#
# If kernel.randomize_va_space present in /etc/sysctl.conf, change value to "2"
# else, add "kernel.randomize_va_space = 2" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^kernel.randomize_va_space' "2" 'CCE-26999-3'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure sysctl kernel.randomize_va_space is set to 2
sysctl:
name: kernel.randomize_va_space
value: 2
state: present
reload: yes
tags:
- sysctl_kernel_randomize_va_space
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26999-3
- NIST-800-53-SC-30(2)
- DISA-STIG-RHEL-06-000078
|
Enable Execute Disable (XD) or No Execute (NX) Support on
x86 Systems
[ref]groupRecent processors in the x86 family support the
ability to prevent code execution on a per memory page basis.
Generically and on AMD processors, this ability is called No
Execute (NX), while on Intel processors it is called Execute
Disable (XD). This ability can help prevent exploitation of buffer
overflow vulnerabilities and should be activated whenever possible.
Extra steps must be taken to ensure that this protection is
enabled, particularly on 32-bit x86 systems. Other processors, such
as Itanium and POWER, have included such support since inception
and the standard kernel for those platforms supports the
feature. |
contains 1 rule |
Enable NX or XD Support in the BIOS
[ref]ruleReboot the system and enter the BIOS or Setup configuration menu.
Navigate the BIOS configuration menu and make sure that the option is enabled. The setting may be located
under a Security section. Look for Execute Disable (XD) on Intel-based systems and No Execute (NX)
on AMD-based systems. Rationale:Computers with the ability to prevent this type of code execution frequently put an option in the BIOS that will
allow users to turn the feature on or off at will. |
SELinux
[ref]groupSELinux is a feature of the Linux kernel which can be
used to guard against misconfigured or compromised programs.
SELinux enforces the idea that programs should be limited in what
files they can access and what actions they can take.
The default SELinux policy, as configured on CentOS Linux 6, has been
sufficiently developed and debugged that it should be usable on
almost any Red Hat machine with minimal configuration and a small
amount of system administrator training. This policy prevents
system services - including most of the common network-visible
services such as mail servers, FTP servers, and DNS servers - from
accessing files which those services have no valid reason to
access. This action alone prevents a huge amount of possible damage
from network attacks against services, from trojaned software, and
so forth.
This guide recommends that SELinux be enabled using the
default (targeted) policy on every Red Hat system, unless that
system has unusual requirements which make a stronger policy
appropriate.
|
contains 5 rules |
Ensure SELinux Not Disabled in /etc/grub.conf
[ref]ruleSELinux can be disabled at boot time by an argument in
/etc/grub.conf .
Remove any instances of selinux=0 from the kernel arguments in that
file to prevent SELinux from being disabled at boot.
Rationale:
Disabling a major host protection feature, such as SELinux, at boot time prevents
it from confining system services at boot time. Further, it increases
the chances that it will remain off during system operation.
Remediation Shell script: (show)
sed -i --follow-symlinks "s/selinux=0//gI" /etc/grub.conf
sed -i --follow-symlinks "s/enforcing=0//gI" /etc/grub.conf
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: Ensure SELinux Not Disabled in /etc/default/grub
replace:
dest: /etc/default/grub
regexp: selinux=0
tags:
- enable_selinux_bootloader
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26956-3
- NIST-800-53-AC-3
- NIST-800-53-AC-3(3)
- NIST-800-53-AC-6
- NIST-800-53-AU-9
- DISA-STIG-RHEL-06-000017
|
Ensure SELinux State is Enforcing
[ref]ruleThe SELinux state should be set to enforcing at
system boot time. In the file /etc/selinux/config , add or correct the
following line to configure the system to boot into enforcing mode:
SELINUX=enforcing
Rationale:
Setting the SELinux state to enforcing ensures SELinux is able to confine
potentially compromised processes to the security policy, which is designed to
prevent them from causing damage to the system or further elevating their
privileges.
Remediation Shell script: (show)
var_selinux_state="enforcing"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysconfig/selinux' '^SELINUX=' $var_selinux_state 'CCE-26969-6' '%s=%s'
fixfiles onboot
fixfiles -f relabel
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_selinux_state # promote to variable
set_fact:
var_selinux_state: enforcing
tags:
- always
- name: "Ensure SELinux State is Enforcing"
selinux:
state: "{{ var_selinux_state }}"
tags:
- selinux_state
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26969-6
- NIST-800-53-AC-3
- NIST-800-53-AC-3(3)
- NIST-800-53-AC-4
- NIST-800-53-AC-6
- NIST-800-53-AU-9
- DISA-STIG-RHEL-06-000020
|
Configure SELinux Policy
[ref]ruleThe SELinux targeted policy is appropriate for
general-purpose desktops and servers, as well as systems in many other roles.
To configure the system to use this policy, add or correct the following line
in /etc/selinux/config :
SELINUXTYPE=mls
Other policies, such as mls , provide additional security labeling
and greater confinement but are not compatible with many general-purpose
use cases.
Rationale:
Setting the SELinux policy to targeted or a more specialized policy
ensures the system will confine processes that are likely to be
targeted for exploitation, such as network or system services.
Note: During the development or debugging of SELinux modules, it is common to
temporarily place non-production systems in permissive mode. In such
temporary cases, SELinux policies should be developed, and once work
is completed, the system should be reconfigured to
mls .
Remediation Shell script: (show)
var_selinux_policy_name="mls"
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysconfig/selinux' '^SELINUXTYPE=' $var_selinux_policy_name 'CCE-26875-5' '%s=%s'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_selinux_policy_name # promote to variable
set_fact:
var_selinux_policy_name: mls
tags:
- always
- name: "Configure SELinux Policy"
selinux:
policy: "{{ var_selinux_policy_name }}"
tags:
- selinux_policytype
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26875-5
- NIST-800-53-AC-3
- NIST-800-53-AC-3(3)
- NIST-800-53-AC-4
- NIST-800-53-AC-6
- NIST-800-53-AU-9
- DISA-STIG-RHEL-06-000023
|
Ensure No Daemons are Unconfined by SELinux
[ref]rule
Daemons for which the SELinux policy does not contain rules will inherit the
context of the parent process. Because daemons are launched during
startup and descend from the init process, they inherit the initrc_t context.
To check for unconfined daemons, run the following command:
$ sudo ps -eZ | egrep "initrc" | egrep -vw "tr|ps|egrep|bash|awk" | tr ':' ' ' | awk '{ print $NF }'
It should produce no output in a well-configured system.
Rationale:
Daemons which run with the initrc_t context may cause AVC denials,
or allow privileges that the daemon does not require.
|
Ensure No Device Files are Unknown to SELinux
[ref]ruleDevice files, which are used for communication with important
system resources, should be labeled with proper SELinux types. If any device
files carry the SELinux type device_t , report the bug so that policy can be corrected. Supply information about what the device is and what programs use it.
Rationale:
If a device file carries the SELinux type device_t , then SELinux
cannot properly restrict access to the device file.
|
Account and Access Control
[ref]groupIn 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
CentOS Linux 6. |
contains 25 rules |
Protect Accounts by Restricting Password-Based Login
[ref]groupConventionally, 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 12 rules |
Restrict Root Logins
[ref]group
Direct root logins should be allowed only for emergency use.
In normal situations, the administrator should access the system
via a unique unprivileged account, and then use su or sudo to execute
privileged commands. Discouraging administrators from accessing the
root account directly ensures an audit trail in organizations with
multiple administrators. Locking down the channels through which
root can connect directly also reduces opportunities for
password-guessing against the root account. The login program
uses the file /etc/securetty to determine which interfaces
should allow root logins.
The virtual devices /dev/console
and /dev/tty* represent the system consoles (accessible via
the Ctrl-Alt-F1 through Ctrl-Alt-F6 keyboard sequences on a default
installation). The default securetty file also contains /dev/vc/* .
These are likely to be deprecated in most environments, but may be retained
for compatibility. Root should also be prohibited from connecting
via network protocols. Other sections of this document
include guidance describing how to prevent root from logging in via SSH.
|
contains 4 rules |
Direct root Logins Not Allowed
[ref]ruleTo further limit access to the root account, administrators
can disable root logins at the console by editing the /etc/securetty file.
This file lists all devices the root user is allowed to login to. If the file does
not exist at all, the root user can login through any communication device on the
system, whether via the console or via a raw network interface. This is dangerous
as user can login to his machine as root via Telnet, which sends the password in
plain text over the network. By default, Red Hat Enteprise Linux's
/etc/securetty file only allows the root user to login at the console
physically attached to the machine. To prevent root from logging in, remove the
contents of this file. To prevent direct root logins, remove the contents of this
file by typing the following command:
$ sudo echo > /etc/securetty
Rationale:
Disabling direct root logins ensures proper accountability and multifactor
authentication to privileged accounts. Users will first login, then escalate
to privileged (root) access via su / sudo. This is required for FISMA Low
and FISMA Moderate systems.
Remediation Shell script: (show) Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Direct root Logins Not Allowed"
shell: echo > /etc/securetty
tags:
- no_direct_root_logins
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26891-2
- NIST-800-53-IA-2(1)
|
Restrict Virtual Console Root Logins
[ref]rule
To restrict root logins through the (deprecated) virtual console devices,
ensure lines of this form do not appear in /etc/securetty :
vc/1
vc/2
vc/3
vc/4
Rationale:
Preventing direct root login to virtual console devices
helps ensure accountability for actions taken on the system
using the root account.
Remediation Shell script: (show)
sed -i '/^vc\//d' /etc/securetty
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Restrict Virtual Console Root Logins"
lineinfile:
dest: /etc/securetty
regexp: '^vc'
state: absent
tags:
- securetty_root_login_console_only
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26855-7
- NIST-800-53-AC-6(2)
- DISA-STIG-RHEL-06-000027
|
Restrict Serial Port Root Logins
[ref]ruleTo restrict root logins on serial ports,
ensure lines of this form do not appear in /etc/securetty :
ttyS0
ttyS1
Rationale:
Preventing direct root login to serial port interfaces
helps ensure accountability for actions taken on the systems
using the root account.
Remediation Shell script: (show)
sed -i '/ttyS/d' /etc/securetty
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Restrict Serial Port Root Logins"
lineinfile:
dest: /etc/securetty
regexp: 'ttyS[0-9]'
state: absent
tags:
- restrict_serial_port_logins
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27047-0
- NIST-800-53-AC-6(2)
- DISA-STIG-RHEL-06-000028
|
Verify Only Root Has UID 0
[ref]rule
If any account other than root has a UID of 0,
this misconfiguration should be investigated and the
accounts other than root should be removed or have their UID changed.
Rationale:
An account has root authority if it has a UID of 0. Multiple accounts
with a UID of 0 afford more opportunity for potential intruders to
guess a password for a privileged account. Proper configuration of
sudo is recommended to afford multiple system administrators
access to root privileges in an accountable manner.
Remediation Shell script: (show)
awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs passwd -l
|
Verify Proper Storage and Existence of Password
Hashes
[ref]group
By 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 |
Prevent Log In to Accounts With Empty Password
[ref]ruleIf an account is configured for password authentication
but does not have an assigned password, it may be possible to log
onto the account without authentication. Remove any instances of the nullok
option 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.
Remediation Shell script: (show)
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 Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Strategy: | configure |
---|
- name: "Prevent Log In to Accounts With Empty Password - system-auth"
replace:
dest: /etc/pam.d/system-auth
follow: yes
regexp: 'nullok'
tags:
- no_empty_passwords
- high_severity
- configure_strategy
- low_complexity
- medium_disruption
- CCE-27038-9
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(a)
- PCI-DSS-Req-8.2.3
- DISA-STIG-RHEL-06-000030
- name: "Prevent Log In to Accounts With Empty Password - password-auth"
replace:
dest: /etc/pam.d/password-auth
follow: yes
regexp: 'nullok'
tags:
- no_empty_passwords
- high_severity
- configure_strategy
- low_complexity
- medium_disruption
- CCE-27038-9
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(a)
- PCI-DSS-Req-8.2.3
- DISA-STIG-RHEL-06-000030
|
Verify All Account Password Hashes are Shadowed
[ref]rule
If any password hashes are stored in /etc/passwd (in the second field,
instead of an x ), 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.
|
Verify No netrc Files Exist
[ref]ruleThe .netrc files contain login information
used to auto-login into FTP servers and reside in the user's home
directory. These files may contain unencrypted passwords to
remote FTP servers making them susceptible to access by unauthorized
users and should not be used. Any .netrc files should be removed.
Rationale:
Unencrypted passwords for remote FTP servers may be stored in .netrc
files. DoD policy requires passwords be encrypted in storage and not used
in access scripts.
|
Set Password Expiration Parameters
[ref]groupThe 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 3 rules |
Set Password Minimum Length in login.defs
[ref]ruleTo specify password length requirements for new accounts,
edit the file /etc/login.defs and add or correct the following
lines:
PASS_MIN_LEN 12
As of the DISA Red Hat 6 STIG - Ver 1, Rel 13 (28-OCT-2016), the DoD requirement is now 15 .
The FISMA requirement is 12 .
If a program consults /etc/login.defs and also another PAM module
(such as pam_cracklib ) during a password change operation,
then the most restrictive must be satisfied. See PAM section
for more information about enforcing password quality requirements.
Rationale:
Requiring a minimum password length makes password
cracking attacks more difficult by ensuring a larger
search space. However, any security benefit from an onerous requirement
must be carefully weighed against usability problems, support costs, or counterproductive
behavior that may result.
Remediation Shell script: (show)
var_accounts_password_minlen_login_defs="12"
grep -q ^PASS_MIN_LEN /etc/login.defs && \
sed -i "s/PASS_MIN_LEN.*/PASS_MIN_LEN $var_accounts_password_minlen_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_MIN_LEN $var_accounts_password_minlen_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_password_minlen_login_defs # promote to variable
set_fact:
var_accounts_password_minlen_login_defs: 12
tags:
- always
- name: "Set Password Minimum Length in login.defs"
lineinfile:
dest: /etc/login.defs
regexp: "^PASS_MIN_LEN *[0-9]*"
state: present
line: "PASS_MIN_LEN {{ var_accounts_password_minlen_login_defs }}"
tags:
- accounts_password_minlen_login_defs
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27002-5
- NIST-800-53-IA-5(f)
- NIST-800-53-IA-5(1)(a)
- DISA-STIG-RHEL-06-000050
|
Set Password Maximum Age
[ref]ruleTo specify password maximum age for new accounts,
edit the file /etc/login.defs
and add or correct the following line:
PASS_MAX_DAYS 180
A value of 180 days is sufficient for many environments.
The DoD requirement is 60.
Rationale:
Setting the password maximum age ensures users are required to
periodically change their passwords. This could possibly decrease
the utility of a stolen password. Requiring shorter password lifetimes
increases the risk of users writing down the password in a convenient
location subject to physical compromise. Remediation Shell script: (show)
var_accounts_maximum_age_login_defs="180"
grep -q ^PASS_MAX_DAYS /etc/login.defs && \
sed -i "s/PASS_MAX_DAYS.*/PASS_MAX_DAYS $var_accounts_maximum_age_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_MAX_DAYS $var_accounts_maximum_age_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_maximum_age_login_defs # promote to variable
set_fact:
var_accounts_maximum_age_login_defs: 180
tags:
- always
- name: Set Password Maximum Age
lineinfile:
create: yes
dest: /etc/login.defs
regexp: ^#?PASS_MAX_DAYS
line: "PASS_MAX_DAYS {{ var_accounts_maximum_age_login_defs }}"
tags:
- accounts_maximum_age_login_defs
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26985-2
- NIST-800-53-IA-5(f)
- NIST-800-53-IA-5(g)
- NIST-800-53-IA-5(1)(d)
- PCI-DSS-Req-8.2.4
- DISA-STIG-RHEL-06-000053
|
Set Password Warning Age
[ref]ruleTo specify how many days prior to password
expiration that a warning will be issued to users,
edit the file /etc/login.defs and add or correct
the following line:
PASS_WARN_AGE 7
The DoD requirement is 7.
Rationale:
Setting the password warning age enables users to
make the change at a practical time.
Remediation Shell script: (show)
var_accounts_password_warn_age_login_defs="7"
grep -q ^PASS_WARN_AGE /etc/login.defs && \
sed -i "s/PASS_WARN_AGE.*/PASS_WARN_AGE $var_accounts_password_warn_age_login_defs/g" /etc/login.defs
if ! [ $? -eq 0 ]; then
echo "PASS_WARN_AGE $var_accounts_password_warn_age_login_defs" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_accounts_password_warn_age_login_defs # promote to variable
set_fact:
var_accounts_password_warn_age_login_defs: 7
tags:
- always
- name: "Set Password Warning Age"
lineinfile:
dest: /etc/login.defs
regexp: "^PASS_WARN_AGE *[0-9]*"
state: present
line: "PASS_WARN_AGE {{ var_accounts_password_warn_age_login_defs }}"
tags:
- accounts_password_warn_age_login_defs
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26988-6
- NIST-800-53-AC-2(2)
- NIST-800-53-IA-5(f)
- DISA-STIG-RHEL-06-000054
|
Set Account Expiration Parameters
[ref]groupAccounts 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.
|
contains 2 rules |
Set Account Expiration Following Inactivity
[ref]ruleTo specify the number of days after a password expires (which
signifies inactivity) until an account is permanently disabled, add or correct
the following lines in /etc/default/useradd , substituting
NUM_DAYS appropriately:
INACTIVE=(N/A)
A value of 35 is recommended.
If a password is currently on the
verge of expiration, then 35 days remain until the account is automatically
disabled. However, if the password will not expire for another 60 days, then 95
days could elapse until the account would be automatically disabled. See the
useradd man page for more information. Determining the inactivity
timeout must be done with careful consideration of the length of a "normal"
period of inactivity for users in the particular environment. Setting
the timeout too low incurs support costs and also has the potential to impact
availability of the system to legitimate users.
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.
Remediation Shell script: (show)
var_account_disable_post_pw_expiration="35"
grep -q ^INACTIVE /etc/default/useradd && \
sed -i "s/INACTIVE.*/INACTIVE=$var_account_disable_post_pw_expiration/g" /etc/default/useradd
if ! [ $? -eq 0 ]; then
echo "INACTIVE=$var_account_disable_post_pw_expiration" >> /etc/default/useradd
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_account_disable_post_pw_expiration # promote to variable
set_fact:
var_account_disable_post_pw_expiration: 35
tags:
- always
- name: Set Account Expiration Following Inactivity
lineinfile:
create: yes
dest: /etc/default/useradd
regexp: ^INACTIVE
line: "INACTIVE={{ var_account_disable_post_pw_expiration }}"
tags:
- account_disable_post_pw_expiration
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27283-1
- NIST-800-53-AC-2(2)
- NIST-800-53-AC-2(3)
- PCI-DSS-Req-8.1.4
- DISA-STIG-RHEL-06-000334
|
Assign Expiration Date to Temporary Accounts
[ref]rule
In the event temporary or emergency accounts are required, configure the system
to terminate them after a documented time period. For every temporary and
emergency account, run the following command to set an expiration date on it,
substituting USER and YYYY-MM-DD appropriately:
$ sudo chage -E YYYY-MM-DD USER
YYYY-MM-DD indicates the documented expiration date for the account.
Rationale:
When temporary and emergency accounts are created, there is a risk they may
remain in place and active after the need for them no longer exists. Account
expiration greatly reduces the risk of accounts being misused or hijacked.
|
Protect Accounts by Configuring PAM
[ref]groupPAM, 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 10 rules |
Set Password Quality Requirements
[ref]groupThe default pam_cracklib 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 man page pam_cracklib(8) provides information on the
capabilities and configuration of each. |
contains 6 rules |
Set Password Quality Requirements, if using
pam_cracklib
[ref]groupThe pam_cracklib PAM module can be configured to meet
requirements for a variety of policies.
For example, to configure pam_cracklib to require at least one uppercase
character, lowercase character, digit, and other (special)
character, locate the following line in /etc/pam.d/system-auth :
password requisite pam_cracklib.so try_first_pass retry=3
and then alter it to read:
password required pam_cracklib.so try_first_pass retry=3 maxrepeat=3 minlen=14 dcredit=-1 ucredit=-1 ocredit=-1 lcredit=-1 difok=4
If no such line exists, add one as the first line of the password section in /etc/pam.d/system-auth .
The arguments can be modified to ensure compliance with
your organization's security policy. Discussion of each parameter follows.
Warning:
Note that the password quality
requirements are not enforced for the root account for some
reason. |
contains 6 rules |
Set Password Retry Prompts Permitted Per-Session
[ref]ruleTo configure the number of retry prompts that are permitted per-session:
Edit the pam_cracklib.so statement in /etc/pam.d/system-auth to
show retry=3 , or a lower value if site policy is more restrictive.
The DoD requirement is a maximum of 3 prompts per session.
Rationale:
Setting the password retry prompts that are permitted on a per-session basis to a low value
requires some software, such as SSH, to re-connect. This can slow down and
draw additional attention to some types of password-guessing attacks. Note that this
is different from account lockout, which is provided by the pam_faillock module.
|
Set Password Strength Minimum Digit Characters
[ref]ruleThe pam_cracklib module's dcredit parameter controls requirements for
usage of digits in a password. When set to a negative number, any password will be required to
contain that many digits. When set to a positive number, pam_cracklib will grant +1 additional
length credit for each digit.
Add dcredit=-1 after pam_cracklib.so to require use of a digit in passwords.
Rationale:
Requiring digits makes password guessing attacks more difficult by ensuring a larger
search space.
Remediation Shell script: (show)
var_password_pam_dcredit="-1"
if grep -q "dcredit=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(dcredit *= *\).*/\1$var_password_pam_dcredit/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ dcredit=$var_password_pam_dcredit/" /etc/pam.d/system-auth
fi
|
Set Password Strength Minimum Uppercase Characters
[ref]ruleThe pam_cracklib module's ucredit= parameter controls requirements for
usage of uppercase letters in a password. When set to a negative number, any password will be required to
contain that many uppercase characters. When set to a positive number, pam_cracklib will grant +1 additional
length credit for each uppercase character.
Add ucredit=-1 after pam_cracklib.so to require use of an upper case character in passwords.
Rationale:
Requiring a minimum number of uppercase characters makes password guessing attacks
more difficult by ensuring a larger search space.
Remediation Shell script: (show)
var_password_pam_ucredit="-1"
if grep -q "ucredit=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(ucredit *= *\).*/\1$var_password_pam_ucredit/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ ucredit=$var_password_pam_ucredit/" /etc/pam.d/system-auth
fi
|
Set Password Strength Minimum Special Characters
[ref]ruleThe pam_cracklib module's ocredit= parameter controls requirements for
usage of special (or ``other'') characters in a password. When set to a negative number, any password will be required to
contain that many special characters. When set to a positive number, pam_cracklib will grant +1 additional
length credit for each special character.
Add ocredit=-1 after pam_cracklib.so to require use of a special character in passwords.
Rationale:
Requiring a minimum number of special characters makes password guessing attacks
more difficult by ensuring a larger search space.
Remediation Shell script: (show)
var_password_pam_ocredit="-1"
if grep -q "ocredit=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(ocredit *= *\).*/\1$var_password_pam_ocredit/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ ocredit=$var_password_pam_ocredit/" /etc/pam.d/system-auth
fi
|
Set Password Strength Minimum Lowercase Characters
[ref]ruleThe pam_cracklib module's lcredit= parameter controls requirements for
usage of lowercase letters in a password. When set to a negative number, any password will be required to
contain that many lowercase characters. When set to a positive number, pam_cracklib will grant +1 additional
length credit for each lowercase character.
Add lcredit=-1 after pam_cracklib.so to require use of a lowercase character in passwords.
Rationale:
Requiring a minimum number of lowercase characters makes password guessing attacks
more difficult by ensuring a larger search space.
Remediation Shell script: (show)
var_password_pam_lcredit="-1"
if grep -q "lcredit=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(lcredit *= *\).*/\1$var_password_pam_lcredit/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ lcredit=$var_password_pam_lcredit/" /etc/pam.d/system-auth
fi
|
Set Password Strength Minimum Different Characters
[ref]ruleThe pam_cracklib module's difok parameter controls requirements for
usage of different characters during a password change.
Add difok=4 after pam_cracklib.so to require differing
characters when changing passwords. The DoD requirement is 4 .
Rationale:
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.
Remediation Shell script: (show)
var_password_pam_difok="4"
if grep -q "difok=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(difok *= *\).*/\1$var_password_pam_difok/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/pam_cracklib.so/ s/$/ difok=$var_password_pam_difok/" /etc/pam.d/system-auth
fi
|
Set Lockouts for Failed Password Attempts
[ref]groupThe pam_faillock PAM module provides the capability to
lock out user accounts after a number of failed login attempts. Its
documentation is available in
/usr/share/doc/pam-VERSION/txts/README.pam_faillock .
Warning:
Locking out user accounts presents the
risk of a denial-of-service attack. The lockout policy
must weigh whether the risk of such a
denial-of-service attack outweighs the benefits of thwarting
password guessing attacks. |
contains 1 rule |
Limit Password Reuse
[ref]ruleDo not allow users to reuse recent passwords. This can be
accomplished by using the remember option for the pam_unix
or pam_pwhistory PAM modules. In the file
/etc/pam.d/system-auth , append remember=5
to the line which refers to the pam_unix.so or
pam_pwhistory.so module, as shown below:
The DoD STIG requirement is 5 passwords.Rationale:
Preventing re-use of previous passwords helps ensure that a compromised password is not re-used by a user.
Remediation Shell script: (show)
var_password_pam_unix_remember="5"
if grep -q "remember=" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "s/\(^password.*sufficient.*pam_unix.so.*\)\(\(remember *= *\)[^ $]*\)/\1remember=$var_password_pam_unix_remember/" /etc/pam.d/system-auth
else
sed -i --follow-symlinks "/^password[[:space:]]\+sufficient[[:space:]]\+pam_unix.so/ s/$/ remember=$var_password_pam_unix_remember/" /etc/pam.d/system-auth
fi
|
Set Password Hashing Algorithm
[ref]groupThe 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/pam.d/system-auth
[ref]rule
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:
Using a stronger hashing algorithm makes password cracking attacks more difficult.
Remediation Shell script: (show)
if ! grep -q "^password.*sufficient.*pam_unix.so.*sha512" /etc/pam.d/system-auth; then
sed -i --follow-symlinks "/^password.*sufficient.*pam_unix.so/ s/$/ sha512/" /etc/pam.d/system-auth
fi
|
Set Password Hashing Algorithm in /etc/login.defs
[ref]rule
In /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:
Using a stronger hashing algorithm makes password cracking attacks more difficult.
Remediation Shell script: (show)
if grep --silent ^ENCRYPT_METHOD /etc/login.defs ; then
sed -i 's/^ENCRYPT_METHOD.*/ENCRYPT_METHOD SHA512/g' /etc/login.defs
else
echo "" >> /etc/login.defs
echo "ENCRYPT_METHOD SHA512" >> /etc/login.defs
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: Set Password Hashing Algorithm in /etc/login.defs
lineinfile:
dest: /etc/login.defs
regexp: ^#?ENCRYPT_METHOD
line: ENCRYPT_METHOD SHA512
state: present
tags:
- set_password_hashing_algorithm_logindefs
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27228-6
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-7
- PCI-DSS-Req-8.2.1
- DISA-STIG-RHEL-06-000063
|
Set Password Hashing Algorithm in /etc/libuser.conf
[ref]rule
In /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:
Using a stronger hashing algorithm makes password cracking attacks more difficult.
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: Set Password Hashing Algorithm in /etc/libuser.conf
lineinfile:
dest: /etc/libuser.conf
insertafter: "^.default]"
regexp: ^#?crypt_style
line: crypt_style = sha512
state: present
tags:
- set_password_hashing_algorithm_libuserconf
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27229-4
- NIST-800-53-IA-5(b)
- NIST-800-53-IA-5(c)
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-7
- PCI-DSS-Req-8.2.1
- DISA-STIG-RHEL-06-000064
|
Protect Physical Console Access
[ref]groupIt is impossible to fully protect a system from an
attacker with physical access, so securing the space in which the
system is located should be considered a necessary step. However,
there are some steps which, if taken, make it more difficult for an
attacker to quickly or undetectably modify a system from its
console. |
contains 2 rules |
Set Boot Loader Password
[ref]groupDuring 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 CentOS Linux boot loader for x86 systems is called GRUB.
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 1 rule |
Set Boot Loader Password
[ref]ruleThe grub boot loader should have password protection
enabled to protect boot-time settings.
To do so, select a password and then generate a hash from it by running the following command:
$ grub-crypt --sha-512
When prompted to enter a password, insert the following line into /etc/grub.conf
immediately after the header comments. (Use the output from grub-crypt as the
value of password-hash):
password --encrypted password-hash
NOTE: To meet FISMA Moderate, the bootloader password MUST differ from the root password.
Rationale:
Password protection on the boot loader configuration ensures
users with physical access cannot trivially alter
important bootloader settings. These include which kernel to use,
and whether to enter single-user mode.
|
Disable Interactive Boot
[ref]rule
To disable the ability for users to perform interactive startups, perform both
of the following:
- Edit the file
/etc/sysconfig/init . Add or correct the line:
PROMPT=no - Inspect the kernel boot arguments (which follow the word
kernel )
in /etc/grub.conf and ensure the confirm argument is not
present.
Both the PROMPT option of the /etc/sysconfig/init file and
the confirm kernel boot argument of the /etc/grub.conf file
allow the console user to perform an interactive system startup, in which it is
possible to select the set of services which are started on boot.
Rationale:
Using interactive boot, the console user could disable auditing, firewalls, or
other services, weakening system security.
Remediation Shell script: (show)
# Ensure value of PROMPT key in /etc/sysconfig/init is set to 'no'
grep -q ^PROMPT /etc/sysconfig/init && \
sed -i "s/PROMPT.*/PROMPT=no/g" /etc/sysconfig/init
if ! [ $? -eq 0 ]; then
echo "PROMPT=no" >> /etc/sysconfig/init
fi
# Ensure 'confirm' kernel boot argument is not present in some of
# kernel lines in /etc/grub.conf
sed -i --follow-symlinks "s/confirm//gI" /etc/grub.conf
|
Warning Banners for System Accesses
[ref]groupEach system should expose as little information about
itself as possible.
System banners, which are typically displayed just before a
login prompt, give out information about the service or the host's
operating system. This might include the distribution name and the
system kernel version, and the particular version of a network
service. This information can assist intruders in gaining access to
the system as it can reveal whether the system is running
vulnerable software. Most network services can be configured to
limit what information is displayed.
Many organizations implement security policies that require a
system banner provide notice of the system's ownership, provide
warning to unauthorized users, and remind authorized users of their
consent to monitoring. |
contains 1 rule |
Implement a GUI Warning Banner
[ref]groupIn the default graphical environment, users logging
directly into the system are greeted with a login screen provided
by the GNOME Display Manager (GDM). The warning banner should be
displayed in this graphical environment for these users.
The following sections describe how to configure the GDM login
banner.
|
contains 1 rule |
Enable GUI Warning Banner
[ref]rule
To enable displaying a login warning banner in the GNOME
Display Manager's login screen, run the following command:
$ sudo gconftool-2 --direct \
--config-source xml:readwrite:/etc/gconf/gconf.xml.mandatory \
--type bool \
--set /apps/gdm/simple-greeter/banner_message_enable true
To display a banner, this setting must be enabled and then
banner text must also be set.
Rationale:
An appropriate warning message reinforces policy awareness during the login
process and facilitates possible legal action against attackers.
Remediation Shell script: (show)
# Install GConf2 package if not installed
if ! rpm -q GConf2; then
yum -y install GConf2
fi
# Enable displaying of a login warning banner in the GNOME Display Manager's
# login screen
gconftool-2 --direct \
--config-source "xml:readwrite:/etc/gconf/gconf.xml.mandatory" \
--type bool \
--set /apps/gdm/simple-greeter/banner_message_enable true
|
Network Configuration and Firewalls
[ref]groupMost machines 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 30 rules |
Kernel Parameters Which Affect Networking
[ref]groupThe 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 15 rules |
Network Parameters for Hosts Only
[ref]groupIf 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 3 rules |
Disable Kernel Parameter for Sending ICMP Redirects by Default
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.default.send_redirects = 0
Rationale:Sending ICMP redirects permits the system to instruct other systems
to update their routing information. The ability to send ICMP redirects is
only appropriate for systems acting as routers. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# 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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.send_redirects' "0" 'CCE-27001-7'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- 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: yes
tags:
- sysctl_net_ipv4_conf_default_send_redirects
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27001-7
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000080
|
Disable Kernel Parameter for Sending ICMP Redirects for All Interfaces
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.send_redirects = 0
Rationale:Sending ICMP redirects permits the system to instruct other systems
to update their routing information. The ability to send ICMP redirects is
only appropriate for systems acting as routers. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# 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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.send_redirects' "0" 'CCE-27004-1'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- 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: yes
tags:
- sysctl_net_ipv4_conf_all_send_redirects
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27004-1
- NIST-800-53-CM-7
- NIST-800-53-SC-5(1)
- DISA-STIG-RHEL-06-000081
|
Disable Kernel Parameter for IP Forwarding
[ref]rule
To set the runtime status of the net.ipv4.ip_forward kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.ip_forward=0
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.ip_forward = 0
Rationale:IP forwarding permits the kernel to forward packets from one network
interface to another. The ability to forward packets between two networks is
only appropriate for systems acting as routers. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
#
# Set runtime for net.ipv4.ip_forward
#
/sbin/sysctl -q -n -w net.ipv4.ip_forward=0
#
# If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0"
# else, add "net.ipv4.ip_forward = 0" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.ip_forward' "0" 'CCE-26866-4'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure sysctl net.ipv4.ip_forward is set to 0
sysctl:
name: net.ipv4.ip_forward
value: 0
state: present
reload: yes
tags:
- sysctl_net_ipv4_ip_forward
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26866-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000082
|
Network Related Kernel Runtime Parameters for Hosts and Routers
[ref]groupCertain 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 12 rules |
Configure Kernel Parameter for Accepting Source-Routed Packets for All Interfaces
[ref]rule
To set the runtime status of the net.ipv4.conf.all.accept_source_route kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.all.accept_source_route=0
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.accept_source_route = 0
Rationale:Accepting source-routed packets in the IPv4 protocol has few legitimate
uses. It should be disabled unless it is absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_all_accept_source_route_value="0"
#
# Set runtime for net.ipv4.conf.all.accept_source_route
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.accept_source_route=$sysctl_net_ipv4_conf_all_accept_source_route_value
#
# If net.ipv4.conf.all.accept_source_route present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.all.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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.accept_source_route' "$sysctl_net_ipv4_conf_all_accept_source_route_value" 'CCE-27037-1'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_source_route_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_accept_source_route_value: 0
tags:
- always
- name: Ensure sysctl net.ipv4.conf.all.accept_source_route is set
sysctl:
name: net.ipv4.conf.all.accept_source_route
value: "{{ sysctl_net_ipv4_conf_all_accept_source_route_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_all_accept_source_route
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27037-1
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000083
|
Configure Kernel Parameter for Accepting ICMP Redirects for All Interfaces
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.accept_redirects = 0
Rationale:Accepting ICMP redirects has few legitimate
uses. It should be disabled unless it is absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$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-27027-2'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_all_accept_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_accept_redirects_value: 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: yes
tags:
- sysctl_net_ipv4_conf_all_accept_redirects
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27027-2
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000084
|
Configure Kernel Parameter for Accepting Secure Redirects for All Interfaces
[ref]rule
To set the runtime status of the net.ipv4.conf.all.secure_redirects kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.all.secure_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.secure_redirects = 0
Rationale:Accepting "secure" ICMP redirects (from those gateways listed as
default gateways) has few legitimate uses. It should be disabled unless it is
absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_all_secure_redirects_value="1"
#
# Set runtime for net.ipv4.conf.all.secure_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.secure_redirects=$sysctl_net_ipv4_conf_all_secure_redirects_value
#
# If net.ipv4.conf.all.secure_redirects present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.all.secure_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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.secure_redirects' "$sysctl_net_ipv4_conf_all_secure_redirects_value" 'CCE-26854-0'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_all_secure_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_secure_redirects_value: 1
tags:
- always
- name: Ensure sysctl net.ipv4.conf.all.secure_redirects is set
sysctl:
name: net.ipv4.conf.all.secure_redirects
value: "{{ sysctl_net_ipv4_conf_all_secure_redirects_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_all_secure_redirects
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26854-0
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000086
|
Configure Kernel Parameter to Log Martian Packets
[ref]rule
To set the runtime status of the net.ipv4.conf.all.log_martians kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.all.log_martians=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.log_martians = 1
Rationale:The presence of "martian" packets (which have impossible addresses)
as well as spoofed packets, source-routed packets, and redirects could be a
sign of nefarious network activity. Logging these packets enables this activity
to be detected. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_all_log_martians_value="0"
#
# Set runtime for net.ipv4.conf.all.log_martians
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.log_martians=$sysctl_net_ipv4_conf_all_log_martians_value
#
# If net.ipv4.conf.all.log_martians present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.all.log_martians = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.log_martians' "$sysctl_net_ipv4_conf_all_log_martians_value" 'CCE-27066-0'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_all_log_martians_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_log_martians_value: 0
tags:
- always
- name: Ensure sysctl net.ipv4.conf.all.log_martians is set
sysctl:
name: net.ipv4.conf.all.log_martians
value: "{{ sysctl_net_ipv4_conf_all_log_martians_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_all_log_martians
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27066-0
- NIST-800-53-AC-3(10)
- NIST-800-53-CM-7
- NIST-800-53-SC-5(3)
- DISA-STIG-RHEL-06-000088
|
Configure Kernel Parameter for Accepting Source-Routed Packets By Default
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.default.accept_source_route = 0
Rationale:Accepting source-routed packets in the IPv4 protocol has few legitimate
uses. It should be disabled unless it is absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$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-26983-7'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- 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: 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: yes
tags:
- sysctl_net_ipv4_conf_default_accept_source_route
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26983-7
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000089
|
Configure Kernel Parameter for Accepting ICMP Redirects By Default
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.default.accept_redirects = 0
Rationale:This feature of the IPv4 protocol has few legitimate
uses. It should be disabled unless it is absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$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-27015-7'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_default_accept_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_default_accept_redirects_value: 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: yes
tags:
- sysctl_net_ipv4_conf_default_accept_redirects
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27015-7
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000091
|
Configure Kernel Parameter for Accepting Secure Redirects By Default
[ref]rule
To set the runtime status of the net.ipv4.conf.default.secure_redirects kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.default.secure_redirects=0
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.default.secure_redirects = 0
Rationale:Accepting "secure" ICMP redirects (from those gateways listed as
default gateways) has few legitimate uses. It should be disabled unless it is
absolutely required. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_default_secure_redirects_value="1"
#
# Set runtime for net.ipv4.conf.default.secure_redirects
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.secure_redirects=$sysctl_net_ipv4_conf_default_secure_redirects_value
#
# If net.ipv4.conf.default.secure_redirects present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.default.secure_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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.secure_redirects' "$sysctl_net_ipv4_conf_default_secure_redirects_value" 'CCE-26831-8'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_default_secure_redirects_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_default_secure_redirects_value: 1
tags:
- always
- name: Ensure sysctl net.ipv4.conf.default.secure_redirects is set
sysctl:
name: net.ipv4.conf.default.secure_redirects
value: "{{ sysctl_net_ipv4_conf_default_secure_redirects_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_default_secure_redirects
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26831-8
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000090
|
Configure Kernel Parameter to Ignore ICMP Broadcast Echo Requests
[ref]rule
To 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
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.icmp_echo_ignore_broadcasts = 1
Rationale:Ignoring ICMP echo requests (pings) sent to broadcast or multicast
addresses makes the system slightly more difficult to enumerate on the network.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$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-26883-9'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value # promote to variable
set_fact:
sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value: 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: yes
tags:
- sysctl_net_ipv4_icmp_echo_ignore_broadcasts
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26883-9
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000092
|
Configure Kernel Parameter to Ignore Bogus ICMP Error Responses
[ref]rule
To set the runtime status of the net.ipv4.icmp_ignore_bogus_error_responses kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.icmp_ignore_bogus_error_responses=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.icmp_ignore_bogus_error_responses = 1
Rationale:Ignoring bogus ICMP error responses reduces
log size, although some activity would not be logged. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value="1"
#
# Set runtime for net.ipv4.icmp_ignore_bogus_error_responses
#
/sbin/sysctl -q -n -w net.ipv4.icmp_ignore_bogus_error_responses=$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value
#
# If net.ipv4.icmp_ignore_bogus_error_responses present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.icmp_ignore_bogus_error_responses = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.icmp_ignore_bogus_error_responses' "$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" 'CCE-26993-6'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value # promote to variable
set_fact:
sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value: 1
tags:
- always
- name: Ensure sysctl net.ipv4.icmp_ignore_bogus_error_responses is set
sysctl:
name: net.ipv4.icmp_ignore_bogus_error_responses
value: "{{ sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_icmp_ignore_bogus_error_responses
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26993-6
- NIST-800-53-CM-7
- NIST-800-53-SC-5
- DISA-STIG-RHEL-06-000093
|
Configure Kernel Parameter to Use TCP Syncookies
[ref]rule
To set the runtime status of the net.ipv4.tcp_syncookies kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.tcp_syncookies=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
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.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
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 four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.tcp_syncookies' "$sysctl_net_ipv4_tcp_syncookies_value" 'CCE-27053-8'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_tcp_syncookies_value # promote to variable
set_fact:
sysctl_net_ipv4_tcp_syncookies_value: 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: yes
tags:
- sysctl_net_ipv4_tcp_syncookies
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-27053-8
- NIST-800-53-AC-4
- NIST-800-53-SC-5(2)
- NIST-800-53-SC-5(3)
- DISA-STIG-RHEL-06-000095
|
Configure Kernel Parameter to Use Reverse Path Filtering for All Interfaces
[ref]rule
To set the runtime status of the net.ipv4.conf.all.rp_filter kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.all.rp_filter=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.all.rp_filter = 1
Rationale:Enabling reverse path filtering drops packets with source addresses
that should not have been able to be received on the interface they were
received on. It should not be used on systems which are routers for
complicated networks, but is helpful for end hosts and routers serving small
networks. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_all_rp_filter_value="1"
#
# Set runtime for net.ipv4.conf.all.rp_filter
#
/sbin/sysctl -q -n -w net.ipv4.conf.all.rp_filter=$sysctl_net_ipv4_conf_all_rp_filter_value
#
# If net.ipv4.conf.all.rp_filter present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.all.rp_filter = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.all.rp_filter' "$sysctl_net_ipv4_conf_all_rp_filter_value" 'CCE-26979-5'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_all_rp_filter_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_all_rp_filter_value: 1
tags:
- always
- name: Ensure sysctl net.ipv4.conf.all.rp_filter is set
sysctl:
name: net.ipv4.conf.all.rp_filter
value: "{{ sysctl_net_ipv4_conf_all_rp_filter_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_all_rp_filter
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26979-5
- NIST-800-53-AC-4
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000096
|
Configure Kernel Parameter to Use Reverse Path Filtering by Default
[ref]rule
To set the runtime status of the net.ipv4.conf.default.rp_filter kernel parameter,
run the following command:
$ sudo sysctl -w net.ipv4.conf.default.rp_filter=1
If this is not the system's default value, add the following line to /etc/sysctl.conf :
net.ipv4.conf.default.rp_filter = 1
Rationale:Enabling reverse path filtering drops packets with source addresses
that should not have been able to be received on the interface they were
received on. It should not be used on systems which are routers for
complicated networks, but is helpful for end hosts and routers serving small
networks. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
sysctl_net_ipv4_conf_default_rp_filter_value="1"
#
# Set runtime for net.ipv4.conf.default.rp_filter
#
/sbin/sysctl -q -n -w net.ipv4.conf.default.rp_filter=$sysctl_net_ipv4_conf_default_rp_filter_value
#
# If net.ipv4.conf.default.rp_filter present in /etc/sysctl.conf, change value to appropriate value
# else, add "net.ipv4.conf.default.rp_filter = value" to /etc/sysctl.conf
#
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysctl.conf' '^net.ipv4.conf.default.rp_filter' "$sysctl_net_ipv4_conf_default_rp_filter_value" 'CCE-26915-9'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: XCCDF Value sysctl_net_ipv4_conf_default_rp_filter_value # promote to variable
set_fact:
sysctl_net_ipv4_conf_default_rp_filter_value: 1
tags:
- always
- name: Ensure sysctl net.ipv4.conf.default.rp_filter is set
sysctl:
name: net.ipv4.conf.default.rp_filter
value: "{{ sysctl_net_ipv4_conf_default_rp_filter_value }}"
state: present
reload: yes
tags:
- sysctl_net_ipv4_conf_default_rp_filter
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26915-9
- NIST-800-53-AC-4
- NIST-800-53-SC-5
- NIST-800-53-SC-7
- DISA-STIG-RHEL-06-000097
|
Wireless Networking
[ref]groupWireless 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 4 rules |
Disable Wireless Through Software Configuration
[ref]groupIf 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 4 rules |
Disable WiFi or Bluetooth in BIOS
[ref]ruleSome systems that include built-in wireless support offer the
ability to disable the device through the BIOS. This is system-specific;
consult your hardware manual or explore the BIOS setup during
boot. Rationale:Disabling wireless support in the BIOS prevents easy
activation of the wireless interface, generally requiring administrators
to reboot the system first.
|
Deactivate Wireless Network Interfaces
[ref]rule
Deactivating wireless network interfaces should prevent
normal usage of the wireless capability.
First, identify the interfaces available with the command:
$ ifconfig -a
Additionally, the following command may be used to
determine whether wireless support is included for a
particular interface, though this may not always be a clear
indicator:
$ iwconfig
After identifying any wireless interfaces (which may have
names like wlan0 , ath0 , wifi0 , em1 or
eth0 ), deactivate the interface with the command:
$ sudo ifdown interface
These changes will only last until the next reboot. To
disable the interface for future boots, remove the appropriate
interface file from /etc/sysconfig/network-scripts :
$ sudo rm /etc/sysconfig/network-scripts/ifcfg-interface
Rationale:
The use of wireless networking can introduce many different attack vectors into
the organization's network. Common attack vectors such as malicious
association and ad hoc networks will allow an attacker to spoof a wireless
access point (AP), allowing validated systems to connect to the malicious AP and
enabling the attacker to monitor and record network traffic. These malicious APs
can also serve to create a man-in-the-middle attack or be used to create a
denial of service to valid network resources.
|
Disable Bluetooth Service
[ref]rule
The bluetooth service can be disabled with the following command:
$ sudo chkconfig bluetooth off
$ sudo service bluetooth stop
Rationale:Disabling the bluetooth service prevents the system from attempting
connections to Bluetooth devices, which entails some security risk.
Nevertheless, variation in this risk decision may be expected due to the
utility of Bluetooth connectivity and its limited range. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable bluetooth
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service bluetooth
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- bluetooth
tags:
- service_bluetooth_disabled
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27081-9
- NIST-800-53-AC-18(a)
- NIST-800-53-AC-18(d)
- NIST-800-53-AC-18(3)
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000331
|
Disable Bluetooth Kernel Modules
[ref]ruleThe 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. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^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
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'bluetooth' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- bluetooth
tags:
- kernel_module_bluetooth_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26763-3
- NIST-800-53-AC-18(a)
- NIST-800-53-AC-18(d)
- NIST-800-53-AC-18(3)
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000315
|
The system includes support for Internet Protocol
version 6. A major and often-mentioned improvement over IPv4 is its
enormous increase in the number of available addresses. Another
important feature is its support for automatic configuration of
many network settings. |
contains 2 rules |
Disable Support for IPv6 Unless Needed
[ref]group
Despite configuration that suggests support for IPv6 has
been disabled, link-local IPv6 address auto-configuration occurs
even when only an IPv4 address is assigned. The only way to
effectively prevent execution of the IPv6 networking stack is to
instruct the system not to activate the IPv6 kernel module.
|
contains 2 rules |
Disable IPv6 Networking Support Automatic Loading
[ref]ruleTo prevent the IPv6 kernel module (ipv6 ) from binding to the
IPv6 networking stack, add the following line to
/etc/modprobe.d/disabled.conf (or another file in
/etc/modprobe.d ):
options ipv6 disable=1
This permits the IPv6 module to be loaded (and thus satisfy other modules that
depend on it), while disabling support for the IPv6 protocol.
Rationale:
Any unnecessary network stacks - including IPv6 - should be disabled, to reduce
the vulnerability to exploitation.
Remediation Shell script: (show)
# Prevent the IPv6 kernel module (ipv6) from loading the IPv6 networking stack
echo "options ipv6 disable=1" > /etc/modprobe.d/ipv6.conf
# Since according to: https://access.redhat.com/solutions/72733
# "ipv6 disable=1" options doesn't always disable the IPv6 networking stack from
# loading, instruct also sysctl configuration to disable IPv6 according to:
# https://access.redhat.com/solutions/8709#rhel6disable
declare -a IPV6_SETTINGS=("net.ipv6.conf.all.disable_ipv6" "net.ipv6.conf.default.disable_ipv6")
for setting in ${IPV6_SETTINGS[@]}
do
# Set runtime =1 for setting
/sbin/sysctl -q -n -w "$setting=1"
# If setting is present in /etc/sysctl.conf, change value to "1"
# else, add "$setting = 1" to /etc/sysctl.conf
if grep -q ^"$setting" /etc/sysctl.conf ; then
sed -i "s/^$setting.*/$setting = 1/g" /etc/sysctl.conf
else
echo "" >> /etc/sysctl.conf
echo "# Set $setting = 1 per security requirements" >> /etc/sysctl.conf
echo "$setting = 1" >> /etc/sysctl.conf
fi
done
|
Disable Support for RPC IPv6
[ref]ruleRPC services for NFSv4 try to load transport modules for
udp6 and tcp6 by default, even if IPv6 has been disabled in
/etc/modprobe.d . To prevent RPC services such as rpc.mountd
from attempting to start IPv6 network listeners, remove or comment out the
following two lines in /etc/netconfig :
udp6 tpi_clts v inet6 udp - -
tcp6 tpi_cots_ord v inet6 tcp - -
Remediation Shell script: (show)
# Drop 'tcp6' and 'udp6' entries from /etc/netconfig to prevent RPC
# services for NFSv4 from attempting to start IPv6 network listeners
declare -a IPV6_RPC_ENTRIES=("tcp6" "udp6")
for rpc_entry in ${IPV6_RPC_ENTRIES[@]}
do
sed -i "/^$rpc_entry[[:space:]]\+tpi\_.*inet6.*/d" /etc/netconfig
done
|
iptables and ip6tables
[ref]groupA host-based firewall called netfilter is included as
part of the Linux kernel distributed with the system. It is
activated by default. This firewall is controlled by the program
iptables , and the entire capability is frequently referred to by
this name. An analogous program called ip6tables handles filtering
for IPv6.
Unlike TCP Wrappers, which depends on the network server
program to support and respect the rules written, netfilter
filtering occurs at the kernel level, before a program can even
process the data from the network packet. As such, any program on
the system is affected by the rules written.
This section provides basic information about strengthening
the iptables and ip6tables configurations included with the system.
For more complete information that may allow the construction of a
sophisticated ruleset tailored to your environment, please consult
the references at the end of this section. |
contains 3 rules |
Inspect and Activate Default Rules
[ref]groupView the currently-enforced iptables rules by running
the command:
$ sudo iptables -nL --line-numbers
The command is analogous for ip6tables .
If the firewall does not appear to be active (i.e., no rules
appear), activate it and ensure that it starts at boot by issuing
the following commands (and analogously for ip6tables ):
$ sudo service iptables restart
The default iptables rules are:
Chain INPUT (policy ACCEPT)
num target prot opt source destination
1 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0 state RELATED,ESTABLISHED
2 ACCEPT icmp -- 0.0.0.0/0 0.0.0.0/0
3 ACCEPT all -- 0.0.0.0/0 0.0.0.0/0
4 ACCEPT tcp -- 0.0.0.0/0 0.0.0.0/0 state NEW tcp dpt:22
5 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain FORWARD (policy ACCEPT)
num target prot opt source destination
1 REJECT all -- 0.0.0.0/0 0.0.0.0/0 reject-with icmp-host-prohibited
Chain OUTPUT (policy ACCEPT)
num target prot opt source destination
The ip6tables default rules are essentially the same. |
contains 1 rule |
Verify iptables Enabled
[ref]rule
The iptables service can be enabled with the following command:
$ sudo chkconfig --level 2345 iptables on
Rationale:
The iptables service provides the system's host-based firewalling
capability for IPv4 and ICMP.
References:
AC-4, CA-3(c), CM-7, CCI-000032, CCI-000066, CCI-001115, CCI-001118, CCI-001092, CCI-001117, CCI-001098, CCI-001100, CCI-001097, CCI-001414, SV-50313r2_rule, SRG-OS-000146, SRG-OS-000152, SRG-OS-000145 Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable iptables
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service iptables
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- iptables
tags:
- service_iptables_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27018-1
- NIST-800-53-AC-4
- NIST-800-53-CA-3(c)
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000117
|
Strengthen the Default Ruleset
[ref]groupThe default rules can be strengthened. The system
scripts that activate the firewall rules expect them to be defined
in the configuration files iptables and ip6tables in the directory
/etc/sysconfig . Many of the lines in these files are similar
to the command line arguments that would be provided to the programs
/sbin/iptables or /sbin/ip6tables - but some are quite
different.
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 iptables program to load in rules, and then invokes service
iptables save to write those loaded rules to
/etc/sysconfig/iptables.
The following alterations can be made directly to
/etc/sysconfig/iptables and /etc/sysconfig/ip6tables .
Instructions apply to both unless otherwise noted. Language and address
conventions for regular iptables are used throughout this section;
configuration for ip6tables will be either analogous or explicitly
covered. Warning:
The program system-config-securitylevel
allows additional services to penetrate the default firewall rules
and automatically adjusts /etc/sysconfig/iptables . This program
is only useful if the default ruleset meets your security
requirements. Otherwise, this program should not be used to make
changes to the firewall configuration because it re-writes the
saved configuration file. |
contains 2 rules |
Set Default iptables Policy for Incoming Packets
[ref]ruleTo set the default policy to DROP (instead of ACCEPT) for
the built-in INPUT chain which processes incoming packets,
add or correct the following line in
/etc/sysconfig/iptables :
:INPUT DROP [0:0]
Rationale:In iptables the default policy is applied only after all
the applicable rules in the table are examined for a match. Setting the
default policy to DROP implements proper design for a firewall, i.e.
any packets which are not explicitly permitted should not be
accepted. Remediation Shell script: (show)
sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/iptables
|
Set Default iptables Policy for Forwarded Packets
[ref]ruleTo set the default policy to DROP (instead of ACCEPT) for
the built-in FORWARD chain which processes packets that will be forwarded from
one interface to another,
add or correct the following line in
/etc/sysconfig/iptables :
:FORWARD DROP [0:0]
Rationale:In iptables , the default policy is applied only after all
the applicable rules in the table are examined for a match. Setting the
default policy to DROP implements proper design for a firewall, i.e.
any packets which are not explicitly permitted should not be
accepted. Remediation Shell script: (show)
sed -i 's/^:FORWARD ACCEPT.*/:FORWARD DROP [0:0]/g' /etc/sysconfig/iptables
|
Uncommon Network Protocols
[ref]groupThe 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 4 rules |
Disable DCCP Support
[ref]rule
The 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.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^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
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'dccp' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- dccp
tags:
- kernel_module_dccp_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26448-1
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000124
|
Disable SCTP Support
[ref]rule
The 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.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^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
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'sctp' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- sctp
tags:
- kernel_module_sctp_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26410-1
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000125
|
Disable RDS Support
[ref]rule
The Reliable Datagram Sockets (RDS) protocol is a transport
layer protocol designed to provide reliable high- bandwidth,
low-latency communications between nodes in a cluster.
To configure the system to prevent the rds
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install rds /bin/true
Rationale:
Disabling RDS protects
the system against exploitation of any flaws in its implementation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install rds" /etc/modprobe.d/rds.conf ; then
sed -i 's/^install rds.*/install rds /bin/true/g' /etc/modprobe.d/rds.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/rds.conf
echo "install rds /bin/true" >> /etc/modprobe.d/rds.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'rds' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- rds
tags:
- kernel_module_rds_disabled
- low_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26239-4
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000126
|
Disable TIPC Support
[ref]rule
The Transparent Inter-Process Communication (TIPC) protocol
is designed to provide communications between nodes in a
cluster.
To configure the system to prevent the tipc
kernel module from being loaded, add the following line to a file in the directory /etc/modprobe.d :
install tipc /bin/true
Rationale:
Disabling TIPC protects
the system against exploitation of any flaws in its implementation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
if grep --silent "^install tipc" /etc/modprobe.d/tipc.conf ; then
sed -i 's/^install tipc.*/install tipc /bin/true/g' /etc/modprobe.d/tipc.conf
else
echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/tipc.conf
echo "install tipc /bin/true" >> /etc/modprobe.d/tipc.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | medium |
---|
Reboot: | true |
---|
Strategy: | disable |
---|
- name: Ensure kernel module 'tipc' is disabled
lineinfile:
create=yes
dest="/etc/modprobe.d/{{item}}.conf"
regexp="{{item}}"
line="install {{item}} /bin/true"
with_items:
- tipc
tags:
- kernel_module_tipc_disabled
- medium_severity
- disable_strategy
- low_complexity
- medium_disruption
- CCE-26696-5
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000127
|
Disable Zeroconf Networking
[ref]ruleZeroconf networking allows the system to assign itself an IP
address and engage in IP communication without a statically-assigned address or
even a DHCP server. Automatic address assignment via Zeroconf (or DHCP) is not
recommended. To disable Zeroconf automatic route assignment in the 169.254.0.0
subnet, add or correct the following line in /etc/sysconfig/network :
NOZEROCONF=yes
Rationale:Zeroconf addresses are in the network 169.254.0.0. The networking
scripts add entries to the system's routing table for these addresses. Zeroconf
address assignment commonly occurs when the system is configured to use DHCP
but fails to receive an address assignment from the DHCP server.
Remediation Shell script: (show)
echo "NOZEROCONF=yes" >> /etc/sysconfig/network
|
Ensure System is Not Acting as a Network Sniffer
[ref]ruleThe system should not be acting as a network sniffer, which can
capture all traffic on the network to which it is connected. Run the following
to determine if any interface is running in promiscuous mode:
$ ip link | grep PROMISC
Rationale:If any results are returned, then a sniffing process (such as tcpdump
or Wireshark) is likely to be using the interface and this should be
investigated.
|
Configure Syslog
[ref]groupThe syslog service has been the default Unix logging mechanism for
many years. It has a number of downsides, including inconsistent log format,
lack of authentication for received messages, and lack of authentication,
encryption, or reliable transport for messages sent over a network. However,
due to its long history, syslog is a de facto standard which is supported by
almost all Unix applications.
In CentOS Linux 6, rsyslog has replaced ksyslogd as the
syslog daemon of choice, and it includes some additional security features
such as reliable, connection-oriented (i.e. TCP) transmission of logs, the
option to log to database formats, and the encryption of log data en route to
a central logging server.
This section discusses how to configure rsyslog for
best effect, and how to use tools provided with the system to maintain and
monitor logs. |
contains 7 rules |
Ensure Proper Configuration of Log Files
[ref]group
The file /etc/rsyslog.conf controls where log message are written.
These are controlled by lines called rules, which consist of a
selector and an action.
These rules are often customized depending on the role of the system, the
requirements of the environment, and whatever may enable
the administrator to most effectively make use of log data.
The default rules in CentOS Linux 6 are:
*.info;mail.none;authpriv.none;cron.none /var/log/messages
authpriv.* /var/log/secure
mail.* -/var/log/maillog
cron.* /var/log/cron
*.emerg *
uucp,news.crit /var/log/spooler
local7.* /var/log/boot.log
See the man page rsyslog.conf(5) for more information.
Note that the rsyslog daemon can be configured to use a timestamp format that
some log processing programs may not understand. If this occurs,
edit the file /etc/rsyslog.conf and add or edit the following line:
$ ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat
|
contains 1 rule |
Ensure Log Files Are Owned By Appropriate Group
[ref]ruleThe group-owner of all log files written by
rsyslog should be root.
These log files are determined by the second part of each Rule line in
/etc/rsyslog.conf and typically all appear in /var/log .
For each log file LOGFILE referenced in /etc/rsyslog.conf ,
run the following command to inspect the file's group owner:
$ ls -l LOGFILE
If the owner is not root , run the following command to
correct this:
$ sudo chgrp root LOGFILE
Rationale:The log files generated by rsyslog contain valuable information regarding system
configuration, user authentication, and other such information. Log files should be
protected from unauthorized access. |
Configure rsyslogd to Accept Remote Messages If Acting as a Log Server
[ref]group
By default, rsyslog does not listen over the network
for log messages. If needed, modules can be enabled to allow
the rsyslog daemon to receive messages from other systems and for the system
thus to act as a log server.
If the machine is not a log server, then lines concerning these modules
should remain commented out.
|
contains 3 rules |
Ensure rsyslog Does Not Accept Remote Messages Unless Acting As Log Server
[ref]ruleThe rsyslog daemon should not accept remote messages
unless the system acts as a log server.
To ensure that it is not listening on the network, ensure the following lines are
not found in /etc/rsyslog.conf :
$ModLoad imtcp
$InputTCPServerRun port
$ModLoad imudp
$UDPServerRun port
$ModLoad imrelp
$InputRELPServerRun port
Rationale:
Any process which receives messages from the network incurs some risk
of receiving malicious messages. This risk can be eliminated for
rsyslog by configuring it not to listen on the network.
|
Enable rsyslog to Accept Messages via TCP, if Acting As Log Server
[ref]ruleThe rsyslog daemon should not accept remote messages
unless the system acts as a log server.
If the system needs to act as a central log server, add the following lines to
/etc/rsyslog.conf to enable reception of messages over TCP:
$ModLoad imtcp
$InputTCPServerRun 514
Rationale:
If the system needs to act as a log server, this ensures that it can receive
messages over a reliable TCP connection.
|
Enable rsyslog to Accept Messages via UDP, if Acting As Log Server
[ref]ruleThe rsyslog daemon should not accept remote messages
unless the system acts as a log server.
If the system needs to act as a central log server, add the following lines to
/etc/rsyslog.conf to enable reception of messages over UDP:
$ModLoad imudp
$UDPServerRun 514
Rationale:
Many devices, such as switches, routers, and other Unix-like systems, may only support
the traditional syslog transmission over UDP. If the system must act as a log server,
this enables it to receive their messages as well.
|
Ensure All Logs are Rotated by logrotate
[ref]groupEdit the file /etc/logrotate.d/syslog . Find the first
line, which should look like this (wrapped for clarity):
/var/log/messages /var/log/secure /var/log/maillog /var/log/spooler \
/var/log/boot.log /var/log/cron {
Edit this line so that it contains a one-space-separated
listing of each log file referenced in /etc/rsyslog.conf .
All logs in use on a system must be rotated regularly, or the
log files will consume disk space over time, eventually interfering
with system operation. The file /etc/logrotate.d/syslog is the
configuration file used by the logrotate program to maintain all
log files written by syslog . By default, it rotates logs weekly and
stores four archival copies of each log. These settings can be
modified by editing /etc/logrotate.conf , but the defaults are
sufficient for purposes of this guide.
Note that logrotate is run nightly by the cron job
/etc/cron.daily/logrotate . If particularly active logs need to be
rotated more often than once a day, some other mechanism must be
used. |
contains 1 rule |
Ensure Logrotate Runs Periodically
[ref]ruleThe logrotate utility allows for the automatic rotation of
log files. The frequency of rotation is specified in /etc/logrotate.conf ,
which triggers a cron task. To configure logrotate to run daily, add or correct
the following line in /etc/logrotate.conf :
# rotate log files frequency
daily
Rationale:Log files that are not properly rotated run the risk of growing so large
that they fill up the /var/log partition. Valuable logging information could be lost
if the /var/log partition becomes full. Remediation Shell script: (show)
LOGROTATE_CONF_FILE="/etc/logrotate.conf"
CRON_DAILY_LOGROTATE_FILE="/etc/cron.daily/logrotate"
# daily rotation is configured
grep -q "^daily$" $LOGROTATE_CONF_FILE|| echo "daily" >> $LOGROTATE_CONF_FILE
# remove any line configuring weekly, monthly or yearly rotation
sed -i -r "/^(weekly|monthly|yearly)$/d" $LOGROTATE_CONF_FILE
# configure cron.daily if not already
if ! grep -q "^[[:space:]]*/usr/sbin/logrotate[[:alnum:][:blank:][:punct:]]*$LOGROTATE_CONF_FILE$" $CRON_DAILY_LOGROTATE_FILE; then
echo "#!/bin/sh" > $CRON_DAILY_LOGROTATE_FILE
echo "/usr/sbin/logrotate $LOGROTATE_CONF_FILE" >> $CRON_DAILY_LOGROTATE_FILE
fi
|
Ensure rsyslog is Installed
[ref]rule
Rsyslog is installed by default.
The rsyslog package can be installed with the following command:
$ sudo yum install rsyslog
Rationale:
The rsyslog package provides the rsyslog daemon, which provides
system logging services.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command install rsyslog
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Ensure rsyslog is installed
package:
name="{{item}}"
state=present
with_items:
- rsyslog
tags:
- package_rsyslog_installed
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-26809-4
- NIST-800-53-AU-9(2)
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
include install_rsyslog
class install_rsyslog {
package { 'rsyslog':
ensure => 'installed',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
package --add=rsyslog
|
Enable rsyslog Service
[ref]ruleThe rsyslog service provides syslog-style logging by default on CentOS Linux 6.
The rsyslog service can be enabled with the following command:
$ sudo chkconfig --level 2345 rsyslog on
Rationale:The rsyslog service must be running in order to provide
logging services, which are essential to system administration.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable rsyslog
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service rsyslog
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- rsyslog
tags:
- service_rsyslog_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-26807-8
- NIST-800-53-AU-12
|
System Accounting with auditd
[ref]groupThe 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.
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
http://docs.fedoraproject.org/en-US/Fedora/13/html/Security-Enhanced_Linux/sect-Security-Enhanced_Linux-Fixing_Problems-Raw_Audit_Messages.html
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 38 rules |
Configure auditd Data Retention
[ref]group
The 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.
References:
AU-11, CCI-000138 |
contains 7 rules |
Configure auditd Number of Logs Retained
[ref]ruleDetermine 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. Remediation Shell script: (show)
var_auditd_num_logs="5"
AUDITCONFIG=/etc/audit/auditd.conf
grep -q ^num_logs $AUDITCONFIG && \
sed -i 's/^num_logs.*/num_logs = '"$var_auditd_num_logs"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
echo "num_logs = $var_auditd_num_logs" >> $AUDITCONFIG
fi
|
Configure auditd Max Log File Size
[ref]ruleDetermine 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. Remediation Shell script: (show)
var_auditd_max_log_file="6"
AUDITCONFIG=/etc/audit/auditd.conf
grep -q ^max_log_file $AUDITCONFIG && \
sed -i 's/^max_log_file.*/max_log_file = '"$var_auditd_max_log_file"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
echo "max_log_file = $var_auditd_max_log_file" >> $AUDITCONFIG
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_auditd_max_log_file # promote to variable
set_fact:
var_auditd_max_log_file: 6
tags:
- always
- name: Configure auditd Max Log File Size
lineinfile:
dest: /etc/audit/auditd.conf
line: "max_log_file {{ var_auditd_max_log_file }}"
state: present
#notify: reload auditd
tags:
- auditd_data_retention_max_log_file
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27550-3
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-11
- NIST-800-53-IR-5
- PCI-DSS-Req-10.7
- DISA-STIG-RHEL-06-000160
|
Configure auditd max_log_file_action Upon Reaching Maximum Log Size
[ref]rule The 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:
ignore 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. Remediation Shell script: (show)
var_auditd_max_log_file_action="keep_logs"
AUDITCONFIG=/etc/audit/auditd.conf
grep -q ^max_log_file_action $AUDITCONFIG && \
sed -i 's/^max_log_file_action.*/max_log_file_action = '"$var_auditd_max_log_file_action"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
echo "max_log_file_action = $var_auditd_max_log_file_action" >> $AUDITCONFIG
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_auditd_max_log_file_action # promote to variable
set_fact:
var_auditd_max_log_file_action: keep_logs
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 }}"
state: present
#notify: reload auditd
tags:
- auditd_data_retention_max_log_file_action
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27237-7
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-4
- NIST-800-53-AU-11
- NIST-800-53-IR-5
- PCI-DSS-Req-10.7
- DISA-STIG-RHEL-06-000161
|
Configure auditd space_left Action on Low Disk Space
[ref]ruleThe 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:
ignore 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. Remediation Shell script: (show)
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
#
if grep --silent ^space_left_action /etc/audit/auditd.conf ; then
sed -i 's/^space_left_action.*/space_left_action = '"$var_auditd_space_left_action"'/g' /etc/audit/auditd.conf
else
echo -e "\n# Set space_left_action to $var_auditd_space_left_action per security requirements" >> /etc/audit/auditd.conf
echo "space_left_action = $var_auditd_space_left_action" >> /etc/audit/auditd.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_auditd_space_left_action # promote to variable
set_fact:
var_auditd_space_left_action: 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: ^space_left_action*
#notify: reload auditd
tags:
- auditd_data_retention_space_left_action
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27238-5
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-4
- NIST-800-53-AU-5(b)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.7
- DISA-STIG-RHEL-06-000005
|
Configure auditd admin_space_left Action on Low Disk Space
[ref]ruleThe 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.
Remediation Shell script: (show)
var_auditd_admin_space_left_action="single"
grep -q ^admin_space_left_action /etc/audit/auditd.conf && \
sed -i "s/admin_space_left_action.*/admin_space_left_action = $var_auditd_admin_space_left_action/g" /etc/audit/auditd.conf
if ! [ $? -eq 0 ]; then
echo "admin_space_left_action = $var_auditd_admin_space_left_action" >> /etc/audit/auditd.conf
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_auditd_admin_space_left_action # promote to variable
set_fact:
var_auditd_admin_space_left_action: 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: "^admin_space_left_action*"
#notify: reload auditd
tags:
- auditd_data_retention_admin_space_left_action
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27239-3
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-4
- NIST-800-53-AU-5(b)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.7
- DISA-STIG-RHEL-06-000163
|
Configure auditd mail_acct Action on Low Disk Space
[ref]ruleThe 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. Remediation Shell script: (show)
var_auditd_action_mail_acct="root"
AUDITCONFIG=/etc/audit/auditd.conf
grep -q ^action_mail_acct $AUDITCONFIG && \
sed -i 's/^action_mail_acct.*/action_mail_acct = '"$var_auditd_action_mail_acct"'/g' $AUDITCONFIG
if ! [ $? -eq 0 ]; then
echo "action_mail_acct = $var_auditd_action_mail_acct" >> $AUDITCONFIG
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: XCCDF Value var_auditd_action_mail_acct # promote to variable
set_fact:
var_auditd_action_mail_acct: 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
#notify: reload auditd
tags:
- auditd_data_retention_action_mail_acct
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27241-9
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-4
- NIST-800-53-AU-5(a)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.7.a
- DISA-STIG-RHEL-06-000313
|
Configure auditd to use audispd's syslog plugin
[ref]ruleTo 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 Remediation Shell script: (show)
grep -q ^active /etc/audisp/plugins.d/syslog.conf && \
sed -i "s/active.*/active = yes/g" /etc/audisp/plugins.d/syslog.conf
if ! [ $? -eq 0 ]; then
echo "active = yes" >> /etc/audisp/plugins.d/syslog.conf
fi
|
Configure auditd Rules for Comprehensive Auditing
[ref]groupThe 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 29 rules |
Records Events that Modify Date and Time Information
[ref]groupArbitrary 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 adjtimex
[ref]ruleOn a 32-bit system, add the following to /etc/audit/audit.rules :
# audit_time_rules
-a always,exit -F arch=b32 -S adjtimex -k audit_time_rules
On a 64-bit system, add the following to /etc/audit/audit.rules :
# audit_time_rules
-a always,exit -F arch=b64 -S adjtimex -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.
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 -S settimeofday -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.4.2.b, CCI-001487, CCI-000169, SRG-OS-000062 Remediation Shell script: (show)
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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
}
# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on CentOS Linux 6 OS
function rhel6_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 itself
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
rhel6_perform_audit_adjtimex_settimeofday_stime_remediation
|
Record attempts to alter time through settimeofday
[ref]ruleOn a 32-bit system, add the following to /etc/audit/audit.rules :
# audit_time_rules
-a always,exit -F arch=b32 -S settimeofday -k audit_time_rules
On a 64-bit system, add the following to /etc/audit/audit.rules :
# audit_time_rules
-a always,exit -F arch=b64 -S settimeofday -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.
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 -S settimeofday -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.4.2.b, CCI-001487, CCI-000169, SV-50323r3_rule, SRG-OS-000062 Remediation Shell script: (show)
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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
}
# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on CentOS Linux 6 OS
function rhel6_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 itself
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
rhel6_perform_audit_adjtimex_settimeofday_stime_remediation
|
Record Attempts to Alter Time Through stime
[ref]ruleAdd the following line to /etc/audit/audit.rules for both
32-bit and 64-bit systems:
# audit_time_rules
-a always,exit -F arch=b32 -S stime -k 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 syscalls:
-a always,exit -F arch=b64 -S adjtimex -S settimeofday -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.4.2.b, CCI-001487, CCI-000169, SV-50326r4_rule, SRG-OS-000062 Remediation Shell script: (show)
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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
}
# Perform the remediation for the 'adjtimex', 'settimeofday', and 'stime' audit
# system calls on CentOS Linux 6 OS
function rhel6_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 itself
fix_audit_syscall_rule "auditctl" "$PATTERN" "$GROUP" "$ARCH" "$FULL_RULE"
done
}
rhel6_perform_audit_adjtimex_settimeofday_stime_remediation
|
Record Attempts to Alter Time Through clock_settime
[ref]ruleOn a 32-bit system, add the following to /etc/audit/audit.rules :
# time-change
-a always,exit -F arch=b32 -S clock_settime -F a0=0x0 -F key=time-change
On a 64-bit system, add the following to /etc/audit/audit.rules :
# time-change
-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 -S settimeofday -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.4.2.b, CCI-001487, CCI-000169, SV-50328r3_rule, SRG-OS-000062 Remediation Shell script: (show)
# 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"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
|
Record Attempts to Alter the localtime File
[ref]ruleAdd the following to /etc/audit/audit.rules :
-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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.4.2.b, CCI-001487, CCI-000169, SV-50331r2_rule, SRG-OS-000062 Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Record Events that Modify the System's Discretionary Access Controls
[ref]groupAt 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 -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b32 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k 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 -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
-a always,exit -F arch=b64 -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
|
contains 13 rules |
Record Events that Modify the System's Discretionary Access Controls - chmod
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S chmod -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S chmod -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50344r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chmod"
FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit chmod tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_chmod
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_chmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26280-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000184
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_chmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26280-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000184
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the chmod rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S chmod -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_chmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26280-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000184
- name: Inserts/replaces the chmod rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S chmod -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_chmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26280-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000184
|
Record Events that Modify the System's Discretionary Access Controls - chown
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S chown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S chown -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50346r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chown"
FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit chown tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_chown
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_chown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27173-4
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000185
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_chown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27173-4
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000185
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the chown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S chown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_chown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27173-4
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000185
- name: Inserts/replaces the chown rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S chown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_chown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27173-4
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000185
|
Record Events that Modify the System's Discretionary Access Controls - fchmod
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fchmod -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchmod -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50348r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chmod"
FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fchmod tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fchmod
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fchmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27174-2
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000186
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27174-2
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000186
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fchmod rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fchmod -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fchmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27174-2
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000186
- name: Inserts/replaces the fchmod rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fchmod -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchmod
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27174-2
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000186
|
Record Events that Modify the System's Discretionary Access Controls - fchmodat
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchmodat -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50351r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chmod"
FULL_RULE="-a always,exit -F arch=$ARCH -S chmod -S fchmod -S fchmodat -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fchmodat tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fchmodat
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fchmodat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27175-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000187
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchmodat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27175-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000187
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fchmodat rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fchmodat -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fchmodat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27175-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000187
- name: Inserts/replaces the fchmodat rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fchmodat -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchmodat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27175-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000187
|
Record Events that Modify the System's Discretionary Access Controls - fchown
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fchown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchown -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50353r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chown"
FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fchown tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fchown
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27177-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000188
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27177-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000188
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fchown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fchown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27177-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000188
- name: Inserts/replaces the fchown rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fchown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27177-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000188
|
Record Events that Modify the System's Discretionary Access Controls - fchownat
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fchownat -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fchownat -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50355r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chown"
FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fchownat tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fchownat
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fchownat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27178-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000189
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchownat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27178-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000189
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fchownat rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fchownat -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fchownat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27178-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000189
- name: Inserts/replaces the fchownat rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fchownat -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fchownat
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27178-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000189
|
Record Events that Modify the System's Discretionary Access Controls - fremovexattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fremovexattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50357r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fremovexattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fremovexattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27179-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000190
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27179-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000190
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fremovexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fremovexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27179-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000190
- name: Inserts/replaces the fremovexattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fremovexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27179-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000190
|
Record Events that Modify the System's Discretionary Access Controls - fsetxattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S fsetxattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50358r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit fsetxattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_fsetxattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_fsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27180-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000191
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27180-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000191
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the fsetxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S fsetxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_fsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27180-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000191
- name: Inserts/replaces the fsetxattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S fsetxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_fsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27180-9
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000191
|
Record Events that Modify the System's Discretionary Access Controls - lchown
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lchown -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50359r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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>=500 -F auid!=4294967295 -k *"
GROUP="chown"
FULL_RULE="-a always,exit -F arch=$ARCH -S chown -S fchown -S fchownat -S lchown -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit lchown tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_lchown
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_lchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27181-7
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000192
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27181-7
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000192
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the lchown rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S lchown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_lchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27181-7
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000192
- name: Inserts/replaces the lchown rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S lchown -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lchown
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27181-7
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000192
|
Record Events that Modify the System's Discretionary Access Controls - lremovexattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lremovexattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50360r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit lremovexattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_lremovexattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_lremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27182-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000193
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27182-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000193
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the lremovexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S lremovexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_lremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27182-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000193
- name: Inserts/replaces the lremovexattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S lremovexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lremovexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27182-5
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000193
|
Record Events that Modify the System's Discretionary Access Controls - lsetxattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S lsetxattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50362r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit lsetxattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_lsetxattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_lsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27183-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000194
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27183-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000194
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the lsetxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S lsetxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_lsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27183-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000194
- name: Inserts/replaces the lsetxattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S lsetxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_lsetxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27183-3
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000194
|
Record Events that Modify the System's Discretionary Access Controls - removexattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S removexattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S removexattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50364r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit removexattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_removexattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_removexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27184-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000195
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_removexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27184-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000195
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the removexattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S removexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_removexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27184-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000195
- name: Inserts/replaces the removexattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S removexattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_removexattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27184-1
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000195
|
Record Events that Modify the System's Discretionary Access Controls - setxattr
[ref]ruleAt a minimum the audit system should collect file
permission changes for all users and root. Add the following to
/etc/audit/audit.rules :
-a always,exit -F arch=b32 -S setxattr -F auid>=500 -F auid!=4294967295 -k perm_mod
If the system is 64 bit then also add the following:
-a always,exit -F arch=b64 -S setxattr -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.5.5, SV-50366r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 auid>=500 -F auid!=4294967295 -k *"
GROUP="xattr"
FULL_RULE="-a always,exit -F arch=${ARCH} -S setxattr -S lsetxattr -S fsetxattr -S removexattr -S lremovexattr -S fremovexattr -F auid>=500 -F auid!=4294967295 -k perm_mod"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
#
# What architecture are we on?
#
- name: Set architecture for audit setxattr tasks
set_fact:
audit_arch: "b{{ ansible_architecture | regex_replace('.*(\\d\\d$)','\\1') }}"
#
# Inserts/replaces the rule in /etc/audit/rules.d
#
- name: Search /etc/audit/rules.d for other DAC audit rules
find:
paths: "/etc/audit/rules.d"
recurse: no
contains: "-F key=perm_mod$"
patterns: "*.rules"
register: find_setxattr
- 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 == 0
- 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 > 0
- 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!=4294967295 -F key=perm_mod"
create: yes
tags:
- audit_rules_dac_modification_setxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27185-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000196
- 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!=4294967295 -F key=perm_mod"
create: yes
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_setxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27185-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000196
#
# Inserts/replaces the rule in /etc/audit/audit.rules
#
- name: Inserts/replaces the setxattr rule in /etc/audit/audit.rules when on x86
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
with_items:
- "-a always,exit -F arch=b32 -S setxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
tags:
- audit_rules_dac_modification_setxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27185-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000196
- name: Inserts/replaces the setxattr rule in audit.rules when on x86_64
lineinfile:
line: "{{ item }}"
state: present
dest: /etc/audit/audit.rules
create: yes
with_items:
- "-a always,exit -F arch=b64 -S setxattr -F auid>=1000 -F auid!=4294967295 -F key=perm_mod"
when: audit_arch == 'b64'
tags:
- audit_rules_dac_modification_setxattr
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27185-8
- NIST-800-53-AC-3(10)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10.5.5
- DISA-STIG-RHEL-06-000196
|
Record Events that Modify User/Group Information
[ref]ruleAdd the following to /etc/audit/audit.rules , in order
to capture events that modify account changes:
# audit_rules_usergroup_modification
-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
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. References:
AC-2(4), AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000018, CCI-001403, CCI-001404, CCI-001405, CCI-001684, CCI-001683, CCI-001685, CCI-001686, Req-10.2.5, SV-50332r2_rule, SRG-OS-000004, SRG-OS-000239, SRG-OS-000240, SRG-OS-000241 Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Record Events that Modify the System's Network Environment
[ref]ruleAdd the following to /etc/audit/audit.rules , setting
ARCH to either b32 or b64 as appropriate for your system:
# audit_rules_networkconfig_modification
-a always,exit -F arch=ARCH -S sethostname -S setdomainname -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.5.5, SV-50341r4_rule, SRG-OS-999999 Remediation Shell script: (show)
# 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"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
# Then perform the remediations for the watch rules
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/etc/sysconfig/network" "wa" "audit_rules_networkconfig_modification"
|
System Audit Logs Must Have Mode 0640 or Less Permissive
[ref]rule
If 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.
Remediation Shell script: (show)
if `grep -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
chmod 0640 /etc/audit/audit*
chmod 0640 /etc/audit/rules.d/*
else
chmod 0600 /var/log/audit/audit.log
chmod 0400 /var/log/audit/audit.log.*
chmod 0640 /etc/audit/audit*
chmod 0640 /etc/audit/rules.d/*
fi
|
System Audit Logs Must Be Owned By Root
[ref]rule
To properly set the owner of /var/log , run the command:
$ sudo chown root /var/log
Rationale:Failure to give ownership of the audit log files to root allows the designated
owner, and unauthorized users, potential access to sensitive information. Remediation Shell script: (show)
if `grep -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
|
Record Events that Modify the System's Mandatory Access Controls
[ref]ruleAdd the following to /etc/audit/audit.rules :
-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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, Req-10.5.5, SV-50342r2_rule, SRG-OS-999999 Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Record Attempts to Alter Login and Logout Events
[ref]rule
The audit system already collects login info for all users and root. To watch for attempted manual edits of
files involved in storing login events, add the following to /etc/audit/audit.rules :
-w /var/log/tallylog -p wa -k logins
-w /var/run/faillock/ -p wa -k logins
-w /var/log/lastlog -p wa -k logins
Rationale:Manual editing of these files may indicate nefarious activity, such
as an attacker attempting to remove evidence of an intrusion. Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Record Attempts to Alter Process and Session Initiation Information
[ref]rule The audit system already collects process information for all
users and root. To watch for attempted manual edits of files involved in
storing such process information, add the following to
/etc/audit/audit.rules :
-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. Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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 "auditctl" "/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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Ensure auditd Collects Information on Exporting to Media (successful)
[ref]ruleAt a minimum the audit system should collect media
exportation events for all users and root. Add the following to
/etc/audit/audit.rules , setting ARCH to either b32 or b64 as
appropriate for your system:
-a always,exit -F arch=ARCH -S mount -F auid>=500 -F auid!=4294967295 -k 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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.2.7, SV-50369r3_rule, SRG-OS-000064 Remediation Shell script: (show)
# 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 .* -F auid>=500 -F auid!=4294967295 -k *"
GROUP="mount"
FULL_RULE="-a always,exit -F arch=$ARCH -S mount -F auid>=500 -F auid!=4294967295 -k export"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
|
Ensure auditd Collects System Administrator Actions
[ref]ruleAt a minimum the audit system should collect
administrator actions for all users and root. Add the following to
/etc/audit/audit.rules :
-w /etc/sudoers -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. References:
AC-2(7)(b), AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.2.2, Req-10.2.5.b, SV-50379r2_rule, SRG-OS-000064 Remediation Shell script: (show)
# Perform the remediation
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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"
|
Ensure auditd Collects Information on Kernel Module Loading and Unloading
[ref]ruleAdd the following to /etc/audit/audit.rules in order
to capture kernel module loading and unloading events, setting ARCH to either b32 or b64 as appropriate for your system:
-w /sbin/insmod -p x -k modules
-w /sbin/rmmod -p x -k modules
-w /sbin/modprobe -p x -k modules
-a always,exit -F arch=ARCH -S init_module -S delete_module -k modules
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. References:
AC-3(10), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-12(a), AU-12(c), IR-5, CCI-000126, Req-10.2.7, SV-50381r2_rule, SRG-OS-000064 Remediation Shell script: (show)
# First perform the remediation of the syscall rule
# Retrieve hardware architecture of the underlying system
# Note: 32-bit kernel modules can't be loaded / unloaded on 64-bit kernel =>
# it's not required on a 64-bit system to check also for the presence
# of 32-bit's equivalent of the corresponding rule. Therefore for
# each system it's enought to check presence of system's native rule form.
[ $(getconf LONG_BIT) = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("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="\(init\|delete\)_module"
FULL_RULE="-a always,exit -F arch=$ARCH -S init_module -S delete_module -k modules"
# 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=("${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
key=$(expr "$full_rule" : '.*-k[[:space:]]\([^[:space:]]\+\)')
# Check if particular audit rule is already defined
IFS=$'\n' 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
# Reset IFS back to default
unset IFS
for match in "${matches[@]}"
do
files_to_inspect=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_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
IFS=$'\n' existing_rules=($(sed -e "\;${pattern};!d" -e "/${arch}/!d" -e "/${group}/!d" "$audit_file"))
if [ $? -ne 0 ]
then
retval=1
fi
# Reset IFS back to default
unset IFS
# 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=$'-S' read -a rule_syscalls_as_array <<< "$rule_syscalls"
# Reset IFS back to default
unset IFS
# 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"
done
# Then perform the remediations for the watch rules
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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" "/sbin/insmod" "x" "modules"
fix_audit_watch_rule "auditctl" "/sbin/rmmod" "x" "modules"
# 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
# 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=("${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
# Case when particular audit rule is already defined in some of /etc/audit/rules.d/*.rules file
# Get pair -- filepath : matching_row into @matches array
IFS=$'\n' matches=($(grep -P "[\s]*-w[\s]+$path" /etc/audit/rules.d/*.rules))
# Reset IFS back to default
unset IFS
# 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=("${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
files_to_inspect="/etc/audit/rules.d/$key.rules"
# If the $key.rules file doesn't exist yet, create it with correct permissions
if [ ! -e "$files_to_inspect" ]
then
touch "$files_to_inspect"
chmod 0640 "$files_to_inspect"
fi
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" "/sbin/modprobe" "x" "modules"
|
Make the auditd Configuration Immutable
[ref]ruleAdd the following to /etc/audit/audit.rules in order
to make the 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 Remediation Shell script: (show)
readonly AUDIT_RULES='/etc/audit/audit.rules'
# If '-e .*' setting present in audit.rules already, delete it since the
# auditctl(8) manual page instructs it should be the last rule in configuration
sed -i '/-e[[:space:]]\+.*/d' $AUDIT_RULES
# Append '-e 2' requirement at the end of audit.rules
echo '' >> $AUDIT_RULES
echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_RULES
echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_RULES
echo '-e 2' >> $AUDIT_RULES
|
Enable auditd Service
[ref]ruleThe 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 chkconfig --level 2345 auditd on
Rationale:Ensuring the auditd service is active ensures
audit records generated by the kernel can be written to disk, or that appropriate
actions will be taken if other obstacles exist.
References:
AC-17(1), AU-1(b), AU-10, AU-12(a), AU-12(c), IR-5, CCI-000347, CCI-000157, CCI-000172, CCI-000880, CCI-001353, CCI-001462, CCI-001487, CCI-001115, CCI-001454, CCI-000067, CCI-000158, CCI-000831, CCI-001190, CCI-001312, CCI-001263, CCI-000130, CCI-000120, CCI-001589, Req-10, SV-50429r2_rule, SRG-OS-000255, SRG-OS-000032, SRG-OS-000037 Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable auditd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service auditd
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- auditd
tags:
- service_auditd_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27058-7
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-10
- NIST-800-53-AU-12(a)
- NIST-800-53-AU-12(c)
- NIST-800-53-IR-5
- PCI-DSS-Req-10
- DISA-STIG-RHEL-06-000145
|
Enable Auditing for Processes Which Start Prior to the Audit Daemon
[ref]ruleTo ensure all processes can be audited, even
those which start prior to the audit daemon, add the argument
audit=1 to the kernel line in /etc/grub.conf , in the manner below:
kernel /vmlinuz-version ro vga=ext root=/dev/VolGroup00/LogVol00 rhgb quiet audit=1
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.
References:
AC-17(1), AU-14(1), AU-1(b), AU-2(a), AU-2(c), AU-2(d), AU-10, IR-5, CCI-000169, Req-10.3, SV-50238r4_rule, SRG-OS-000062 Remediation Shell script: (show)
/sbin/grubby --update-kernel=ALL --args="audit=1"
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Reboot: | true |
---|
Strategy: | restrict |
---|
- name: "Enable Auditing for Processes Which Start Prior to the Audit Daemon"
shell: /sbin/grubby --update-kernel=ALL --args="audit=1"
tags:
- bootloader_audit_argument
- low_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26785-6
- NIST-800-53-AC-17(1)
- NIST-800-53-AU-14(1)
- NIST-800-53-AU-1(b)
- NIST-800-53-AU-2(a)
- NIST-800-53-AU-2(c)
- NIST-800-53-AU-2(d)
- NIST-800-53-AU-10
- NIST-800-53-IR-5
- PCI-DSS-Req-10.3
- DISA-STIG-RHEL-06-000525
|
Services
[ref]group
The best protection against vulnerable software is running less software. This section describes how to review
the software which CentOS Linux 6 installs on a system and disable software which is not needed. It
then enumerates the software packages installed on a default CentOS Linux 6 system and provides guidance about which
ones can be safely disabled.
CentOS Linux 6 provides a convenient minimal install option that essentially installs the bare necessities for a functional
system. When building CentOS Linux 6 systems, it is highly recommended to select the minimal packages and then build up
the system from there.
|
contains 62 rules |
Obsolete Services
[ref]groupThis section discusses a number of network-visible
services which have historically caused problems for system
security, and for which disabling or severely limiting the service
has been the best available guidance for some time. As a result of
this, many of these services are not installed as part of CentOS Linux 6
by default.
Organizations which are running these services should
switch to more secure equivalents as soon as possible.
If it remains absolutely necessary to run one of
these services for legacy reasons, care should be taken to restrict
the service as much as possible, for instance by configuring host
firewall software such as iptables to restrict access to the
vulnerable service to only those remote hosts which have a known
need to use it. |
contains 9 rules |
Rlogin, Rsh, and Rexec
[ref]groupThe Berkeley r-commands are legacy services which
allow cleartext remote access and have an insecure trust
model. |
contains 4 rules |
Uninstall rsh-server Package
[ref]ruleThe rsh-server package can be uninstalled with
the following command:
$ sudo yum erase rsh-server
Rationale:The rsh-server package provides several obsolete and insecure
network services. Removing it
decreases the risk of those services' accidental (or intentional)
activation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove rsh-server
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure rsh-server is removed
package:
name="{{item}}"
state=absent
with_items:
- rsh-server
tags:
- package_rsh-server_removed
- high_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27062-9
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000213
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_rsh-server
class remove_rsh-server {
package { 'rsh-server':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=rsh-server
|
Disable rexec Service
[ref]ruleThe rexec service, which is available with
the rsh-server package and runs as a service through xinetd,
should be disabled.
The rexec service can be disabled with the following command:
$ sudo chkconfig rexec off
Rationale:The rexec service uses unencrypted network communications, which
means that data from the login session, including passwords and
all other information transmitted during the session, can be
stolen by eavesdroppers on the network.
|
Disable rsh Service
[ref]ruleThe rsh service, which is available with
the rsh-server package and runs as a service through xinetd,
should be disabled.
The rsh service can be disabled with the following command:
$ sudo chkconfig rsh off
Rationale:The rsh service uses unencrypted network communications, which
means that data from the login session, including passwords and
all other information transmitted during the session, can be
stolen by eavesdroppers on the network.
|
Disable rlogin Service
[ref]ruleThe rlogin service, which is available with
the rsh-server package and runs as a service through xinetd,
should be disabled.
The rlogin service can be disabled with the following command:
$ sudo chkconfig rlogin off
Rationale:The rlogin service uses unencrypted network communications, which
means that data from the login session, including passwords and
all other information transmitted during the session, can be
stolen by eavesdroppers on the network.
|
The Network Information Service (NIS), also known as 'Yellow
Pages' (YP), and its successor NIS+ have been made obsolete by
Kerberos, LDAP, and other modern centralized authentication
services. NIS should not be used because it suffers from security
problems inherent in its design, such as inadequate protection of
important authentication information. |
contains 2 rules |
Uninstall ypserv Package
[ref]ruleThe ypserv package can be uninstalled with
the following command:
$ sudo yum erase ypserv
Rationale:Removing the ypserv package decreases the risk of the
accidental (or intentional) activation of NIS or NIS+ services.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove ypserv
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure ypserv is removed
package:
name="{{item}}"
state=absent
with_items:
- ypserv
tags:
- package_ypserv_removed
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27079-3
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000220
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_ypserv
class remove_ypserv {
package { 'ypserv':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=ypserv
|
Disable ypbind Service
[ref]ruleThe ypbind service, which allows the system to act as a client in
a NIS or NIS+ domain, should be disabled.
The ypbind service can be disabled with the following command:
$ sudo chkconfig ypbind off
Rationale:
Disabling the ypbind service ensures the system is not acting
as a client in a NIS or NIS+ domain.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable ypbind
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service ypbind
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- ypbind
tags:
- service_ypbind_disabled
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26894-6
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000221
|
TFTP Server
[ref]group
TFTP is a lightweight version of the FTP protocol which has
traditionally been used to configure networking equipment. However,
TFTP provides little security, and modern versions of networking
operating systems frequently support configuration via SSH or other
more secure protocols. A TFTP server should be run only if no more
secure method of supporting existing equipment can be
found. |
contains 3 rules |
Disable tftp Service
[ref]ruleThe tftp service should be disabled.
The tftp service can be disabled with the following command:
$ sudo chkconfig tftp off
Rationale:
Disabling the tftp service ensures the system is not acting
as a TFTP server, which does not provide encryption or authentication.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable tftp
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service tftp
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- tftp
tags:
- service_tftp_disabled
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27055-3
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000223
|
Uninstall tftp-server Package
[ref]rule
The tftp-server package can be removed with the following command:
$ sudo yum erase tftp-server
Rationale:
Removing the tftp-server package decreases the risk of the
accidental (or intentional) activation of tftp services.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove tftp-server
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure tftp-server is removed
package:
name="{{item}}"
state=absent
with_items:
- tftp-server
tags:
- package_tftp-server_removed
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26946-4
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000222
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_tftp-server
class remove_tftp-server {
package { 'tftp-server':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=tftp-server
|
Ensure tftp Daemon Uses Secure Mode
[ref]ruleIf running the tftp service is necessary, it should be configured
to change its root directory at startup. To do so, ensure
/etc/xinetd.d/tftp includes -s as a command line argument, as shown in
the following example (which is also the default):
server_args = -s /var/lib/tftpboot
Rationale:Using the -s option causes the TFTP service to only serve files from the
given directory. Serving files from an intentionally-specified directory
reduces the risk of sharing files which should remain private.
|
Base Services
[ref]groupThis section addresses the base services that are installed on a
CentOS Linux 6 default installation which are not covered in other
sections. Some of these services listen on the network and
should be treated with particular discretion. Other services are local
system utilities that may or may not be extraneous. In general, system services
should be disabled if not required. |
contains 21 rules |
Disable Automatic Bug Reporting Tool (abrtd)
[ref]ruleThe Automatic Bug Reporting Tool (abrtd ) daemon collects
and reports crash data when an application crash is detected. Using a variety
of plugins, abrtd can email crash reports to system administrators, log crash
reports to files, or forward crash reports to a centralized issue tracking
system such as RHTSupport.
The abrtd service can be disabled with the following command:
$ sudo chkconfig abrtd off
Rationale: Mishandling crash data could expose sensitive information about
vulnerabilities in software executing on the local system, as well as sensitive
information from within a process's address space or registers. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable abrtd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service abrtd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- abrtd
tags:
- service_abrtd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27247-6
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000261
|
Disable Advanced Configuration and Power Interface (acpid)
[ref]ruleThe Advanced Configuration and Power Interface Daemon (acpid )
dispatches ACPI events (such as power/reset button depressed) to userspace
programs.
The acpid service can be disabled with the following command:
$ sudo chkconfig acpid off
Rationale:ACPI support is highly desirable for systems in some network roles,
such as laptops or desktops. For other systems, such as servers, it may permit
accidental or trivially achievable denial of service situations and disabling
it is appropriate. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable acpid
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service acpid
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- acpid
tags:
- service_acpid_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27061-1
- NIST-800-53-CM-7
|
Disable Certmonger Service (certmonger)
[ref]ruleCertmonger is a D-Bus based service that attempts to simplify interaction
with certifying authorities on networks which use public-key infrastructure. It is often
combined with Red Hat's IPA (Identity Policy Audit) security information management
solution to aid in the management of certificates.
The certmonger service can be disabled with the following command:
$ sudo chkconfig certmonger off
Rationale:The services provided by certmonger may be essential for systems
fulfilling some roles a PKI infrastructure, but its functionality is not necessary
for many other use cases. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable certmonger
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service certmonger
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- certmonger
tags:
- service_certmonger_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27267-4
- NIST-800-53-CM-7
|
Disable Control Group Config (cgconfig)
[ref]ruleControl groups allow an administrator to allocate system resources (such as CPU,
memory, network bandwidth, etc) among a defined group (or groups) of processes executing on
a system. The cgconfig daemon starts at boot and establishes the predefined control groups.
The cgconfig service can be disabled with the following command:
$ sudo chkconfig cgconfig off
Rationale:Unless control groups are used to manage system resources, running the cgconfig
service is not necessary.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable cgconfig
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service cgconfig
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- cgconfig
tags:
- service_cgconfig_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27250-0
- NIST-800-53-CM-7
|
Disable CPU Speed (cpuspeed)
[ref]ruleThe cpuspeed service can adjust the clock speed of supported CPUs based upon
the current processing load thereby conserving power and reducing heat.
The cpuspeed service can be disabled with the following command:
$ sudo chkconfig cpuspeed off
Rationale:The cpuspeed service is only necessary if adjusting the CPU clock speed
provides benefit. Traditionally this has included laptops (to enhance battery life),
but may also apply to server or desktop environments where conserving power is
highly desirable or necessary.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable cpuspeed
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service cpuspeed
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- cpuspeed
tags:
- service_cpuspeed_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26973-8
- NIST-800-53-CM-7
|
Disable Hardware Abstraction Layer Service (haldaemon)
[ref]ruleThe Hardware Abstraction Layer Daemon (haldaemon ) collects
and maintains information about the system's hardware configuration.
This service is required on a workstation
running a desktop environment, and may be necessary on any system which
deals with removable media or devices.
The haldaemon service can be disabled with the following command:
$ sudo chkconfig haldaemon off
Rationale:The haldaemon provides essential functionality on systems
that use removable media or devices, but can be disabled for systems
that do not require these.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable haldaemon
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service haldaemon
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- haldaemon
tags:
- service_haldaemon_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27086-8
- NIST-800-53-CM-7
|
Disable Software RAID Monitor (mdmonitor)
[ref]ruleThe mdmonitor service is used for monitoring a software RAID array; hardware
RAID setups do not use this service.
The mdmonitor service can be disabled with the following command:
$ sudo chkconfig mdmonitor off
Rationale:If software RAID monitoring is not required,
there is no need to run this service. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable mdmonitor
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service mdmonitor
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- mdmonitor
tags:
- service_mdmonitor_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27193-2
- NIST-800-53-CM-7
|
Disable D-Bus IPC Service (messagebus)
[ref]ruleD-Bus provides an IPC mechanism used by
a growing list of programs, such as those used for Gnome, Bluetooth, and Avahi.
Due to these dependencies, disabling D-Bus may not be practical for
many systems.
The messagebus service can be disabled with the following command:
$ sudo chkconfig messagebus off
Rationale:If no services which require D-Bus are needed, then it
can be disabled. As a broker for IPC between processes of different privilege levels,
it could be a target for attack. However, disabling D-Bus is likely to be
impractical for any system which needs to provide
a graphical login session.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable messagebus
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service messagebus
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- messagebus
tags:
- service_messagebus_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26913-4
- NIST-800-53-CM-7
|
Disable Network Console (netconsole)
[ref]ruleThe netconsole service is responsible for loading the
netconsole kernel module, which logs kernel printk messages over UDP to a
syslog server. This allows debugging of problems where disk logging fails and
serial consoles are impractical.
The netconsole service can be disabled with the following command:
$ sudo chkconfig netconsole off
Rationale:The netconsole service is not necessary unless there is a need to debug
kernel panics, which is not common.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable netconsole
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service netconsole
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- netconsole
tags:
- service_netconsole_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27254-2
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000289
|
Disable ntpdate Service (ntpdate)
[ref]ruleThe ntpdate service sets the local hardware clock by polling NTP servers
when the system boots. It synchronizes to the NTP servers listed in
/etc/ntp/step-tickers or /etc/ntp.conf
and then sets the local hardware clock to the newly synchronized
system time.
The ntpdate service can be disabled with the following command:
$ sudo chkconfig ntpdate off
Rationale:The ntpdate service may only be suitable for systems which
are rebooted frequently enough that clock drift does not cause problems between
reboots. In any event, the functionality of the ntpdate service is now
available in the ntpd program and should be considered deprecated. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable ntpdate
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service ntpdate
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- ntpdate
tags:
- service_ntpdate_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27256-7
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000265
|
Disable Odd Job Daemon (oddjobd)
[ref]ruleThe oddjobd service exists to provide an interface and
access control mechanism through which
specified privileged tasks can run tasks for unprivileged client
applications. Communication with oddjobd through the system message bus.
The oddjobd service can be disabled with the following command:
$ sudo chkconfig oddjobd off
Rationale:The oddjobd service may provide necessary functionality in
some environments, and can be disabled if it is not needed. Execution of
tasks by privileged programs, on behalf of unprivileged ones, has traditionally
been a source of privilege escalation security issues. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable oddjobd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service oddjobd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- oddjobd
tags:
- service_oddjobd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27257-5
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000266
|
Disable Portreserve (portreserve)
[ref]ruleThe portreserve service is a TCP port reservation utility that can
be used to prevent portmap from binding to well known TCP ports that are
required for other services.
The portreserve service can be disabled with the following command:
$ sudo chkconfig portreserve off
Rationale:The portreserve service provides helpful functionality by
preventing conflicting usage of ports in the reserved port range, but it can be
disabled if not needed. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable portreserve
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service portreserve
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- portreserve
tags:
- service_portreserve_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27258-3
- NIST-800-53-CM-7
|
Enable Process Accounting (psacct)
[ref]ruleThe process accounting service, psacct , works with programs
including acct and ac to allow system administrators to view
user activity, such as commands issued by users of the system.
The psacct service can be enabled with the following command:
$ sudo chkconfig --level 2345 psacct on
Rationale:The psacct service can provide administrators a convenient
view into some user activities. However, it should be noted that the auditing
system and its audit records provide more authoritative and comprehensive
records. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable psacct
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service psacct
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- psacct
tags:
- service_psacct_enabled
- low_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27259-1
- NIST-800-53-AU-12
- NIST-800-53-CM-7
|
Disable Apache Qpid (qpidd)
[ref]ruleThe qpidd service provides high speed, secure,
guaranteed delivery services. It is an implementation of the Advanced Message
Queuing Protocol. By default the qpidd service will bind to port 5672 and
listen for connection attempts.
The qpidd service can be disabled with the following command:
$ sudo chkconfig qpidd off
Rationale:The qpidd service is automatically installed when the "base"
package selection is selected during installation. The qpidd service listens
for network connections, which increases the attack surface of the system. If
the system is not intended to receive AMQP traffic, then the qpidd
service is not needed and should be disabled or removed. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable qpidd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service qpidd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- qpidd
tags:
- service_qpidd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26928-2
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000267
|
Disable Quota Netlink (quota_nld)
[ref]ruleThe quota_nld service provides notifications to
users of disk space quota violations. It listens to the kernel via a netlink
socket for disk quota violations and notifies the appropriate user of the
violation using D-Bus or by sending a message to the terminal that the user has
last accessed.
The quota_nld service can be disabled with the following command:
$ sudo chkconfig quota_nld off
Rationale:If disk quotas are enforced on the local system, then the
quota_nld service likely provides useful functionality and should
remain enabled. However, if disk quotas are not used or user notification of
disk quota violation is not desired then there is no need to run this
service. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable quota_nld
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service quota_nld
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- quota_nld
tags:
- service_quota_nld_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27260-9
- NIST-800-53-CM-7
|
Disable Network Router Discovery Daemon (rdisc)
[ref]ruleThe rdisc service implements the client side of the ICMP
Internet Router Discovery Protocol (IRDP), which allows discovery of routers on
the local subnet. If a router is discovered then the local routing table is
updated with a corresponding default route. By default this daemon is disabled.
The rdisc service can be disabled with the following command:
$ sudo chkconfig rdisc off
Rationale:General-purpose systems typically have their network and routing
information configured statically by a system administrator. Workstations or
some special-purpose systems often use DHCP (instead of IRDP) to retrieve
dynamic network configuration information. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable rdisc
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service rdisc
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- rdisc
tags:
- service_rdisc_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27261-7
- NIST-800-53-AC-4
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000268
|
Disable Red Hat Network Service (rhnsd)
[ref]ruleThe Red Hat Network service automatically queries Red Hat Network
servers to determine whether there are any actions that should be executed,
such as package updates. This only occurs if the system was registered to an
RHN server or satellite and managed as such.
The rhnsd service can be disabled with the following command:
$ sudo chkconfig rhnsd off
Rationale:Although systems management and patching is extremely important to
system security, management by a system outside the enterprise enclave is not
desirable for some environments. However, if the system is being managed by RHN or
RHN Satellite Server the rhnsd daemon can remain on. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable rhnsd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service rhnsd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- rhnsd
tags:
- service_rhnsd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26846-6
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000009
|
Disable Red Hat Subscription Manager Daemon (rhsmcertd)
[ref]ruleThe Red Hat Subscription Manager (rhsmcertd) periodically checks for
changes in the entitlement certificates for a registered system and updates it
accordingly.
The rhsmcertd service can be disabled with the following command:
$ sudo chkconfig rhsmcertd off
Rationale:The rhsmcertd service can provide administrators with some
additional control over which of their systems are entitled to particular
subscriptions. However, for systems that are managed locally or which are not
expected to require remote changes to their subscription status, it is
unnecessary and can be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable rhsmcertd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service rhsmcertd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- rhsmcertd
tags:
- service_rhsmcertd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27262-5
- NIST-800-53-CM-7
|
Disable Cyrus SASL Authentication Daemon (saslauthd)
[ref]ruleThe saslauthd service handles plaintext authentication requests on
behalf of the SASL library. The service isolates all code requiring superuser
privileges for SASL authentication into a single process, and can also be used
to provide proxy authentication services to clients that do not understand SASL
based authentication.
The saslauthd service can be disabled with the following command:
$ sudo chkconfig saslauthd off
Rationale:The saslauthd service provides essential functionality for
performing authentication in some directory environments, such as those which
use Kerberos and LDAP. For others, however, in which only local files may be
consulted, it is not necessary and should be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable saslauthd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service saslauthd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- saslauthd
tags:
- service_saslauthd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27263-3
- NIST-800-53-CM-7
|
Disable SMART Disk Monitoring Service (smartd)
[ref]ruleSMART (Self-Monitoring, Analysis, and Reporting Technology) is a
feature of hard drives that allows them to detect symptoms of disk failure and
relay an appropriate warning.
The smartd service can be disabled with the following command:
$ sudo chkconfig smartd off
Rationale:SMART can help protect against denial of
service due to failing hardware. Nevertheless, if it is not needed or the
system's drives are not SMART-capable (such as solid state drives), it can be
disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable smartd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service smartd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- smartd
tags:
- service_smartd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26853-2
- NIST-800-53-CM-7
|
Disable System Statistics Reset Service (sysstat)
[ref]ruleThe sysstat service resets various I/O and CPU
performance statistics to zero in order to begin counting from a fresh state
at boot time.
The sysstat service can be disabled with the following command:
$ sudo chkconfig sysstat off
Rationale:By default the sysstat service merely runs a program at
boot to reset the statistics, which can be retrieved using programs such as
sar and sadc . These may provide useful insight into system
operation, but unless used this service can be disabled. Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable sysstat
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service sysstat
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- sysstat
tags:
- service_sysstat_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27265-8
- NIST-800-53-CM-7
|
Cron and At Daemons
[ref]groupThe cron and at services are used to allow commands to
be executed at a later time. The cron service is required by almost
all systems to perform necessary maintenance tasks, while at may or
may not be required on a given system. Both daemons should be
configured defensively. |
contains 3 rules |
Enable cron Service
[ref]ruleThe crond service is used to execute commands at
preconfigured times. It is required by almost all systems to perform necessary
maintenance tasks, such as notifying root of system activity.
The crond service can be enabled with the following command:
$ sudo chkconfig --level 2345 crond on
Rationale:Due to its usage for maintenance and security-supporting tasks,
enabling the cron daemon is essential.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable crond
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service crond
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- crond
tags:
- service_crond_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27070-2
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000224
|
Disable anacron Service
[ref]ruleThe cronie-anacron package, which provides anacron
functionality, is installed by default.
The cronie-anacron package can be removed with the following command:
$ sudo yum erase cronie-anacron
Rationale:
The anacron service provides cron functionality for systems
such as laptops and workstations that may be shut down during the normal times
that cron jobs are scheduled to run. On systems which do not require this
additional functionality, anacron could needlessly increase the possible
attack surface for an intruder.
|
Disable At Service (atd)
[ref]ruleThe at and batch commands can be used to
schedule tasks that are meant to be executed only once. This allows delayed
execution in a manner similar to cron, except that it is not
recurring. The daemon atd keeps track of tasks scheduled via
at and batch , and executes them at the specified time.
The atd service can be disabled with the following command:
$ sudo chkconfig atd off
Rationale:
The atd service could be used by an unsophisticated insider to carry
out activities outside of a normal login session, which could complicate
accountability. Furthermore, the need to schedule tasks with at or
batch is not common.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable atd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service atd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- atd
tags:
- service_atd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27249-2
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000262
|
SSH Server
[ref]groupThe 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,
http://www.openssh.org. Its server program is called sshd and
provided by the RPM package openssh-server . |
contains 3 rules |
Configure OpenSSH Server if Necessary
[ref]groupIf 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 3 rules |
Allow Only SSH Protocol 2
[ref]ruleOnly 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
Rationale:
SSH protocol version 1 suffers from design flaws that
result in security vulnerabilities and
should not be used.
Remediation Shell script: (show)
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/ssh/sshd_config' '^Protocol' '2' 'CCE-27072-8' '%s %s'
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Allow Only SSH Protocol 2"
lineinfile:
dest: /etc/ssh/sshd_config
regexp: "^Protocol [0-9]"
line: "Protocol 2"
validate: sshd -t -f %s
#notify: :reload ssh
tags:
- sshd_allow_only_protocol2
- high_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27072-8
- NIST-800-53-AC-3(10)
- NIST-800-53-IA-5(1)(c)
- DISA-STIG-RHEL-06-000227
|
Disable SSH Root Login
[ref]ruleThe 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:
Permitting direct root login reduces auditable information about who ran
privileged commands on the system
and also allows direct attack attempts on root's password.
Remediation Shell script: (show)
SSHD_CONFIG='/etc/ssh/sshd_config'
# Obtain line number of first uncommented case-insensitive occurrence of Match
# block directive (possibly prefixed with whitespace) present in $SSHD_CONFIG
FIRST_MATCH_BLOCK=$(sed -n '/^[[:space:]]*Match[^\n]*/I{=;q}' $SSHD_CONFIG)
# Obtain line number of first uncommented case-insensitive occurence of
# PermitRootLogin directive (possibly prefixed with whitespace) present in
# $SSHD_CONFIG
FIRST_PERMIT_ROOT_LOGIN=$(sed -n '/^[[:space:]]*PermitRootLogin[^\n]*/I{=;q}' $SSHD_CONFIG)
# Case: Match block directive not present in $SSHD_CONFIG
if [ -z "$FIRST_MATCH_BLOCK" ]
then
# Case: PermitRootLogin directive not present in $SSHD_CONFIG yet
if [ -z "$FIRST_PERMIT_ROOT_LOGIN" ]
then
# Append 'PermitRootLogin no' at the end of $SSHD_CONFIG
echo -e "\nPermitRootLogin no" >> $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG already
else
# Replace first uncommented case-insensitive occurrence
# of PermitRootLogin directive
sed -i "$FIRST_PERMIT_ROOT_LOGIN s/^[[:space:]]*PermitRootLogin.*$/PermitRootLogin no/I" $SSHD_CONFIG
fi
# Case: Match block directive present in $SSHD_CONFIG
else
# Case: PermitRootLogin directive not present in $SSHD_CONFIG yet
if [ -z "$FIRST_PERMIT_ROOT_LOGIN" ]
then
# Prepend 'PermitRootLogin no' before first uncommented
# case-insensitive occurrence of Match block directive
sed -i "$FIRST_MATCH_BLOCK s/^\([[:space:]]*Match[^\n]*\)/PermitRootLogin no\n\1/I" $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG and placed
# before first Match block directive
elif [ "$FIRST_PERMIT_ROOT_LOGIN" -lt "$FIRST_MATCH_BLOCK" ]
then
# Replace first uncommented case-insensitive occurrence
# of PermitRootLogin directive
sed -i "$FIRST_PERMIT_ROOT_LOGIN s/^[[:space:]]*PermitRootLogin.*$/PermitRootLogin no/I" $SSHD_CONFIG
# Case: PermitRootLogin directive present in $SSHD_CONFIG and placed
# after first Match block directive
else
# Prepend 'PermitRootLogin no' before first uncommented
# case-insensitive occurrence of Match block directive
sed -i "$FIRST_MATCH_BLOCK s/^\([[:space:]]*Match[^\n]*\)/PermitRootLogin no\n\1/I" $SSHD_CONFIG
fi
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: "Disable SSH Root Login"
lineinfile:
create: yes
dest: "/etc/ssh/sshd_config"
regexp: "^PermitRootLogin"
line: "PermitRootLogin no"
insertafter: '(?i)^#?authentication'
validate: sshd -t -f %s
#notify: restart sshd
tags:
- sshd_disable_root_login
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-27100-7
- NIST-800-53-AC-3
- NIST-800-53-AC-6(2)
- NIST-800-53-IA-2(1)
- DISA-STIG-RHEL-06-000237
|
Use Only Approved Ciphers
[ref]ruleLimit 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.
Rationale:
Approved algorithms should impart some level of confidence in their
implementation. These are also required for compliance.
References:
AC-3, AC-17(2), SI-7, IA-5(1)(c), IA-7, CCI-000803, CCI-001144, CCI-001145, CCI-001146, SV-50418r1_rule, SRG-OS-000169 Remediation Shell script: (show)
grep -q ^Ciphers /etc/ssh/sshd_config && \
sed -i "s/Ciphers.*/Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc/g" /etc/ssh/sshd_config
if ! [ $? -eq 0 ]; then
echo "Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc" >> /etc/ssh/sshd_config
fi
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | restrict |
---|
- name: Use Only Approved Ciphers
lineinfile:
create: yes
dest: /etc/ssh/sshd_config
regexp: ^Ciphers
line: Ciphers aes128-ctr,aes192-ctr,aes256-ctr,aes128-cbc,3des-cbc,aes192-cbc,aes256-cbc
validate: sshd -t -f %s
#notify: restart sshd
tags:
- sshd_use_approved_ciphers
- medium_severity
- restrict_strategy
- low_complexity
- low_disruption
- CCE-26555-3
- NIST-800-53-AC-3
- NIST-800-53-AC-17(2)
- NIST-800-53-SI-7
- NIST-800-53-IA-5(1)(c)
- NIST-800-53-IA-7
- DISA-STIG-RHEL-06-000243
|
X Window System
[ref]groupThe X Window System implementation included with the
system is called X.org. |
contains 1 rule |
Disable X Windows
[ref]groupUnless there is a mission-critical reason for the
system to run a graphical user interface, ensure X is not set to start
automatically at boot and remove the X Windows software packages.
There is usually no reason to run X Windows
on a dedicated server system, as it increases the system's attack surface and consumes
system resources. Administrators of server systems should instead login via
SSH or on the text console. |
contains 1 rule |
Disable X Windows Startup By Setting Runlevel
[ref]ruleSetting the system's runlevel to 3 will prevent automatic startup
of the X server. To do so, ensure the following line in /etc/inittab
features a 3 as shown:
id:3:initdefault:
Rationale:Unnecessary services should be disabled to decrease the attack surface of the system. |
Avahi Server
[ref]groupThe Avahi daemon implements the DNS Service Discovery
and Multicast DNS protocols, which provide service and host
discovery on a network. It allows a system to automatically
identify resources on the network, such as printers or web servers.
This capability is also known as mDNSresponder and is a major part
of Zeroconf networking. |
contains 1 rule |
Disable Avahi Server if Possible
[ref]groupBecause the Avahi daemon service keeps an open network
port, it is subject to network attacks.
Disabling it can reduce the system's vulnerability to such attacks.
|
contains 1 rule |
Disable Avahi Server Software
[ref]rule
The avahi-daemon service can be disabled with the following command:
$ sudo chkconfig avahi-daemon off
Rationale:
Because the Avahi daemon service keeps an open network
port, it is subject to network attacks. Its functionality
is convenient but is only appropriate if the local network
can be trusted.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable avahi-daemon
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service avahi-daemon
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- avahi-daemon
tags:
- service_avahi-daemon_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27087-6
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000246
|
Print Support
[ref]groupThe Common Unix Printing System (CUPS) service provides both local
and network printing support. A system running the CUPS service can accept
print jobs from other systems, process them, and send them to the appropriate
printer. It also provides an interface for remote administration through a web
browser. The CUPS service is installed and activated by default. The project
homepage and more detailed documentation are available at http://www.cups.org.
|
contains 2 rules |
Configure the CUPS Service if Necessary
[ref]groupCUPS provides the ability to easily share local printers with
other systems over the network. It does this by allowing systems to share
lists of available printers. Additionally, each system that runs the CUPS
service can potentially act as a print server. Whenever possible, the printer
sharing and print server capabilities of CUPS should be limited or disabled.
The following recommendations should demonstrate how to do just that.
|
contains 1 rule |
Disable Print Server Capabilities
[ref]ruleTo prevent remote users from potentially connecting to and using
locally configured printers, disable the CUPS print server sharing
capabilities. To do so, limit how the server will listen for print jobs by
removing the more generic port directive from /etc/cups/cupsd.conf:
Port 631
and replacing it with the Listen directive:
Listen localhost:631
This will prevent remote users from printing to locally configured printers
while still allowing local users on the system to print normally.
Rationale:By default, locally configured printers will not be shared over the
network, but if this functionality has somehow been enabled, these
recommendations will disable it again. Be sure to disable outgoing printer list
broadcasts, or remote users will still be able to see the locally configured
printers, even if they cannot actually print to them. To limit print serving to
a particular set of users, use the Policy directive.
|
Disable the CUPS Service
[ref]rule
The cups service can be disabled with the following command:
$ sudo chkconfig cups off
Rationale:Turn off unneeded services to reduce attack surface.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable cups
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service cups
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- cups
tags:
- service_cups_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26899-5
- NIST-800-53-CM-7
|
The Dynamic Host Configuration Protocol (DHCP) allows
systems to request and obtain an IP address and other configuration
parameters from a server.
This guide recommends configuring networking on clients by manually editing
the appropriate files under /etc/sysconfig . Use of DHCP can make client
systems vulnerable to compromise by rogue DHCP servers, and should be avoided
unless necessary. If using DHCP is necessary, however, there are best practices
that should be followed to minimize security risk.
|
contains 6 rules |
Disable DHCP Server
[ref]group
The DHCP server dhcpd is not installed or activated by
default. If the software was installed and activated, but the
system does not need to act as a DHCP server, it should be disabled
and removed.
|
contains 2 rules |
Disable DHCP Service
[ref]ruleThe dhcpd service should be disabled on
any system that does not need to act as a DHCP server.
The dhcpd service can be disabled with the following command:
$ sudo chkconfig dhcpd off
Rationale:
Unmanaged or unintentionally activated DHCP servers may provide faulty information
to clients, interfering with the operation of a legitimate site
DHCP server if there is one.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable dhcpd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service dhcpd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- dhcpd
tags:
- service_dhcpd_disabled
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27074-4
- NIST-800-53-CM-7
|
Uninstall DHCP Server Package
[ref]ruleIf the system does not need to act as a DHCP server,
the dhcp package can be uninstalled.
The dhcp package can be removed with the following command:
$ sudo yum erase dhcp
Rationale:
Removing the DHCP server ensures that it cannot be easily or
accidentally reactivated and disrupt network operation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove dhcp
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure dhcp is removed
package:
name="{{item}}"
state=absent
with_items:
- dhcp
tags:
- package_dhcp_removed
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27120-5
- NIST-800-53-CM-7
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_dhcp
class remove_dhcp {
package { 'dhcp':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=dhcp
|
Disable DHCP Server
[ref]groupIf the system must act as a DHCP server, the configuration
information it serves should be minimized. Also, support for other protocols
and DNS-updating schemes should be explicitly disabled unless needed. The
configuration file for dhcpd is called /etc/dhcp/dhcpd.conf . The file
begins with a number of global configuration options. The remainder of the file
is divided into sections, one for each block of addresses offered by dhcpd,
each of which contains configuration options specific to that address
block. |
contains 3 rules |
Do Not Use Dynamic DNS
[ref]ruleTo prevent the DHCP server from receiving DNS information from
clients, edit /etc/dhcp/dhcpd.conf , and add or correct the following global
option: ddns-update-style none;
Warning:
The ddns-update-style option controls only whether
the DHCP server will attempt to act as a Dynamic DNS client. As long as the DNS
server itself is correctly configured to reject DDNS attempts, an incorrect
ddns-update-style setting on the client is harmless (but should be fixed as a
best practice). Rationale:The Dynamic DNS protocol is used to remotely update the data served
by a DNS server. DHCP servers can use Dynamic DNS to publish information about
their clients. This setup carries security risks, and its use is not
recommended. If Dynamic DNS must be used despite the risks it poses, it is
critical that Dynamic DNS transactions be protected using TSIG or some other
cryptographic authentication mechanism. See dhcpd.conf(5) for more information
about protecting the DHCP server from passing along malicious DNS data from its
clients. |
Deny Decline Messages
[ref]ruleEdit /etc/dhcp/dhcpd.conf and add or correct the following
global option to prevent the DHCP server from responding the DHCPDECLINE
messages, if possible: deny declines; Rationale:The DHCPDECLINE message can be sent by a DHCP client to indicate
that it does not consider the lease offered by the server to be valid. By
issuing many DHCPDECLINE messages, a malicious client can exhaust the DHCP
server's pool of IP addresses, causing the DHCP server to forget old address
allocations. |
Deny BOOTP Queries
[ref]ruleUnless your network needs to support older BOOTP clients, disable
support for the bootp protocol by adding or correcting the global option:
deny bootp;
Rationale:The bootp option tells dhcpd to respond to BOOTP queries. If support
for this simpler protocol is not needed, it should be disabled to remove attack
vectors against the DHCP server.
|
Disable DHCP Client
[ref]group
DHCP is the default network configuration method provided by the system
installer, and common on many networks. Nevertheless, manual management
of IP addresses for systems implies a greater degree of management and
accountability for network activity.
|
contains 1 rule |
Disable DHCP Client
[ref]rule
For each interface on the system (e.g. eth0), edit
/etc/sysconfig/network-scripts/ifcfg-interface and make the
following changes:
Rationale:
DHCP relies on trusting the local network. If the local network is not trusted,
then it should not be used. However, the automatic configuration provided by
DHCP is commonly used and the alternative, manual configuration, presents an
unacceptable burden in many circumstances. |
Network Time Protocol
[ref]groupThe Network Time Protocol is used to manage the system
clock over a network. Computer clocks are not very accurate, so
time will drift unpredictably on unmanaged systems. Central time
protocols can be used both to ensure that time is consistent among
a network of systems, and that their time is consistent with the
outside world.
If every system on a network reliably reports the same time, then it is much
easier to correlate log messages in case of an attack. In addition, a number of
cryptographic protocols (such as Kerberos) use timestamps to prevent certain
types of attacks. If your network does not have synchronized time, these
protocols may be unreliable or even unusable.
Depending on the specifics of the network, global time accuracy may be just as
important as local synchronization, or not very important at all. If your
network is connected to the Internet, using a
public timeserver (or one provided by your enterprise) provides globally
accurate timestamps which may be essential in investigating or responding to
an attack which originated outside of your network.
A typical network setup involves a small number of internal systems operating as NTP
servers, and the remainder obtaining time information from those
internal servers.
More information on how to configure the NTP server software,
including configuration of cryptographic authentication for
time data, is available at http://www.ntp.org.
|
contains 3 rules |
Enable the NTP Daemon
[ref]rule
The ntpd service can be enabled with the following command:
$ sudo chkconfig --level 2345 ntpd on
Rationale:Enabling the ntpd service ensures that the ntpd
service will be running and that the system will synchronize its time to
any servers specified. This is important whether the system is configured to be
a client (and synchronize only its own clock) or it is also acting as an NTP
server to other systems. Synchronizing time is essential for authentication
services such as Kerberos, but it is also important for maintaining accurate
logs and auditing possible security breaches.
The NTP daemon offers all of the functionality of ntpdate , which is now
deprecated. Additional information on this is available at
http://support.ntp.org/bin/view/Dev/DeprecatingNtpdate Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command enable ntpd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | enable |
---|
- name: Enable service ntpd
service:
name="{{item}}"
enabled="yes"
state="started"
with_items:
- ntpd
tags:
- service_ntpd_enabled
- medium_severity
- enable_strategy
- low_complexity
- low_disruption
- CCE-27093-4
- NIST-800-53-AU-8(1)
- PCI-DSS-Req-10.4
- DISA-STIG-RHEL-06-000247
|
Specify a Remote NTP Server
[ref]ruleTo specify a remote NTP server for time synchronization, edit
the file /etc/ntp.conf . Add or correct the following lines,
substituting the IP or hostname of a remote NTP server for ntpserver:
server ntpserver
This instructs the NTP software to contact that remote server to obtain time
data.
Rationale:Synchronizing with an NTP server makes it possible
to collate system logs from multiple sources or correlate computer events with
real time events.
|
Specify Additional Remote NTP Servers
[ref]ruleAdditional NTP servers can be specified for time synchronization
in the file /etc/ntp.conf . To do so, add additional lines of the
following form, substituting the IP address or hostname of a remote NTP server for
ntpserver:
server ntpserver
Rationale:Specifying additional NTP servers increases the availability of
accurate time data, in the event that one of the specified servers becomes
unavailable. This is typical for a system acting as an NTP server for
other systems.
|
Mail Server Software
[ref]group
Mail servers are used to send and receive email over the network.
Mail is a very common service, and Mail Transfer Agents (MTAs) are obvious
targets of network attack.
Ensure that systems are not running MTAs unnecessarily,
and configure needed MTAs as defensively as possible.
Very few systems at any site should be configured to directly receive email over the
network. Users should instead use mail client programs to retrieve email
from a central server that supports protocols such as IMAP or POP3.
However, it is normal for most systems to be independently capable of sending email,
for instance so that cron jobs can report output to an administrator.
Most MTAs, including Postfix, support a submission-only mode in which mail can be sent from
the local system to a central site MTA (or directly delivered to a local account),
but the system still cannot receive mail directly over a network.
The alternatives program in CentOS Linux permits selection of other mail server software
(such as Sendmail), but Postfix is the default and is preferred.
Postfix was coded with security in mind and can also be more effectively contained by
SELinux as its modular design has resulted in separate processes performing specific actions.
More information is available on its website, http://www.postfix.org.
|
contains 2 rules |
Configure SMTP For Mail Clients
[ref]groupThis section discusses settings for Postfix in a submission-only
e-mail configuration. |
contains 1 rule |
Disable Postfix Network Listening
[ref]rule
Edit the file /etc/postfix/main.cf to ensure that only the following
inet_interfaces line appears:
inet_interfaces = localhost
Rationale:
This ensures postfix accepts mail messages
(such as cron job reports) from the local system only,
and not from the network, which protects it from network attack.
|
Uninstall Sendmail Package
[ref]ruleSendmail is not the default mail transfer agent and is
not installed by default.
The sendmail package can be removed with the following command:
$ sudo yum erase sendmail
Rationale:The sendmail software was not developed with security in mind and
its design prevents it from being effectively contained by SELinux. Postfix
should be used instead.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove sendmail
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure sendmail is removed
package:
name="{{item}}"
state=absent
with_items:
- sendmail
tags:
- package_sendmail_removed
- medium_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27515-6
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000288
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_sendmail
class remove_sendmail {
package { 'sendmail':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=sendmail
|
LDAP is a popular directory service, that is, a
standardized way of looking up information from a central database.
CentOS Linux 6 includes software that enables a system to act as both
an LDAP client and server.
|
contains 3 rules |
Configure OpenLDAP Clients
[ref]groupThis section provides information on which security settings are
important to configure in OpenLDAP clients by manually editing the appropriate
configuration files. CentOS Linux 6 provides an automated configuration tool called
authconfig and a graphical wrapper for authconfig called
system-config-authentication . However, these tools do not provide as
much control over configuration as manual editing of configuration files. The
authconfig tools do not allow you to specify locations of SSL certificate
files, which is useful when trying to use SSL cleanly across several protocols.
Installation and configuration of OpenLDAP on CentOS Linux 6 is available at
https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Directory_Servers.html.
Warning:
Before configuring any system to be an
LDAP client, ensure that a working LDAP server is present on the
network. |
contains 2 rules |
Configure LDAP Client to Use TLS For All Transactions
[ref]ruleConfigure LDAP to enforce TLS use. First, edit the file
/etc/pam_ldap.conf , and add or correct the following lines:
ssl start_tls
Then review the LDAP server and ensure TLS has been configured.
Rationale:The ssl directive specifies whether to use ssl or not. If
not specified it will default to no. It should be set to start_tls rather
than doing LDAP over SSL. Remediation Shell script: (show)
# Use LDAP for authentication
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/sysconfig/authconfig' 'USELDAPAUTH' 'yes' 'CCE-26690-8' '%s=%s'
# Configure client to use TLS for all authentications
# Function to replace configuration setting in config file or add the configuration setting if
# it does not exist.
#
# Expects four 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
#
# 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 config_file=$1
local key=$2
local value=$3
local cce=$4
local format=$5
# Check sanity of the input
if [ $# -lt "3" ]
then
echo "Usage: replace_or_append 'config_file_location' 'key_to_search' 'new_value'"
echo
echo "If symlinks need to be taken into account, add yes/no to the last argument"
echo "to allow to 'follow_symlinks'."
echo "Aborting."
exit 1
fi
# Test if the config_file is a symbolic link. If so, use --follow-symlinks with sed.
# Otherwise, regular sed command will do.
if test -L $config_file; then
sed_command="sed -i --follow-symlinks"
else
sed_command="sed -i"
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 ! [ "x$cce" = x ] && [ "$cce" != '@CCENUM@' ]; then
cce="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)
# If there is no print format specified in the last arg, use the default format.
if ! [ "x$format" = x ] ; then
printf -v formatted_output "$format" "$stripped_key" "$value"
else
formatted_output="$stripped_key = $value"
fi
# If the key exists, change it. Otherwise, add it to the config_file.
if `grep -qi "$key" $config_file` ; then
eval '$sed_command "s/$key.*/$formatted_output/g" $config_file'
else
# \n is precaution for case where file ends without trailing newline
echo -e "\n# Per $cce: Set $formatted_output in $config_file" >> $config_file
echo -e "$formatted_output" >> $config_file
fi
}
replace_or_append '/etc/nslcd.conf' 'ssl' 'start_tls' 'CCE-26690-8' '%s %s'
|
Configure Certificate Directives for LDAP Use of TLS
[ref]ruleEnsure a copy of a trusted CA certificate has been placed in
the file /etc/pki/tls/CA/cacert.pem . Configure LDAP to enforce TLS
use and to trust certificates signed by that CA. First, edit the file
/etc/pam_ldap.conf , and add or correct either of the following lines:
tls_cacertdir /etc/pki/tls/CA
or
tls_cacertfile /etc/pki/tls/CA/cacert.pem
Then review the LDAP server and ensure TLS has been configured.
Rationale:The tls_cacertdir or tls_cacertfile directives are required when
tls_checkpeer is configured (which is the default for openldap versions 2.1 and
up). These directives define the path to the trust certificates signed by the
site CA. |
Configure OpenLDAP Server
[ref]groupThis section details some security-relevant settings
for an OpenLDAP server. Installation and configuration of OpenLDAP on CentOS Linux 6 is available at:
https://access.redhat.com/site/documentation/en-US/Red_Hat_Enterprise_Linux/6/html/Deployment_Guide/ch-Directory_Servers.html.
|
contains 1 rule |
Uninstall openldap-servers Package
[ref]ruleThe openldap-servers package should be removed if not in use.
Is this system the OpenLDAP server? If not, remove the package.
$ sudo yum erase openldap-servers
The openldap-servers RPM is not installed by default on CentOS Linux 6
systems. It is needed only by the OpenLDAP server, not by the
clients which use LDAP for authentication. If the system is not
intended for use as an LDAP Server it should be removed.
Rationale:The openldap-servers package is not installed by default on RHEL6 systems.
It is needed only by the OpenLDAP server system, not clients which use LDAP for authentication. If
the system is not intended for use as an LDAP server, openldap-servers should be removed.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove openldap-servers
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure openldap-servers is removed
package:
name="{{item}}"
state=absent
with_items:
- openldap-servers
tags:
- package_openldap-servers_removed
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26858-1
- NIST-800-53-CM-7
- DISA-STIG-RHEL-06-000256
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_openldap-servers
class remove_openldap-servers {
package { 'openldap-servers':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=openldap-servers
|
DNS Server
[ref]groupMost organizations have an operational need to run at
least one nameserver. However, there are many common attacks
involving DNS server software, and this server software should
be disabled on any system
on which it is not needed. |
contains 3 rules |
Disable DNS Server
[ref]group
DNS software should be disabled on any machine which does not
need to be a nameserver. Note that the BIND DNS server software is
not installed on CentOS Linux 6 by default. The remainder of this section
discusses secure configuration of machines which must be
nameservers.
|
contains 2 rules |
Disable DNS Server
[ref]rule
The named service can be disabled with the following command:
$ sudo chkconfig named off
Rationale:
All network services involve some risk of compromise due to
implementation flaws and should be disabled if possible.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable named
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service named
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- named
tags:
- service_named_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26873-0
- NIST-800-53-CM-7
|
Uninstall bind Package
[ref]ruleTo remove the bind package, which contains the
named service, run the following command:
$ sudo yum erase bind
Rationale:
If there is no need to make DNS server software available,
removing it provides a safeguard against its activation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove bind
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure bind is removed
package:
name="{{item}}"
state=absent
with_items:
- bind
tags:
- package_bind_removed
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-27030-6
- NIST-800-53-CM-7
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_bind
class remove_bind {
package { 'bind':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=bind
|
Protect DNS Data from Tampering or Attack
[ref]groupThis section discusses DNS configuration options which make it
more difficult for attackers to gain access to private DNS data or to modify
DNS data. |
contains 1 rule |
Authenticate Zone Transfers
[ref]ruleIf it is necessary for a secondary nameserver to receive zone data
via zone transfer from the primary server, follow the instructions here. Use
dnssec-keygen to create a symmetric key file in the current directory:
$ cd /tmp
$ sudo dnssec-keygen -a HMAC-MD5 -b 128 -n HOST dns.example.com
Kdns.example.com .+aaa +iiiii
This output is the name of a file containing the new key. Read the file to find
the base64-encoded key string:
$ sudo cat Kdns.example.com .+NNN +MMMMM .key
dns.example.com IN KEY 512 3 157 base64-key-string
Add the directives to /etc/named.conf on the primary server:
key zone-transfer-key {
algorithm hmac-md5;
secret "base64-key-string ";
};
zone "example.com " IN {
type master;
allow-transfer { key zone-transfer-key; };
...
};
Add the directives below to /etc/named.conf on the secondary nameserver:
key zone-transfer-key {
algorithm hmac-md5;
secret "base64-key-string ";
};
server IP-OF-MASTER {
keys { zone-transfer-key; };
};
zone "example.com " IN {
type slave;
masters { IP-OF-MASTER ; };
...
};
Warning:
The purpose of the dnssec-keygen command is to
create the shared secret string base64-key-string. Once this secret has been
obtained and inserted into named.conf on the primary and secondary servers, the
key files Kdns.example.com .+NNN +MMMMM .key and Kdns.example.com .+NNN +MMMMM
.private are no longer needed, and may safely be deleted. Rationale:The BIND transaction signature (TSIG) functionality allows primary
and secondary nameservers to use a shared secret to verify authorization to
perform zone transfers. This method is more secure than using IP-based limiting
to restrict nameserver access, since IP addresses can be easily spoofed.
However, if you cannot configure TSIG between your servers because, for
instance, the secondary nameserver is not under your control and its
administrators are unwilling to configure TSIG, you can configure an
allow-transfer directive with numerical IP addresses or ACLs as a last resort.
|
FTP Server
[ref]groupFTP is a common method for allowing remote access to
files. Like telnet, the FTP protocol is unencrypted, which means
that passwords and other data transmitted during the session can be
captured and that the session is vulnerable to hijacking.
Therefore, running the FTP server software is not recommended.
However, there are some FTP server configurations which may
be appropriate for some environments, particularly those which
allow only read-only anonymous access as a means of downloading
data available to the public. |
contains 2 rules |
Disable vsftpd if Possible
[ref]groupTo minimize attack surface, disable vsftpd if at all
possible. |
contains 2 rules |
Disable vsftpd Service
[ref]rule
The vsftpd service can be disabled with the following command:
$ sudo chkconfig vsftpd off
Rationale:
Running FTP server software provides a network-based avenue
of attack, and should be disabled if not needed.
Furthermore, the FTP protocol is unencrypted and creates
a risk of compromising sensitive information.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to enable/disable and start/stop services on RHEL and Fedora systems.
#
# Example Call(s):
#
# service_command enable bluetooth
# service_command disable bluetooth.service
#
# Using xinetd:
# service_command disable rsh.socket xinetd=rsh
#
function service_command {
# Load function arguments into local variables
local service_state=$1
local service=$2
local xinetd=$(echo $3 | cut -d'=' -f2)
# Check sanity of the input
if [ $# -lt "2" ]
then
echo "Usage: service_command 'enable/disable' 'service_name.service'"
echo
echo "To enable or disable xinetd services add \'xinetd=service_name\'"
echo "as the last argument"
echo "Aborting."
exit 1
fi
# If systemctl is installed, use systemctl command; otherwise, use the service/chkconfig commands
if [ -f "/usr/bin/systemctl" ] ; then
service_util="/usr/bin/systemctl"
else
service_util="/sbin/service"
chkconfig_util="/sbin/chkconfig"
fi
# If disable is not specified in arg1, set variables to enable services.
# Otherwise, variables are to be set to disable services.
if [ "$service_state" != 'disable' ] ; then
service_state="enable"
service_operation="start"
chkconfig_state="on"
else
service_state="disable"
service_operation="stop"
chkconfig_state="off"
fi
# If chkconfig_util is not empty, use chkconfig/service commands.
if [ "x$chkconfig_util" != x ] ; then
$service_util $service $service_operation
$chkconfig_util --level 0123456 $service $chkconfig_state
else
$service_util $service_operation $service
$service_util $service_state $service
# The service may not be running because it has been started and failed,
# so let's reset the state so OVAL checks pass.
# Service should be 'inactive', not 'failed' after reboot though.
$service_util reset-failed $service
fi
# Test if local variable xinetd is empty using non-bashism.
# If empty, then xinetd is not being used.
if [ "x$xinetd" != x ] ; then
grep -qi disable /etc/xinetd.d/$xinetd && \
if [ "$service_operation" = 'disable' ] ; then
sed -i "s/disable.*/disable = no/gI" /etc/xinetd.d/$xinetd
else
sed -i "s/disable.*/disable = yes/gI" /etc/xinetd.d/$xinetd
fi
fi
}
service_command disable vsftpd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Disable service vsftpd
service:
name="{{item}}"
enabled="no"
state="stopped"
with_items:
- vsftpd
tags:
- service_vsftpd_disabled
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26948-0
- NIST-800-53-CM-7
|
Uninstall vsftpd Package
[ref]rule
The vsftpd package can be removed with the following command:
$ sudo yum erase vsftpd
Rationale:
Removing the vsftpd package decreases the risk of its
accidental activation.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
# Function to install or uninstall packages on RHEL and Fedora systems.
#
# Example Call(s):
#
# package_command install aide
# package_command remove telnet-server
#
function package_command {
# Load function arguments into local variables
local package_operation=$1
local package=$2
# Check sanity of the input
if [ $# -ne "2" ]
then
echo "Usage: package_command 'install/uninstall' 'rpm_package_name"
echo "Aborting."
exit 1
fi
# If dnf is installed, use dnf; otherwise, use yum
if [ -f "/usr/bin/dnf" ] ; then
install_util="/usr/bin/dnf"
else
install_util="/usr/bin/yum"
fi
if [ "$package_operation" != 'remove' ] ; then
# If the rpm is not installed, install the rpm
if ! /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
else
# If the rpm is installed, uninstall the rpm
if /bin/rpm -q --quiet $package; then
$install_util -y $package_operation $package
fi
fi
}
package_command remove vsftpd
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
- name: Ensure vsftpd is removed
package:
name="{{item}}"
state=absent
with_items:
- vsftpd
tags:
- package_vsftpd_removed
- low_severity
- disable_strategy
- low_complexity
- low_disruption
- CCE-26687-4
- NIST-800-53-CM-7
Remediation Puppet snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
include remove_vsftpd
class remove_vsftpd {
package { 'vsftpd':
ensure => 'purged',
}
}
Remediation Anaconda snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | disable |
---|
package --remove=vsftpd
|
Web Server
[ref]groupThe web server is responsible for providing access to
content via the HTTP protocol. Web servers represent a significant
security risk because:
- The HTTP port is commonly probed by malicious sources
- Web server software is very complex, and includes a long
history of vulnerabilities
- The HTTP protocol is unencrypted and vulnerable to passive
monitoring
The system's default web server software is Apache 2 and is
provided in the RPM package httpd . |
contains 3 rules |
Secure Apache Configuration
[ref]groupThe httpd configuration file is
/etc/httpd/conf/httpd.conf . Apply the recommendations in the remainder
of this section to this file. |
contains 3 rules |
Restrict Web Server Information Leakage
[ref]group
The ServerTokens and ServerSignature directives determine how
much information the web server discloses about the configuration of the
system. |
contains 1 rule |
Set httpd ServerTokens Directive to Prod
[ref]ruleServerTokens Prod restricts information in page headers, returning only the word "Apache."
Add or correct the following directive in /etc/httpd/conf/httpd.conf :
ServerTokens Prod
Rationale:
Information disclosed to clients about the configuration of the web server and system could be used
to plan an attack on the given system. This information disclosure should be restricted to a minimum.
|
Configure Operating System to Protect Web Server
[ref]group
The following configuration steps should be taken on the system which hosts the
web server, in order to provide as safe an environment as possible for the web server.
|
contains 2 rules |
Restrict File and Directory Access
[ref]group
Minimize access to critical httpd files and directories.
|
contains 2 rules |
Set Permissions on the /var/log/httpd/ Directory
[ref]rule
Ensure that the permissions on the web server log directory is set to 700:
$ sudo chmod 700 /var/log/httpd/
This is its default setting.
Rationale:
Access to the web server's log files may allow an unauthorized user or attacker
to access information about the web server or alter the server's log files.
|
Set Permissions on All Configuration Files Inside /etc/httpd/conf/
[ref]rule
Set permissions on the web server configuration files to 640:
$ sudo chmod 640 /etc/httpd/conf/*
Rationale:
Access to the web server's configuration files may allow an unauthorized user or attacker
to access information about the web server or to alter the server's configuration files.
Remediation Shell script: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
chmod 0640 /etc/httpd/conf/*
Remediation Ansible snippet: (show)
Complexity: | low |
---|
Disruption: | low |
---|
Strategy: | configure |
---|
- name: Find /etc/httpd/conf/* file(s)
find:
paths: "{{ '/etc/httpd/conf/*' | dirname }}"
patterns: "{{ '/etc/httpd/conf/*' | basename }}"
register: files_found
tags:
- file_permissions_httpd_server_conf_files
- low_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27316-9
- NIST-800-53-CM-7
- name: Set permissions
file:
path: "{{ item.path }}"
mode: 0640
with_items:
- "{{ files_found.files }}"
tags:
- file_permissions_httpd_server_conf_files
- low_severity
- configure_strategy
- low_complexity
- low_disruption
- CCE-27316-9
- NIST-800-53-CM-7
|