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
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
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
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:
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
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
Publicar un comentario