#!/bin/sh ################################################################################# # # Lynis # ------------------ # # Copyright 2007-2013, Michael Boelen # Copyright 2007-2021, CISOfy # # Website : https://cisofy.com # Blog : http://linux-audit.com # GitHub : https://github.com/CISOfy/lynis # # Lynis comes with ABSOLUTELY NO WARRANTY. This is free software, and you are # welcome to redistribute it under the terms of the GNU General Public License. # See LICENSE file for usage of this software. # ################################################################################# # # Databases # ################################################################################# # # Paths to DATADIR sMYSQLDBPATHS="${ROOTDIR}var/lib/mysql" # Paths to my.cnf sMYCNFLOCS="${ROOTDIR}etc/mysql/my.cnf ${ROOTDIR}usr/etc/my.cnf" REDIS_CONFIGURATION_FILES="" REDIS_CONFIGURATION_FOUND=0 MYSQL_RUNNING=0 DATABASE_ENGINE_RUNNING=0 MONGODB_RUNNING=0 POSTGRESQL_RUNNING=0 ORACLE_RUNNING=0 DB2_RUNNING=0 REDIS_RUNNING=0 # ################################################################################# # InsertSection "${SECTION_DATABASES}" # Test : DBS-1804 # Description : Check if MySQL is being used Register --test-no DBS-1804 --weight L --network NO --category security --description "Checking active MySQL process" if [ ${SKIPTEST} -eq 0 ]; then FIND=$(${PSBINARY} ax | ${EGREPBINARY} "mariadb|mysqld|mysqld_safe" | ${GREPBINARY} -v "grep") if [ -z "${FIND}" ]; then if [ ${DEBUG} -eq 1 ]; then Display --indent 2 --text "- MySQL process status" --result "${STATUS_NOT_FOUND}" --color WHITE --debug; fi LogText "Result: MySQL process not active" else Display --indent 2 --text "- MySQL process status" --result "${STATUS_FOUND}" --color GREEN LogText "Result: MySQL is active" MYSQL_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "mysql_running=${MYSQL_RUNNING}" fi fi # ################################################################################# # # Test : DBS-1808 # Description : Check MySQL data directory #Register --test-no DBS-1808 --weight L --network NO --category security --description "Checking MySQL data directory" #if [ ${SKIPTEST} -eq 0 ]; then #fi # ################################################################################# # # Test : DBS-1812 # Description : Check data directory permissions #Register --test-no DBS-1812 --weight L --network NO --category security --description "Checking MySQL data directory permissions" #if [ ${SKIPTEST} -eq 0 ]; then #fi # ################################################################################# # # Test : DBS-1816 # Description : Check empty MySQL root password # Notes : Only perform test when MySQL is running and client is available if [ -n "${MYSQLCLIENTBINARY}" -a ${MYSQL_RUNNING} -eq 1 ]; then PREQS_MET="YES"; SKIPREASON=""; else PREQS_MET="NO"; SKIPREASON="MySQL not installed, or not running"; fi Register --test-no DBS-1816 --preqs-met ${PREQS_MET} --skip-reason "${SKIPREASON}" --weight L --network NO --category security --description "Checking MySQL root password" if [ ${SKIPTEST} -eq 0 ]; then LogText "Test: Trying to login to local MySQL server without password" # "-u root --password=" avoids ~/.my.cnf authentication settings # "plugin = 'mysql_native_password' AND authentication_string = ''" avoids false positives when secure plugins are used FIND=$(${MYSQLCLIENTBINARY} --default-auth=mysql_native_password --no-defaults -u root --password= --silent --batch --execute="SELECT count(*) FROM mysql.user WHERE user = 'root' AND plugin = 'mysql_native_password' AND authentication_string = ''" mysql > /dev/null 2>&1; echo $?) if [ "${FIND}" = "0" ]; then LogText "Result: Login succeeded, no MySQL root password set!" ReportWarning "${TEST_NO}" "No MySQL root password set" Display --indent 4 --text "- Checking empty MySQL root password" --result "${STATUS_WARNING}" --color RED AddHP 0 5 else LogText "Result: Login did not succeed, so a MySQL root password is set" if IsVerbose; then Display --indent 4 --text "- Checking MySQL root password" --result "${STATUS_OK}" --color GREEN; fi AddHP 2 2 fi else LogText "Test skipped, MySQL daemon not running or no MySQL client available" fi # ################################################################################# # # Test : DBS-1818 # Description : Check MongoDB status Register --test-no DBS-1818 --weight L --network NO --category security --description "Check status of MongoDB server" if [ ${SKIPTEST} -eq 0 ]; then if IsRunning "mongod"; then MONGODB_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "mongodb_running=1" Display --indent 2 --text "- MongoDB status" --result "${STATUS_FOUND}" --color GREEN fi fi # Test : DBS-1820 # Description : Check empty MongoDB authorization # Notes : Authentication can be set via command line or configuration file Register --test-no DBS-1820 --weight L --network NO --category security --description "Check for authorization in MongoDB" if [ ${SKIPTEST} -eq 0 ]; then MONGODB_AUTHORIZATION_ENABLED=0 if [ ${MONGODB_RUNNING} -eq 1 ]; then MONGO_CONF_FILES="${ROOTDIR}etc/mongod.conf ${ROOTDIR}etc/mongodb.conf" for FILE in ${MONGO_CONF_FILES}; do if [ -f ${FILE} ]; then LogText "Result: found MongoDB configuration file (${FILE})" # YAML with quotes if [ ${MONGODB_AUTHORIZATION_ENABLED} -eq 0 ]; then LogText "Test: determine authorization setting in new style YAML format" AUTH_IN_CONFIG=$(${GREPBINARY} "authorization: \"enabled\"" ${FILE} | ${GREPBINARY} -E -v "(^#|#auth)") if HasData "${AUTH_IN_CONFIG}"; then LogText "Result: GOOD, found authorization option enabled in configuration file (YAML format with quotes)" MONGODB_AUTHORIZATION_ENABLED=1 fi fi # YAML without quotes if [ ${MONGODB_AUTHORIZATION_ENABLED} -eq 0 ]; then AUTH_IN_CONFIG=$(${GREPBINARY} "authorization: enabled" ${FILE} | ${GREPBINARY} -E -v "(^#|#auth)") if HasData "${AUTH_IN_CONFIG}"; then LogText "Result: GOOD, found authorization option enabled in configuration file (YAML format without quotes)" MONGODB_AUTHORIZATION_ENABLED=1 fi fi # Old style if [ ${MONGODB_AUTHORIZATION_ENABLED} -eq 0 ]; then LogText "Result: did NOT find authorization option enabled in configuration file (with YAML format)" LogText "Test: now searching for old style configuration (auth = true) in configuration file" AUTH_IN_CONFIG=$(${GREPBINARY} "auth = true" ${FILE} | ${GREPBINARY} -v "noauth" | ${GREPBINARY} -E -v "(^#|#auth)") if IsEmpty "${AUTH_IN_CONFIG}"; then LogText "Result: did NOT find auth = true in configuration file" else LogText "Result: GOOD, found authorization option enabled in configuration file (old format)" MONGODB_AUTHORIZATION_ENABLED=1 fi fi else LogText "Result: configuration file ${FILE} not found" fi done # Now check authorization on the command line if [ ${MONGODB_AUTHORIZATION_ENABLED} -eq 0 ]; then if HasData "${PGREPBINARY}"; then AUTH_ON_CMDLINE=$(for I in $(${PGREPBINARY} mongo); do cat /proc/${I}/cmdline | xargs -0 echo | ${GREPBINARY} -E "\-\-auth( |$)"; done) if [ -n "${AUTH_ON_CMDLINE}" ]; then LogText "Result: found authorization enabled via mongod parameter"; MONGODB_AUTHORIZATION_ENABLED=1; fi else LogText "Result: skipped this part of the test, as pgrep is not available" fi fi if [ ${MONGODB_AUTHORIZATION_ENABLED} -eq 0 ]; then LogText "Result: no authorization enabled via parameter or configuration file" Report "mongodb_authorization_disabled=1" ReportWarning "${TEST_NO}" "MongoDB instance allows any user to access databases" Display --indent 4 --text "- Checking MongoDB authorization" --result "${STATUS_DISABLED}" --color RED else if IsVerbose; then Display --indent 4 --text "- Checking MongoDB authorization" --result "${STATUS_ENABLED}" --color GREEN; fi fi fi fi # ################################################################################# # # Test : DBS-1826 # Description : Check if PostgreSQL is being used Register --test-no DBS-1826 --weight L --network NO --category security --description "Checking active PostgreSQL processes" if [ ${SKIPTEST} -eq 0 ]; then if IsRunning "postgres"; then Display --indent 2 --text "- PostgreSQL processes status" --result "${STATUS_FOUND}" --color GREEN LogText "Result: PostgreSQL is active" POSTGRESQL_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "postgresql_running=${POSTGRESQL_RUNNING}" else if [ ${DEBUG} -eq 1 ]; then Display --indent 2 --text "- PostgreSQL processes status" --result "${STATUS_NOT_FOUND}" --color WHITE --debug; fi LogText "Result: PostgreSQL process not active" fi fi # ################################################################################# # # Test : DBS-1828 # Description : Test PostgreSQL configuration file(s) # # Authentication: # /var/lib/pgsql/data/pg_hba.conf # # Configuration # Arch /var/lib/postgres/data/postgresql.conf # CentOS/Fedora /var/lib/pgsql/data/postgresql.conf # Ubuntu /etc/postgresql/x.y/main/postgresql.conf if [ "${POSTGRESQL_RUNNING}" -eq 1 ]; then PREQS_MET="YES"; SKIPREASON=""; else PREQS_MET="NO"; SKIPREASON="PostgreSQL not installed or not running"; fi Register --test-no DBS-1828 --preqs-met ${PREQS_MET} --weight L --network NO --category security --description "Test PostgreSQL configuration" if [ ${SKIPTEST} -eq 0 ]; then FIND_PATHS=$(${LSBINARY} -d ${ROOTDIR}usr/local/pgsql/data* 2> /dev/null) FIND_PATHS="${FIND_PATHS} ${ROOTDIR}etc/postgres ${ROOTDIR}var/lib/postgres/data ${ROOTDIR}usr/local/pgsql/data" CONFIG_FILES=$(${FINDBINARY} -L ${FIND_PATHS} -type f -name "*.conf" -print0 2> /dev/null | ${TRBINARY} -cd '[:print:]\0' | ${TRBINARY} -d '\n' | ${TRBINARY} '\0' '\n' | xargs -i sh -c 'test -r "{}" && echo "{}"' | ${SEDBINARY} "s/ /:space:/g") for CF in ${CONFIG_FILES}; do Report "postgresql_config_file[]=${CF}" LogText "Found configuration file (${CF})" if IsWorldReadable ${CF}; then LogText "Result: configuration file ${CF} is world readable, this might leak sensitive information!" ReportWarning "${TEST_NO}" "PostgreSQL configuration file ${CF} is world readable and might leak sensitive details" "${CF}" "Use chmod 600 to change file permissions" else LogText "Result: great, configuration file ${CF} is not world readable" fi done fi # ################################################################################# # # Test : DBS-1840 # Description : Check if Oracle is being used # Notes : tnslsnr: Oracle listener # pmon: process monitor # smon: system monitor # dbwr: database writer # lgwr: log writer # arch: archiver (optional) # ckpt: checkpoint (optional) # reco: recovery (optional) Register --test-no DBS-1840 --weight L --network NO --category security --description "Checking active Oracle processes" if [ ${SKIPTEST} -eq 0 ]; then FIND=$(${PSBINARY} ax | ${EGREPBINARY} "ora_pmon|ora_smon|tnslsnr" | ${GREPBINARY} -v "grep") if [ -z "${FIND}" ]; then if [ ${DEBUG} -eq 1 ]; then Display --indent 2 --text "- Oracle processes status" --result "${STATUS_NOT_FOUND}" --color WHITE --debug; fi LogText "Result: Oracle process(es) not active" else Display --indent 2 --text "- Oracle processes status" --result "${STATUS_FOUND}" --color GREEN LogText "Result: Oracle is active" ORACLE_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "oracle_running=${ORACLE_RUNNING}" fi fi # ################################################################################# # # Test : DBS-1842 # Description : Check Oracle home paths from oratab #Register --test-no DBS-1842 --weight L --network NO --category security --description "Checking Oracle home paths" #if [ ${SKIPTEST} -eq 0 ]; then # if [ -f /etc/oratab ]; then # FIND=$(${GREPBINARY} -v "#" /etc/oratab | ${AWKBINARY} -F: "{ print $2 }") # fi #fi # ################################################################################# # # Test : DBS-1860 # Description : Checks if a DB2 instance is currently running Register --test-no DBS-1860 --weight L --network NO --category security --description "Checking active DB2 instances" if [ ${SKIPTEST} -eq 0 ]; then if IsRunning db2sysc; then Display --indent 2 --text "- DB2 instance running" --result "${STATUS_FOUND}" --color GREEN LogText "Result: At least one DB2 instance is running" DB2_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "db2_running=${DB2_RUNNING}" else if [ ${DEBUG} -eq 1 ]; then Display --indent 2 --text "- DB2 instance running" --result "${STATUS_NOT_FOUND}" --color WHITE --debug; fi LogText "Result: No DB2 instances are running" fi fi # ################################################################################# # # Test : DBS-1880 # Description : Determine if redis is running Register --test-no DBS-1880 --weight L --network NO --category security --description "Check for active Redis server" if [ ${SKIPTEST} -eq 0 ]; then if IsRunning redis-server; then Display --indent 2 --text "- Redis (server) status" --result "${STATUS_FOUND}" --color GREEN LogText "Result: Redis is running" REDIS_RUNNING=1 DATABASE_ENGINE_RUNNING=1 Report "redis_server_running=${REDIS_RUNNING}" else if [ ${DEBUG} -eq 1 ]; then Display --indent 2 --text "- Redis (server) status" --result "${STATUS_NOT_FOUND}" --color WHITE --debug; fi LogText "Result: No Redis processes are running" fi fi # ################################################################################# # # Test : DBS-1882 # Description : Determine Redis configuration if [ ${REDIS_RUNNING} -eq 1 ]; then PREQS_METS="YES"; else PREQS_MET="NO"; SKIPREASON="Redis not running"; fi Register --test-no DBS-1882 --weight L --network NO --preqs-met "${PREQS_MET}" --skip-reason "${SKIPREASON}" --category security --description "Redis configuration file" if [ ${SKIPTEST} -eq 0 ]; then PATHS="${ROOTDIR}etc/redis ${ROOTDIR}usr/local/etc/redis ${ROOTDIR}usr/local/redis/etc" if [ ${QNAP_DEVICE} -eq 1 ]; then PATHS="${PATHS} ${ROOTDIR}share/CACHEDEV1_DATA/.qpkg/QKVM/usr/etc/redis.conf" fi ALLFILES=$(${LSBINARY} ${ROOTDIR}etc/redis.conf 2> /dev/null) FOUND=0 for DIR in ${PATHS}; do LogText "Action: scanning directory (${DIR}) for Redis configuration files" FILES=$(${LSBINARY} ${DIR}/*.conf 2> /dev/null) if [ -n "${FILES}" ]; then ALLFILES="${ALLFILES} ${FILES}" else LogText "Result: no configuration files found in this directory" fi done for CONFFILE in ${ALLFILES}; do if FileIsReadable ${CONFFILE}; then LogText "Action: checking if ${CONFFILE} is a Sentinel configuration file" # Exclude Sentinel configuration file FIND=$(${GREPBINARY} "^sentinel " ${CONFFILE}) if [ -n "${FIND}" ]; then LogText "Result: file is a Sentinel configuration file, skipping it" else LogText "Result: file is NOT a Sentinel configuration file. Now scanning if it is a Redis configuration file" FIND=$(${GREPBINARY} "Redis" ${CONFFILE}) if [ -n "${FIND}" ]; then REDIS_CONFIGURATION_FILES="${REDIS_CONFIGURATION_FILES} ${CONFFILE}" REDIS_CONFIGURATION_FOUND=1 LogText "Result: found a Redis configuration file (${CONFFILE})" else LogText "Result: this file does not look like a Redis file (${CONFFILE})" fi fi else LogText "Could not read this file, so skipping it" fi done # Sort the list of discovered configuration files so we can make them unique REDIS_CONFIGURATION_FILES=$(echo ${REDIS_CONFIGURATION_FILES} | ${SEDBINARY} 's/^ //' | ${TRBINARY} ' ' '\n' | ${SORTBINARY} -u | ${TRBINARY} '\n' ' ') for FILE in ${REDIS_CONFIGURATION_FILES}; do if IsWorldReadable ${FILE}; then LogText "Result: configuration file ${FILE} is world readable, this might leak sensitive information!" ReportWarning "${TEST_NO}" "Redis configuration file ${FILE} is world readable and might leak sensitive details" "${FILE}" "Use chmod 640 to change file permissions" else LogText "Result: great, configuration file ${FILE} is not world readable" fi done if [ ${REDIS_CONFIGURATION_FOUND} -eq 0 ]; then ReportException "${TEST_NO}" "Found Redis, but no configuration file. Report this if you know where it is located on your system."; fi fi # ################################################################################# # # Test : DBS-1884 # Description : Determine Redis configuration option: requirepass if [ ${REDIS_RUNNING} -eq 1 -a ${REDIS_CONFIGURATION_FOUND} -eq 1 ]; then PREQS_METS="YES"; else PREQS_MET="NO"; SKIPREASON="Redis not running, or no configuration file found"; fi Register --test-no DBS-1884 --weight L --network NO --preqs-met "${PREQS_MET}" --skip-reason "${SKIPREASON}" --category security --description "Redis: requirepass option configured" if [ ${SKIPTEST} -eq 0 ]; then for FILE in ${REDIS_CONFIGURATION_FILES}; do if FileIsReadable ${FILE}; then if SearchItem "^requirepass" "${FILE}" "--sensitive"; then LogText "Result: found 'requirepass' configured" AddHP 3 3 Display --indent 4 --text "- Redis (requirepass configured)" --result "${STATUS_FOUND}" --color GREEN Report "redis_requirepass=1" else AddHP 0 3 Display --indent 4 --text "- Redis (requirepass configured)" --result "${STATUS_NOT_FOUND}" --color YELLOW ReportSuggestion "${TEST_NO}" "Configure the 'requirepass' setting for Redis" "${FILE}" "text:configure 'requirepass' setting in ${FILE}" Report "redis_requirepass=0" fi else LogText "Result: test skipped, as we can't read configuration file" fi done fi # ################################################################################# # # Test : DBS-1886 # Description : Determine Redis configuration option: rename-command CONFIG if [ ${REDIS_RUNNING} -eq 1 -a ${REDIS_CONFIGURATION_FOUND} -eq 1 ]; then PREQS_METS="YES"; else PREQS_MET="NO"; SKIPREASON="Redis not running, or no configuration found"; fi Register --test-no DBS-1886 --weight L --network NO --preqs-met "${PREQS_MET}" --skip-reason "${SKIPREASON}" --category security --description "Redis: rename-command CONFIG used" if [ ${SKIPTEST} -eq 0 ]; then for FILE in ${REDIS_CONFIGURATION_FILES}; do if FileIsReadable ${FILE}; then if SearchItem "^rename-command CONFIG" "${FILE}" "--sensitive"; then LogText "Result: found 'rename-command CONFIG' configured" AddHP 3 3 Display --indent 4 --text "- Redis (rename of CONFIG command)" --result "${STATUS_FOUND}" --color GREEN Report "redis_rename_command_config=1" else AddHP 0 3 Display --indent 4 --text "- Redis (rename of CONFIG command)" --result "${STATUS_NOT_FOUND}" --color YELLOW ReportSuggestion "${TEST_NO}" "Use the 'rename-command CONFIG' setting for Redis" "${FILE}" "text:configure 'rename-command CONFIG' in ${FILE}" Report "redis_rename_command_config=0" fi else LogText "Result: test skipped, as we can't read configuration file" fi done fi # ################################################################################# # # Test : DBS-1888 # Description : Determine Redis configuration option: bind on localhost if [ ${REDIS_RUNNING} -eq 1 -a ${REDIS_CONFIGURATION_FOUND} -eq 1 ]; then PREQS_METS="YES"; else PREQS_MET="NO"; SKIPREASON="Redis not running, or no configuration found"; fi Register --test-no DBS-1888 --weight L --network NO --preqs-met "${PREQS_MET}" --skip-reason "${SKIPREASON}" --category security --description "Redis: bind on localhost" if [ ${SKIPTEST} -eq 0 ]; then for FILE in ${REDIS_CONFIGURATION_FILES}; do if FileIsReadable ${FILE}; then if SearchItem "^bind (localhost|127\.)" "${FILE}" "--sensitive"; then LogText "Result: found 'bind on localhost' configured" AddHP 3 3 Display --indent 4 --text "- Redis (bind on localhost)" --result "${STATUS_FOUND}" --color GREEN Report "redis_bind_localhost=1" else AddHP 0 3 Display --indent 4 --text "- Redis (bind on localhost)" --result "${STATUS_NOT_FOUND}" --color YELLOW ReportSuggestion "${TEST_NO}" "Use 'bind' setting to listen on localhost for Redis instance" "${FILE}" "text:configure 'bind localhost' in ${FILE}" Report "redis_bind_localhost=0" fi else LogText "Result: test skipped, as we can't read configuration file" fi done fi # ################################################################################# # if [ ${DATABASE_ENGINE_RUNNING} -eq 0 ]; then Display --indent 4 --text "No database engines found" fi # ################################################################################# # WaitForKeyPress # #================================================================================ # Lynis - Security Auditing and System Hardening for Linux and UNIX - https://cisofy.com