#!/bin/bash
#
# cpanel_incident_audit.sh
#
# READ-ONLY forensics for a cPanel server suspected of compromise.
# Produces a report under /var/log/cpanel_audit_<timestamp>/ containing:
#
#   01_mail_queue.txt      - Exim queue stats, top senders/recipients, frozen
#   02_recent_mailboxes.txt - mailboxes created in the last N days
#   03_forwarders.txt      - every forwarder on the box (review for exfil)
#   04_cron.txt            - root + every cPanel user's crontab
#   05_ssh_keys.txt        - authorized_keys for root + every user
#   06_logins.txt          - recent successful logins (last, lastlog)
#   07_listening_ports.txt - listening sockets and the processes behind them
#   08_recent_files.txt    - files modified in the last N days under web roots
#   09_php_recent.txt      - recently changed PHP files (potential webshells)
#   10_tmp_files.txt       - executables and scripts in /tmp, /var/tmp, /dev/shm
#   11_passwd_diff.txt     - /etc/passwd users with UID < 1000 added recently
#   12_cpanel_access.txt   - recent cPanel/WHM logins (who, when, from where)
#   summary.txt            - high-level pointers to suspicious findings
#
# Nothing is modified. Safe to run on a live server.
#
# Usage:
#   ./cpanel_incident_audit.sh                 # last 7 days
#   ./cpanel_incident_audit.sh --days 30       # widen the window
#

set -u

DAYS=7
while [[ $# -gt 0 ]]; do
    case "$1" in
        --days) DAYS="$2"; shift 2 ;;
        -h|--help) grep '^#' "$0" | head -30 | sed 's/^# \{0,1\}//'; exit 0 ;;
        *) echo "Unknown arg: $1" >&2; exit 1 ;;
    esac
done

[[ $EUID -eq 0 ]] || { echo "ERROR: must run as root" >&2; exit 1; }

OUT="/var/log/cpanel_audit_$(date +%Y%m%d_%H%M%S)"
mkdir -p "$OUT"

echo "Writing report to $OUT"
echo "Window: last $DAYS days"
echo

list_users() {
    for f in /var/cpanel/users/*; do
        [[ -f "$f" ]] || continue
        basename "$f"
    done
}

# ---------- 1. Mail queue ----------
{
    echo "=== Exim queue stats ==="
    echo "Total queued: $(exim -bpc 2>/dev/null)"
    echo
    echo "=== Top 20 senders in queue ==="
    exim -bp 2>/dev/null | awk '/^ *[0-9]+[mhd]/ {getline; print $1}' \
        | sort | uniq -c | sort -rn | head -20
    echo
    echo "=== Top 20 recipients in queue ==="
    exim -bp 2>/dev/null | awk '/^ *[0-9]+[mhd]/ {getline; getline; print $1}' \
        | sort | uniq -c | sort -rn | head -20
    echo
    echo "=== Frozen messages ==="
    exim -bp 2>/dev/null | grep frozen | head -50
    echo
    echo "Frozen count: $(exim -bp 2>/dev/null | grep -c frozen)"
} > "$OUT/01_mail_queue.txt" 2>&1

# ---------- 2. Recent mailboxes ----------
{
    echo "Mailbox dirs modified in the last $DAYS days"
    echo "(creation time of /home/USER/mail/DOMAIN/LOCAL is when mailbox was made)"
    echo
    find /home/*/mail -maxdepth 3 -type d -mtime "-$DAYS" -printf "%TY-%Tm-%Td %TH:%TM  %p\n" 2>/dev/null \
        | sort
} > "$OUT/02_recent_mailboxes.txt"

