Iniciación al reversing de firmware: extracción del sistema de archivos

No son poco frecuentes las noticias acerca de la aparición de backdoors presentes en miles y miles de dispositivos. Su descubrimiento, muchas veces explotado por atacantes malintencionados, suele ser el resultado de un análisis exhaustivo del firmware correspondiente. Si nunca os habéis enfrentado a este tipo de ejercicio, en el blog de 15/85 Security daban una breve introducción para extraer el sistema de ficheros de un binario, una buena piedra de toque:

1.- Descarga de firmware de prueba, en este caso la versión 1.14.04 de la cámara D-Link modelo DCS-932L:

$ wget ftp://ftp2.dlink.com/PRODUCTS/DCS-932L/REVA/DCS-932L_REVA_FIRMWARE_1.14.04.ZIP

$ unzip DCS-932L_REVA_FIRMWARE_1.14.04.ZIP
Archive:  DCS-932L_REVA_FIRMWARE_1.14.04.ZIP
  inflating: DCS-932L_REVA_RELEASENOTES_1.14.04_EN.PDF 
  inflating: dcs932l_v1.14.04.bin   

2.- Para empezar echamos un vistazo a las strings con 10 o más caracteres imprimibles:

$ strings -10 dcs932l_v1.14.04.bin | more
NetInitTcp
NetTcpSend
NetReceive
send_syn_ack
send_reset
ArpTimeoutCheck
HttpHandler
mpfd_decode
do_httpsvr
rf      - read/write rf register
rf r <reg>        - read rf register
rf w <reg> <data> - write rf register (reg: decimal, data: hex)
Signature: DCS-930            932L  Release 1.11 (2011-05-31)
*** failed ***
relocate_code Pointer at: %08lx
Please choose the operation: 
   %d: Load system code to SDRAM via TFTP. 
   %d: Load system code then write to Flash via TFTP. 
   %d: Boot system code via Flash (default).
   %d: Entr boot command line interface.
   %d: Load Boot Loader code then write to Flash via Serial. 
   %d: Load Boot Loader code then write to Flash via TFTP. 
 Please Input new ones /or Ctrl-C to discard
    Input device IP 
    Input server IP 
0x80200000
0x88001000
0x80100000
    Input Uboot filename 
    Input Linux FileSystem filename 
0x80800000
    Input Linux Kernel filename 
Entering HTTP server.
Entering program & boot linux.
Erase linux block (0x%x ~ 0x%x)
Erase linux block (0x%x ~ 0x%x
### ERROR ### Please RESET the board ###
Warning: Abort rw rf register: too busy
Warning: still busy
Error: rw register failed
rf reg <%d> = 0x%x
*** Error: D+/D- is 1/1, config usb failed.
config usb
Watchdog Reset Occurred
******************************
Software System Reset Occurred
Software CPU Reset Occurred
============================================ 
Ralink UBoot Version: %s
-------------------------------------------- 
(Port5<->None)
DRAM component: %d Mbits %s
DRAM bus: %d bit
...
...

3.- Ahora usamos binwalk, me atravería a decir la herramienta de facto cuando nos enfrentamos a análisis de firmware. Al ejecutarlo directamente nos devolverá varios "hits" del binario:

$ binwalk dcs932l_v1.14.04.bin
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
106352        0x19F70         U-Boot version string, "U-Boot 1.1.3"
106816        0x1A140         CRC32 polynomial table, little endian
124544        0x1E680         HTML document header
124890        0x1E7DA         HTML document footer
124900        0x1E7E4         HTML document header
125092        0x1E8A4         HTML document footer
125260        0x1E94C         HTML document header
125953        0x1EC01         HTML document footer
327680        0x50000         uImage header, header size: 64 bytes, header CRC: 0x88345E96, created: 2016-09-09 13:52:27, image size: 3804958 bytes, Data Address: 0x80000000, Entry Point: 0x803B8000, data CRC: 0x531E94DE, OS: Linux, CPU: MIPS, image type: OS Kernel Image, compression type: lzma, image name: "Linux Kernel Image"
327744        0x50040         LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 6558763 bytes

Mirando la primera línea, vemos que binwalk encontró un string U-Boot en 106352, ya sabéis, el cargador de arranque. Y por supuesto, en 327680, podemos ver un encabezado de uImage que nos indica que encontraremos la imagen del kernel del SO en un archivo LZMA que comienza en 327744.

4.- El siguiente paso seguro que lo habéis adivinado: extraer el LZMA.

$ dd if=dcs932l_v1.14.04.bin skip=327744 bs=1 of=kernel.lzma
3866560+0 records in
3866560+0 records out
3866560 bytes (3,9 MB, 3,7 MiB) copied, 5,02835 s, 769 kB/s

Así de sencillo, comprobamos el fichero extraído:

$ file kernel.lzma
kernel.lzma: LZMA compressed data, non-streamed, size 6558763

Y lo descomprimimos. En mi caso con "unlzma kernel.lzma" me arrojaba el error: "Compressed data is corrupt". Algo "bypasseable" con 7z:

$ 7z x kernel.lzma
7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,12 CPUs Intel(R) Core(TM) i7-8750H CPU @ 2.20GHz (906EA),ASM,AES-NI)

