#!/usr/bin/env bash
##
# Linux Malware Detect - CVE-2026-41940 one-shot defensive remediation
#             (C) 2002-2025, R-fx Networks <proj@rfxn.com>
#             (C) 2025, Ryan MacDonald <ryan@rfxn.com>
# This program may be freely redistributed under the terms of the GNU GPL v2
##
# Companion to internals/host_meta. cPanel-only, fingerprint-gated, sentinel-armed.
# Triggered from install.sh on first install/upgrade. Bash 4.1 / EL6 floor.
##

[ -e /usr/local/cpanel/cpanel ] || exit 0
[ -e /usr/local/maldetect/cve-2026-41940.done ] && exit 0
[ -d /usr/local/maldetect ] || exit 0

# Arm sentinel before any work; past this point no failure (source error,
# prerun hang, OOM, kill -9) can cause re-execution on next install.sh.
# Operator reset: rm /usr/local/maldetect/cve-2026-41940.done
: > /usr/local/maldetect/cve-2026-41940.done 2>/dev/null
[ -e /usr/local/maldetect/cve-2026-41940.done ] || exit 0

ver=1.6.6

. /usr/local/maldetect/conf.maldet 2>/dev/null              || exit 0
. /usr/local/maldetect/internals/internals.conf 2>/dev/null || exit 0
. /usr/local/maldetect/internals/functions 2>/dev/null      || exit 0

# Force quarantine action regardless of operator conf; we only act on
# explicit-known-IOC matches that mirror host_meta exactly.
quarantine_hits=1
quarantine_clean=0
quarantine_suspend_user=0

prerun

scanid="$datestamp.$$"
scan_session="$tmpdir/.sess.$$"
nsess="$sessdir/session.$datestamp.$$"
nsess_hits="$sessdir/session.hits.$datestamp.$$"
touch "$scan_session"
scan_start_hr=`date +"%b %e %Y %H:%M:%S %z"`
scan_start=`date +"%s"`
spath="$inspath/internals/cve-2026-41940"
hrspath="CVE-2026-41940 one-shot defensive remediation"
email_subj="CVE-2026-41940 remediation report from $(hostname)"

eout "{cve-2026-41940} starting one-shot cPanel defensive remediation (scanid: $scanid)" 1

_quar() { record_hit "$1" "$2"; quarantine "$1" "$2"; }

_cve_letter_A() {
    local pid_dir target pid f
    for pid_dir in /proc/[0-9]*/; do
        target=$(readlink "${pid_dir}exe" 2>/dev/null)
        case "$target" in
            /root/sshd|/root/sshd\ *)
                pid="${pid_dir#/proc/}"; pid="${pid%/}"
                kill -9 "$pid" 2>/dev/null \
                    && eout "{cve-2026-41940}A killed pid $pid (/root/sshd)" 1
                ;;
        esac
    done
    [ -f /root/sshd ] && _quar /root/sshd "{CVE-2026-41940}A.sshd_encryptor"
    for f in /root/*.sorry /root/.*.sorry; do
        [ -f "$f" ] && _quar "$f" "{CVE-2026-41940}A.sorry_marker"
    done
}

_cve_letter_C() {
    local pid_dir target pid p s f
    for pid_dir in /proc/[0-9]*/; do
        target=$(readlink "${pid_dir}exe" 2>/dev/null)
        case "$target" in
            */nuclear.x86|*/nuclear.x64|*/nuclear.x86_64|*/nuclear.aarch64|*/nuclear.arm|*/nuclear.mips|\
            */nuclear.x86\ *|*/nuclear.x64\ *|*/nuclear.x86_64\ *|*/nuclear.aarch64\ *|*/nuclear.arm\ *|*/nuclear.mips\ *)
                pid="${pid_dir#/proc/}"; pid="${pid%/}"
                kill -9 "$pid" 2>/dev/null \
                    && eout "{cve-2026-41940}C killed pid $pid ($target)" 1
                ;;
        esac
    done
    for p in /tmp /root /var/tmp; do
        for s in x86 x64 x86_64 aarch64 arm mips; do
            f="$p/nuclear.$s"
            [ -f "$f" ] && _quar "$f" "{CVE-2026-41940}C.nuclear_dropper"
        done
    done
}