# ---------- 3. All forwarders ----------
{
    echo "All forwarders on the server (review the destination column carefully)"
    echo "Source files: /etc/valiases/<domain>"
    echo
    for f in /etc/valiases/*; do
        [[ -f "$f" ]] || continue
        d=$(basename "$f")
        # skip the default catch-all line, show real forwarders
        grep -vE '^\s*(\*|#|$)' "$f" | grep -vE ':\s*:fail:' \
            | sed "s|^|$d  |"
    done
} > "$OUT/03_forwarders.txt"

# ---------- 4. Cron ----------
{
    echo "=== /etc/crontab ==="
    cat /etc/crontab 2>/dev/null
    echo
    echo "=== /etc/cron.d/ ==="
    ls -la /etc/cron.d/ 2>/dev/null
    for f in /etc/cron.d/*; do
        [[ -f "$f" ]] || continue
        echo "--- $f ---"
        cat "$f"
    done
    echo
    echo "=== root crontab ==="
    crontab -u root -l 2>/dev/null
    echo
    echo "=== cPanel user crontabs ==="
    for u in $(list_users); do
        c=$(crontab -u "$u" -l 2>/dev/null)
        if [[ -n "$c" ]]; then
            echo "--- $u ---"
            echo "$c"
            echo
        fi
    done
    echo
    echo "=== /var/spool/cron/ files modified in last $DAYS days ==="
    find /var/spool/cron -type f -mtime "-$DAYS" -printf "%TY-%Tm-%Td %TH:%TM  %p\n" 2>/dev/null
} > "$OUT/04_cron.txt"

# ---------- 5. SSH authorized_keys ----------
{
    echo "All authorized_keys files on the server"
    echo "Review every key — if it isn't yours, it's a backdoor."
    echo
    for f in /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys; do
        [[ -f "$f" ]] || continue
        echo "--- $f (mtime: $(stat -c %y "$f")) ---"
        cat "$f"
        echo
    done
} > "$OUT/05_ssh_keys.txt"

# ---------- 6. Logins ----------
{
    echo "=== last (most recent 100) ==="
    last -100 2>/dev/null
    echo
    echo "=== lastlog (only users who have ever logged in) ==="
    lastlog 2>/dev/null | grep -v 'Never logged in'
    echo
    echo "=== /var/log/secure recent successful auth (last 200 lines) ==="
    grep -E 'Accepted|session opened' /var/log/secure* 2>/dev/null | tail -200
} > "$OUT/06_logins.txt"

# ---------- 7. Listening ports ----------
{
    echo "=== Listening TCP/UDP sockets ==="
    if command -v ss >/dev/null; then
        ss -tulpn
    else
        netstat -tulpn 2>/dev/null
    fi
    echo
    echo "=== Established connections (top remote IPs) ==="
    if command -v ss >/dev/null; then
        ss -tn state established 2>/dev/null | awk 'NR>1 {print $4}' \
            | awk -F: '{print $1}' | sort | uniq -c | sort -rn | head -20
    fi
} > "$OUT/07_listening_ports.txt"

# ---------- 8. Recent files in web roots ----------
{
    echo "Files modified in the last $DAYS days under /home/*/public_html"
    echo
    find /home/*/public_html -type f -mtime "-$DAYS" \
        ! -path '*/cache/*' ! -path '*/tmp/*' \
        -printf "%TY-%Tm-%Td %TH:%TM  %u  %p\n" 2>/dev/null \
        | sort | head -500
} > "$OUT/08_recent_files.txt"

# ---------- 9. Recent PHP files (likely webshell candidates) ----------
{
    echo "PHP files modified in last $DAYS days — highest risk for webshells"
    echo "Watch for: short obfuscated files, files in upload dirs, oddly-named files"
    echo
    find /home/*/public_html -type f -name '*.php' -mtime "-$DAYS" \
        -printf "%TY-%Tm-%Td %TH:%TM  %s bytes  %p\n" 2>/dev/null \
        | sort
    echo
    echo "=== PHP files containing suspicious functions (eval/base64_decode/shell_exec/system/exec/passthru) ==="
    find /home/*/public_html -type f -name '*.php' -mtime "-$DAYS" 2>/dev/null \
        | xargs grep -lE '(eval|base64_decode|shell_exec|passthru|system|`)\s*\(' 2>/dev/null \
        | head -100
} > "$OUT/09_php_recent.txt"

# ---------- 10. /tmp etc. ----------
{
    for d in /tmp /var/tmp /dev/shm; do
        echo "=== $d ==="
        ls -la "$d" 2>/dev/null
        echo
        echo "--- executables and scripts in $d ---"
        find "$d" -type f \( -perm -u+x -o -name '*.sh' -o -name '*.pl' -o -name '*.py' -o -name '*.php' \) -printf "%TY-%Tm-%Td %TH:%TM  %M  %u  %s  %p\n" 2>/dev/null
        echo
    done
} > "$OUT/10_tmp_files.txt"

# ---------- 11. /etc/passwd anomalies ----------
{
    echo "=== /etc/passwd entries (UID >= 1000) ==="
    awk -F: '$3 >= 1000 {print $0}' /etc/passwd
    echo
    echo "=== /etc/passwd mtime ==="
    stat /etc/passwd
    echo
    echo "=== Accounts with login shells ==="
    awk -F: '$7 ~ /(bash|sh|zsh|fish)$/ {print $1 " (uid="$3") shell="$7}' /etc/passwd
} > "$OUT/11_passwd.txt"

# ---------- 12. cPanel access logs ----------
{
    echo "=== Recent cPanel/WHM logins ==="
    # accounting.log has account creation/modification events
    if [[ -f /var/cpanel/accounting.log ]]; then
        echo "--- /var/cpanel/accounting.log (last 100) ---"
        tail -100 /var/cpanel/accounting.log
    fi
    echo
    if [[ -f /usr/local/cpanel/logs/access_log ]]; then
        echo "--- cPanel access_log: distinct source IPs (last 24h) ---"
        awk -v cutoff="$(date -d '24 hours ago' '+%Y-%m-%d %H:%M:%S')" \
            '$0 > cutoff {print $1}' /usr/local/cpanel/logs/access_log 2>/dev/null \
            | sort | uniq -c | sort -rn | head -30
    fi
    echo
    if [[ -f /usr/local/cpanel/logs/login_log ]]; then
        echo "--- cPanel login_log (last 100) ---"
        tail -100 /usr/local/cpanel/logs/login_log
    fi
} > "$OUT/12_cpanel_access.txt"

# ---------- summary ----------
{
    echo "cPanel incident audit — $(date)"
    echo "Window: last $DAYS days"
    echo
    echo "Quick-look counters (verify the corresponding file for details):"
    echo
    printf "  %-40s %s\n" "Mail queue size:"             "$(exim -bpc 2>/dev/null)"
    printf "  %-40s %s\n" "Frozen messages:"             "$(exim -bp 2>/dev/null | grep -c frozen)"
    printf "  %-40s %s\n" "Mailbox dirs new in $DAYS days:" "$(find /home/*/mail -maxdepth 3 -type d -mtime -$DAYS 2>/dev/null | wc -l)"
    printf "  %-40s %s\n" "Forwarders total:"            "$(grep -hvE '^\s*(\*|#|$)' /etc/valiases/* 2>/dev/null | grep -vE ':\s*:fail:' | wc -l)"
    printf "  %-40s %s\n" "Authorized_keys files:"       "$(ls /root/.ssh/authorized_keys /home/*/.ssh/authorized_keys 2>/dev/null | wc -l)"
    printf "  %-40s %s\n" "PHP files changed in $DAYS days:"  "$(find /home/*/public_html -type f -name '*.php' -mtime -$DAYS 2>/dev/null | wc -l)"
    printf "  %-40s %s\n" "  ...with suspicious functions:"  "$(find /home/*/public_html -type f -name '*.php' -mtime -$DAYS 2>/dev/null | xargs grep -lE '(eval|base64_decode|shell_exec|passthru|system|\`)\s*\(' 2>/dev/null | wc -l)"
    printf "  %-40s %s\n" "Executables in /tmp /var/tmp /dev/shm:" "$(find /tmp /var/tmp /dev/shm -type f -perm -u+x 2>/dev/null | wc -l)"
    printf "  %-40s %s\n" "Listening sockets:"           "$(ss -tulpn 2>/dev/null | tail -n +2 | wc -l)"
    echo
    echo "Open the numbered files in $OUT for the raw evidence."
} > "$OUT/summary.txt"

cat "$OUT/summary.txt"
echo
echo "Full report: $OUT/"
echo "Tarball it for safekeeping:  tar czf ${OUT##*/}.tgz -C $(dirname $OUT) ${OUT##*/}"
