
Descriptografar e extrair imagens de firmware do FortiOS 8.0.0.
Descriptografar e extrair imagens de firmware FortiOS 8.0.0.
Este script estende o Forticrack do Bishop Fox para suportar imagens de firmware 8.0.0. Além disso, o artigo da RandoriSec sobre criptografia de firmware FortiGate 7.4.7 foi muito útil para reverter a criptografia do FortiOS 8.0.0, já que parece uma iteração mais recente do 7.4.7.
Funciona tanto para imagens FGT quanto FFW.
Isto foi testado tanto em FGT quanto em FFW v8.0.0.F-build0167. Outras compilações podem exigir reversão do kernel novamente para encontrar os segmentos exatos do kernel e os endereços virtuais da chave pública RSA e da chave XOR.
O script tenta detectar automaticamente se o arquivo .out corresponde a FGT ou FFW com base no nome. Também pode ser especificado como um argumento opcional, apenas para garantir.
$ python3 forticrack_v8.py
[x] Uso: python3 forticrack_v8.py <.out file> [FGT|FFW]
Demonstração:

Diretório resultante:

Já existem inúmeros artigos e scripts sobre descriptografia de fortiOS por aí (como os mencionados anteriormente). No entanto, nenhum deles se aplica ao FortiOS 8.0.0, pois a Fortinet modificou sua criptografia mais uma vez.
A Fortinet permite baixar imagens de upgrade tanto para FortiFirewall quanto para FortiGate em https://support.fortinet.com/ > Login > Suporte > Imagens VM. Essas imagens são arquivos .out, que é o que este script espera como entrada. Quando executado, ele realiza 4 operações principais:
.out (trabalho do Bishop Fox)O arquivo de upgrade .out é criptografado com uma cifra de bloco personalizada baseada em XOR. Bishop Fox fez engenharia reversa e publicou o forticrack junto com um ótimo artigo. Esta parte do script usa praticamente o mesmo código do forticrack original do Bishop Fox, que extrai a chave correspondente de 32 bytes e descriptografa o arquivo .out. Sugiro que você leia o artigo se quiser saber mais sobre isso.
O arquivo descriptografado é uma imagem de firmware Fortinet padrão. O script o extrai usando binwalk, produzindo o seguinte sistema de arquivos:
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
Onde:
boot/ : Diretório com arquivos do bootloaderdatafs.tar.gz : Sistema de arquivos de dadosflatkc : Kernel Linux bzImagerootfs.gz : Sistema de arquivos criptografadoTodos os arquivos interessantes para pesquisadores de vulnerabilidades, como /sbin/init, estão criptografados dentro de rootfs.gz.
rootfs.gz (a parte nova)Esta é a parte que é nova para a versão 8.0.0. Para descobrir, Claude Code foi amplamente utilizado para reverter a lógica de descriptografia correspondente e obter os endereços virtuais codificados dentro da imagem do kernel, tomando como referência o artigo da RandoriSec sobre FortiGate 7.4.7. Pela minha experiência, a reversão assistida por IA realmente se destaca ao analisar questões de criptografia, que era uma tarefa classicamente difícil quando a reversão manual era a única opção.
O arquivo rootfs.gz é criptografado com uma cifra de fluxo personalizada chamada FORT-RC4. A chave para descriptografá-lo está embutida dentro de uma assinatura RSA PKCS#1 anexada ao final do arquivo. Para descriptografar esta assinatura, a chave pública RSA correspondente deve ser usada, que pode ser recuperada da imagem do kernel.
Como flatkc é um bzImage, o ELF do kernel pode ser facilmente extraído localizando o payload gzip dentro dele e o descomprimindo. Dentro do ELF, no endereço virtual 0xffffffff8179a1a0, existem 270 bytes de dados DER codificados com XOR representando a chave pública RSA. A chave XOR de 32 bytes para decodificá-la está em 0xffffffff8179a2c0. A decodificação é apenas decoded[i] = encoded[i] ^ xor_key[i & 0x1f], e o resultado é analisado como uma estrutura DER RSAPublicKey PKCS#1 padrão (uma chave pública RSA-2048).
Com a chave pública RSA recuperada, o bloco de assinatura (últimos 256 bytes de rootfs.gz) é descriptografado calculando m = sig^e mod n. O resultado de 256 bytes é uma mensagem preenchida PKCS#1 v1.5 Tipo 1 com o seguinte layout:
m[0x00] = 0x00
m[0x01] = 0x01
m[0x02..0x9E] = 0xFF (157 bytes de preenchimento)
m[0x9F] = 0x00
m[0xA0..0xBF] = SHA256(rootfs.gz[:-256])
m[0xC0..0xDF] = (não utilizado)
m[0xE0..0xFF] = Chave RC4 (32 bytes)
O hash SHA-256 é verificado em relação ao corpo de rootfs.gz como uma verificação de integridade, e a chave RC4 de 32 bytes no final é a que realmente é usada para descriptografar o arquivo.
Em relação ao FORT-RC4, ele foi completamente revertido por vibração pelo Claude. Aqui está como funciona:
FORT-RC4 tem um KSA padrão, mas um PRGA modificado: em vez de produzir um byte de keystream por rodada a partir de uma única consulta S-box, ele faz duas consultas extras usando versões com bits misturados de
iej, aplica XOR com0xAAem um índice de mistura e combina dois valores S-box para produzir o byte final. Há também uma diferença entre FGT e FFW: no FGT, tantoiquantojsão redefinidos para 0 após o KSA antes do PRGA começar, enquanto no FFWjcontinua do KSA. Isso é visível no kernel FGT no offset+0x83dentro da função de cifra como a sequência de bytes31 c0 31 d2(xor eax,eax; xor edx,edx), que está ausente no FFW. É por isso que o script precisa saber a variante.
Conforme mencionado, o artigo da RandoriSec sobre 7.4.7 foi uma referência útil, mas o mecanismo de criptografia mudou o suficiente para que sua abordagem não se aplicasse diretamente ao 8.0.0, então estas são as principais diferenças:
.init.data do kernel. No 8.0.0, essa seção é toda zeros, então um esquema XOR simples é usado.rootfs é criptografado com AES-CTR. No 8.0.0, é FORT-RC4 (um algoritmo de criptografia personalizado).rsa_parse_pub_key. O kernel 8.0.0 é removido de símbolos, então os endereços virtuais tiveram que ser encontrados revertendo a rotina de descriptografia diretamente.Os endereços virtuais para o blob da chave RSA são próximos entre as versões (deslocados por 0x3000), o que também ajudou durante a reversão.
A saída descriptografada é um arquivo gzip real. Ao descomprimi-lo, obtém-se um arquivo CPIO, que é o formato padrão initrd do Linux e pode ser facilmente extraído com cpio -idmv.