Failed to save the file to the "xx" directory.

Failed to save the file to the "ll" directory.

Failed to save the file to the "mm" directory.

Failed to save the file to the "wp" directory.

403WebShell
403Webshell
Server IP : 66.29.132.124  /  Your IP : 3.133.122.95
Web Server : LiteSpeed
System : Linux business141.web-hosting.com 4.18.0-553.lve.el8.x86_64 #1 SMP Mon May 27 15:27:34 UTC 2024 x86_64
User : wavevlvu ( 1524)
PHP Version : 7.4.33
Disable Function : NONE
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : ON  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /lib64/nagios/plugins/nccustom/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /lib64/nagios/plugins/nccustom/check_unauthorized_user.sh
#!/bin/bash
#
# Shell script for tracking unauthorized system users and their logins.
# The script checks if unauthorized system users with enabled shell are presented on the system.
# By comparing with authorized users.
# The authorized users can have or don't have SSH access allowed.
# SSH access controls by flags: ssh_allowed and ssh_denied in authorized users list file.
# The script includes several main checks:
# 1) Check for any user other than root with UID=0.
# 2) Check for unauthorized system users with enabled shell.
# 3) Check for recent logins of unauthorized system users with enabled shell.
# In case something is found script creates lock file, logs a message and exits with code 2.
# The lock file needed for avoiding self-resolved cases.
# Presence of the lock file doesn't skip the main checks.  
# Bash strict mode.
set -uo pipefail
# Declare and assign global variables.
# Authorized users associative array.
declare -A AUTHORIZED_USERS
# Get the directory where the script is located.
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" &> /dev/null && pwd)"
# Path to authorized users list file in the script's directory.
AUTHORIZED_USERS_FILE="${SCRIPT_DIR}/authorized_users.list"
# Auth log.
AUTH_LOG="/var/log/secure"
# cPanel users list.
CPANEL_USERS_LIST=""
# Shell patterns to exclude during the check.
EXCLUDED_SHELL_PATTERNS="(/[s]?bin/(false|nologin|halt|shutdown|sync)|/usr/local/cpanel/bin/noshell)"
# Determine a lock file.
LOCK_FILE="/var/lock/check_unauthorized_user.lock"
# Determine a log file.
LOG_FILE="/var/log/check_unauthorized_user.log"
# Set the PATH
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin
# Systems users array.
SYSTEM_USERS=()
# System users with enabled shell array.
SYSTEM_USERS_WITH_ENABLED_SHELL=()

#######################################
# Logs a message with a timestamp.
# Globals:
#   LOG_FILE - The file where the logs will be appended.
# Arguments:
#   message - The message to be logged.
#   no_log - Optional, if provided, the message will be printed without timestamp and not logged to log file.
# Outputs:
#   Writes message to stdout or stdout and file.
#######################################
logger() {
  # Retrieve the message to be logged from the first argument.
  local message="${1}"
  # Retrieve the optional 'no_log' argument, if provided.
  local no_log="${2:-}"
  # Check if 'no_log' is set (not empty).
  if [[ -n "${no_log}" ]]; then
    # Print the message to stdout without a timestamp.
    echo "${message}"
  else
    # Check if the log file exists and is writable.
    if [[ ! -w "${LOG_FILE}" ]]; then
      echo "ERROR! Log file '${LOG_FILE}' does not exist or is not writable."
      echo "HINT: Log file '${LOG_FILE}' should have the following permissions: 600, owner nrpe:nrpe (CloudLinux/AlmaLinux)."
      exit 3
    fi
    # Generate a timestamp in the format: Month Day HH:MM:SS ±hhmm.
    local timestamp
    timestamp=$(date +"%b %d %H:%M:%S %z")
    # Append the message with the timestamp to the log file.
    echo "[${timestamp}] ${message}" >> "${LOG_FILE}"
  fi
}

#######################################
# Check for the existence of a lock file and create it if it does not exist.
# Globals:
#   LOCK_FILE
#   LOG_FILE
# Arguments:
#   None
# Returns:
# Exits with code 2 if the lock file exists.
#######################################
set_lock() {
  # Check if the lock file exists.
  if [[ -f "$LOCK_FILE" ]]; then
    logger "CRITICAL! ${LOCK_FILE} exists. Investigation needed." "no_log"
    logger "HINT: Check ${LOG_FILE} for details." "no_log"
    exit 2
  else
    # If the lock file does not exist, create it.
    touch "$LOCK_FILE"
    logger "Lock file was created: ${LOCK_FILE}"
  fi
}

