#!/bin/sh ####################################################################################################### # This service monitors the auditd log for suspicious events performed by mysql and apache signifying # # a hacker gained a shell. If a shell is detected, the service purges all suspicious processes and # # nework connections, and add suspicious IPs for established connections to the iptables ipset block # # list. The algorithm works as follows: # # 1. Run ausearch to examine the recent auditd logs for a key containing ALERT # # 2. Extract the ppids in the ALERT event logs # # 3. Create a process tree for each ppid (related processes associated with ALERT event) # # 4. Kill each process in the tree except the root httpd or mysql processes # # 5. Add all suspicious network connections to the iptables ipset block list # # 6. Kill all external network connections # # 7. Log relevant details (network connections, running processs, actions performed) for analysis # # 8. Send an alert slack message # ####################################################################################################### logf=/tmp/hacked.log; touch $logf # attack information log ignores=/tmp/ignores.txt; touch $ignores # list of PPIDs from ausearch already processed users=("apache" "mysql") # service accounts monitored by this process process=("httpd" "mysqld") # root process names that should never be killed rootprocs=("httpd" "php-fpm" "mysqld" "") rootprocs2=("sh" "convert") trusted=("127.0.0" "ssm-agent" "wazuh" "agent") # set of trusted network connections trustconns="/tmp/tconns.txt"; rm -f "$trustconns" # file containing set of trusted net connections to use in grep while true; do if [ ! -f "$trustconns" ]; then cat /dev/null > $trustconns for t in "${trusted[@]}"; do echo "$t" >> $trustconns; done # write 'trusted' network connections into a list for grep call fi tmessage="" # initialize auditd hacker detection message # query for recent ALERT events alert=$(/sbin/ausearch -ts recent -k ALERT 2>/dev/null | grep ALERT | grep -v CONFIG_CHANGE | grep -v -f $ignores) # is there are recent (last 10 mins) ALERT log? # grep out previous ALERTs already processed if [ -n "$alert" ]; then # hacker shell detected ps -eaf >> $logf # save the ALERT event logs for incident handling echo "$alert" >> $logf # get the PPID(s) that triggered the ALERT event log ppids=( $(echo "$alert" | sed -rn 's/.* ppid=([0-9]+) pid.*/\1/p') ) # filter list for unique PPIDs uppids=($(for pid in "${ppids[@]}"; do echo "${pid}"; done | sort -u)) echo "uppids=$uppids" >> $logf # traverse the list of unique PPIDs for ppid in ${uppids[@]}; do # create a list of PPID ignores (used above) so the loop doesn't repeatedly process the same hack events # since the ALERT logs will exist for 10 minutes echo "ppid=$ppid" >> $ignores # determine if PPID is still active before establishing a killchain running=$(ps -eaf | grep -q $ppid | grep -v grep) if [ -n "$running" ]; then # build the process chain for the ppid chain=$(pstree -s -p -a -G -l $ppid 2>/dev/null) # build a killchain of PIDs by extracting the PID from each process chain level killchain=( $(echo "$chain" | sed -rn 's/.*,([0-9]+).*$/\1/p') ) echo "Killchain: ${killchain[@]}" >> $logf len="${#killchain[@]}" # ensure the killchain is not empty if [ "$len" -gt "0" ]; then # traverse the killchain from the bottom up for (( i=$len-1; i>=0; i-- )); do # get user of process in killchain user=$(/bin/ps -o uname= -p ${killchain[$i]}) runningprocess=$(/bin/ps -o cmd= -p ${killchain[$i]}) # only kill process if user is apache/mysql and process is not the root httpd/mysqld process if [[ "${users[@]}" =~ "$user" ]] && [[ ! "$runningprocess" =~ "$process" ]]; then # write processes killed in log for analysis echo "kill -9 ${killchain[$i]}" >> $logf kill -9 ${killchain[$i]} 2>/dev/null fi done fi fi done # get potentially suspicious external IPs of established connections and add to permblacklist; ignore the known trusted connections myip=$(dig +short myip.com) kips=$(netstat -tnp | grep -v -f "$trustconns" | grep -v "$myip" | sed -rn '/^.*[0-9]+\/.+$/p' | awk '{print $5}' | sed -rn 's/([0-9]+):.*/\1/p') if [ -n "$kips" ]; then for ip in $kips; do /usr/sbin/ipset add --exist permblacklist $ip echo "added $ip to permblacklist" >> $logf done fi # get potentially suspicious external network PIDs of sessions and kill them; ignore the known trusted connections continue=true while $continue; do netstat -tnp >> $logf kpids=$(netstat -tnp | grep -v -f "$trustconns" | awk '{print $7}' | sed -rn 's/([0-9]+)\/.*/\1/p') if [ -n "$kpids" ]; then for k in $kpids; do echo "kill pid: $k" >> $logf kill -9 "$k" | tee -a $logf done sleep 1 else continue=false fi done tmessage=$(echo "$alert" | sed -rn 's/.*(uid=[0-9]+) .* (comm=\".*?\") exe=.*/\1 \2/p') else # clear the already processed PPIDs (ignore file) after the ausearch recent timeline recents=$(/sbin/ausearch -ts recent -k ALERT 2>/dev/null | grep ALERT | grep -v CONFIG_CHANGE) if [ -z "$recents" ]; then cat /dev/null > $ignores; fi fi if [ -n "$tmessage" ]; then echo "HACKED - $tmessage" >> $logf curl -s -X POST -H 'Content-type: application/json' --data '{"text":"ALERT: chueyise hacked - Examine /tmp/hacked.log for details"}' https://hooks.slack.com/services/XXXXXXXXX > /dev/null fi sleep 1 done