Scanning the drive for archives:
1 file, 3866560 bytes (3776 KiB)

Extracting archive: kernel.lzma
--
Path = kernel.lzma
Type = lzma

ERROR: There are some data after the end of the payload data : kernel

Sub items Errors: 1

Archives with Errors: 1

Sub items Errors: 1

El resultado:
$ file kernel
kernel: data

$ du -h kernel
6,3M    kernel

5.- Al igual que antes, vamos a ejecutar binwalk contra el archivo de datos resultante:
DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
3145804       0x30004C        Linux kernel version "2.6.21 (andy@ipcam-linux.alphanetworks.com) (gcc version 3.4.2) #3121 Fri Sep 9 21:52:18 CST 2016"
3175792       0x307570        SHA256 hash constants, little endian
3408260       0x340184        Copyright string: "Copyright (c) 2010 Alpha Networks Inc."
3422378       0x3438AA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/mlme.c:%d assert SupRateLen <= MAX_LEN_OF_SUPPORTED_RATESfailed
3422502       0x343926        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/mlme.c:%d assert ExtRateLen <= MAX_LEN_OF_SUPPORTED_RATESfailed
3423342       0x343C6E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/mlme.c:%d assert !(ATE_ON(pAd))failed
3425158       0x344386        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/action.c:%d assert pAd->BATable.BAOriEntry[i].Wcid < MAX_LEN_OF_MAC_TABLEfailed
3425298       0x344412        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/action.c:%d assert pBAEntry->Wcid < MAX_LEN_OF_MAC_TABLEfailed
3425770       0x3445EA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert 0failed
3425858       0x344642        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert mpdu_blkfailed
3425954       0x3446A2        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert listfailed
3426046       0x3446FE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert mpdu_blk->pPacketfailed
3426150       0x344766        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pBAEntry->list.qlen == 0failed
3426630       0x344946        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pEntryfailed
3426946       0x344A82        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pAd->BATable.numAsOriginator != 0failed
3427066       0x344AFA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pAd->BATable.numAsRecipient != 0failed
3427514       0x344CBA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pAd->MacTab.Content[Elem->Wcid].Sst == SST_ASSOCfailed
3428102       0x344F06        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pRxBlk->pRxPacketfailed
3428206       0x344F6E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert (0<= pBAEntry->list.qlen) && (pBAEntry->list.qlen <= pBAEntr
3428414       0x34503E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert pBAEntryfailed
3428510       0x34509E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/ba_action.c:%d assert (pBAEntry->list.qlen == 0) && (pBAEntry->list.next == NULL)f
3428902       0x345226        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pEntryfailed
3429054       0x3452BE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert Length <= MGMT_DMA_BUFFER_SIZEfailed
3429342       0x3453DE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pTxWIfailed
3429666       0x345522        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pProbeEntryfailed
3429762       0x345582        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pProbeEntry != NULLfailed
3429918       0x34561E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pSrcBuffailed
3430010       0x34567A        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert (pktLen > 34)failed
3430110       0x3456DE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pRxBlk->pRxPacketfailed
3430214       0x345746        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pHeaderfailed
3430306       0x3457A2        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pAd->FragFrame.pFragPacketfailed
3430418       0x345812        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data.c:%d assert pAd->FragFrame.LastFrag == 0failed
3432322       0x345F82        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/rtmp_init.c:%d assert (Length==0) || (pDest && pSrc)failed
3433174       0x3462D6        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/rtmp_init.c:%d assert dev_pfailed
3447398       0x349A66        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_asic.c:%d assert BssIndex < 4failed
3447498       0x349ACA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_asic.c:%d assert KeyIdx < 4failed
3451114       0x34A8EA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data_pci.c:%d assert QueIdx < NUM_OF_TX_RINGfailed
3451226       0x34A95A        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data_pci.c:%d assert pAd->ate.QID == 0failed
3451470       0x34AA4E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/common/cmm_data_pci.c:%d assert pPacket == NULLfailed
3457110       0x34C056        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert pRxWI->WirelessCliID == BSSID_WCIDfailed
3457230       0x34C0CE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert 0failed
3457570       0x34C222        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert pAd->SharedKey[BSS0][0].CipherAlg <= CIPHER_CKIP128failed
3457706       0x34C2AA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert pTxBlkfailed
3457798       0x34C306        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert pTxBlk->MpduHeaderLen >= 24failed
3457910       0x34C376        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert (pTxBlk->TxPacketList.Number > 1)failed
3458026       0x34C3EA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert (pTxBlk->TxPacketList.Number== 2)failed
3458142       0x34C45E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert TX_BLK_TEST_FLAG(pTxBlk, fTX_bAllowFrag)failed
3458266       0x34C4DA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/rtmp_data.c:%d assert pTxBlk->TxPacketList.Numberfailed
3458498       0x34C5C2        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/sta/connect.c:%d assert SsidLen <= MAX_LEN_OF_SSIDfailed
3462790       0x34D686        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert memfailed
3462958       0x34D72E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pPacketfailed
3463054       0x34D78E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pInPacketfailed
3463150       0x34D7EE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert ppOutPacketfailed
3463266       0x34D862        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pDatafailed
3463358       0x34D8BE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert DataLenfailed
3463506       0x34D952        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert DataSize < 1530failed
3463610       0x34D9BA        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pRxBlk->pRxPacketfailed
3463714       0x34DA22        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pHeader802_3failed
3464130       0x34DBC2        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pTaskfailed
3464462       0x34DD0E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert pNetDevfailed
3464766       0x34DE3E        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert (prefixLen < IFNAMSIZ)failed
3464878       0x34DEAE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_linux.c:%d assert ((slotNameLen + prefixLen) < IFNAMSIZ)failed
3480306       0x351AF2        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_ate.c:%d assert (TxPower >= -7)failed
3484022       0x352976        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_ate.c:%d assert (BbpValue == 0x00)failed
3484126       0x3529DE        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_ate.c:%d assert (BbpValue == 0x04)failed
3485810       0x353072        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_ate.c:%d assert bbp_data == valuefailed
3486762       0x35342A        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_ate.c:%d assert pRaCfg != NULLfailed
3487126       0x353596        Unix path: /net/wireless/rt2860v2_sta/../rt2860v2/os/linux/rt_pci_rbus.c:%d assert pAdfailed
3491536       0x3546D0        Unix path: /etc/Wireless/RT2860STA/RT2860STA.dat
3573187       0x3685C3        Neighborly text, "neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)(%s)"
3807776       0x3A1A20        CRC32 polynomial table, little endian
4038656       0x3DA000        LZMA compressed data, properties: 0x5D, dictionary size: 1048576 bytes, uncompressed size: 8072704 bytes

6.- Como veis, en la parte superior de la salida de binwalk podemos ver la versión del kernel de Linux. También hay otro archivo LZMA en 4038656, así que repetimos la operación de antes:

$ dd if=kernel skip=4038656 bs=1 of=mystery.lzma
2520107+0 records in
2520107+0 records out
2520107 bytes (2,5 MB, 2,4 MiB) copied, 3,25778 s, 774 kB/s
$ unlzma mystery.lzma
 file mystery
mystery: ASCII cpio archive (SVR4 with no CRC)

7.- Bien! tenemos un archivo CPIO, que es otro formato ... y es el tipo de lugar donde es probable que encuentre el sistema de archivos. Así que creamos un directorio donde vamos a volcar el contenido y lo descomprimimos:

$ mkdir fs; cd fs
$ cpio -idm --no-absolute-filenames < ../mystery
cpio: Removing leading `/' from member names
15767 blocks

Y ya está. Si todo ha ido bien podemos explotar el sistema de archivos:

$ ls -las
total 68
4 drwxr-xr-x 17 root root 4096 may  4 11:29 .
4 drwxr-xr-x  3 root root 4096 may  4 11:23 ..
4 drwxrwxr-x  2  501  501 4096 may  4 11:29 bin
4 drwxrwxr-x  3  501  501 4096 may  4 11:29 dev
4 drwxrwxr-x  2  501  501 4096 may  4 11:29 etc
4 drwxrwxr-x  9  501  501 4096 may  4 11:29 etc_ro
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 home
0 lrwxrwxrwx  1  501  501   11 may  4 11:29 init -> bin/busybox
4 drwxr-xr-x  4  501  501 4096 may  4 11:29 lib
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 media
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 mnt
4 drwxrwxr-x  2  501  501 4096 may  4 11:29 mydlink
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 proc
4 drwxrwxr-x  2  501  501 4096 may  4 11:29 sbin
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 sys
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 tmp
4 drwxrwxr-x  5  501  501 4096 may  4 11:29 usr
4 drwxrwxr-x  2  501  501 4096 sep  9  2016 var

Comentarios