##
# Linux Malware Detect — host metadata referer extension
#             (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
##
#
# Enriches lmd_referer with cheap on-disk telemetry — OS family, cPanel
# state, parsed cPanel build, IOC fingerprint. cdn.rfxn.com applies all
# classification; this emitter ships raw facts only. Bash 4.1 / EL6 floor.
# Defensive sourcing in internals.conf leaves the upstream lmd_referer
# intact if this file is missing or fails to parse.
#
# Format: LMD:<ver>:<hostid>:<os>:cp<Y|N>:b<tier.build|->:c<letters|N>:r<Y|N>
#
# Pattern letters (per IC-5790 PATTERNS_v6 + 2026-05-05 ps-hunt):
#   A=PAY-Encrypt    B=PAY-DBWipe   C=PAY-Mirai     D=PERS-Reseller
#   G=PERS-SSHKey    H=PAY-SeoBot   I=PERS-ProfileD J=PERS-Udev/Systemd
#   M=Miner          P=PHP-cronbot  R=Rev-shell(GSocket)
#
# r field: Y if cve-2026-41940 one-shot remediation has completed on this
# host (sentinel /usr/local/maldetect/cve-2026-41940.done present); N otherwise.
#

_lmd_meta_os() {
    local id ver
    if [ -r /etc/os-release ]; then
        # head -1 keeps a multi-line ID/VERSION_ID from injecting a newline
        # into the HTTP Referer header.
        id=$(grep -E '^ID='         /etc/os-release | head -1 | cut -d= -f2- | tr -d '"')
        ver=$(grep -E '^VERSION_ID=' /etc/os-release | head -1 | cut -d= -f2- | tr -d '"')
        [ -n "$id" ] && { printf '%s%s' "$id" "${ver%%.*}"; return; }
    fi
    if [ -r /etc/redhat-release ]; then
        case "$(head -1 /etc/redhat-release)" in
            *CloudLinux*release\ 6*) echo cloudlinux6 ;;
            *CentOS*release\ 6*)     echo centos6 ;;
            *)                       echo redhat ;;
        esac
        return
    fi
    echo other
}

_lmd_meta_cpanel() { [ -e /usr/local/cpanel/cpanel ] && echo Y || echo N; }

_lmd_meta_remediated() { [ -e /usr/local/maldetect/cve-2026-41940.done ] && echo Y || echo N; }

_lmd_meta_build() {
    [ -r /usr/local/cpanel/version ] || { echo "-"; return; }
    local v
    v=$(head -1 /usr/local/cpanel/version 2>/dev/null | tr -d '\r ')
    case "$v" in
        11.*.0.*) v="${v#11.}"; printf '%s.%s' "${v%%.*}" "${v##*.}" ;;
        *.0.*)    printf '%s.%s' "${v%%.*}" "${v##*.}" ;;
        *)        echo "-" ;;
    esac
}

