/* * BastionGuard™ — AUR Supply-Chain Detection * Copyright (C) 2025–2026 Calogero Scarnà * * Rileva PKGBUILD e file .install compromessi. * Pensato per il vettore della campagna "atomic-lockfile / Atomic Arch" * (giugno 2026) ma generalizzato per intercettare compromissioni future. * * NOTA: queste regole girano su FILE DI TESTO (PKGBUILD, *.install, package.json), * NON su ELF. Per questo il gate e' "uint32(0) != 0x7f454c46": vogliamo il * VETTORE, non il payload. Il payload ELF lo coprono le tue regole cookie-stealer. * * Licenza: GPLv3. "BastionGuard" e' un marchio e non e' coperto dalla GPL. */ /* ------------------------------------------------------------------------- * 1) IOC SPECIFICI DELLA CAMPAGNA — confidenza massima * ------------------------------------------------------------------------- */ rule AUR_Atomic_Lockfile_Campaign_IOC { meta: description = "AUR: IOC campagna atomic-lockfile / Atomic Arch (giugno 2026)" severity = "critical" score = 95 response = "quarantine" author = "BastionGuard" reference = "PKGBUILD -> npm atomic-lockfile -> preinstall -> ELF src/hooks/deps" date = "2026-06" strings: $ioc1 = "atomic-lockfile" ascii nocase $ioc2 = "src/hooks/deps" ascii nocase // marker di contesto AUR per ridurre i FP su testi che ne parlano soltanto $ctx1 = "pkgname=" ascii $ctx2 = "pkgver=" ascii $ctx3 = /\bpackage\s*\(\s*\)/ $ctx4 = /\bprepare\s*\(\s*\)/ $ctx5 = /\bpost_install\s*\(\s*\)/ $ctx6 = "\"preinstall\"" ascii // package.json condition: uint32(0) != 0x7f454c46 and filesize < 1MB and ( $ioc1 // "atomic-lockfile" e' gia' distintivo or ($ioc2 and any of ($ctx*)) // il path da solo e' piu' generico ) } /* ------------------------------------------------------------------------- * 2) PACKAGE MANAGER JS IN PKGBUILD/.install — vettore principale * npm/bun/npx/pnpm/yarn che entrano dove non dovrebbero * ------------------------------------------------------------------------- */ rule AUR_PKGBUILD_JS_PackageManager_Vector { meta: description = "AUR: npm/bun/npx/pnpm/yarn in PKGBUILD o .install (vettore tipo atomic-lockfile)" severity = "high" score = 80 response = "quarantine_or_confirm" author = "BastionGuard" fix_notes = "npm in build()/package() di un progetto Node e' legittimo: per questo NON basta npm. Richiediamo npm DENTRO una scriptlet .install (gira come root) OPPURE accanto a lifecycle preinstall/postinstall." strings: $pm1 = "npm install" ascii $pm2 = "npm ci" ascii $pm3 = "npx " ascii $pm4 = "bun install" ascii $pm5 = "bunx" ascii $pm6 = "pnpm " ascii $pm7 = /yarn\s+(add|install)/ // scriptlet .install -> eseguite come root da pacman $inst1 = /\bpost_install\s*\(\s*\)/ $inst2 = /\bpost_upgrade\s*\(\s*\)/ $inst3 = /\bpre_install\s*\(\s*\)/ $inst4 = /\bpre_upgrade\s*\(\s*\)/ // lifecycle npm sospetti in PKGBUILD/package.json $life1 = "preinstall" ascii $life2 = "postinstall" ascii condition: uint32(0) != 0x7f454c46 and filesize < 1MB and 1 of ($pm*) and (1 of ($inst*) or 1 of ($life*)) } /* ------------------------------------------------------------------------- * 3) REMOTE EXEC — download pipato verso shell / eval / base64 decode * Il classico curl|sh nascosto in build()/prepare()/scriptlet * ------------------------------------------------------------------------- */ rule AUR_PKGBUILD_RemoteExec_Pipe_Shell { meta: description = "AUR: download remoto pipato a shell, o decode/eval (RCE in fase di build/install)" severity = "critical" score = 90 response = "quarantine" author = "BastionGuard" fix_notes = "I sorgenti legittimi si scaricano via array source=() verificato da sha256sums. curl|sh inline dentro le funzioni e' il segnale: questo NON matcha source=()." strings: // contesto: deve essere un PKGBUILD o una .install $ctx1 = "pkgname=" ascii $ctx2 = /\bbuild\s*\(\s*\)/ $ctx3 = /\bprepare\s*\(\s*\)/ $ctx4 = /\bpackage\s*\(\s*\)/ $ctx5 = /\bpost_install\s*\(\s*\)/ $ctx6 = /\bpost_upgrade\s*\(\s*\)/ // download -> shell $pipe1 = /curl[^\n]{0,300}\|\s*(ba)?sh\b/ $pipe2 = /wget[^\n]{0,300}\|\s*(ba)?sh\b/ $pipe3 = /\$\(\s*(curl|wget)[^\n)]{0,200}\)\s*\|\s*(ba)?sh/ // offuscamento -> shell $dec1 = /base64\s+(-d|--decode)[^\n]{0,120}\|\s*(ba)?sh/ $dec2 = /eval\s+["'`$]?\(?\s*(curl|wget|base64)/ $dec3 = /(curl|wget)[^\n]{0,200}\|\s*base64\s+(-d|--decode)/ condition: uint32(0) != 0x7f454c46 and filesize < 1MB and any of ($ctx*) and any of ($pipe*, $dec*) } /* ------------------------------------------------------------------------- * 4) .install SCRIPTLET — rete o persistenza in contesto root * Le scriptlet .install girano come root: rete/persistenza qui = quasi sempre malevolo * ------------------------------------------------------------------------- */ rule AUR_Install_Scriptlet_Network_Or_Persistence { meta: description = "AUR .install: scriptlet root con accesso rete o meccanismi di persistenza" severity = "high" score = 78 response = "quarantine_or_confirm" author = "BastionGuard" fix_notes = "'systemctl enable' e' sconsigliato ma talvolta usato da pacchetti aggressivi: tenuto come segnale debole. I segnali forti sono rete + autostart/cron/ld.so.preload/bashrc." strings: $sl1 = /\bpost_install\s*\(\s*\)/ $sl2 = /\bpost_upgrade\s*\(\s*\)/ $sl3 = /\bpre_install\s*\(\s*\)/ $sl4 = /\bpre_upgrade\s*\(\s*\)/ // rete (rarissima e sospetta in una scriptlet) $net1 = /\bcurl\s/ $net2 = /\bwget\s/ $net3 = /\bnc\s+-/ $net4 = "/dev/tcp/" ascii // persistenza $per1 = "crontab" ascii $per2 = "/etc/systemd/system/" ascii $per3 = "/etc/profile.d/" ascii $per4 = ".config/autostart" ascii $per5 = "ld.so.preload" ascii $per6 = /(>>?|tee)[^\n]{0,40}\.bashrc/ $per7 = "systemctl enable" ascii condition: uint32(0) != 0x7f454c46 and filesize < 1MB and 1 of ($sl*) and (1 of ($net*) or 1 of ($per*)) }