BastionGuard/data/yara/AUR_SupplyChain.yara
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

172 lines
6.7 KiB
Text

/*
* 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*))
}