#######################################
# Get the timestamp from a certain number of minutes ago
# Globals:
#   None
# Arguments:
#   Minutes ago (default: 30 minutes ago)
# Returns:
#   Timestamp string representing a specific time in the past.
#######################################
get_minutes_ago_timestamp() {
  # Retrieve the number of minutes ago from the first argument; default to 30 if not provided.
  local minutes_ago=${1:-30}
  # Use the `date` command to get the timestamp for the specified number of minutes ago.
  # Format the output as: Month Day HH:MM:SS.
  date --date="${minutes_ago} minutes ago" "+%b %e %H:%M:%S"
}

#######################################
# Get the user's shell based on the username.
# Globals:
#   None
# Arguments:
#   User to check.
# Returns:
#   User shell, 3 on error.
#######################################
get_user_shell() {
  local username="$1"
  # Check if the username is provided (not empty).
  if [[ -z "$username" ]]; then
    # Log an error message and exit with code 3 if username is not provided.
    logger "ERROR! Username is not provided" "no_log"
    exit 3
  fi
  # Get the login shell for the user by querying the passwd database.
  # `getent passwd "$username"` retrieves the user entry.
  # `cut -d: -f7` extracts the 7th field (the shell) from the entry.
  local user_shell
  user_shell=$(getent passwd "$username" | cut -d: -f7)
  echo "$user_shell"
}

#######################################
# Get the user's uid based on the username.
# Globals:
#   None
# Arguments:
#   User to check.
# Returns:
#   User uid, 3 on error.
#######################################
get_user_uid() {
  local username="$1"
  # Check if the username is provided (not empty).
  if [[ -z "$username" ]]; then
    # Log an error message and exit with code 3 if username is not provided.
    logger "ERROR! Username is not provided." "no_log"
    exit 3
  fi
  # Get the uid for the user by querying the passwd database.
  # `getent passwd "$username"` retrieves the user entry.
  # `cut -d: -f3` extracts the 7th field (the user uid) from the entry.
  local user_uid
  user_uid=$(getent passwd "${username}" | cut -d: -f3)
  echo "$user_uid"
}

#######################################
# Get any user other than root with UID=0.
# Globals:
#   None
# Arguments:
#   None
# Returns:
#   Total amount of non-root users with UID=0, exit with code 2.
#######################################
# New function to check
get_users_with_uid0() {
  local -i uid0_users_count=0
  local uid0_users_details=""
  # Loop through each line of the passwd file, splitting by colon.
  while IFS=: read -r username _ uid _; do
  # Check if the UID is 0 and the username is not root.
  if [[ "${uid}" -eq 0 && "${username}" != "root" ]]; then
    # Log a critical message if a non-root user with UID=0 is found.
    logger "CRITICAL! Non-root user ${username} has UID=0"
    # Increment the counter for users with UID=0.
    (( uid0_users_count += 1 ))
    # Append the user details.
    uid0_users_details+="${username}, "
  fi
  done < <(getent passwd) # Use getent to read the passwd database.
  # If any non-root users with UID=0 were found.
  if [[ "${uid0_users_count}" -gt 0 ]]; then
    # Invoke function to set lock file.
    set_lock
    # Remove trailing comma and space.
    uid0_users_details="${uid0_users_details%, }"
    # Log a critical message with the list of non-root users with UID=0.
    logger "CRITICAL! Non-root users with UID=0 found (${uid0_users_count}): ${uid0_users_details}" "no_log"
    # Exit with code 2 to indicate that any non-root users with UID=0 were found.
    exit 2
  fi
}

#######################################
# Get list of cPanel users from WHM API
# Globals:
#   CPANEL_USERS_LIST
# Arguments:
#   None
# Returns:
#   cPanel users list.
#######################################
get_cpanel_users() {
  # Get cPanel users using WHM API call and format the output.
  CPANEL_USERS_LIST=$(whmapi1 --output=jsonpretty listaccts | jq '.data.acct[].user' | tr -d "\"" | sort)
}

#######################################
# Get list of system users excluding cPanel users
# Globals:
#   CPANEL_USERS_LIST
#   SYSTEM_USERS
# Arguments:
#   None
# Returns:
#   System users list.
#######################################
get_system_users() {
  # Read each username from the list of system users.
  while read -r username; do
    # Check if the username is not present in the cPanel users list.
    # `grep -q "^${username}$"` searches for an exact match of the username.
    # `<<< "${CPANEL_USERS_LIST}"` provides the cPanel users list as input.
    if ! grep -q "^${username}$" <<< "${CPANEL_USERS_LIST}"; then
      # Add the username to the SYSTEM_USERS array if not in the cPanel users list.
      SYSTEM_USERS+=("${username}")
    fi
  done < <(getent passwd | cut -d: -f1 | sort) # Get the sorted list of system usernames.
}