# Campaign domain (4ef72197.cpx.local) is the unique IOC; sptadm-name alone is FP-prone.
_cve_letter_D() {
    local has_campaign=0 has_sptadm=0 whmapi
    grep -qF '4ef72197.cpx.local' /etc/userdomains     2>/dev/null && has_campaign=1
    grep -qF '4ef72197.cpx.local' /etc/trueuserdomains 2>/dev/null && has_campaign=1
    [ -d /home/sptadm ]                && has_sptadm=1
    [ -f /var/cpanel/users/sptadm ]    && has_sptadm=1
    [ -d /var/cpanel/userdata/sptadm ] && has_sptadm=1
    if [ "$has_campaign" -eq 0 ] || [ "$has_sptadm" -eq 0 ]; then
        [ "$has_campaign" -ne "$has_sptadm" ] \
            && eout "{cve-2026-41940}D.fingerprint_miss campaign=$has_campaign sptadm=$has_sptadm; skip" 1
        return 0
    fi

    whmapi=$(command -v whmapi1 2>/dev/null)
    [ -z "$whmapi" ] && whmapi=/usr/local/cpanel/bin/whmapi1
    if [ ! -x "$whmapi" ]; then
        eout "{cve-2026-41940}D whmapi1 not found, manual suspendreseller required for sptadm" 1
        return 1
    fi
    if "$whmapi" suspendreseller user=sptadm reason='CVE-2026-41940 IR (one-shot)' >/dev/null 2>&1; then
        eout "{cve-2026-41940}D sptadm reseller suspended; rollback: $whmapi unsuspendreseller user=sptadm" 1
    else
        eout "{cve-2026-41940}D whmapi1 suspendreseller user=sptadm returned non-zero" 1
    fi

    if [ -d /home/sptadm ]; then
        echo "${utime}:${hostid}:{CVE-2026-41940}D.reseller_suspended::::::/home/sptadm" >> "$hits_history"
        echo "{CVE-2026-41940}D.reseller_suspended : /home/sptadm" >> "$scan_session"
    fi
}

_cve_letter_H() {
    [ -f /tmp/seobot.zip ] && _quar /tmp/seobot.zip "{CVE-2026-41940}H.seobot_dropper"
}

_cve_letter_I() {
    local pid_dir target pid f hn
    for pid_dir in /proc/[0-9]*/; do
        target=$(readlink "${pid_dir}exe" 2>/dev/null)
        case "$target" in
            /root/.local/bin/system-service|/root/.local/bin/system-service\ *)
                pid="${pid_dir#/proc/}"; pid="${pid%/}"
                kill -9 "$pid" 2>/dev/null \
                    && eout "{cve-2026-41940}I killed pid $pid ($target)" 1
                ;;
        esac
    done
    for f in /etc/profile.d/system_profiled_service.sh /root/.local/bin/system-service; do
        [ -f "$f" ] || continue
        case "$f" in
            *profile.d*) hn="{CVE-2026-41940}I.profiled_persistence" ;;
            *)           hn="{CVE-2026-41940}I.local_bin_systemservice" ;;
        esac
        _quar "$f" "$hn"
    done
}

