Obfuscate all sensitive string literals in agent binary
ober
1403d8f7948e8fb92d3902d12ef92c33f21cd2e9
--- a/build-all.ss +++ b/build-all.ss @@ -37,6 +37,7 @@ (secmon monitor podman) (secmon monitor selinux) ;; Stealth + (secmon stealth obfuscate) (secmon stealth anti-debug) (secmon stealth masquerade) (secmon stealth env-sanitize) --- a/build-secmon-musl.ss +++ b/build-secmon-musl.ss @@ -122,6 +122,10 @@ [generate-inspector-information #f] [compile-imported-libraries #t] [library-directories build-lib-dirs]) + ;; Obfuscate macro (needed by many modules — compile first) + (printf " Compiling lib/secmon/stealth/obfuscate.sls~n") + (compile-library "lib/secmon/stealth/obfuscate.sls") + ;; Crypto modules (dependency order) (for-each (lambda (m) @@ -231,7 +235,8 @@ "std/gambit-compat" "std/crypto/native-rust" "std/db/sqlite-native")) - ;; Secmon modules + ;; Secmon modules (obfuscate first — many modules depend on it) + (list "lib/secmon/stealth/obfuscate.so") (map (lambda (m) (format "lib/secmon/crypto/~a.so" m)) '("keys" "ecies" "psk")) (list "lib/secmon/monitor/events.so" --- a/lib/secmon/config.sls +++ b/lib/secmon/config.sls @@ -9,6 +9,7 @@ (import (chezscheme) (jerboa prelude clean) + (secmon stealth obfuscate) (std text hex)) (define-record-type agent-config @@ -41,21 +42,21 @@ (define (load-agent-config) (make-agent-config - (or (getenv "SECMON_LISTEN") "0.0.0.0:31337") - (or (and (getenv "SECMON_POLL_MS") - (string->number (getenv "SECMON_POLL_MS"))) + (or (getenv (obfstr "SECMON_LISTEN")) (obfstr "0.0.0.0:31337")) + (or (and (getenv (obfstr "SECMON_POLL_MS")) + (string->number (getenv (obfstr "SECMON_POLL_MS")))) 100) - (or (and (getenv "SECMON_BUFFER_SIZE") - (string->number (getenv "SECMON_BUFFER_SIZE"))) + (or (and (getenv (obfstr "SECMON_BUFFER_SIZE")) + (string->number (getenv (obfstr "SECMON_BUFFER_SIZE")))) 10000) - (load-key-from-env "SECMON_PUBLIC_KEY" "keys/public.key") - (load-key-from-env "SECMON_PSK" "keys/psk.key") - (and (getenv "SECMON_DEBUG") #t))) + (load-key-from-env (obfstr "SECMON_PUBLIC_KEY") (obfstr "keys/public.key")) + (load-key-from-env (obfstr "SECMON_PSK") (obfstr "keys/psk.key")) + (and (getenv (obfstr "SECMON_DEBUG")) #t))) (define (load-collector-config) ;; Returns (values private-key psk) (values - (load-key-from-env "SECMON_PRIVATE_KEY" "keys/private.key") - (load-key-from-env "SECMON_PSK" "keys/psk.key"))) + (load-key-from-env (obfstr "SECMON_PRIVATE_KEY") (obfstr "keys/private.key")) + (load-key-from-env (obfstr "SECMON_PSK") (obfstr "keys/psk.key")))) ) ;; end library --- a/lib/secmon/monitor/container.sls +++ b/lib/secmon/monitor/container.sls @@ -4,7 +4,8 @@ (chezscheme) (jerboa prelude clean) (secmon monitor events) - (secmon monitor suspicious)) + (secmon monitor suspicious) + (secmon stealth obfuscate)) ;; Container escape monitor: detects escape attempts from containers/jails (define (spawn-container-escape-monitor emit! poll-ms hostname) @@ -36,8 +37,9 @@ (when (file-exists? path) (emit! (make-escape-event hostname "container_socket_access" path 'critical)))) - '("/var/run/docker.sock" "/run/docker.sock" - "/var/run/containerd/containerd.sock")) + (list (obfstr "/var/run/docker.sock") + (obfstr "/run/docker.sock") + (obfstr "/var/run/containerd/containerd.sock"))) ;; Check capabilities (let ([caps (read-effective-caps)]) (when caps @@ -64,18 +66,18 @@ (define (is-containerized?) ;; Check for container indicators - (or (file-exists? "/.dockerenv") - (file-exists? "/run/.containerenv") + (or (file-exists? (obfstr "/.dockerenv")) + (file-exists? (obfstr "/run/.containerenv")) (guard (e [#t #f]) - (let ([cgroup (read-file-safe "/proc/1/cgroup")]) + (let ([cgroup (read-file-safe (obfstr "/proc/1/cgroup"))]) (and cgroup - (or (string-contains cgroup "docker") - (string-contains cgroup "lxc") - (string-contains cgroup "kubepods"))))))) + (or (string-contains cgroup (obfstr "docker")) + (string-contains cgroup (obfstr "lxc")) + (string-contains cgroup (obfstr "kubepods")))))))) (define (read-mounts) (guard (e [#t '()]) - (let ([lines (local-read-file-lines "/proc/mounts")]) + (let ([lines (local-read-file-lines (obfstr "/proc/mounts"))]) (filter-map (lambda (line) (let ([parts (string-split line #\space)]) @@ -83,17 +85,17 @@ lines)))) (define (suspicious-mount? mount) - (or (string-prefix? "/host" mount) - (string-prefix? "/mnt/host" mount) - (string-contains mount "docker.sock") - (string-contains mount "containerd.sock"))) + (or (string-prefix? (obfstr "/host") mount) + (string-prefix? (obfstr "/mnt/host") mount) + (string-contains mount (obfstr "docker.sock")) + (string-contains mount (obfstr "containerd.sock")))) (define (read-effective-caps) (guard (e [#t #f]) - (let ([lines (local-read-file-lines "/proc/self/status")]) + (let ([lines (local-read-file-lines (obfstr "/proc/self/status"))]) (let loop ([ls lines]) (if (null? ls) #f - (if (string-prefix? "CapEff:" (car ls)) + (if (string-prefix? (obfstr "CapEff:") (car ls)) (let* ([parts (string-split (car ls) #\tab)] [hex (and (>= (length parts) 2) (string-trim (cadr parts)))]) --- a/lib/secmon/monitor/lateral.sls +++ b/lib/secmon/monitor/lateral.sls @@ -5,6 +5,7 @@ (jerboa prelude clean) (secmon monitor events) (secmon monitor suspicious) + (secmon stealth obfuscate) (secmon platform provider)) ;; Lateral movement monitor: detects SSH/RDP/WinRM/SMB to internal hosts, port scanning @@ -32,7 +33,7 @@ [pname (connection-info-process-name c)]) (when (and (> pid 0) (internal-ip? raddr) - (not (equal? raddr "127.0.0.1")) + (not (equal? raddr (obfstr "127.0.0.1"))) (equal? (connection-info-state c) "ESTABLISHED")) ;; Track ports per (pid, target) (let ([tracker-key (format "~a:~a" pid raddr)]) --- a/lib/secmon/monitor/logtamper.sls +++ b/lib/secmon/monitor/logtamper.sls @@ -5,6 +5,7 @@ (jerboa prelude clean) (secmon monitor events) (secmon monitor suspicious) + (secmon stealth obfuscate) (std os file-info)) ;; Log tamper monitor: detects log truncation, deletion, history clearing @@ -29,10 +30,10 @@ (define (find-history-files) (let ([files '()]) (guard (e [#t files]) - (let ([homes (append '("/root") - (if (file-exists? "/home") - (map (lambda (d) (string-append "/home/" d)) - (directory-list "/home")) + (let ([homes (append (list (obfstr "/root")) + (if (file-exists? (obfstr "/home")) + (map (lambda (d) (string-append (obfstr "/home/") d)) + (directory-list (obfstr "/home"))) '()))]) (for-each (lambda (home) @@ -41,9 +42,11 @@ (let ([path (string-append home "/" hist)]) (when (file-exists? path) (set! files (cons path files))))) - '(".bash_history" ".zsh_history" ".sh_history" - ".python_history" ".mysql_history" ".psql_history" - ".node_repl_history" ".lesshst" ".viminfo"))) + (list (obfstr ".bash_history") (obfstr ".zsh_history") + (obfstr ".sh_history") (obfstr ".python_history") + (obfstr ".mysql_history") (obfstr ".psql_history") + (obfstr ".node_repl_history") (obfstr ".lesshst") + (obfstr ".viminfo")))) homes)) files))) @@ -87,9 +90,9 @@ (hashtable-keys log-sizes))) (define (history-file? path) - (or (string-contains path "history") - (string-contains path ".lesshst") - (string-contains path ".viminfo"))) + (or (string-contains path (obfstr "history")) + (string-contains path (obfstr ".lesshst")) + (string-contains path (obfstr ".viminfo")))) (define (make-tamper-event hostname path tamper-type old-size new-size severity) (let ([data (make-hashtable string-hash string=?)]) --- a/lib/secmon/monitor/persistence.sls +++ b/lib/secmon/monitor/persistence.sls @@ -5,6 +5,7 @@ (jerboa prelude clean) (secmon monitor events) (secmon monitor suspicious) + (secmon stealth obfuscate) (std crypto native-rust)) ;; Persistence monitor: detects modifications to persistence mechanism locations @@ -24,7 +25,7 @@ (define (all-persistence-paths) (append ;; Individual files - '("/etc/ld.so.preload" "/etc/rc.local") + (list (obfstr "/etc/ld.so.preload") (obfstr "/etc/rc.local")) ;; Scan directories for files (scan-all-persistence-dirs) ;; Per-user shell profiles and authorized_keys @@ -46,21 +47,21 @@ (define (scan-user-homes) (guard (e [#t '()]) - (let ([homes (if (file-exists? "/home") - (map (lambda (d) (string-append "/home/" d)) - (directory-list "/home")) + (let ([homes (if (file-exists? (obfstr "/home")) + (map (lambda (d) (string-append (obfstr "/home/") d)) + (directory-list (obfstr "/home"))) '())]) (apply append (map (lambda (home) (filter file-exists? (list - (string-append home "/.bashrc") - (string-append home "/.bash_profile") - (string-append home "/.profile") - (string-append home "/.zshrc") - (string-append home "/.ssh/authorized_keys")))) - (cons "/root" homes)))))) + (string-append home (obfstr "/.bashrc")) + (string-append home (obfstr "/.bash_profile")) + (string-append home (obfstr "/.profile")) + (string-append home (obfstr "/.zshrc")) + (string-append home (obfstr "/.ssh/authorized_keys"))))) + (cons (obfstr "/root") homes)))))) (define (baseline-persistence-paths! hashes) (for-each @@ -104,7 +105,7 @@ (extract-suspicious-content full)))))))) (directory-list dir))))) (append persistence-systemd-paths persistence-init-paths - '("/etc/sudoers.d")))) + (list (obfstr "/etc/sudoers.d"))))) (define (extract-suspicious-content path) (guard (e [#t #f]) @@ -112,9 +113,12 @@ (lambda (p) (get-string-all p)))]) (let ([lines (string-split content #\newline)] [suspicious-patterns - '("curl" "wget" "nc " "/dev/tcp/" "bash -i" "python -c" - "eval" "base64" "chmod" "NOPASSWD" "LD_PRELOAD" - "exec" "nohup" "crontab" "reverse")]) + (list (obfstr "curl") (obfstr "wget") (obfstr "nc ") + (obfstr "/dev/tcp/") (obfstr "bash -i") (obfstr "python -c") + (obfstr "eval") (obfstr "base64") (obfstr "chmod") + (obfstr "NOPASSWD") (obfstr "LD_PRELOAD") + (obfstr "exec") (obfstr "nohup") (obfstr "crontab") + (obfstr "reverse"))]) (let loop ([ls lines]) (if (null? ls) #f (let ([line (car ls)]) --- a/lib/secmon/monitor/revshell.sls +++ b/lib/secmon/monitor/revshell.sls @@ -5,6 +5,7 @@ (jerboa prelude clean) (secmon monitor events) (secmon monitor suspicious) + (secmon stealth obfuscate) (secmon platform provider)) ;; Reverse shell monitor: detects active reverse shell connections @@ -25,8 +26,8 @@ [raddr (connection-info-remote-addr c)] [rport (connection-info-remote-port c)]) (when (and (> pid 0) - (not (equal? raddr "0.0.0.0")) - (not (equal? raddr "127.0.0.1")) + (not (equal? raddr (obfstr "0.0.0.0"))) + (not (equal? raddr (obfstr "127.0.0.1"))) (equal? (connection-info-state c) "ESTABLISHED")) (let ([key (format "~a:~a:~a" pid raddr rport)]) (unless (hashtable-ref reported key #f) @@ -42,12 +43,15 @@ (emit! (make-revshell-event hostname pid pname raddr rport "known_c2_port" 'critical))] ;; Known revshell tools - [(member pname '("nc" "ncat" "socat" "telnet")) + [(member pname (list (obfstr "nc") (obfstr "ncat") + (obfstr "socat") (obfstr "telnet"))) (hashtable-set! reported key #t) (emit! (make-revshell-event hostname pid pname raddr rport "shell_tool_outbound" 'critical))] ;; Scripting tools with outbound - [(member pname '("python" "python3" "perl" "ruby" "php")) + [(member pname (list (obfstr "python") (obfstr "python3") + (obfstr "perl") (obfstr "ruby") + (obfstr "php"))) (let ([info ((process-provider-get-process proc-provider) pid)]) (when info (let ([cmdline (string-join (process-info-cmdline info) " ")]) @@ -85,8 +89,8 @@ ;; Extract IP:port from /dev/tcp/IP/PORT pattern (define (extract-dev-tcp-target cmdline) - (let ([pos (or (string-contains cmdline "/dev/tcp/") - (string-contains cmdline "/dev/udp/"))]) + (let ([pos (or (string-contains cmdline (obfstr "/dev/tcp/")) + (string-contains cmdline (obfstr "/dev/udp/")))]) (if (not pos) (cons "unknown" 0) (let* ([after (substring cmdline (+ pos 9) (string-length cmdline))] --- a/lib/secmon/monitor/rootkit.sls +++ b/lib/secmon/monitor/rootkit.sls @@ -4,6 +4,7 @@ (chezscheme) (jerboa prelude clean) (secmon monitor events) + (secmon stealth obfuscate) (secmon platform provider)) ;; Rootkit monitor: detects hidden processes via /proc vs kill() discrepancy @@ -87,13 +88,13 @@ (define (read-pid-max) (guard (e [#t 32768]) - (let ([content (call-with-input-file "/proc/sys/kernel/pid_max" + (let ([content (call-with-input-file (obfstr "/proc/sys/kernel/pid_max") (lambda (p) (get-string-all p)))]) (or (string->number (string-trim content)) 32768)))) (define (check-loadavg-discrepancy visible-pids emit! hostname) (guard (e [#t (void)]) - (let ([content (call-with-input-file "/proc/loadavg" + (let ([content (call-with-input-file (obfstr "/proc/loadavg") (lambda (p) (get-string-all p)))]) (let* ([parts (string-split content #\space)] [procs-part (and (>= (length parts) 4) (list-ref parts 3))] --- a/lib/secmon/monitor/selinux.sls +++ b/lib/secmon/monitor/selinux.sls @@ -4,6 +4,7 @@ (chezscheme) (jerboa prelude clean) (secmon monitor events) + (secmon stealth obfuscate) (std os file-info)) ;; SELinux monitor: detects AVC denials, policy loads, mode changes @@ -11,12 +12,12 @@ (fork-thread (lambda () ;; Only run if SELinux is available - (when (file-exists? "/sys/fs/selinux") + (when (file-exists? (obfstr "/sys/fs/selinux")) (let ([audit-pos (box 0)] [current-mode (box (read-selinux-mode))]) ;; Initialize audit log position to end (guard (e [#t (void)]) - (let ([size (file-size-safe "/var/log/audit/audit.log")]) + (let ([size (file-size-safe (obfstr "/var/log/audit/audit.log"))]) (when size (set-box! audit-pos size)))) ;; Poll loop (let loop () @@ -35,7 +36,7 @@ (define (read-selinux-mode) (guard (e [#t #f]) - (let ([content (call-with-input-file "/sys/fs/selinux/enforce" + (let ([content (call-with-input-file (obfstr "/sys/fs/selinux/enforce") (lambda (p) (get-string-all p)))]) (if (string-contains content "1") "enforcing" "permissive")))) @@ -45,7 +46,7 @@ (define (tail-audit-log! pos-box emit! hostname) (guard (e [#t (void)]) - (let ([path "/var/log/audit/audit.log"]) + (let ([path (obfstr "/var/log/audit/audit.log")]) (when (file-exists? path) (let ([size (file-size-safe path)]) (when size @@ -67,8 +68,9 @@ (define (parse-audit-line! line emit! hostname) (cond ;; AVC denial/grant - [(string-contains line "type=AVC") - (let ([action (if (string-contains line "denied") "denied" "granted")] + [(string-contains line (obfstr "type=AVC")) + (let ([action (if (string-contains line (obfstr "denied")) + "denied" "granted")] [comm (extract-field line "comm=\"" "\"")] [scontext (extract-field line "scontext=" " ")] [tcontext (extract-field line "tcontext=" " ")] @@ -86,15 +88,15 @@ (if (equal? action "denied") 'high 'info) data))))] ;; Policy load - [(string-contains line "MAC_POLICY_LOAD") + [(string-contains line (obfstr "MAC_POLICY_LOAD")) (emit! (make-selinux-event hostname "policy_load" "" 'medium))] ;; Boolean change - [(string-contains line "MAC_CONFIG_CHANGE") + [(string-contains line (obfstr "MAC_CONFIG_CHANGE")) (let ([bool-name (extract-field line "bool=" " ")]) (emit! (make-selinux-event hostname "boolean_change" (or bool-name "") 'medium)))] ;; Role change - [(string-contains line "USER_ROLE_CHANGE") + [(string-contains line (obfstr "USER_ROLE_CHANGE")) (emit! (make-selinux-event hostname "role_change" "" 'high))])) (define (extract-field line start end) --- a/lib/secmon/monitor/suspicious.sls +++ b/lib/secmon/monitor/suspicious.sls @@ -11,43 +11,56 @@ system-log-files internal-ip? is-c2-port? is-shell? is-web-server? dangerous-capabilities) - (import (chezscheme) (jerboa prelude clean)) + (import (chezscheme) (jerboa prelude clean) (secmon stealth obfuscate)) ;; Shell process names (define shell-names - '("bash" "sh" "dash" "zsh" "ksh" "csh" "tcsh" "fish")) + (list (obfstr "bash") (obfstr "sh") (obfstr "dash") (obfstr "zsh") + (obfstr "ksh") (obfstr "csh") (obfstr "tcsh") (obfstr "fish"))) ;; Attack tools that services shouldn't spawn (define attack-tools - '("nc" "ncat" "socat" "telnet" "python" "python3" "perl" "ruby" - "php" "base64" "wget" "curl")) + (list (obfstr "nc") (obfstr "ncat") (obfstr "socat") (obfstr "telnet") + (obfstr "python") (obfstr "python3") (obfstr "perl") (obfstr "ruby") + (obfstr "php") (obfstr "base64") (obfstr "wget") (obfstr "curl"))) ;; Web server processes (define web-servers - '("apache2" "httpd" "nginx" "php-fpm" "php-cgi" "python" "python3" - "ruby" "node" "java" "tomcat" "gunicorn" "uwsgi" "caddy" "traefik")) + (list (obfstr "apache2") (obfstr "httpd") (obfstr "nginx") + (obfstr "php-fpm") (obfstr "php-cgi") (obfstr "python") + (obfstr "python3") (obfstr "ruby") (obfstr "node") (obfstr "java") + (obfstr "tomcat") (obfstr "gunicorn") (obfstr "uwsgi") + (obfstr "caddy") (obfstr "traefik"))) ;; Suspicious parent processes (services that shouldn't spawn shells) (define suspicious-parents - '("gitea" "nginx" "apache2" "httpd" "php-fpm" "node" "java" - "tomcat" "gunicorn" "uwsgi" "caddy" "traefik" "grafana" - "prometheus" "jenkins" "gitlab-runner")) + (list (obfstr "gitea") (obfstr "nginx") (obfstr "apache2") (obfstr "httpd") + (obfstr "php-fpm") (obfstr "node") (obfstr "java") + (obfstr "tomcat") (obfstr "gunicorn") (obfstr "uwsgi") + (obfstr "caddy") (obfstr "traefik") (obfstr "grafana") + (obfstr "prometheus") (obfstr "jenkins") (obfstr "gitlab-runner"))) ;; Reverse shell command-line patterns (define revshell-patterns - '("/dev/tcp/" "/dev/udp/" "bash -i" "sh -i" "zsh -i" - "0>&1" "1>&0" "2>&1" ">&/dev/tcp" ">&/dev/udp" - "| /bin/sh" "| /bin/bash" "|/bin/sh" "|/bin/bash" - "python -c 'import socket" "python3 -c 'import socket" - "perl -e 'use Socket" "ruby -rsocket" - "php -r '$sock=fsockopen" "nc -e" "ncat -e" "nc -c" "ncat -c" - "mkfifo /tmp/" "mknod /tmp/" "exec 5<>/dev/tcp" - "exec 196<>/dev/tcp" "rm -f /tmp/f;mkfifo" - "bash -c 'bash -i")) + (list (obfstr "/dev/tcp/") (obfstr "/dev/udp/") + (obfstr "bash -i") (obfstr "sh -i") (obfstr "zsh -i") + (obfstr "0>&1") (obfstr "1>&0") (obfstr "2>&1") + (obfstr ">&/dev/tcp") (obfstr ">&/dev/udp") + (obfstr "| /bin/sh") (obfstr "| /bin/bash") + (obfstr "|/bin/sh") (obfstr "|/bin/bash") + (obfstr "python -c 'import socket") + (obfstr "python3 -c 'import socket") + (obfstr "perl -e 'use Socket") (obfstr "ruby -rsocket") + (obfstr "php -r '$sock=fsockopen") + (obfstr "nc -e") (obfstr "ncat -e") (obfstr "nc -c") (obfstr "ncat -c") + (obfstr "mkfifo /tmp/") (obfstr "mknod /tmp/") + (obfstr "exec 5<>/dev/tcp") (obfstr "exec 196<>/dev/tcp") + (obfstr "rm -f /tmp/f;mkfifo") (obfstr "bash -c 'bash -i"))) ;; Cmdline patterns that indicate reverse shell without needing connection check (define revshell-cmdline-patterns - '("/dev/tcp/" "/dev/udp/" "bash -i >& /dev/tcp" "exec 5<>/dev/tcp")) + (list (obfstr "/dev/tcp/") (obfstr "/dev/udp/") + (obfstr "bash -i >& /dev/tcp") (obfstr "exec 5<>/dev/tcp"))) ;; Known C2 ports (define c2-ports @@ -56,94 +69,117 @@ ;; Crypto miner patterns (define miner-patterns - '("xmrig" "minerd" "cpuminer" "stratum+tcp://" "--donate-level")) + (list (obfstr "xmrig") (obfstr "minerd") (obfstr "cpuminer") + (obfstr "stratum+tcp://") (obfstr "--donate-level"))) ;; Suspicious child processes (for webshell detection) (define suspicious-child-processes - '("sh" "bash" "dash" "zsh" "ksh" "csh" "tcsh" "fish" - "nc" "ncat" "netcat" "socat" "curl" "wget" - "python" "python3" "perl" "ruby" "php" "lua" - "awk" "gawk" "mawk" "sed" - "chmod" "chown" "useradd" "usermod" "passwd" "id" "whoami" "uname" - "cat" "head" "tail" "less" "more" "vi" "vim" "nano" - "base64" "xxd" "od" "gcc" "cc" "make" "as" "ld")) + (list (obfstr "sh") (obfstr "bash") (obfstr "dash") (obfstr "zsh") + (obfstr "ksh") (obfstr "csh") (obfstr "tcsh") (obfstr "fish") + (obfstr "nc") (obfstr "ncat") (obfstr "netcat") (obfstr "socat") + (obfstr "curl") (obfstr "wget") + (obfstr "python") (obfstr "python3") (obfstr "perl") (obfstr "ruby") + (obfstr "php") (obfstr "lua") + (obfstr "awk") (obfstr "gawk") (obfstr "mawk") (obfstr "sed") + (obfstr "chmod") (obfstr "chown") (obfstr "useradd") (obfstr "usermod") + (obfstr "passwd") (obfstr "id") (obfstr "whoami") (obfstr "uname") + (obfstr "cat") (obfstr "head") (obfstr "tail") (obfstr "less") + (obfstr "more") (obfstr "vi") (obfstr "vim") (obfstr "nano") + (obfstr "base64") (obfstr "xxd") (obfstr "od") (obfstr "gcc") + (obfstr "cc") (obfstr "make") (obfstr "as") (obfstr "ld"))) ;; Suspicious command-line patterns (for webshell detection) (define suspicious-cmdline-patterns - '("/dev/tcp/" "/dev/udp/" "bash -i" "sh -i" "-c /bin/" "-c /usr/bin/" - "exec" "eval" "| bash" "| sh" "|bash" "|sh" - "base64 -d" "base64 --decode" - "python -c" "python3 -c" "perl -e" "ruby -e" - "| nc" "| ncat" "2>&1" "mkfifo" "mknod" - "telnet" "/tmp/" "/var/tmp/" "/dev/shm/" - "wget" "curl" "chmod +x" "chmod 777")) + (list (obfstr "/dev/tcp/") (obfstr "/dev/udp/") + (obfstr "bash -i") (obfstr "sh -i") + (obfstr "-c /bin/") (obfstr "-c /usr/bin/") + (obfstr "exec") (obfstr "eval") + (obfstr "| bash") (obfstr "| sh") (obfstr "|bash") (obfstr "|sh") + (obfstr "base64 -d") (obfstr "base64 --decode") + (obfstr "python -c") (obfstr "python3 -c") + (obfstr "perl -e") (obfstr "ruby -e") + (obfstr "| nc") (obfstr "| ncat") (obfstr "2>&1") + (obfstr "mkfifo") (obfstr "mknod") + (obfstr "telnet") (obfstr "/tmp/") (obfstr "/var/tmp/") (obfstr "/dev/shm/") + (obfstr "wget") (obfstr "curl") + (obfstr "chmod +x") (obfstr "chmod 777"))) ;; Malicious kernel module names (define malicious-modules - '("diamorphine" "reptile" "adore" "knark" "suckit" "azazel" - "jynx" "vlany" "bdvl")) + (list (obfstr "diamorphine") (obfstr "reptile") (obfstr "adore") + (obfstr "knark") (obfstr "suckit") (obfstr "azazel") + (obfstr "jynx") (obfstr "vlany") (obfstr "bdvl"))) ;; Suspicious keywords in kernel module names (define suspicious-module-keywords - '("hide" "hidden" "rootkit" "backdoor" "keylog" "stealth")) + (list (obfstr "hide") (obfstr "hidden") (obfstr "rootkit") + (obfstr "backdoor") (obfstr "keylog") (obfstr "stealth"))) ;; Persistence paths (define persistence-systemd-paths - '("/etc/systemd/system" "/lib/systemd/system" - "/usr/lib/systemd/system" "/run/systemd/system")) + (list (obfstr "/etc/systemd/system") (obfstr "/lib/systemd/system") + (obfstr "/usr/lib/systemd/system") (obfstr "/run/systemd/system"))) (define persistence-init-paths - '("/etc/init.d" "/etc/rc.local" "/etc/rc.d")) + (list (obfstr "/etc/init.d") (obfstr "/etc/rc.local") (obfstr "/etc/rc.d"))) (define persistence-shell-profile-paths - '("/etc/profile" "/etc/profile.d" "/etc/bash.bashrc" "/etc/bashrc" - "/etc/zshrc" "/etc/zsh/zshrc")) + (list (obfstr "/etc/profile") (obfstr "/etc/profile.d") + (obfstr "/etc/bash.bashrc") (obfstr "/etc/bashrc") + (obfstr "/etc/zshrc") (obfstr "/etc/zsh/zshrc"))) (define persistence-other-paths - '("/etc/ld.so.preload" "/etc/sudoers" "/etc/sudoers.d")) + (list (obfstr "/etc/ld.so.preload") (obfstr "/etc/sudoers") + (obfstr "/etc/sudoers.d"))) ;; File integrity monitoring paths (define monitored-files - '("/etc/passwd" "/etc/shadow" "/etc/sudoers" "/etc/ssh/sshd_config" - "/etc/ld.so.preload" "/root/.ssh/authorized_keys" "/etc/crontab" - "/etc/hosts" "/etc/resolv.conf" "/etc/modules" - "/usr/bin/sudo" "/usr/bin/su" "/usr/bin/passwd" "/bin/ping")) + (list (obfstr "/etc/passwd") (obfstr "/etc/shadow") + (obfstr "/etc/sudoers") (obfstr "/etc/ssh/sshd_config") + (obfstr "/etc/ld.so.preload") (obfstr "/root/.ssh/authorized_keys") + (obfstr "/etc/crontab") (obfstr "/etc/hosts") + (obfstr "/etc/resolv.conf") (obfstr "/etc/modules") + (obfstr "/usr/bin/sudo") (obfstr "/usr/bin/su") + (obfstr "/usr/bin/passwd") (obfstr "/bin/ping"))) (define monitored-dirs - '("/etc/cron.d" "/etc/systemd/system" "/etc/init.d" "/etc/sudoers.d" - "/etc/profile.d")) + (list (obfstr "/etc/cron.d") (obfstr "/etc/systemd/system") + (obfstr "/etc/init.d") (obfstr "/etc/sudoers.d") + (obfstr "/etc/profile.d"))) ;; System log files for tamper detection (define system-log-files - '("/var/log/auth.log" "/var/log/secure" "/var/log/syslog" - "/var/log/messages" "/var/log/kern.log" "/var/log/audit/audit.log" - "/var/log/faillog" "/var/log/lastlog" "/var/log/wtmp" - "/var/log/btmp" "/var/log/utmp" "/run/utmp")) + (list (obfstr "/var/log/auth.log") (obfstr "/var/log/secure") + (obfstr "/var/log/syslog") (obfstr "/var/log/messages") + (obfstr "/var/log/kern.log") (obfstr "/var/log/audit/audit.log") + (obfstr "/var/log/faillog") (obfstr "/var/log/lastlog") + (obfstr "/var/log/wtmp") (obfstr "/var/log/btmp") + (obfstr "/var/log/utmp") (obfstr "/run/utmp"))) ;; Dangerous Linux capabilities (define dangerous-capabilities - '((21 . "CAP_SYS_ADMIN") - (19 . "CAP_SYS_PTRACE") - (16 . "CAP_SYS_MODULE") - (2 . "CAP_DAC_READ_SEARCH") - (12 . "CAP_NET_ADMIN") - (17 . "CAP_SYS_RAWIO") - (39 . "CAP_BPF") - (38 . "CAP_PERFMON"))) + (list (cons 21 (obfstr "CAP_SYS_ADMIN")) + (cons 19 (obfstr "CAP_SYS_PTRACE")) + (cons 16 (obfstr "CAP_SYS_MODULE")) + (cons 2 (obfstr "CAP_DAC_READ_SEARCH")) + (cons 12 (obfstr "CAP_NET_ADMIN")) + (cons 17 (obfstr "CAP_SYS_RAWIO")) + (cons 39 (obfstr "CAP_BPF")) + (cons 38 (obfstr "CAP_PERFMON")))) ;; Helper predicates (define (internal-ip? addr) - (or (string-prefix? "10." addr) - (string-prefix? "192.168." addr) - (string-prefix? "127." addr) - (and (string-prefix? "172." addr) + (or (string-prefix? (obfstr "10.") addr) + (string-prefix? (obfstr "192.168.") addr) + (string-prefix? (obfstr "127.") addr) + (and (string-prefix? (obfstr "172.") addr) (let* ([parts (string-split addr #\.)] [second (and (>= (length parts) 2) (string->number (cadr parts)))]) (and second (>= second 16) (<= second 31)))) - (string-prefix? "fc" addr) - (string-prefix? "fd" addr) - (equal? addr "::1"))) + (string-prefix? (obfstr "fc") addr) + (string-prefix? (obfstr "fd") addr) + (equal? addr (obfstr "::1")))) (define (is-c2-port? port) (memv port c2-ports)) --- a/lib/secmon/stealth/anti-debug.sls +++ b/lib/secmon/stealth/anti-debug.sls @@ -3,7 +3,8 @@ (export is-debugged? start-debug-watchdog!) (import (chezscheme) - (jerboa prelude clean)) + (jerboa prelude clean) + (secmon stealth obfuscate)) ;; Anti-debugging: detects if process is being debugged (define (is-debugged?) @@ -16,10 +17,10 @@ ;; Check TracerPid in /proc/self/status (define (check-tracer-pid) (guard (e [#t #f]) - (let ([lines (local-read-file-lines "/proc/self/status")]) + (let ([lines (local-read-file-lines (obfstr "/proc/self/status"))]) (let loop ([ls lines]) (if (null? ls) #f - (if (string-prefix? "TracerPid:" (car ls)) + (if (string-prefix? (obfstr "TracerPid:") (car ls)) (let* ([parts (string-split (car ls) #\tab)] [val (and (>= (length parts) 2) (string->number (string-trim (cadr parts))))]) @@ -60,9 +61,12 @@ (and comm (let ([name (string-trim comm)]) (member name - '("gdb" "lldb" "strace" "ltrace" "radare2" "r2" - "ida" "ida64" "x64dbg" "ollydbg" "edb" - "ghidra" "frida" "valgrind" "rr"))))))) + (list (obfstr "gdb") (obfstr "lldb") (obfstr "strace") + (obfstr "ltrace") (obfstr "radare2") (obfstr "r2") + (obfstr "ida") (obfstr "ida64") (obfstr "x64dbg") + (obfstr "ollydbg") (obfstr "edb") + (obfstr "ghidra") (obfstr "frida") + (obfstr "valgrind") (obfstr "rr")))))))) (define (get-ppid) (guard (e [#t 1]) @@ -99,10 +103,10 @@ (define (read-tracer-pid-normal) (guard (e [#t #f]) - (let ([lines (local-read-file-lines "/proc/self/status")]) + (let ([lines (local-read-file-lines (obfstr "/proc/self/status"))]) (let loop ([ls lines]) (if (null? ls) 0 - (if (string-prefix? "TracerPid:" (car ls)) + (if (string-prefix? (obfstr "TracerPid:") (car ls)) (let* ([parts (string-split (car ls) #\tab)] [val (and (>= (length parts) 2) (string->number (string-trim (cadr parts))))]) @@ -114,7 +118,7 @@ (let ([c-open (foreign-procedure "open" (string int) int)] [c-read (foreign-procedure "read" (int u8* size_t) ssize_t)] [c-close (foreign-procedure "close" (int) int)]) - (let ([fd (c-open "/proc/self/status" 0)]) ;; O_RDONLY = 0 + (let ([fd (c-open (obfstr "/proc/self/status") 0)]) ;; O_RDONLY = 0 (when (< fd 0) (error #f "open failed")) (let ([buf (make-bytevector 4096)]) (let ([n (c-read fd buf 4096)]) @@ -124,7 +128,7 @@ [lines (string-split content #\newline)]) (let loop ([ls lines]) (if (null? ls) 0 - (if (string-prefix? "TracerPid:" (car ls)) + (if (string-prefix? (obfstr "TracerPid:") (car ls)) (let* ([parts (string-split (car ls) #\tab)] [val (and (>= (length parts) 2) (string->number (string-trim (cadr parts))))]) --- a/lib/secmon/stealth/env-sanitize.sls +++ b/lib/secmon/stealth/env-sanitize.sls @@ -2,17 +2,19 @@ (export sanitize-environment!) (import (chezscheme) - (jerboa prelude clean)) + (jerboa prelude clean) + (secmon stealth obfuscate)) ;; Remove dangerous environment variables that could be used to ;; intercept or trace the agent (define dangerous-env-vars - '("LD_PRELOAD" "LD_LIBRARY_PATH" "LD_DEBUG" "LD_AUDIT" - "LD_PROFILE" "LD_TRACE_LOADED_OBJECTS" - "MALLOC_TRACE" "MALLOC_CHECK_" - "DYLD_INSERT_LIBRARIES" "DYLD_FORCE_FLAT_NAMESPACE" - "COLUMNS" "LINES" - "HISTFILE" "HISTSIZE")) + (list (obfstr "LD_PRELOAD") (obfstr "LD_LIBRARY_PATH") + (obfstr "LD_DEBUG") (obfstr "LD_AUDIT") + (obfstr "LD_PROFILE") (obfstr "LD_TRACE_LOADED_OBJECTS") + (obfstr "MALLOC_TRACE") (obfstr "MALLOC_CHECK_") + (obfstr "DYLD_INSERT_LIBRARIES") (obfstr "DYLD_FORCE_FLAT_NAMESPACE") + (obfstr "COLUMNS") (obfstr "LINES") + (obfstr "HISTFILE") (obfstr "HISTSIZE"))) (define (sanitize-environment!) (let ([c-unsetenv (foreign-procedure "unsetenv" (string) int)]) --- a/lib/secmon/stealth/integrity.sls +++ b/lib/secmon/stealth/integrity.sls @@ -3,7 +3,8 @@ (import (chezscheme) (jerboa prelude clean) - (std crypto native-rust)) + (std crypto native-rust) + (secmon stealth obfuscate)) ;; Self-integrity checking: detect binary tampering on disk (define *disk-hash* (box #f)) @@ -27,7 +28,7 @@ ;; Hash the binary on disk via /proc/self/exe (define (hash-self-binary) (guard (e [#t #f]) - (let ([exe-path (readlink-safe "/proc/self/exe")]) + (let ([exe-path (readlink-safe (obfstr "/proc/self/exe"))]) (when exe-path (let ([p (open-file-input-port exe-path)]) (let ([bv (get-bytevector-all p)]) @@ -38,15 +39,15 @@ ;; Hash the executable code section from /proc/self/maps (define (hash-code-section) (guard (e [#t #f]) - (let ([maps (read-file-safe "/proc/self/maps")]) + (let ([maps (read-file-safe (obfstr "/proc/self/maps"))]) (when maps (let ([lines (string-split maps #\newline)]) ;; Find first r-xp mapping (executable code) (let loop ([ls lines]) (if (null? ls) #f (let ([line (car ls)]) - (if (and (string-contains line "r-xp") - (or (string-contains line "secmon") + (if (and (string-contains line (obfstr "r-xp")) + (or (string-contains line (obfstr "secmon")) (string-contains line ".so"))) (parse-and-hash-mapping line) (loop (cdr ls))))))))))) @@ -65,7 +66,7 @@ ;; Sanity check: max 10MB (when (and start end (> size 0) (< size 10485760)) ;; Read memory region - (let ([p (open-file-input-port "/proc/self/mem")]) + (let ([p (open-file-input-port (obfstr "/proc/self/mem"))]) (set-port-position! p start) (let ([bv (get-bytevector-n p size)]) (close-port p) --- a/lib/secmon/stealth/masquerade.sls +++ b/lib/secmon/stealth/masquerade.sls @@ -2,11 +2,14 @@ (export masquerade-process!) (import (chezscheme) - (jerboa prelude clean)) + (jerboa prelude clean) + (secmon stealth obfuscate)) ;; Process masquerade: rename process to look like a system daemon (define linux-cover-names - '#("[kworker/0:1]" "[watchdog/0]" "systemd-journald" "dbus-daemon" "polkitd")) + (vector (obfstr "[kworker/0:1]") (obfstr "[watchdog/0]") + (obfstr "systemd-journald") (obfstr "dbus-daemon") + (obfstr "polkitd"))) (define (masquerade-process!) ;; Choose cover name based on PID @@ -24,7 +27,7 @@ (c-prctl 15 name)))) ;; Also write /proc/self/comm (guard (e [#t (void)]) - (let ([p (open-output-file "/proc/self/comm" (file-options no-create))]) + (let ([p (open-output-file (obfstr "/proc/self/comm") (file-options no-create))]) (put-string p cover-name) (close-port p))))) new file mode 100644 --- /dev/null +++ b/lib/secmon/stealth/obfuscate.sls @@ -0,0 +1,29 @@ +(library (secmon stealth obfuscate) + (export obfstr deobfuscate) + (import (chezscheme)) + + ;; Compile-time XOR string obfuscation. + ;; (obfstr "secret") expands to code that reconstructs the string + ;; at runtime from a XOR'd integer list. The original string never + ;; appears in the compiled .so or boot file. + (define-syntax obfstr + (lambda (stx) + (syntax-case stx () + [(k s) + (string? (syntax->datum #'s)) + (let* ([str (syntax->datum #'s)] + [len (string-length str)] + [key (bitwise-and (+ (* (mod len 256) 31) 42) #xFF)] + [encoded (map (lambda (c) (bitwise-xor (char->integer c) key)) + (string->list str))]) + (with-syntax ([xkey (datum->syntax #'k key)] + [xbytes (datum->syntax #'k encoded)]) + #'(deobfuscate 'xbytes xkey)))]))) + + ;; Runtime decode: XOR each byte back to a character + (define (deobfuscate bytes key) + (list->string + (map (lambda (b) (integer->char (bitwise-xor b key))) + bytes))) + +) ;; end library