
Decrittografa ed estrai le immagini firmware di FortiOS 8.0.0.
Decrittare ed estrarre le immagini firmware FortiOS 8.0.0.
Questo script estende il Forticrack di Bishop Fox per supportare le immagini firmware 8.0.0. Inoltre, l'articolo di RandoriSec sulla cifratura del firmware FortiGate 7.4.7 è stato davvero utile per fare reverse engineering della cifratura di FortiOS 8.0.0, dato che sembra essere un'iterazione più recente di quella della 7.4.7.
Funziona sia per le immagini FGT che FFW.
È stato testato sia su FGT che su FFW v8.0.0.F-build0167. Altre build potrebbero richiedere di fare nuovamente reverse engineering del kernel per trovare gli esatti segmenti del kernel e gli indirizzi virtuali della chiave pubblica RSA e della chiave XOR.
Lo script tenta di rilevare automaticamente se il file .out corrisponde a FGT o FFW in base al nome. Può anche essere specificato come argomento opzionale, per sicurezza.
$ python3 forticrack_v8.py
[x] Usage: python3 forticrack_v8.py <.out file> [FGT|FFW]
Demo:

Directory risultante:

Esistono già numerosi articoli e script sulla decrittazione di fortiOS (come quelli menzionati in precedenza). Tuttavia, nessuno di essi si applica a FortiOS 8.0.0, poiché Fortinet ha modificato nuovamente la propria cifratura.
Fortinet consente di scaricare le immagini di aggiornamento sia per FortiFirewall che per FortiGate su https://support.fortinet.com/ > Login > Support > VM Images. Queste immagini sono file .out, che è ciò che questo script si aspetta come input. Quando viene eseguito, esegue 4 operazioni principali:
.out (lavoro di Bishop Fox)Il file di aggiornamento .out è cifrato con un cifrario a blocchi personalizzato basato su XOR. Bishop Fox ne ha fatto il reverse engineering e ha pubblicato forticrack insieme a un'ottima analisi. Questa parte dello script usa praticamente lo stesso codice del forticrack originale di Bishop Fox, che estrae la corrispondente chiave da 32 byte e decritta il file .out. Suggerisco di leggere l'analisi se volete saperne di più.
Il file decrittato è un'immagine firmware Fortinet standard. Lo script la estrae usando binwalk, producendo il seguente filesystem:
ext-root
├── boot
│ ├── cert.der
│ └── grub
│ ├── BOOTX64.EFI
│ ├── grub.cfg
│ └── grubx64.efi
├── boot.msg
├── datafs.tar.gz
├── datafs.tar.gz.bak
├── datafs.tar.gz.chk
├── datafs.tar.gz.chk.bak
├── extlinux.conf
├── filechecksum
├── flatkc
├── flatkc.chk
├── flatkc.sig
├── hash_bin.sha256
├── ldlinux.c32
├── ldlinux.sys
├── rootfs.gz
└── rootfs.gz.chk
Dove:
boot/ : Directory con i file del bootloaderdatafs.tar.gz : Filesystem dei datiflatkc : Kernel Linux bzImagerootfs.gz : Filesystem cifratoTutti i file interessanti per i ricercatori di vulnerabilità, come /sbin/init, sono cifrati all'interno di rootfs.gz.
rootfs.gz (la parte nuova)Questa è la parte nuova per la versione 8.0.0. Per capirla, Claude Code è stato ampiamente utilizzato per fare reverse engineering della corrispondente logica di decrittazione e ottenere gli indirizzi virtuali hardcoded all'interno dell'immagine del kernel, prendendo come riferimento l'articolo di RandoriSec su FortiGate 7.4.7. Dalla mia esperienza, il reverse engineering assistito da IA brilla davvero quando si analizza roba crittografica, che era un compito classico da hardcore quando il reverse engineering manuale era l'unica opzione.
Il file rootfs.gz è cifrato con un cifrario a flusso personalizzato chiamato FORT-RC4. La chiave per decrittarlo è incorporata all'interno di una firma RSA PKCS#1 aggiunta alla fine del file. Per decrittare questa firma, deve essere usata la corrispondente chiave pubblica RSA, che può essere recuperata dall'immagine del kernel.
Poiché flatkc è un bzImage, l'ELF del kernel può essere facilmente estratto individuando il payload gzip al suo interno e decomprimendolo. Dentro l'ELF, all'indirizzo virtuale 0xffffffff8179a1a0, ci sono 270 byte di dati DER codificati con XOR che rappresentano la chiave pubblica RSA. La chiave XOR da 32 byte per decodificarli si trova a 0xffffffff8179a2c0. La decodifica è semplicemente decoded[i] = encoded[i] ^ xor_key[i & 0x1f], e il risultato viene interpretato come una struttura DER RSAPublicKey PKCS#1 standard (una chiave pubblica RSA-2048).
Una volta recuperata la chiave pubblica RSA, il blocco della firma (gli ultimi 256 byte di rootfs.gz) viene decrittato calcolando m = sig^e mod n. Il risultato da 256 byte è un messaggio con padding PKCS#1 v1.5 Type 1 con il seguente layout:
m[0x00] = 0x00
m[0x01] = 0x01
m[0x02..0x9E] = 0xFF (157 padding bytes)
m[0x9F] = 0x00
m[0xA0..0xBF] = SHA256(rootfs.gz[:-256])
m[0xC0..0xDF] = (unused)
m[0xE0..0xFF] = RC4 key (32 bytes)
L'hash SHA-256 viene verificato rispetto al corpo di rootfs.gz come controllo di integrità, e la chiave RC4 da 32 byte alla fine è ciò che viene effettivamente usato per decrittare il file.
Per quanto riguarda FORT-RC4, è stato completamente vibe-reversed da Claude. Ecco come funziona:
FORT-RC4 ha un KSA standard ma un PRGA modificato: invece di produrre un byte di keystream per round da una singola lookup nella S-box, esegue due lookup aggiuntive usando versioni bit-mixate di
iej, XORa0xAAin un indice di mix e combina due valori della S-box per produrre il byte finale. C'è anche una differenza tra FGT e FFW: in FGT, siaichejvengono azzerati dopo il KSA prima che il PRGA inizi, mentre in FFWjprosegue dal KSA. Questo è visibile nel kernel FGT all'offset+0x83all'interno della funzione di cifratura come sequenza di byte31 c0 31 d2(xor eax,eax; xor edx,edx), che è assente in FFW. Ecco perché lo script deve conoscere la variante.
Come accennato, l'articolo di RandoriSec sulla 7.4.7 è stato un riferimento utile, ma il meccanismo di cifratura è cambiato abbastanza da rendere il loro approccio non direttamente applicabile alla 8.0.0, quindi queste sono le differenze principali:
.init.data del kernel. Nell'8.0.0, quella sezione è tutta zeri, quindi viene usato un semplice schema XOR.rootfs è cifrata con AES-CTR. Nell'8.0.0, è FORT-RC4 (un algoritmo di cifratura personalizzato).rsa_parse_pub_key. Il kernel dell'8.0.0 è strippato, quindi gli indirizzi virtuali hanno dovuto essere trovati facendo reverse engineering diretto della routine di decrittazione.Gli indirizzi virtuali del blob della chiave RSA sono vicini tra le versioni (spostati di 0x3000), il che ha aiutato anche durante il reverse engineering.
L'output decrittato è un vero file gzip. Decomprimendolo, si ottiene un archivio CPIO, che è il formato standard Linux initrd e può essere facilmente estratto con cpio -idmv.