# Fingerprint-gate every systemctl/udev call to avoid touching foreign units sharing names.
_cve_letter_J() {
    local sysctl udevadm unit f
    sysctl=$(command -v systemctl 2>/dev/null)
    udevadm=$(command -v udevadm  2>/dev/null)

    for unit in /etc/systemd/system/dbus-broker-helper.service \
                /usr/lib/systemd/system/dbus-broker-helper.service; do
        [ -f "$unit" ] || continue
        if grep -qE 'ExecStart=.*dbus-broker-helper' "$unit" 2>/dev/null \
           && grep -qF '/usr/share/dbus-1/dbus-broker-helper' "$unit" 2>/dev/null; then
            [ -x "$sysctl" ] && "$sysctl" disable --now dbus-broker-helper.service >/dev/null 2>&1 \
                && eout "{cve-2026-41940}J disabled dbus-broker-helper.service" 1
            _quar "$unit" "{CVE-2026-41940}J.dbus_broker_helper"
        else
            eout "{cve-2026-41940}J.fingerprint_miss skipped $unit" 1
        fi
    done
    [ -f /usr/share/dbus-1/dbus-broker-helper ] \
        && _quar /usr/share/dbus-1/dbus-broker-helper "{CVE-2026-41940}J.dbus_broker_helper"

    f="/etc/udev/rules.d/89-cdrom-id-helper.rules"
    if [ -f "$f" ]; then
        if grep -qF 'RUN+="/usr/lib/udev/cdrom-id-helper"' "$f" 2>/dev/null; then
            [ -x "$udevadm" ] && "$udevadm" control --reload-rules >/dev/null 2>&1 \
                && eout "{cve-2026-41940}J reloaded udev rules" 1
            _quar "$f" "{CVE-2026-41940}J.udev_cdrom_helper"
        else
            eout "{cve-2026-41940}J.fingerprint_miss skipped $f" 1
        fi
    fi
    [ -f /usr/lib/udev/cdrom-id-helper ] \
        && _quar /usr/lib/udev/cdrom-id-helper "{CVE-2026-41940}J.udev_cdrom_helper"
}

_cve_letter_M() {
    local f m_hit=0
    [ -f /root/c3pool/xmrig ]            && m_hit=1
    [ -f /root/c3pool/config.json ]      && m_hit=1
    [ -f /root/moneroocean/xmrig ]       && m_hit=1
    [ -f /root/moneroocean/config.json ] && m_hit=1
    [ "$m_hit" -eq 0 ] \
        && ps -eo args= 2>/dev/null | grep -qE '^\./\.?ld-linux\.so( |$)|^\./https .*-a rx/|^\./python3 .*--donate-level' \
        && m_hit=1
    [ "$m_hit" -eq 0 ] && return 0

    pkill -9 -x xmrig                          2>/dev/null && eout "{cve-2026-41940}M killed xmrig" 1
    pkill -9 -f '\./\.?ld-linux\.so( |$)'      2>/dev/null && eout "{cve-2026-41940}M killed ld-linux.so camouflage" 1
    pkill -9 -f '^\./https .*-a rx/'           2>/dev/null && eout "{cve-2026-41940}M killed https camouflage" 1
    pkill -9 -f '^\./python3 .*--donate-level' 2>/dev/null && eout "{cve-2026-41940}M killed python3 camouflage" 1
    for f in /root/c3pool/xmrig      /root/c3pool/config.json \
             /root/moneroocean/xmrig /root/moneroocean/config.json; do
        [ -f "$f" ] && _quar "$f" "{CVE-2026-41940}M.xmrig_pool"
    done
}

_cve_letter_P() {
    [ -f /tmp/codeItems3 ] && _quar /tmp/codeItems3 "{CVE-2026-41940}P.codeitems3_loader"
}