#######################################
# Get list of system users with enabled shell.
# Globals:
#   EXCLUDED_SHELL_PATTERNS
#   SYSTEM_USERS_WITH_ENABLED_SHELL
#   SYSTEM_USERS
# Arguments:
#   None
# Returns:
#   System users with enabled shell list.
#######################################
get_system_users_with_enabled_shell() {
  # Iterate over each user in the SYSTEM_USERS array.
  for user in "${SYSTEM_USERS[@]}"; do
    # Get the login shell for the user by calling the get_user_shell function.
    local user_shell
    user_shell=$(get_user_shell "${user}")
    # Check if the user's shell does not match any of the excluded shell patterns.
    # `grep -q -E "${EXCLUDED_SHELL_PATTERNS}"` searches for any match of the excluded patterns.
    # `<<< "${user_shell}"` provides the user’s shell as input.
    if ! grep -q -E "${EXCLUDED_SHELL_PATTERNS}" <<< "${user_shell}"; then
      # Add the user to the SYSTEM_USERS_WITH_ENABLED_SHELL array if their shell is not excluded.
      SYSTEM_USERS_WITH_ENABLED_SHELL+=("${user}")
    fi
  done
}

#######################################
# Get unauthorized system users with enabled shell
# Globals:
#   AUTHORIZED_USERS
#   SYSTEM_USERS_WITH_ENABLED_SHELL
# Arguments:
#   None
# Returns:
#   Total amount of unauthorized system users with exit code 2
#######################################
get_unauthorized_system_users() {
  local -i unauthorized_system_users_count=0
  local unauthorized_system_users_details=""
  # Iterate over each user in the SYSTEM_USERS_WITH_ENABLED_SHELL array.
  for user in "${SYSTEM_USERS_WITH_ENABLED_SHELL[@]}"; do
    local authorized_user=false
    # Check if the user is in the authorized users list.
    if [[ -n "${AUTHORIZED_USERS[$user]+x}" ]]; then
      authorized_user=true
    fi
    # If the user is not in the authorized users list.
    if [[ "${authorized_user}" == false ]]; then
      # Get the user's UID by calling the get_user_uid function.
      local user_uid
      user_uid=$(get_user_uid "${user}")
      # Get the user's shell by calling the get_user_shell function.
      local user_shell
      user_shell=$(get_user_shell "${user}")
      # Log a message to log file if the user is not in the authorized users list.
      logger "CRITICAL! Found unauthorized system user ${user} with uid ${user_uid} and enabled ${user_shell} shell."
      # Increment the counter for unauthorized system users.
      (( unauthorized_system_users_count += 1 ))
      # Append the unauthorized user details to the string.
      unauthorized_system_users_details+="${user} (uid ${user_uid}), "
    fi
  done

  # If any unauthorized system users were found.
  if [[ "${unauthorized_system_users_count}" -gt 0 ]]; then
    # Invoke function to set lock file.
    set_lock
    # Remove trailing comma and space from details string.
    unauthorized_system_users_details="${unauthorized_system_users_details%, }"
    # Log a critical message with the count and details of unauthorized users.
    logger "CRITICAL! Unauthorized system users found (${unauthorized_system_users_count}): ${unauthorized_system_users_details}" "no_log"
    # Exit with code 2 to indicate that unauthorized users were found.
    exit 2
  fi
}