_lmd_meta_compromise() {
    local h="" f d p s m_hit r_hit pid_dir target

    # A — /root/sshd encryptor or /root/*.sorry indicator. Explicit
    # `.*.sorry` because bash glob excludes leading-dot files (dotfile evasion).
    if [ -f /root/sshd ]; then
        h="$h A"
    else
        for f in /root/*.sorry /root/.*.sorry; do
            [ -f "$f" ] && { h="$h A"; break; }
        done
    fi

    # B — DBWipe: mysql/ system db gone but parent has a customer db.
    if [ -d /var/lib/mysql ] && [ ! -d /var/lib/mysql/mysql ]; then
        for d in /var/lib/mysql/*/; do
            [ -d "$d" ] && { h="$h B"; break; }
        done
    fi

    # C — Mirai nuclear.<arch> dropper.
    for p in /tmp /root /var/tmp; do
        for s in x86 x64 x86_64 aarch64 arm mips; do
            [ -f "$p/nuclear.$s" ] && { h="$h C"; break 2; }
        done
    done

    # D — sptadm reseller / 4ef72197.cpx.local campaign domain.
    { [ -d /home/sptadm ]                                                  || \
      [ -f /var/cpanel/users/sptadm ]                                      || \
      [ -d /var/cpanel/userdata/sptadm ]                                   || \
      grep -qF '4ef72197.cpx.local' /etc/userdomains 2>/dev/null           || \
      grep -qF '4ef72197.cpx.local' /etc/trueuserdomains 2>/dev/null; } && h="$h D"

    # G — SSH keys carrying known-attacker IP labels. Broader Pattern G
    # (forged-mtime detection) has customer-paste FP risk; left to ioc-scan.
    grep -qE '209\.59\.141\.49|50\.28\.104\.57' \
        /root/.ssh/authorized_keys /root/.ssh/authorized_keys2 2>/dev/null \
        && h="$h G"

    # H — SeoBot dropper artefact.
    [ -f /tmp/seobot.zip ] && h="$h H"

    # I — system-service profile.d backdoor.
    { [ -f /etc/profile.d/system_profiled_service.sh ] || [ -f /root/.local/bin/system-service ]; } && h="$h I"

    # J — Udev / Systemd persistence units.
    { [ -f /etc/udev/rules.d/89-cdrom-id-helper.rules ]         || \
      [ -f /usr/lib/udev/cdrom-id-helper ]                      || \
      [ -f /etc/systemd/system/dbus-broker-helper.service ]     || \
      [ -f /usr/lib/systemd/system/dbus-broker-helper.service ] || \
      [ -f /usr/share/dbus-1/dbus-broker-helper ]; } && h="$h J"

    # /proc walk for A/C/I/J — catches deleted-binary-still-running.
    # Anchor is EXACT-or-followed-by-space: matches `/path` and `/path (deleted)`,
    # rejects neighbour-name FPs (`/root/sshd-bak`, `nuclear.x86abuse`).
    # BINARY paths only — .service files are config, never /proc/exe targets.
    for pid_dir in /proc/[0-9]*/; do
        target=$(readlink "${pid_dir}exe" 2>/dev/null)
        [ -z "$target" ] && continue
        case "$target" in
            /root/sshd|/root/sshd\ *)
                case "$h" in *A*) ;; *) h="$h A" ;; esac ;;
            */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\ *)
                case "$h" in *C*) ;; *) h="$h C" ;; esac ;;
            /root/.local/bin/system-service|/root/.local/bin/system-service\ *)
                case "$h" in *I*) ;; *) h="$h I" ;; esac ;;
            /usr/lib/udev/cdrom-id-helper|/usr/lib/udev/cdrom-id-helper\ *|\
            /usr/share/dbus-1/dbus-broker-helper|/usr/share/dbus-1/dbus-broker-helper\ *)
                case "$h" in *J*) ;; *) h="$h J" ;; esac ;;
        esac
    done

    # M — xmrig install paths or camouflaged miner process.
    m_hit=0
    { [ -f /root/c3pool/xmrig ]            || \
      [ -f /root/c3pool/config.json ]      || \
      [ -f /root/moneroocean/xmrig ]       || \
      [ -f /root/moneroocean/config.json ]; } && m_hit=1
    if [ "$m_hit" -eq 0 ]; then
        ps -eo args= 2>/dev/null | grep -qE \
            '^\./\.?ld-linux\.so( |$)|^\./https .*-a rx/|^\./python3 .*--donate-level' \
            && m_hit=1
    fi
    [ "$m_hit" -eq 1 ] && h="$h M"

    # P — PHP cron-bot loader (codeItems3 stage-2 payload).
    [ -f /tmp/codeItems3 ] && h="$h P"

    # R — GSocket reverse-shell. File checks cover root-level hijack;
    # comm-name match catches per-user prctl(PR_SET_NAME) masquerades.
    r_hit=0
    { [ -f /dev/shm/.gs ]                          || \
      [ -f /root/.config/htop/defunct.dat ]        || \
      [ -f /root/.config/htop/lscgib.dat ]         || \
      [ -f /root/.config/dbus/gs-dbus.dat ]; } && r_hit=1
    if [ "$r_hit" -eq 0 ]; then
        ps -eo comm= 2>/dev/null | grep -qE '^(defunct|gs-dbus|lscgib)$' \
            && r_hit=1
    fi
    [ "$r_hit" -eq 1 ] && h="$h R"

    [ -z "$h" ] && { echo N; return; }
    h="${h# }"
    echo "${h// /-}"
}

# $ver and $hostid are already in scope from internals.conf.
lmd_referer="LMD:${ver}:${hostid}:$(_lmd_meta_os):cp$(_lmd_meta_cpanel):b$(_lmd_meta_build):c$(_lmd_meta_compromise):r$(_lmd_meta_remediated)"
