User Tools

Site Tools


howtos:ssh-su_trap

SSH and su Trap

If you need to know if someone is abusing your account on a Linux host this script is a simple and brilliant way of knowing and alerting about it.

The idea is that someone can either “su” into your account (like root or someone with root privs) or ssh with your credentials then it pics it up and send a mail.

This idea was created by @freebsdfrau

Place the script in the file “~/.bash_profile”.

ME="example@example.com"       # Set your email address here.
BASTION="10.10.1.1 192.168.7.6"           # Space-separated list of trusted host IPs.

# Function to send email
send_alert() {
  local user=$1
  local eventType=$2
  mail -s "[SECOPS] Unexpected $eventType" "$ME" <<-EOM
Alert triggered at $(date "+%Y-%m-%d %H:%M:%S")
Event Details:
- From IP: $SSH_CLIENT_HOST
- Hostname: $HOSTNAME
- System Load: $(uptime)
- Disk Usage:
$(df -h | egrep 'Filesystem|/dev/sd|/dev/nv')

- Recent Commands:
$(tail -n 10 ~/.bash_history)

- Currently Logged-in Users:
$(who)

- Active Network Connections:
$(ss -tuln)

- Environment Variables:
$(printenv | grep -vE 'AWS_SECRET|PASSWORD')
EOM
}

# Check for SSH connections from unexpected hosts.
if [ "${SSH_CLIENT}" ]; then
  # Extract the IP address of the SSH client.
  SSH_CLIENT_HOST="${SSH_CLIENT%%[$IFS]*}"

  # Variable to check if the SSH connection is from an expected host.
  _expected=

  # Loop over the list of trusted hosts.
  for _host in $BASTION; do
    if [ "${SSH_CLIENT_HOST}" = "$_host" ]; then
      _expected=1
      break
    fi
  done

  # If the host was not expected, send an email alert.
  if [ -z "$_expected" ]; then
    send_alert $SSH_CLIENT_HOST "ssh"
  fi

  # Clean up the variable for the next session.
  unset _expected _host
else
  # If not an SSH session, check for unexpected 'su' command usage.
  # The awk command parses the process list for the 'su' command.
  # The following awk script is designed to parse the output of 'ps auxwwf' to identify unexpected user changes that lead to the current shell process.
  # It operates as follows:
  #
  # 1. BEGIN Block:
  #    - Reads the first line of the input (usually headers from 'ps auxwwf') into the variable 'hdr'.
  #    - Stores this header line in the first index of the array 'lines'.
  #    - Uses the 'match()' function to find the position of the substring "COMMAND" in 'hdr', which indicates the start of the command column in the process list.
  #    - Stores the start position of "COMMAND" in the variable 'C' using 'RSTART', which holds the index of the first character where "COMMAND" is found.
  #
  # 2. Main Processing Block:
  #    - Stores each line of input in the 'lines' array, preserving the full output of 'ps auxwwf'.
  #
  # 3. Conditional Block on Field 2 (PID matching):
  #    - Checks if the second field (PID) of the current line matches the PID of the current shell ($$).
  #    - If a match is found, 'P' is set to the current line number (NR), and 'U' is set to the user running the process (field 1).
  #
  # 4. END Block:
  #    - Iterates backward from the line identified by 'P' (where the current shell PID was found) to trace the process hierarchy.
  #    - Checks each line by comparing the starting user of the command against the user 'U'. If a line with a different user is found:
  #      - If the new user is 'root', updates 'U' to 'root' and continues (to track who elevated to root).
  #      - If the new user is not 'root', sets 'found' to 1 and breaks the loop to capture this transition.
  #    - Stops the loop if the line doesn't begin with a space (indicating a shift in the process hierarchy or reaching the top).
  #    - Prints the last user encountered in this trace (either the non-root user that was found or 'root' if the escalation chain didn't lead to a non-root user).
  #
  # This script helps in detecting privilege escalations and user context switches that are not initiated by the logged-in user, potentially indicating unauthorized actions or security breaches.

  SU_USER=$(ps auxwwf | awk -v pid=$$ '
    BEGIN {
      getline hdr
      lines[1]=hdr
      match(hdr, "COMMAND")
      C=RSTART
    }
    {
      lines[NR]=$0
    }
    $2==pid {
      P=NR
      U=$1
    }
    END {
      for (i = P; i > 0; i--) {
        if ($0 == lines[i]) {
          if ($1 != U) {
            if ($1 == "root") {
              U=$1
            } else {
              found=1
              break
            }
          }
          if (substr($0, C, 1) != " ") {
            break
          }
        }
      }
      print found ? $1 : "root"
    }'
  )

  # If an unexpected user switch is detected, send an email alert.
  if [ "$SU_USER" != "$USER" ]; then
    send_alert $SU_USER "su"
  fi
fi
howtos/ssh-su_trap.txt · Last modified: 27/04/2024 18:06 by domingo