发布时间:2020-11-22 20:20:54来源:FreeBuf
随着5G时代的到来,物联网扮演的角色也越来越重要,同时也伴随更多的安全风险。IOT安全涉及内容广泛,本系列文章将从技术层面谈一谈笔者对IOT漏洞研究的理解。笔者将从固件、web、硬件、IOT协议、移动应用五个维度分别探讨,由于水平能力有限,不当或遗漏之处欢迎大家指正补充。
之所以将固件作为第一个探讨的主题,因为比较基础,IOT漏洞研究一般无法绕过。以下将介绍固件解密(若加密)、解包打包、模拟和从固件整体上作安全评估四部分。
有些IOT设备会对固件加密甚至签名来提高研究门槛和升级时的安全性,因为加解密比较耗费资源,这类设备一般配置会比较高,比如一些路由器和防火墙。
判断固件是否加密比较简单,有经验的小伙伴有二进制编辑器打开就能看出一二,一般会存在以下特性。
除了固件指示头没有可见字符,(除去header)数据按比特展开01频率基本一致binwalk(-e)无法解析固件结构,且(-A)没有识别出任何cpu架构指令
如果满足上述特点,就会猜测固件已被加密,固件解密一般会从这几个角度,但也不局限于下面的方法。
此种方法只限于固件始终以加密状态存在,当系统启动时才通过解密解包加载至flash,且设备缺乏(UART/JTAG等)动态调试手段。由于flash中有完整的解密过程,可以通过编程器读取flash,逆向解密算法和密钥,达到解密固件的目的。比如从某设备的读取的flash内存分布如下:
0x000000-0x020000bootsection0x020000-0x070000encryptsection0x070000-0x200000encryptsection0x200000-0x400000configsection显然我们需要的加密过程在bootsection中,我们需要从中找到加密算法和密钥,一般加密都采用AES等公开分组算法,关键是找到分组模式,IV(非ECB)和密钥。将boot加载到IDApro中,并没有自动识别:可以通过对比ARM代码最开始部分的中断向量表结构手动识别,常见的入口代码如下所示。
.globl_start_start:bresetldrpc,_undefined_instructionldrpc,_software_interruptldrpc,_prefetch_abortldrpc,_data_abortldrpc,_not_usedldrpc,_irqldrpc,_fiq..._irq:.wordirq之后可以就可以逆向得到加密算法为AES,密钥通过设备序列号的sha256哈希获得。通过IDApro识别此类结构将在后文介绍RTOS时探讨,利用这种固件加密方式的设备安全级别教高,一般设备只在升级时进行解密验证。
这个方法最容易理解,即在设备启动后利用UART、JTAG、Console或网络等手段把固件(打包)回传,这样就绕过了解密环节。值得注意的是需要设备提供这些接口,具体方法因设备不同而异,这些接口的使用将会在硬件篇里作介绍。
此种方法适用于厂商一开始没采用加密方案,即旧版固件未加密,在某次升级中添加了解密程序,随后升级使用加密固件。这样我们就可以从一系列固件中找到加密和未加密之间的边界版本,解包最后一个未加密版本逆向升级程序即可还原加密过程。通过下载上图所示某路由器固件,解包后通过搜索包含诸如“firmware”、“upgrade”、“update”、“download”等关键字的组合定位升级程序位置。当然存在调试手段也可以在升级时ps查看进程更新定位升级程序和参数:
/usr/sbin/encimg-d-i
AES_set_decrypt_key(//userinputkeyconstunsignedchar*userKey,//sizeofkeyconstintbits,//encryptionkeystructwhichwillbeusedby//encryptionfunctionAES_KEY*key)AES_cbc_encrypt(//inputbufferconstunsignedchar*in,//outputbufferunsignedchar*out,//bufferlengthsize_tlength,//keystructreturnbypreviousfunctionconstAES_KEY*key,//initializatinvectorunsignedchar*ivec,//isencryptionordecryptionconstintenc)1.1.5逆向升级程序此种方法适用于已经通过接口或边界版本得到升级程序,可以利用分组算法的盒检测工具来判别加密算法和定位位置,当然binwalk也可以解析某些简单情况,比如某工控HMI固件:
iot@attifyos~/Documents>binwalkhmis.tar.gzDECIMALHEXADECIMALDESCRIPTION--------------------------------------------------------------------------------340x22OpenSSLencyption,salted,salt:0x5879382A7直接加载升级程序,定位openssl调用很容易就得到解密命令:
1.1.6漏洞获取密钥
如果找不到边界版本,又找不到调试接口或不熟悉硬件调试,可以考虑采用历史版本漏洞先获取设备控制权,在拿到升级程序逆向加密算法。这种方法比较取巧,需要设备存在RCE漏洞的历史固件,通过降级操作植入漏洞获取权限,下载所需升级程序,然后逆向得到加密算法。
初入IOT安全研究的小伙伴会觉得固件解包很简单,直接binwalk-Me就可以了,但是理想很丰满,现实很骨感,固件测试多了就会发现binwalk很多情况下都解不开。IOT固件一般分为两类,一类存在文件系统,大多基于linux/BSD,另一类固件是一个整体,即我们所说的RTOS(Real-timeoperatingsystem)。
binwalk大家应该都很熟悉,使用binwalk能直接得到rootfs文件系统的情况这里不再赘述,笔者认为binwalk的强大之处在于可以解析识别多重格式的header,为解包提供参考。以下介绍几种需要绕点弯的情况,当然固件千差万别,全看设计者设计,不能一一列举。
1.2.1.1UBI(UnsortedBlockImage)
UBI格式的固件算比较常见的,binwalk并不能直接解包,但是网上有现成的工具ubi_reader,这里有个需要注意的地方:
UBI_reader解包,UBI文件必须是1024bytes的整数倍,需要增删内容对其
比如通过分析某路由器,发现其rootfs是UBI格式:
#binwalkROM/wifi_firmware_c91ea_1.0.50.binDECIMALHEXADECIMALDESCRIPTION--------------------------------------------------------------------------------6840x2ACUBIerasecountheader,version:1,EC:0x0,VIDheaderoffset:0x800,dataoffset:0x1000首先安装ubi_reader:
$sudoapt-getinstallliblzo2-dev$sudopipinstallpython-lzo$gitclonehttps://github.com/jrspruitt/ubi_reader$cdubi_reader$sudopythonsetup.pyinstall或者直接
$sudopipinstallubi_reader然后将根据地址将UBI结构提取出来,利用ubireader_extract_files[options]path/to/file即可解包。
1.2.1.2PFS
有些固件binwalk可以识别出header,但是无法解开,比如下面这个固件
iot@attifyos~/Documents>binwalk-Mev2912_389.allScanTime:2020-11-0418:39:13TargetFile:/home/iot/Documents/v2912_389.allMD5Checksum:180c60197aae7e272191695e906c941eSignatures:396DECIMALHEXADECIMALDESCRIPTION--------------------------------------------------------------------------------15467990x179A2Fgzipcompresseddata,lastmodified:2042-04-2620:13:56(bogusdate)17177440x1A35F0LZ4compresseddata41715130x3FA6F9SHA256hashconstants,littleendian41790980x3FC59ACopyrightstring:"Copyright(c)1998-2000byXXXXXCorp."42145320x404F04Base64standardindextable42247800x40770CHTMLdocumentheader42323690x4094B1SHA256hashconstants,littleendian43078390x41BB7FSHA256hashconstants,littleendian43140170x41D3A1XMLdocument,version:"1.0"47022300x47C016Base64standardindextable47071970x47D37DCertificateinDERformat(x509v3),headerlength:4,sequencelength:87347276090x482339Base64standardindextable47912810x491BF1PFSfilesystem,version1.0,12886files48074010x495AE9Base64standardindextable...iot@attifyos~/Documents>ls_v2912_389.all.extracted/pfs-root/000/WEBLOGIN.HTM_WEBLOGIN.HTM.extracted/iot@attifyos~/Documents>ls_v2912_389.all.extracted/pfs-root/000/_WEBLOGIN.HTM.extracted/3CB3CB.zlibE235E235.zlib运行binwalk后查看结果,发现没有发现任何可识别的东西,此时可以手动分析或者去搜索一些相关工具。在网上找到相关工具,直接根据提示使用命令就可解开固件。
iot@attifyos~/D/draytools>pythondraytools.py-Fv2910_61252.allv2910_61252.all.outwritten,12816484[0x00C39064]bytesFSextractedto[/home/iot/Documents/draytools/fs_out],429filesextractediot@attifyos~/D/draytools>lsfs_out/v2000/v2910.lstiot@attifyos~/D/draytools>lsfs_out/v2000/act_sta.htmCSS/header.htmINDEX2.HTMivr_711u/jg/l_m.htmmenu.htmSTATUS.HTMSYSINFO_C.TXTUPNP/webauth.htmCGI-BIN/DOC/IMAGES/ivr_711a/ivr_729/JS/LOGIN.HTMrpage.htmSTYLE.CSSSYSINFO.TXTVLAN/这里简单看一下固件解包关键代码,关键在于找到类似’\xA5\xA5\xA5\x5A\xA5\x5A’的header,之后根据具体格式解包解压即可,所以固件解包说到底还是数据格式分析。
defdecompress_firmware(data):flen=len(data)sigstart=data.find('\xA5\xA5\xA5\x5A\xA5\x5A')ifsigstart<=0:sigstart=data.find('\x5A\x5A\xA5\x5A\xA5\x5A')ifsigstart>0:ifdraytools.verbose:print'Signaturefoundat[0x%08X]'%sigstartlzosizestart=sigstart+6lzostart=lzosizestart+4lzosize=unpack('>L',data[lzosizestart:lzostart])[0]returndata[0x100:sigstart+2]\+pydelzo.decompress('\xF0'+pack(">L",0x1000000)\+data[lzostart:lzostart+lzosize])...1.2.1.3OpenwrtLua
lua结构解析放在解包这里可能不太恰当,但鉴于Openwrt的使用基数很大,在这里简单提一下。Lua是一门方便嵌入并可扩展的轻量级脚本语言,Openwrt开发中会使用该脚本语言。值得注意的是,有些设备的lua并不是纯文本,存在混淆,需要使用luadec反编译。openwrt中的lua脚本和传统的luajit编译后的有点不一样,需要打几个补丁才能正常使用luadec进行反编译,命令如下:
$cd..$mkdirluadec$cdluadec/$gitclonehttps://github.com/viruscamp/luadec$cdluadec/$gitsubmoduleupdate--initlua-5.1$cdlua-5.1$makelinux$makeclean$mkdirpatch$cdpatch/$gethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/010-lua-5.1.3-lnum-full-260308.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/030-archindependent-bytecode.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/011-lnum-use-double.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/015-lnum-ppc-compat.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/020-shared_liblua.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/040-use-symbolic-functions.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/050-honor-cflags.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/100-no_readline.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/200-lua-path.patch$wgethttps://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/300-opcode_performance.patch$mvpatch/patches$foriin../patches/*.patch;dopatch-p1<$i;done$foriin./patches/*.patch;dopatch-p1<$i;done$makelinux修改lua-5.1/src/MakeFile:
#USE_READLINE=1+PKG_VERSION=5.1.5-CFLAGS=-O2-Wall$(MYCFLAGS)+CFLAGS=-fPIC-O2-Wall$(MYCFLAGS)-$(CC)-o$@-L.-llua$(MYLDFLAGS)$(LUA_O)$(LIBS)+$(CC)-o$@$(LUA_O)$(MYLDFLAGS)-L.-llua$(LIBS)-$(CC)-o$@-L.-llua$(MYLDFLAGS)$(LUAC_O)$(LIBS)+$(CC)-o$@$(LUAC_O)$(MYLDFLAGS)-L.-llua$(LIBS)接着执行:
$makelinux$ldconfig$cd../luadec$makeLUAVER=5.1$sudocpluadec/usr/local/bin/利用luadec显示代码结构:
$luadec-pnsquashfs-root/usr/lib/lua/luci/sgi/uhttpd.lua00_00_0_00_0_10_0_2利用luadec反编译指定的函数(函数0包含子函数):
$luadec-f0squashfs-root/usr/lib/lua/luci/sgi/uhttpd.lua需要注意的是,luadec编译与架构相关,用官方luadec无法解析arm环境下的lua文件,但网上也有相应的工具,这里不再赘述。
很多IOT设备都采用RTOS(实时操作系统)架构,固件本身就是一个可执行文件,不存在文件系统,启动后直接加在运行。对RTOS的分析最重要就是两点:
(一)固件程序入口(二)固件程序符号
1.2.2.1vxworks
首先从应用较广且有套路可循的vxworks说起,VxWorks是WindRiverSystem公司推出的一个实时操作系统,广泛应用在通信、军事、航空、航天嵌入式设备领域。因为有标准,所以好识别,以下面这个固件为例:
iot@attifyos~/Documents>binwalkimage_vx5.binDECIMALHEXADECIMALDESCRIPTION--------------------------------------------------------------------------------3352800x51DB0PEMcertificate...37215560x38C954GIFimagedata,version"89a",10x21085189360x81FD18VxWorksoperatingsystemversion"5.5.1",compiled:"Mar52015,15:56:18"97369880x94931CSHA256hashconstants,littleendian...133745990xCC1487Copyrightstring:"Copyright1999-2001WindRiverSystems."133873880xCC567CVxWorkssymboltable,bigendian,firstentry:[type:function,codeaddress:0xF4A09A00,symboladdress:0xF813C800]133914050xCC562DVxWorkssymboltable,littleendian,firstentry:[type:function,codeaddress:0xB8BD,symboladdress:0xD000C800]binwalk已经识别出固件为Vxworks5.5.1,并且给出了符号表位置。首先需要识别固件的入口点,如果固件被封装成ELF格式,直接利用readelf就可以得到基地址,这里显然不适用。
iot@attifyos~/Documents>readelf-aimage_vx5_arm_little_eniadn.binreadelf:Error:NotanELFfile-ithasthewrongmagicbytesatthestartiot@attifyos~/Documents>binwalk-Aimage_vx5.bin|moreDECIMALHEXADECIMALDESCRIPTION--------------------------------------------------------------------------------2440xF4ARMinstructions,functionprologue4080x198ARMinstructions,functionprologue4400x1B8ARMinstructions,functionprologue4720x1D8ARMinstructions,functionprologue6080x260ARMinstructions,functionprologue通过binwalk-A得到固件架构是ARM,直接用IDApro载入:分析固件开头跳转判断加载地址为0x1000。对于Vxworks一般判断基址办法有:
分析固件头部的初始化代码,找到vxworks启动的第一个函数usrInit跳根据BSS区初始化特征找到BSS边界,根据偏移计算固件加载地址
然后根据binwalk指示的位置,修复符号表名。函数表存放了函数名和函数地址,通过两者定位也可以反过来验证基地址的正确性,比如上图所示的0x00c813f8是函数名:
0x009aa0f4是函数地址:
由于基地址和架构有关,在这里就不详细展开,对于vxworks的分析我们可以借助一款能自动化修复入口和符号的插件—vxhunter。以Ghidra为例,载入固件后直接选择vxhunter_firmware_init.py插件和vxworks版本,就可以自动修复入口和符号:
1.2.2.2U-boot
boot类的固件也是我们常会遇见的一类无文件系统固件,比如很多IOT设备会采用U-boot作引导,因为U-boot开源,我们可以参照源代码分析,对于有些架构的U-boot也可以采用固定套路,比如mips可以根据$gp寄存器等。
1.2.2.3Chipfirmware
有些IOT固件没有资料,逆向困难,比如下面某款ARM芯片的固件,将其载入IDApro发现没有识别出任何函数:这样我们就需要对固件有一个整体的分析了,我们看到固件0x100的位置十分有趣:
4字节排列后都以0x2开头,这里既不是代码,也不是数据,那就很可能是地址了,这里应该是一张表,所以基地址很可能是0x200000。我们rebase之后再查看字符串:
看到许多类似函数名的字符串,找到具体位置后,在固件中二进制搜索0x16852A,即wlc_probresp_attach地址(小端)。
可以看到真的搜索到了,而且也是一个表的结构:
根据基址找到在IDApro中的位置:
可以看到完成了部分的交叉引用,后续分析比较复杂,这里就不再展开,实际上0x100位置是函数地址表,在该固件中这样表有很多。所以类似地址表,字符串都是我们分析固件基址和函数的重要线索。
拆东西容易装东西难,这个道理也适用于固件打包。如果设备留有调试接口,一般不用打包操作,毕竟安全研究是以逆向思维为主。有时缺乏调试手段,我们就要在解开的固件中手动添加。一般将交叉编译好的telnetd,dropbear(sshd),gdb放入固件文件,再替换启动脚本打包。linux的启动脚本套路众多,尤其在IOT设备中,这里笔者一般采用比较讨巧的方法,比如确定/sbin/xxxd服务会开机运行,可以将其替换:
#mvrootfs/sbin/xxxdsbin/xxxdd#touchrootfs/sbin/xxxd#chmod+xrootfs/sbin/xxxd之后在sbin/xxxd添加
#!/bin/sh/usr/sbin/telnetd-F-l/bin/sh-p1234&/sbin/xxxdd&这样开机启动xxxd时就会先运行telnetd。
如果能从正向开发角度来打包当然最方便,也就是交叉编译的事。笔者研究过的一些设备中,主要是路由器固件会部分遵循GPL,就是开源一部分代码软件(一般本来就是基于开源工具),并提供剩下软件的二进制文件和整个固件的打包工具(方法)。比如之前研究的某款路由设备就提供了开源下载:下载该zip包,根据自己的需求编译rootfs,最后利用zip包中自带的工具打包:
./packet-k%s-frootfs-bcompatible_r6400.txt-okkernel-oallimage-orrootfs-iambitCfg.h1.3.2firmware-mod-kitfirmware-mod-kit(fmk)可能是最常用的基于binwalk的解打包工具,但是由于很久没用更新,使用场景有限。fmk的安装使用都比较简单,如下所示:
#Forubuntu$sudoapt-getinstallgitbuild-essentialzlib1g-devliblzma-devpython-magicbsdmainutilsautoconf#Forredhat/centos$yumgroupinstall"DevelopmentTools"$yuminstallgitzlib1g-devxz-develpython-magiczlib-develutil-linux#使用$./extract-firmware.shfirmware.bin//解包$cpnew-telnetdfmk/rootfs/usr/sbin/telnetd//按需修改$./build-firmware.sh//打包1.3.3手动分析打包的难度在于固件要与原固件一致,并通过各种校验,否则轻则刷机失败,重则设备变砖。笔者之前有一篇关于netgearupnp漏洞的文章,涉及netgear固件打包过程,有兴趣的小伙伴可以看一看。固件一般会分成许多section,为了方便解析,每个section会有指示头,头中可能会存放标志、大小和crc校验等信息,这些信息都为解打包提供依据。比如可以先获取固件大小(十六进制),根据固件大小端拆分字节,一般是4字节,然后在固件头上寻找类似字节(固件头上的指示长度会减去头长度),接着从指示大小的字节往后分析就可以澄清格式,和分析网络协议的过程很像。当然大部分头都是有标准的,可以根据标准格式一一对应。值得注意的是,有些厂商会给固件签名,这个就会增加打包的难度。此时我们可以寻找一些遵循GPL的官方打包工具,或者利用openssl生成公私钥对,覆盖设备中的验证公钥,这里当然要有漏洞,不然就掉进鸡生蛋蛋生鸡的循环。当然还有一个比较好且廉价的办法—-固件模拟。
固件模拟根据不同需要可能有以下3个场景:
(一)只需模拟某个应用,比如web、upnpd、dnsmasq等,目的是对该应用作调试。此时可以直接用模拟工具运行该程序,只需考虑动态库是否能加载。(二)需要模拟固件shell,与整个系统有所交互。这里可以通过chroot改变根路径,并利用模拟工具执行/bin/sh。同时可以挂在/proc,这样在ps查看进程时看上去更加真实。(三)需要模拟整个固件启动,并且网卡等也能正常使用。这里要利用可模拟img系统的工具直接加载整个系统,也可以利用“套娃”大法,先模拟该架构的debian.img,再用chroot起设备的roofs。
下面介绍几个常用的模拟工具。
Qemu是最老牌的多架构模拟工具,上述3个使用场景qemu都可以满足。qemu可以下载安装也可以直接利用源安装,这里要注意的是如果模拟应用不仅需要qemu,还需安装qemu-user。
#Forubuntu$sudoapt-getinstallqemu$sudoapt-getinstallqemu-userqemu-uesr-staticqemu模拟应用
以模拟ARM固件为例,解开固件得到rootfs,下面是利用qemu模拟执行busybox:
iot@attifyos~/Document>cp(whichqemu-arm-static)./rootfs/iot@attifyos~/Document>sudochroot./rootfs/qemu-arm-static/bin/busyboxBusyBoxv0.47(2018.08.30-14:14+0000)multi-callbinary--GPL2Usage:busybox[function][arguments]...or:[function][arguments]...BusyBoxisamulti-callbinarythatcombinesmanycommonUnixutilitiesintoasingleexecutable.Mostpeoplewillcreatealinktobusyboxforeachfunctiontheywishtouse,andBusyBoxwillactlikewhateveritwasinvokedas.Currentlydefinedfunctions:busybox,cat,chgrp,chmod,chown,cp,date,dd,df,echo,free,grep,gunzip,gzip,halt,hex,hostname,id,init,kill,killall,ln,ls,mkdir,mknod,more,mount,mv,ping,ps,pwd,reboot,rm,rmdir,sh,sleep,sync,syslogd,tail,tar,touch,tty,umount,uname,zcatqemu模拟shell
利用qemu模拟sh与上述情况相似,可以先mountproc,使得ps查看进程时显得更加真实:
iot@attifyos~/Document>cd./rootfsiot@attifyos~/Document>rm-rfprociot@attifyos~/Document>sudomount-tproc/proc./prociot@attifyos~/Document>cd..iot@attifyos~/Document>sudochroot./rootfs/qemu-arm-static/bin/shBusyBoxv0.47(2018.08.30-14:14+0000)Built-inshellEnter'help'foralistofbuilt-incommands./#lsbingmsbinusrdevlibstorevaretcqemu-arm-staticsys/#psPIDPPIDUidMemCPUStCommand100299360.0Sinitsplash20000.0S[kthreadd]72000.6S[ksoftirqd/0]33210267760.0Ssystemd-journald3581065680.0Ssystemd-udevd37810281440.0Svmware-vmblock-fuse/run/vmblock-fuse-orw,su4951100185000.0Ssystemd-timesyncd6071082440.0Shaveged--Foreground--verbose=1-w102460810427560.0SVGAuthService61510388240.0Svmtoolsd6191037400.0Scron-f621110362160.0Savahi-daemon:running[attifyos.local]...qemu模拟系统
qemu的功能很强大,可以像在vmware、virtualbox中一样安装系统:
$qemu-imgcreate-fqcow2arm.qcow210G$qemu-system-arm-m1024M-sdarm.qcow2-Mvexpress-a9-cpucortex-a9-kernel../Downloads/vmlinuz-3.2.0-4-vexpress-initrd../Downloads/initrd.gz-append"root=/dev/ram"-no-reboot利用qemu模拟完整固件最常规的办法就是将rootfs制作成img或qcow2文件,再用相应架构的qemu模拟执行,这里我们介绍一下前面提到的“套娃”大法。首先下载Debian官方ARM构架的系统镜像。挂在qcow2镜像,将rootfs拷贝至qcow2镜像:
$sudoapt-getinstalllibguestfs-tools#利用guestmount挂在qcow2镜像$guestmount-adebian_wheezy_armel_standard.qcow2-m/dev/sda1/mnt#拷贝rootfs$cp-rf./rootfs/mnt/root/rootfs$guestunmount/mnt然后启动qcow2文件:
qemu-system-arm-Mversatilepb-kernelvmlinuz-3.2.0-4-versatile-initrdinitrd.img-3.2.0-4-versatile-hdadebian_wheezy_armel_standard.qcow2-append"root=/dev/sda1"利用chroot“套娃”系统:
$chroot-u./rootfs/bin/shBusyBoxv0.47(2018.08.30-14:14+0000)Built-inshellEnter'help'foralistofbuilt-incommands./#使用debian现成的qcow2镜像可以省去网卡和其他一些配置过程,提高模拟效率。值得注意的是,qemu在对程序黑盒测试时也经常使用,比如AFL的qemu_mode,在后面的篇章里我们会探讨,当然AFL同样可以使用unicorn_mode,使用的就是下面介绍的模拟器unicorn。
Unicorn一个基于Qemu的cpu指令模拟框架,也就是可以模拟任何的cpu指令。我们一般可以利用Unicorn指令级模拟特性:
对(IOT)程序作模糊测试用于gdb插件,或代码模拟执行的插桩,修改代码逻辑模拟执行一些复杂混淆代码,提高人工逆向效率
关于Unicorn模拟执行修改代码逻辑的教程比较多,这里不再赘述,下面介绍一款基于Unicorn的IDApro插件,该插件可以在IDApro中模拟执行,并给出执行结果,UnicornFuzz相关内容会在后面的篇章中介绍。
#include
.text:00400640.globlcalc.text:00400640calc:#CODEXREF:main+18↓p.text:00400640.text:00400640var_10=-0x10.text:00400640var_4=-4.text:00400640arg_0=0.text:00400640arg_4=4.text:00400640.text:00400640addiu$sp,-0x18.text:00400644sw$fp,0x18+var_4($sp).text:00400648move$fp,$sp.text:0040064Csw$a0,0x18+arg_0($fp).text:00400650sw$a1,0x18+arg_4($fp).text:00400654lw$v1,0x18+arg_0($fp).text:00400658lw$v0,0x18+arg_4($fp).text:0040065Caddu$v0,$v1,$v0.text:00400660sw$v0,0x18+var_10($fp).text:00400664lw$v0,0x18+var_10($fp).text:00400668move$sp,$fp.text:0040066Clw$fp,0x18+var_4($sp).text:00400670addiu$sp,0x18.text:00400674jr$ra.text:00400678nop.text:00400678#Endoffunctioncalc创建一个emuobject
Python>a=EmuMips()配置模拟地址和参数
Python>a.configEmu(0x00400640,0x00400678,[2,3])[*]Initregisterssuccess...[*]Initcodeanddatasegmentsuccess![*]InitStacksuccess...[*]setargs...开始指令模拟
Python>a.beginEmu()[*]emulating...[*]Done!Emulateresultreturn:0x5通过Unicorn模拟执行得到sum(2+3)的结果为0x5。
Qiling是一款很年轻的基于Unicorn的模拟器,专为IOT研究而生。Qiling可以作为IDApro插件,也可以利用QilingUnicornalf进行fuzz,还可以模拟IOT设备固件。Qiling用python3开发,可以直接用pip3installqiling安装,以下是官方模拟ARMj架构路由器固件的部分代码:
importos,socket,sys,threadingsys.path.append("..")fromqilingimport*defpatcher(ql):...defnvram_listener():...defmy_sandbox(path,rootfs):ql=Qiling(path,rootfs,output="debug")ql.add_fs_mapper("/dev/urandom","/dev/urandom")ql.hook_address(patcher,ql.loader.elf_entry)ql.run()if__name__=="__main__":nvram_listener_therad=threading.Thread(target=nvram_listener,daemon=True)nvram_listener_therad.start()my_sandbox(["rootfs/bin/httpd"],"rootfs")可以看到Qiling框架进一步简化了模拟所需代码,同时提供了指令级插桩功能,还是很强大的。
Firmadyne是一个自动化和可扩展的系统,用于对基于Linux的嵌入式固件执行仿真和动态分析。Firmadyne也是是基于Qemu,使用起来大同小异,网上教程也很多,这里主要介绍一款使用Firmadyne的固件灰盒fuzz工具的—Firm-AFL。Firm-AFL的安装分为用户模式和系统模式,用户模式:
$cduser_mode/$./configure--target-list=mipsel-linux-user,mips-linux-user,arm-linux-user--static--disable-werror$make系统模式:
$cdqemu_mode/DECAF_qemu_2.10/$./configure--target-list=mipsel-softmmu,mips-softmmu,arm-softmmu--disable-werror$make以DlinkDIR-815固件为例,首先安装好Firmadyne,做一些初始化工作:
$cdfirmadyne$./sources/extractor/extractor.py-bdlink-sql127.0.0.1-np-nk"../firmware/DIR-815_FIRMWARE_1.01.ZIP"images$./scripts/getArch.sh./images/9050.tar.gz$./scripts/makeImage.sh9050$./scripts/inferNetwork.sh9050$cd..$pythonFirmAFL_setup.py9050mipsel根据路由器架构修改image_9050目录中的run.sh:
ARCH=mipselQEMU="./qemu-system-${ARCH}"KERNEL="./vmlinux.${ARCH}_3.2.1"IMAGE="./image.raw"MEM_FILE="./mem_file"${QEMU}-m256-mem-prealloc-mem-path${MEM_FILE}-M${QEMU_MACHINE}-kernel${KERNEL}\然后就可以启动fuzz脚本开始测试:
$cdimage_9050$sudopythonstart.py90501.5总结固件研究是IOT漏洞研究的基础,本篇尽可能全的介绍了固件研究方面的内容,一方面笔者经验有限,还有些点未能涉及,另一方面篇幅有限,许多内容没能进一步展开,以后有机会再和大家一起探讨。下一篇将介绍IOTweb漏洞研究的相关内容。
https://cloud.tencent.com/developer/article/1005700https://5alt.me/2017/08/某智能设备固件解密/http://blog.nsfocus.net/hmi-firmware-decryption-0522/https://www.ershicimi.com/p/8e818120ac6352368837ef614dd496e4http://blog.nsfocus.net/hmi-firmware-decryption-0522/https://gorgias.me/2019/12/27/固件提取系列-UBI文件系统提取以及重打包/https://paper.seebug.org/771/https://paper.seebug.org/1090/https://blog.csdn.net/yalecaltech/article/details/104113779https://dassecurity-labs.github.io/HatLab_IOT_Wiki/firmware_security/firmware_security_tools/IDA_unicorn_base_tool/
精彩推荐