#######################################
# Get unauthorized system users recent logins using authentication logs
# Globals:
#   AUTHORIZED_USERS
#   AUTH_LOG
#   SYSTEM_USERS_WITH_ENABLED_SHELL
# Arguments:
#   None
# Returns:
#   Total amount of unauthorized system users to login exit code 2
#######################################
get_unauthorized_system_users_logins() {
  local threshold_timestamp
  # Get the timestamp from 20 minutes ago
  threshold_timestamp=$(get_minutes_ago_timestamp 20)
  local -i unauthorized_system_users_logins_count=0
  local unauthorized_system_users_logins_details=""
  # Iterate over each user in the SYSTEM_USERS_WITH_ENABLED_SHELL array.
  for user in "${SYSTEM_USERS_WITH_ENABLED_SHELL[@]}"; do
    local matches
    # Search for log entries related to "sshd" where a session was opened for the specified user.
    # Filter the results to include only those entries that are equal to or greater than the threshold timestamp.
    matches=$(grep -E "sshd.*: session opened for user ${user}" "${AUTH_LOG}" | \
             awk -v threshold="${threshold_timestamp}" '$0 >= threshold')
    # If there are any log entries matching the criteria.
    if [[ -n "${matches}" ]]; then
      local ssh_login_allowed=false
      # If the user is in the authorized users list and has ssh login allowed
      if [[ -n "${AUTHORIZED_USERS[$user]+x}" ]]; then
        if [[ "${AUTHORIZED_USERS[$user]}" == "ssh_allowed" ]]; then
          ssh_login_allowed=true
        fi
      fi
      # If the user is not in the authorized users list or has ssh_denied
      if [[ "${ssh_login_allowed}" == false ]]; then
        # Get the user's UID by calling the get_user_uid function.
        local user_uid
        user_uid=$(get_user_uid "${user}")
        # Get the user's shell by calling the get_user_shell function.
        local user_shell
        user_shell=$(get_user_shell "${user}")
        # Log messages to log file if unauthorized system user login('s) found.
        logger "CRITICAL! Recent login found for unauthorized system user ${user} with uid ${user_uid} and enabled ${user_shell} shell."
        logger "According to the following record('s) from ${AUTH_LOG} log: ${matches}"
        # Increment the counter for unauthorized system users logins.
        (( unauthorized_system_users_logins_count += 1 ))
        # Append the unauthorized user logins details to the string.
        unauthorized_system_users_logins_details+="${user} (uid ${user_uid}), "
      fi
    fi
  done
  # If any unauthorized system users logins were found.
  if [[ "${unauthorized_system_users_logins_count}" -gt 0 ]]; then
    # Invoke function to set lock file.
    set_lock
    # Remove trailing comma and space.
    unauthorized_system_users_logins_details="${unauthorized_system_users_logins_details%, }"
    logger "CRITICAL! Unauthorized system users SSH logins found (${unauthorized_system_users_logins_count}): ${unauthorized_system_users_logins_details}" "no_log"
    # Exit with code 2 to indicate that unauthorized users logins were found.
    exit 2
  fi
}

#######################################
# Get unauthorized system users and their logins.
# Globals:
#   AUTHORIZED_USERS_FILE
#   CPANEL_USERS_LIST
# Arguments:
#   None
#######################################
main() {
  # Check if the file exists and is not empty
  if [[ -f "${AUTHORIZED_USERS_FILE}" && -s "${AUTHORIZED_USERS_FILE}" ]]; then
    # Read the file line by line
    while IFS=, read -r user access; do
      # Skip lines that start with a hash (#) or if either user or access is empty.
      if [[ "$user" =~ ^# || -z "$user" || -z "$access" ]]; then
        continue
      fi
      # Trim any extra whitespace around user and access
      user=$(echo "$user" | xargs)
      access=$(echo "$access" | xargs)
      # Ensure both user and access are non-empty before updating the array
      if [[ -n "$user" && -n "$access" ]]; then
        # Update the array with valid user and access
        AUTHORIZED_USERS["$user"]="$access"
      else
        # Log an error message and exit with code 3 if user or access is invalid
        logger "ERROR! Invalid username or access in ${AUTHORIZED_USERS_FILE}. User: '${user}', Access: '${access}'." "no_log"
        exit 3
    fi
    done < "${AUTHORIZED_USERS_FILE}"
  else
    # Log an error message and exit with code 3 if the file is empty or not found
    logger "ERROR! File ${AUTHORIZED_USERS_FILE} is either empty or not found." "no_log"
    exit 3
  fi
  # Invoke function to get any user other than root with UID=0.
  get_users_with_uid0
  # Check if cPanel binary exists before calling the function.
  if [ ! -f /usr/local/cpanel/cpanel ]; then
    # Define cPanel users list as empty in case if there is no cPanel.
    CPANEL_USERS_LIST=""
  else
    # Invoke function to get cPanel users list if cPanel is installed.
    get_cpanel_users
  fi
  # Invoke function to get list of system users excluding cPanel users.
  get_system_users
  # Invoke function to get list of system users with enabled shell.
  get_system_users_with_enabled_shell
  # Invoke function to get recent logins of unauthorized system users.
  get_unauthorized_system_users_logins
  # Invoke function to get unauthorized system users with enabled shell.
  get_unauthorized_system_users
  # Handle the case when no unauthorized users were found but the lock file exists.
  if [[ -f "$LOCK_FILE" ]]; then
    # Invoke function to control lock file.
    set_lock
  else
    # Print a message if everything is ok and exit with code 0.
    logger "OK. Unauthorized system users and logins are NOT detected." "no_log"
    exit 0
  fi
}

# Run a default mode.
if [[ -z $* ]]; then
  # Invoke main function
  main
fi

Youez - 2016 - github.com/yon3zu
LinuXploit