_cve_letter_R() {
    local f r_hit=0
    [ -f /dev/shm/.gs ]                   && r_hit=1
    [ -f /root/.config/htop/defunct.dat ] && r_hit=1
    [ -f /root/.config/htop/lscgib.dat ]  && r_hit=1
    [ -f /root/.config/dbus/gs-dbus.dat ] && r_hit=1
    [ "$r_hit" -eq 0 ] \
        && ps -eo comm= 2>/dev/null | grep -qE '^(defunct|gs-dbus|lscgib)$' \
        && r_hit=1
    [ "$r_hit" -eq 0 ] && return 0

    pkill -9 -x defunct 2>/dev/null && eout "{cve-2026-41940}R killed defunct" 1
    pkill -9 -x gs-dbus 2>/dev/null && eout "{cve-2026-41940}R killed gs-dbus" 1
    pkill -9 -x lscgib  2>/dev/null && eout "{cve-2026-41940}R killed lscgib"  1
    for f in /dev/shm/.gs                   /root/.config/htop/defunct.dat \
             /root/.config/htop/lscgib.dat  /root/.config/dbus/gs-dbus.dat; do
        [ -f "$f" ] && _quar "$f" "{CVE-2026-41940}R.gsocket_marker"
    done
}

_cve_letter_A
_cve_letter_C
_cve_letter_D
_cve_letter_H
_cve_letter_I
_cve_letter_J
_cve_letter_M
_cve_letter_P
_cve_letter_R

scan_end=`date +"%s"`
scan_end_hr=`date +"%b %e %Y %H:%M:%S %z"`
scan_et=$[scan_end-scan_start]
file_list_et=0

tot_hits=`$wc -l "$scan_session" 2>/dev/null | awk '{print$1}'`
[ -z "$tot_hits" ] && tot_hits=0
tot_files="$tot_hits"
tot_cl=0

eout "{cve-2026-41940} remediation completed: $tot_hits action(s) taken, time ${scan_et}s" 1

if [ "$tot_hits" != "0" ]; then
    cat > "$nsess" <<EOF
==========================================================================
CVE-2026-41940 Defensive Report
==========================================================================

CVE-2026-41940 is a cPanel session-handling vulnerability disclosed on April 28 2026.
See the cPanel advisory at:
https://support.cpanel.net/hc/en-us/articles/40073787579671

LMD's defensive companion runs once per host on first install or upgrade, checks for a
narrow set of indicators that follow known exploitation, and acts on any matches it finds.
This report summarizes that run.

  Host:       $(hostname)
  Scan time:  $scan_start_hr (${scan_et}s)
  Scan ID:    $scanid
  Actions:    $tot_hits

  Action log:
$(awk -F' : ' '{printf "    %s\n      %s\n", $1, $2}' "$scan_session")

The matches above should be investigated further but do not by themselves constitute a
full compromise assessment. The companion checks a fixed list of file and process
indicators and does not inspect cPanel session files, secondary persistence, user or
reseller data, or credentials. For broader triage, two follow-up tools are recommended:

  1. cPanel's official session-file IOC sweep. Download ioc_checksessions_files.sh from
     the advisory KB article above and run it as root.

  2. rfxn's sessionscribe scanner:
       curl -fsSL https://raw.githubusercontent.com/rfxn/cpanel-sessionscribe/main/sessionscribe-ioc-scan.sh | bash
     (source: https://github.com/rfxn/cpanel-sessionscribe)

This was a one-time remediation attempt on this host. The companion will not run again
here, even after future LMD installs or upgrades. To undo anything listed in the action
log:

  Restore a quarantined file:  maldet --restore <basename>.<rnd>
  Unsuspend sptadm reseller:   whmapi1 unsuspendreseller user=sptadm

Linux Malware Detect v$ver <proj@rfxn.com>
EOF
    echo "$scanid" > "$sessdir/session.last"
    eout "{cve-2026-41940} report saved, view: maldet --report $scanid" 1
    if [ "$email_alert" == "1" ]; then
        if [ "$email_ignore_clean" == "1" ] && [ "$tot_hits" != "$tot_cl" ]; then
            genalert file "$nsess"
        elif [ "$email_ignore_clean" == "0" ]; then
            genalert file "$nsess"
        fi
        [ "$email_panel_user_alerts" == "1" ] && genalert panel "$nsess"
    fi
    mv "$scan_session" "$nsess_hits" 2>/dev/null
fi

rm -f "$scan_session" 2>/dev/null
: > /usr/local/maldetect/cve-2026-41940.done
exit 0
