├── README.md └── suspect.sh /README.md: -------------------------------------------------------------------------------- 1 | Suspect is a simple bash script that attempts to detect common IoCs without relying on (much) signature based detection or known "bad" files. 2 | 3 | It first looks at whether the kernel modules loaded are on disk and present in /proc/modules and kallsyms. 4 | 5 | Afterwards, it attempts to check whether /etc/ld.so.preload exists, and if it's being hidden by a preloaded library. Preload kits are a bit out of fashion at the moment, but I felt obliged to include this check. It's accomplished by means of using the LD_PRELOAD environment variable to preload libc, which changes the order of loading preference back to close to how it should be. 6 | 7 | Next, it does some process list stuff, primarly looking for running processes with deleted executables, and checking whether there's a lot of change in the processes. It also looks to see if any executables are compressed with UPX (I need to make this a bit more generic, instead of looking for the UPX! string, but eventually...) 8 | 9 | It does some simple checks, like checking for executables in /tmp/, whether there's more than one root user, whether the extended attributes are set on files (commonly used by rootkits like SHv4), whether any daemon processes have spawned a shell, what processes have keepalive network connections, the number of keys in authorized_keys. 10 | 11 | Suspect is not a replacement for comprehensive manual triage, and it won't find any php backdoors anywhere. 12 | -------------------------------------------------------------------------------- /suspect.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | libc="" 3 | proca="" 4 | #no readlink or strings any more! 5 | #we do use ss though. 6 | #i should probably add a function to prefer ss over netstat and allow the use of both 7 | #effort though 8 | 9 | 10 | banner() { 11 | echo "==== Compromise Triage Script ====" 12 | echo "Because signatures are bad, m'kay." 13 | echo "==================================" 14 | } 15 | 16 | modulecheck() { 17 | #god bless you, NSA's autorootkit 18 | kernelhashes="" 19 | modulea="" 20 | r1="" 21 | moduleb="" 22 | 23 | echo "[*] Comparing kallsyms and /proc/modules." 24 | kernelhashes=$(cat /proc/kallsyms | grep '\[' | awk '{ print substr($4,2,length($4)-2) }' | sort -u | tr --delete "\n" | sha256sum) 25 | if echo $kernelhashes | grep -q `cat /proc/modules | awk '{print $1}' | sort -u | tr --delete "\n" | sha256sum`; then 26 | echo "[*] kallsyms and /proc/modules match. Okay!" 27 | else 28 | echo "[!] kallsyms and /proc/modules DO NOT MATCH. Possible compromise." 29 | fi 30 | echo "[*] Checking if any modules are missing from disk." 31 | #i should really turn the below into a function 32 | echo $moduleb | tr ' ' '\n' | while read i;do 33 | modinfo -F filename $i >/dev/null 2>&1 34 | r1=$? 35 | if [ $r1 -eq "1" ]; then 36 | echo "[!] Module $i can't be found on disk!" 37 | moduleb="qqqqqqq" 38 | fi 39 | done 40 | if echo $moduleb | grep -vq "qqqqqqq"; then 41 | echo "[*] All modules found on disk." 42 | fi 43 | } 44 | 45 | preloadcheck() { 46 | r1="" 47 | r2="" 48 | echo "[*] Investigating ld.so.preload file." 49 | cat /etc/ld.so.preload 2>/dev/null 50 | r1=$(echo $?) 51 | LD_PRELOAD=$libc cat /etc/ld.so.preload 2>/dev/null 52 | r2=$(echo $?) 53 | if [ $r1 -ne $r2 ]; then 54 | echo "[!] /etc/ld.so.preload is being hidden, or something is going on!" 55 | else 56 | echo "[*] /etc/ld.so.preload is either non-existent or not hidden." 57 | if [ -f "/etc/ld.so.preload" ]; then 58 | echo "[*] Contents of ld.so.preload, review manually." 59 | cat /etc/ld.so.preload 60 | fi 61 | fi 62 | } 63 | 64 | missingproc() { 65 | echo "[*] Checking for processes with deleted executables." 66 | echo $proca | tr ' ' '\n' | while read i;do 67 | if file /proc/$i/exe | grep -q deleted; then 68 | echo "[!] PID $i is missing its executable." 69 | ps -p$i --no-header -o "%a" 70 | fi 71 | done 72 | } 73 | 74 | autostart() { 75 | if [ -f "$HOME/.config/autostart" ]; then 76 | echo "[!] $HOME/.config/autostart exists. Investigate" 77 | fi 78 | } 79 | 80 | changingproc() { 81 | procb="" 82 | procc="" 83 | procn="" 84 | echo "[*] Getting process lists." 85 | procb=$(ps -wweo "%p") 86 | echo "[*] Sleeping for three seconds." 87 | sleep 3 88 | procc=$(ps -wweo "%p") 89 | procn=$(echo $proca $procb $procc | tr ' ' '\n' | sort | uniq -u | wc -l) 90 | if [ $procn -gt "15" ]; then 91 | echo "[!] Seems like a lot of processes spawning." 92 | echo "[!] Investigate manually..." 93 | else 94 | echo "[*] There's not a whole lot of process changes." 95 | echo "[*] That doesn't mean there are no malicious processes." 96 | fi 97 | } 98 | 99 | oddexecs() { 100 | echo "[*] Finding weird executables in places they shouldn't be." 101 | find /tmp/ -executable -type f 2>/dev/null 102 | find /var/tmp -executable -type f 2>/dev/null 103 | } 104 | 105 | extraroot() { 106 | roots="" 107 | echo "[*] Checking the number of root users in /etc/passwd." 108 | roots=$(cat /etc/passwd | cut -d ":" -f3 | grep --color=never "^0$" | uniq -u | wc -l) 109 | if [ $roots -gt 1 ]; then 110 | echo "[*] You've got more than one user with uid 0." 111 | echo "[*] Investigate manually." 112 | else 113 | echo "[*] Just one root user, okay!" 114 | 115 | fi 116 | } 117 | 118 | chattrsia() { 119 | attribs="" 120 | echo "[*] Checking for extended attributes on system binaries..." 121 | attribs=$(lsattr -a /usr/bin/ /bin/ /sbin/ /usr/sbin/ 2>/dev/null | grep "\-ia\|^s") 122 | if echo $attribs | grep -q "\-\-"; then 123 | echo "[!] Extended attributes set on some files!" 124 | echo $attribs 125 | else 126 | echo "[*] No extended attributes set." 127 | fi 128 | } 129 | 130 | daemonshells() { 131 | echo "[*] Looking for any daemon processes running a shell..." 132 | ps -weFH | grep -v $(getent passwd | grep sh$ | cut -d ":" -f 1 | tr '\n' '|' | sed s'/|/\\\|/g' | sed s'/..$//') | grep sh 133 | } 134 | 135 | networks() { 136 | netb="" 137 | echo "[*] Getting a list of programs with keepalive connections." 138 | echo "[*] Investigate manually." 139 | netb=$(ss -taoup | grep keepalive | cut -d \" -f 3 | cut -d "=" -f 2 | cut -d "," -f 1 | sort | uniq) 140 | echo" $netb" 141 | #this once did something, but no longer - thanks debian 142 | } 143 | 144 | authkeys() { 145 | homedir="" 146 | echo "[*] Checking the authorized_keys for each user." 147 | homedir=$(getent passwd | grep sh$ | cut -d ":" -f 6) 148 | echo $homedir | tr ' ' '\n' | while read i;do 149 | if [ -f "$i/.ssh/authorized_keys" ]; then 150 | echo "[*] There's `cat "$i/.ssh/authorized_keys" | wc -l` keys in $i/.ssh/authorized_keys" 151 | fi 152 | done 153 | } 154 | 155 | upxcheck() { 156 | echo "[*] Checking for UPX'd binaries." 157 | echo $proca | tr ' ' '\n' | while read i;do 158 | if [ $(cat "/proc/$i/exe" 2>/dev/null | tr -dc '[:print:]' | grep -oi "UPX\!" | wc -l) -gt 1 ] ; then 159 | echo "[*] Proc $i is compressed with UPX!" 160 | ps --no-header -p$i -wwo "%p %u %a" 161 | fi 162 | done 163 | } 164 | 165 | ldinfect() { 166 | libs="" 167 | 168 | echo "[*] Testing for library-based stat hooks..." 169 | libs=$(cat /proc/self/maps | grep "xp 0" | grep -v `which cat`| grep -v "00\:00\ 0" | awk -F ' ' '{print $6}') 170 | echo $libs | tr ' ' '\n' | while read i;do 171 | if [ ! -f $i ]; then 172 | echo "[!] cat is loading a library that doesn't exist!" 173 | echo "[!] Investigate $i further". 174 | fi 175 | done 176 | } 177 | 178 | modified() { 179 | echo "[*] Checking for recent files in system locations." 180 | echo "[*] This may produce a lot of output." 181 | find /usr/bin/ /bin/ /lib/ /lib64/ /usr/lib/ /usr/lib64/ /etc/ /tmp/ -mtime -7 -type f 2>/dev/null 182 | } 183 | 184 | main() { 185 | banner 186 | echo "[*] Checking for suspicious behaviour." 187 | echo "[*] This is not a replacement for a manual audit." 188 | proca=$(ps -wweo "%p") 189 | libc=ldd $(which id) | awk -F " " '{print $3}' | grep --color=never libc 190 | autostart 191 | modulecheck 192 | missingproc 193 | ldinfect 194 | preloadcheck 195 | changingproc 196 | oddexecs 197 | extraroot 198 | chattrsia 199 | daemonshells 200 | networks 201 | authkeys 202 | upxcheck 203 | modified 204 | } 205 | 206 | main 207 | --------------------------------------------------------------------------------