您现在的位置: 首页 > 微信营销 > 养生之道 > 十六进制计算器, IoT漏洞研究(一)固件基础

十六进制计算器, IoT漏洞研究(一)固件基础

作者:   来源:  热度:12  时间:2020-12-01







随着5G时代的到来,物联网扮演的角色也越来越重要,同时也伴随更多的安全风险。IOT安全涉及内容广泛,本系列文章将从技术层面谈一谈笔者对IOT漏洞研究的理解。笔者将从固

随着5G时代的到来,物联网扮演的角色也越来越重要,同时也伴随更多的安全风险。IOT安全涉及内容广泛,本系列文章将从技术层面谈一谈笔者对IOT漏洞研究的理解。笔者将从固件、web、硬件、IOT协议、移动应用五个维度分别探讨,由于水平能力有限,不当或遗漏之处欢迎大家指正补充。

IoT固件基础

之所以将固件作为第一个探讨的主题,因为比较基础,IOT漏洞研究一般无法绕过。以下将介绍固件解密(若加密)、解包打包、模拟和从固件整体上作安全评估四部分。

1.1 固件解密

有些IOT设备会对固件加密甚至签名来提高研究门槛和升级时的安全性,因为加解密比较耗费资源,这类设备一般配置会比较高,比如一些路由器和防火墙。

1.1.1 固件加密判断

判断固件是否加密比较简单,有经验的小伙伴有二进制编辑器打开就能看出一二,一般会存在以下特性。

除了固件指示头没有可见字符,(除去header)数据按比特展开01频率基本一致binwalk(-e)无法解析固件结构,且(-A)没有识别出任何cpu架构指令

如果满足上述特点,就会猜测固件已被加密,固件解密一般会从这几个角度,但也不局限于下面的方法。

1.1.2 硬件获取密钥

此种方法只限于固件始终以加密状态存在,当系统启动时才通过解密解包加载至flash,且设备缺乏(UART/JTAG等)动态调试手段。由于flash中有完整的解密过程,可以通过编程器读取flash,逆向解密算法和密钥,达到解密固件的目的。比如从某设备的读取的flash内存分布如下:

0x000000-0x020000 boot section0x020000-0x070000 encrypt section0x070000-0x200000 encrypt section0x200000-0x400000 config section

显然我们需要的加密过程在boot section中,我们需要从中找到加密算法和密钥,一般加密都采用AES等公开分组算法,关键是找到分组模式,IV(非ECB)和密钥。将boot加载到IDA pro中,并没有自动识别:可以通过对比ARM代码最开始部分的中断向量表结构手动识别,常见的入口代码如下所示。

.globl _start_start:    b       reset    ldr     pc, _undefined_instruction    ldr     pc, _software_interrupt    ldr     pc, _prefetch_abort    ldr     pc, _data_abort    ldr     pc, _not_used    ldr     pc, _irq    ldr     pc, _fiq..._irq:        .word irq

之后可以就可以逆向得到加密算法为AES,密钥通过设备序列号的sha256哈希获得。 这个方法最容易理解,即在设备启动后利用UART、JTAG、Console或网络等手段把固件(打包)回传,这样就绕过了解密环节。值得注意的是需要设备提供这些接口,具体方法因设备不同而异,这些接口的使用将会在硬件篇里作介绍。

1.1.4 对比边界版本

此种方法适用于厂商一开始没采用加密方案,即旧版固件未加密,在某次升级中添加了解密程序,随后升级使用加密固件。这样我们就可以从一系列固件中找到加密和未加密之间的边界版本,解包最后一个未加密版本逆向升级程序即可还原加密过程。通过下载上图所示某路由器固件,解包后通过搜索包含诸如“firmware”、“upgrade”、“update”、“download”等关键字的组合定位升级程序位置。当然存在调试手段也可以在升级时ps查看进程更新定位升级程序和参数:

/usr/sbin/encimg -d -i  -s 

通过IDA pro逆向encimg程序很快得到加解密过程代码,采用了AES CBC模式:

AES_set_decrypt_key (   // user input key   const unsigned char *userKey,   // size of key   const int bits,   // encryption key struct which will be used by   // encryption function   AES_KEY *key)AES_cbc_encrypt (   // input buffer   const unsigned char *in,   // output buffer   unsigned char *out,   // buffer length   size_t length,   // key struct return by previous function   const AES_KEY *key,   // initializatin vector   unsigned char *ivec,   // is encryption or decryption   const int enc)

1.1.5 逆向升级程序

此种方法适用于已经通过接口或边界版本得到升级程序,可以利用分组算法的盒检测工具来判别加密算法和定位位置,当然binwalk也可以解析某些简单情况,比如某工控HMI固件:

iot@attifyos ~/Documents> binwalk hmis.tar.gzDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------34       0x22        OpenSSL encyption, salted, salt:0x5879382A7

直接加载升级程序,定位openssl调用很容易就得到解密命令:

如果找不到边界版本,又找不到调试接口或不熟悉硬件调试,可以考虑采用历史版本漏洞先获取设备控制权,在拿到升级程序逆向加密算法。这种方法比较取巧,需要设备存在RCE漏洞的历史固件,通过降级操作植入漏洞获取权限,下载所需升级程序,然后逆向得到加密算法。

1.2 固件解包

初入IOT安全研究的小伙伴会觉得固件解包很简单,直接binwalk -Me就可以了,但是理想很丰满,现实很骨感,固件测试多了就会发现binwalk很多情况下都解不开。IOT固件一般分为两类,一类存在文件系统,大多基于linux/BSD,另一类固件是一个整体,即我们所说的RTOS(Real-time operating system)。

1.2.1 存在文件系统

binwalk大家应该都很熟悉,使用binwalk能直接得到rootfs文件系统的情况这里不再赘述,笔者认为binwalk的强大之处在于可以解析识别多重格式的header,为解包提供参考。以下介绍几种需要绕点弯的情况,当然固件千差万别,全看设计者设计,不能一一列举。

1.2.1.1 UBI(Unsorted Block Image)

UBI格式的固件算比较常见的,binwalk并不能直接解包,但是网上有现成的工具ubi_reader,这里有个需要注意的地方:

UBI_reader解包,UBI文件必须是1024bytes的整数倍,需要增删内容对其

比如通过分析某路由器,发现其rootfs是UBI格式:

# binwalk ROM/wifi_firmware_c91ea_1.0.50.binDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------684           0x2AC           UBI erase count header, version: 1, EC: 0x0, VID header offset: 0x800, data offset: 0x1000

首先安装ubi_reader:

$ sudo apt-get install liblzo2-dev$ sudo pip install python-lzo$ git clone https://github.com/jrspruitt/ubi_reader$ cd ubi_reader$ sudo python setup.py install

或者直接

$ sudo pip install ubi_reader

然后将根据地址将UBI结构提取出来,利用ubireader_extract_files [options] path/to/file即可解包。

1.2.1.2 PFS

有些固件binwalk可以识别出header,但是无法解开,比如下面这个固件

iot@attifyos ~/Documents> binwalk -Me v2912_389.allScan Time:     2020-11-04 18:39:13Target File:   /home/iot/Documents/v2912_389.allMD5 Checksum:  180c60197aae7e272191695e906c941eSignatures:    396DECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------1546799       0x179A2F        gzip compressed data, last modified: 2042-04-26 20:13:56 (bogus date)1717744       0x1A35F0        LZ4 compressed data4171513       0x3FA6F9        SHA256 hash constants, little endian4179098       0x3FC59A        Copyright string: "Copyright (c) 1998-2000 by XXXXX Corp."4214532       0x404F04        Base64 standard index table4224780       0x40770C        HTML document header4232369       0x4094B1        SHA256 hash constants, little endian4307839       0x41BB7F        SHA256 hash constants, little endian4314017       0x41D3A1        XML document, version: "1.0"4702230       0x47C016        Base64 standard index table4707197       0x47D37D        Certificate in DER format (x509 v3), header length: 4, sequence length: 8734727609       0x482339        Base64 standard index table4791281       0x491BF1        PFS filesystem, version 1.0, 12886 files4807401       0x495AE9        Base64 standard index table...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/3CB  3CB.zlib  E235  E235.zlib

运行binwalk后查看结果,发现没有发现任何可识别的东西,此时可以手动分析或者去搜索一些相关工具。在网上找到相关工具,直接根据提示使用命令就可解开固件。

iot@attifyos ~/D/draytools> python draytools.py -F v2910_61252.all v2910_61252.all.out written, 12816484 [0x00C39064] bytesFS extracted to [/home/iot/Documents/draytools/fs_out], 429 files extractediot@attifyos ~/D/draytools> ls fs_out/v2000/  v2910.lstiot@attifyos ~/D/draytools> ls fs_out/v2000/act_sta.htm  CSS/  header.htm  INDEX2.HTM  ivr_711u/  jg/  l_m.htm    menu.htm   STATUS.HTM  SYSINFO_C.TXT  UPNP/  webauth.htmCGI-BIN/     DOC/  IMAGES/     ivr_711a/   ivr_729/   JS/  LOGIN.HTM  rpage.htm  STYLE.CSS   SYSINFO.TXT    VLAN/

这里简单看一下固件解包关键代码,关键在于找到类似’\xA5\xA5\xA5\x5A\xA5\x5A’的header,之后根据具体格式解包解压即可,所以固件解包说到底还是数据格式分析。

def decompress_firmware(data):    flen = len(data)    sigstart = data.find('\xA5\xA5\xA5\x5A\xA5\x5A')    if sigstart  0:        if draytools.verbose:            print 'Signature found at [0x%08X]' % sigstart        lzosizestart = sigstart + 6        lzostart = lzosizestart + 4        lzosize = unpack('>L', data[lzosizestart:lzostart])[0]        return data[0x100:sigstart+2] \            + pydelzo.decompress('\xF0' + pack(">L",0x1000000) \                + data[lzostart:lzostart+lzosize])    ...

1.2.1.3 Openwrt Lua

lua结构解析放在解包这里可能不太恰当,但鉴于Openwrt的使用基数很大,在这里简单提一下。Lua是一门方便嵌入并可扩展的轻量级脚本语言,Openwrt开发中会使用该脚本语言。值得注意的是,有些设备的lua并不是纯文本,存在混淆,需要使用luadec反编译。openwrt中的lua脚本和传统的luajit编译后的有点不一样,需要打几个补丁才能正常使用luadec进行反编译,命令如下:

$ cd ..$ mkdir luadec$ cd luadec/$ git clone https://github.com/viruscamp/luadec$ cd luadec/$ git submodule update --init lua-5.1$ cd lua-5.1$ make linux$ make clean$ mkdir patch$ cd patch/$ get https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/010-lua-5.1.3-lnum-full-260308.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/030-archindependent-bytecode.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/011-lnum-use-double.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/015-lnum-ppc-compat.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/020-shared_liblua.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/040-use-symbolic-functions.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/050-honor-cflags.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/100-no_readline.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/200-lua-path.patch$ wget https://dev.openwrt.org/export/HEAD/trunk/package/utils/lua/patches/300-opcode_performance.patch$ mv patch/ patches$ for i in ../patches/*.patch; do patch -p1  done$ for i in ./patches/*.patch; do patch -p1  done$ make linux

修改 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)

接着执行:

$ make linux $ ldconfig $ cd ../luadec $ make LUAVER=5.1 $ sudo cp luadec /usr/local/bin/

利用luadec显示代码结构:

$ luadec -pn squashfs-root/usr/lib/lua/luci/sgi/uhttpd.lua0  0_0    0_0_0    0_0_1    0_0_2

利用luadec反编译指定的函数 (函数 0 包含 子函数):

$ luadec -f 0 squashfs-root/usr/lib/lua/luci/sgi/uhttpd.lua

需要注意的是,luadec编译与架构相关,用官方luadec无法解析arm环境下的lua文件,但网上也有相应的工具,这里不再赘述。

1.2.2 RTOS

很多IOT设备都采用RTOS(实时操作系统)架构,固件本身就是一个可执行文件,不存在文件系统,启动后直接加在运行。对RTOS的分析最重要就是两点:

(一) 固件程序入口(二) 固件程序符号

1.2.2.1 vxworks

首先从应用较广且有套路可循的vxworks说起,VxWorks是Wind River System公司推出的一个实时操作系统,广泛应用在通信、军事、航空、航天嵌入式设备领域。因为有标准,所以好识别,以下面这个固件为例:

iot@attifyos ~/Documents> binwalk image_vx5.binDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------335280        0x51DB0         PEM certificate...3721556       0x38C954        GIF image data, version "89a", 10 x 2108518936       0x81FD18        VxWorks operating system version "5.5.1" , compiled: "Mar  5 2015, 15:56:18"9736988       0x94931C        SHA256 hash constants, little endian...13374599      0xCC1487        Copyright string: "Copyright  1999-2001 Wind River Systems."13387388      0xCC567C        VxWorks symbol table, big endian, first entry: [type: function, code address: 0xF4A09A00, symbol address: 0xF813C800]13391405      0xCC562D        VxWorks symbol table, little endian, first entry: [type: function, code address: 0xB8BD, symbol address: 0xD000C800]

binwalk已经识别出固件为Vxworks 5.5.1,并且给出了符号表位置。首先需要识别固件的入口点,如果固件被封装成ELF格式,直接利用readelf就可以得到基地址,这里显然不适用。

iot@attifyos ~/Documents> readelf -a image_vx5_arm_little_eniadn.bin readelf: Error: Not an ELF file - it has the wrong magic bytes at the startiot@attifyos ~/Documents> binwalk -A image_vx5.bin |moreDECIMAL       HEXADECIMAL     DESCRIPTION--------------------------------------------------------------------------------244           0xF4            ARM instructions, function prologue408           0x198           ARM instructions, function prologue440           0x1B8           ARM instructions, function prologue472           0x1D8           ARM instructions, function prologue608           0x260           ARM instructions, function prologue

通过binwalk -A得到固件架构是ARM,直接用IDA pro载入:分析固件开头跳转判断加载地址为0x1000。对于Vxworks一般判断基址办法有:

分析固件头部的初始化代码,找到vxworks启动的第一个函数usrInit跳根据BSS区初始化特征找到BSS边界,根据偏移计算固件加载地址

然后根据binwalk指示的位置,修复符号表名。函数表存放了函数名和函数地址,通过两者定位也可以反过来验证基地址的正确性,比如上图所示的0x00c813f8是函数名:0x009aa0f4是函数地址:由于基地址和架构有关,在这里就不详细展开,对于vxworks的分析我们可以借助一款能自动化修复入口和符号的插件—vxhunter。以Ghidra为例,载入固件后直接选择vxhunter_firmware_init.py插件和vxworks版本,就可以自动修复入口和符号:1.2.2.2 U-boot

boot类的固件也是我们常会遇见的一类无文件系统固件,比如很多IOT设备会采用U-boot作引导,因为U-boot开源,我们可以参照源代码分析,对于有些架构的U-boot也可以采用固定套路,比如mips可以根据$gp寄存器等。

有些IOT固件没有资料,逆向困难,比如下面某款ARM芯片的固件,将其载入IDA pro发现没有识别出任何函数:这样我们就需要对固件有一个整体的分析了,我们看到固件0x100的位置十分有趣: 4字节排列后都以0x2开头,这里既不是代码,也不是数据,那就很可能是地址了,这里应该是一张表,所以基地址很可能是0x200000。我们rebase之后再查看字符串: 看到许多类似函数名的字符串,找到具体位置后,在固件中二进制搜索0x16852A,即wlc_probresp_attach地址(小端)。 可以看到真的搜索到了,而且也是一个表的结构: 根据基址找到在IDA pro中的位置: 可以看到完成了部分的交叉引用,后续分析比较复杂,这里就不再展开,实际上0x100位置是函数地址表,在该固件中这样表有很多。所以类似地址表,字符串都是我们分析固件基址和函数的重要线索。

1.3 固件打包

拆东西容易装东西难,这个道理也适用于固件打包。如果设备留有调试接口,一般不用打包操作,毕竟安全研究是以逆向思维为主。有时缺乏调试手段,我们就要在解开的固件中手动添加。一般将交叉编译好的telnetd,dropbear(sshd),gdb放入固件文件,再替换启动脚本打包。linux的启动脚本套路众多,尤其在IOT设备中,这里笔者一般采用比较讨巧的方法,比如确定/sbin/xxxd服务会开机运行,可以将其替换:

# mv rootfs/sbin/xxxd sbin/xxxdd# touch rootfs/sbin/xxxd# chmod +x rootfs/sbin/xxxd

之后在sbin/xxxd添加

#!/bin/sh/usr/sbin/telnetd -F -l /bin/sh -p 1234 &/sbin/xxxdd &

这样开机启动xxxd时就会先运行telnetd。

1.3.1 交叉编译

如果能从正向开发角度来打包当然最方便,也就是交叉编译的事。笔者研究过的一些设备中,主要是路由器固件会部分遵循GPL,就是开源一部分代码软件(一般本来就是基于开源工具),并提供剩下软件的二进制文件和整个固件的打包工具(方法)。比如之前研究的某款路由设备就提供了开源下载: 下载该zip包,根据自己的需求编译rootfs,最后利用zip包中自带的工具打包:

./packet -k %s -f rootfs -b compatible_r6400.txt         -ok kernel -oall image -or rootfs -i ambitCfg.h

1.3.2 firmware-mod-kit

firmware-mod-kit(fmk)可能是最常用的基于binwalk的解打包工具,但是由于很久没用更新,使用场景有限。fmk的安装使用都比较简单,如下所示:

# For ubuntu$ sudo apt-get install git build-essential zlib1g-dev liblzma-dev python-magic bsdmainutils autoconf# For redhat/centos$ yum groupinstall "Development Tools"$ yum install git zlib1g-dev xz-devel python-magic zlib-devel util-linux# 使用$ ./extract-firmware.sh firmware.bin //解包$ cp new-telnetd fmk/rootfs/usr/sbin/telnetd //按需修改$ ./build-firmware.sh //打包

1.3.3 手动分析

打包的难度在于固件要与原固件一致,并通过各种校验,否则轻则刷机失败,重则设备变砖。笔者之前有一篇关于netgear upnp漏洞的文章,涉及netgear固件打包过程,有兴趣的小伙伴可以看一看。固件一般会分成许多section,为了方便解析,每个section会有指示头,头中可能会存放标志、大小和crc校验等信息,这些信息都为解打包提供依据。比如可以先获取固件大小(十六进制),根据固件大小端拆分字节,一般是4字节,然后在固件头上寻找类似字节(固件头上的指示长度会减去头长度),接着从指示大小的字节往后分析就可以澄清格式,和分析网络协议的过程很像。 当然大部分头都是有标准的,可以根据标准格式一一对应。值得注意的是,有些厂商会给固件签名,这个就会增加打包的难度。此时我们可以寻找一些遵循GPL的官方打包工具,或者利用openssl生成公私钥对,覆盖设备中的验证公钥,这里当然要有漏洞,不然就掉进鸡生蛋蛋生鸡的循环。当然还有一个比较好且廉价的办法—-固件模拟。

1.4 固件模拟

固件模拟根据不同需要可能有以下3个场景:

(一) 只需模拟某个应用,比如web、upnpd、dnsmasq等,目的是对该应用作调试。此时可以直接用模拟工具运行该程序,只需考虑动态库是否能加载。(二) 需要模拟固件shell,与整个系统有所交互。这里可以通过chroot改变根路径,并利用模拟工具执行/bin/sh。同时可以挂在/proc,这样在ps查看进程时看上去更加真实。(三) 需要模拟整个固件启动,并且网卡等也能正常使用。这里要利用可模拟img系统的工具直接加载整个系统,也可以利用“套娃”大法,先模拟该架构的debian.img,再用chroot起设备的roofs。

下面介绍几个常用的模拟工具。

1.4.1 Qemu

Qemu是最老牌的多架构模拟工具,上述3个使用场景qemu都可以满足。qemu可以下载安装也可以直接利用源安装,这里要注意的是如果模拟应用不仅需要qemu,还需安装qemu-user。

# For ubuntu$ sudo apt-get install qemu$ sudo apt-get install qemu-user qemu-uesr-static
  • qemu模拟应用

以模拟ARM固件为例,解开固件得到rootfs,下面是利用qemu模拟执行busybox:

iot@attifyos ~/Document> cp (which qemu-arm-static) ./rootfs/iot@attifyos ~/Document> sudo chroot ./rootfs /qemu-arm-static /bin/busyboxBusyBox v0.47 (2018.08.30-14:14+0000) multi-call binary -- GPL2Usage: busybox [function] [arguments]...   or: [function] [arguments]...    BusyBox is a multi-call binary that combines many common Unix    utilities into a single executable.  Most people will create a    link to busybox for each function they wish to use, and BusyBox    will act like whatever it was invoked as.Currently defined functions:    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, zcat
  • qemu模拟shell

利用qemu模拟sh与上述情况相似,可以先mount proc,使得ps查看进程时显得更加真实:

iot@attifyos ~/Document> cd ./rootfsiot@attifyos ~/Document> rm -rf prociot@attifyos ~/Document> sudo mount -t proc /proc ./prociot@attifyos ~/Document> cd ..iot@attifyos ~/Document> sudo chroot ./rootfs /qemu-arm-static /bin/shBusyBox v0.47 (2018.08.30-14:14+0000) Built-in shellEnter 'help' for a list of built-in commands./ # lsbin             gm              sbin            usrdev             lib             store           varetc             qemu-arm-static sys/ # ps  PID  PPID Uid     Mem  CPU St Command    1     0 0      29936  0.0 S  init splash     2     0 0         0  0.0 S  [kthreadd]    7     2 0         0  0.6 S  [ksoftirqd/0]  332     1 0      26776  0.0 S  systemd-journald   358     1 0      6568  0.0 S  systemd-udevd   378     1 0      28144  0.0 S  vmware-vmblock-fuse /run/vmblock-fuse -o rw,su  495     1 100    18500  0.0 S  systemd-timesyncd   607     1 0      8244  0.0 S  haveged --Foreground --verbose=1 -w 1024   608     1 0      42756  0.0 S  VGAuthService   615     1 0      38824  0.0 S  vmtoolsd   619     1 0      3740  0.0 S  cron -f   621     1 103    6216  0.0 S  avahi-daemon: running [attifyos.local]...
  • qemu模拟系统

qemu的功能很强大,可以像在vmware、virtualbox中一样安装系统:

$ qemu-img create -f qcow2 arm.qcow2 10G$ qemu-system-arm -m 1024M -sd arm.qcow2 -M vexpress-a9 -cpu cortex-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镜像:

$ sudo apt-get install libguestfs-tools# 利用guestmount挂在qcow2镜像$ guestmount -a debian_wheezy_armel_standard.qcow2 -m /dev/sda1 /mnt# 拷贝rootfs$ cp -rf ./rootfs /mnt/root/rootfs$ guestunmount /mnt

然后启动qcow2文件:

qemu-system-arm -M versatilepb -kernel vmlinuz-3.2.0-4-versatile -initrd initrd.img-3.2.0-4-versatile -hda debian_wheezy_armel_standard.qcow2 -append "root=/dev/sda1"

利用chroot“套娃”系统:

$ chroot -u ./rootfs /bin/shBusyBox v0.47 (2018.08.30-14:14+0000) Built-in shellEnter 'help' for a list of built-in commands./ #

使用debian现成的qcow2镜像可以省去网卡和其他一些配置过程,提高模拟效率。值得注意的是,qemu在对程序黑盒测试时也经常使用,比如AFL的qemu_mode,在后面的篇章里我们会探讨,当然AFL同样可以使用unicorn_mode,使用的就是下面介绍的模拟器unicorn。

1.4.2 Unicorn

Unicorn一个基于Qemu的cpu指令模拟框架,也就是可以模拟任何的cpu指令。我们一般可以利用Unicorn指令级模拟特性:

对(IOT)程序作模糊测试用于gdb插件,或代码模拟执行的插桩,修改代码逻辑模拟执行一些复杂混淆代码,提高人工逆向效率

关于Unicorn模拟执行修改代码逻辑的教程比较多,这里不再赘述,下面介绍一款基于Unicorn的IDA pro插件,该插件可以在IDA pro中模拟执行,并给出执行结果,UnicornFuzz相关内容会在后面的篇章中介绍。

#include int calc(int a,int b){        int sum;        sum = a+b;        return sum;}int main(){        calc(2,3);}

上面非常简单的计算器C代码是该插件的官方示例,其mipsel IDA pro反汇编代码如下:

.text:00400640                 .globl calc.text:00400640 calc:                                    # CODE XREF: main+18↓p.text:00400640.text:00400640 var_10          = -0x10.text:00400640 var_4           = -4.text:00400640 arg_0           =  0.text:00400640 arg_4           =  4.text:00400640.text:00400640                 addiu   $sp, -0x18.text:00400644                 sw      $fp, 0x18+var_4($sp).text:00400648                 move    $fp, $sp.text:0040064C                 sw      $a0, 0x18+arg_0($fp).text:00400650                 sw      $a1, 0x18+arg_4($fp).text:00400654                 lw      $v1, 0x18+arg_0($fp).text:00400658                 lw      $v0, 0x18+arg_4($fp).text:0040065C                 addu    $v0, $v1, $v0.text:00400660                 sw      $v0, 0x18+var_10($fp).text:00400664                 lw      $v0, 0x18+var_10($fp).text:00400668                 move    $sp, $fp.text:0040066C                 lw      $fp, 0x18+var_4($sp).text:00400670                 addiu   $sp, 0x18.text:00400674                 jr      $ra.text:00400678                 nop.text:00400678  # End of function calc

创建一个 emu object

Python>a = EmuMips()

配置模拟地址和参数

Python>a.configEmu(0x00400640,0x00400678,[2,3])[*] Init registers success...[*] Init code and data segment success! [*] Init Stack success...[*] set args...

开始指令模拟

Python>a.beginEmu()[*] emulating...[*] Done! Emulate result return: 0x5

通过Unicorn模拟执行得到sum(2+3)的结果为0x5。

1.4.3 Qiling

Qiling是一款很年轻的基于Unicorn的模拟器,专为IOT研究而生。Qiling可以作为IDA pro插件,也可以利用Qiling Unicornalf进行fuzz,还可以模拟IOT设备固件。Qiling用python3开发,可以直接用pip3 install qiling安装,以下是官方模拟ARMj架构路由器固件的部分代码:

import os, socket, sys, threadingsys.path.append("..")from qiling import *def patcher(ql):    ...def nvram_listener():    ...def my_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框架进一步简化了模拟所需代码,同时提供了指令级插桩功能,还是很强大的。

1.4.4 Firmadyne

Firmadyne是一个自动化和可扩展的系统,用于对基于Linux的嵌入式固件执行仿真和动态分析。Firmadyne也是是基于Qemu,使用起来大同小异,网上教程也很多,这里主要介绍一款使用Firmadyne的固件灰盒fuzz工具的—Firm-AFL。$ cd user_mode/$ ./configure --target-list=mipsel-linux-user,mips-linux-user,arm-linux-user --static --disable-werror$ make

系统模式:

$ cd qemu_mode/DECAF_qemu_2.10/$ ./configure --target-list=mipsel-softmmu,mips-softmmu,arm-softmmu --disable-werror$ make

以Dlink DIR-815固件为例,首先安装好Firmadyne,做一些初始化工作:

$ cd firmadyne$ ./sources/extractor/extractor.py -b dlink -sql 127.0.0.1 -np -nk "../firmware/DIR-815_FIRMWARE_1.01.ZIP" images$ ./scripts/getArch.sh ./images/9050.tar.gz$ ./scripts/makeImage.sh 9050$ ./scripts/inferNetwork.sh 9050$ cd ..$ python FirmAFL_setup.py 9050 mipsel

根据路由器架构修改image_9050目录中的run.sh:

ARCH=mipselQEMU="./qemu-system-${ARCH}"KERNEL="./vmlinux.${ARCH}_3.2.1" IMAGE="./image.raw"MEM_FILE="./mem_file"${QEMU} -m 256 -mem-prealloc -mem-path ${MEM_FILE} -M ${QEMU_MACHINE} -kernel ${KERNEL} \

然后就可以启动fuzz脚本开始测试:

$ cd image_9050$ sudo python start.py 9050

1.5 总结

固件研究是IOT漏洞研究的基础,本篇尽可能全的介绍了固件研究方面的内容,一方面笔者经验有限,还有些点未能涉及,另一方面篇幅有限,许多内容没能进一步展开,以后有机会再和大家一起探讨。下一篇将介绍IOT web漏洞研究的相关内容。

参考资料

https://cloud.tencent.com/developer/article/1005700https://5alt.me/2017/08/某智能设备固件解密//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/

精彩推荐

EternalBlueC

EternalBlue suite在C/C++中重新构建,其中包括:MS17-010漏洞、EternalBlue/MS17-010漏洞检测器、DoublePulsar检测器以及DoublePulsarUploadDLL和Shellcode上传工具。

项目目标

1.转换为其他语言,例如Java和C#,并实现扫描和攻击GUI。

2.允许编辑的永恒之蓝漏洞利用有效负载以删除DoublePulsar后门,并允许发送自定义负载和Shellcode。

3.添加EternalRomance(需要命名管道)支持。

附加的网络教育代码

存储库还包含以下用于教育目的的内容:

1.DoublePulsar x86/x64上传DLL python脚本。

2.EternalBlue一体化二进制文件。

3.多体系结构内核队列apc汇编代码和Windows x86/x64多体系内核从环0到环3通过排队的APC内核代码。

4.来自worawit的EternalBlue x64/x86内核有效载荷Shellcode。

5.Eternalblue的回放文件

EternalBlue Suite

**ms17_vuln_status.cpp - **该程序发送了4个SMB数据包。1个协商、1个会话设置、1个树连接和1个TransNamedPipe请求。然后,该程序从TransNamedPipeRequest(PeekNamedPipe请求)中读取NT_STATUS响应,并确定SMB数据包中的NT_STATUS是否=0xC0000205(STATUS_INSUFF_SERVER_RESOURCES)。如果正确,则目标很容易受到MS17-010的攻击。已在未修补的Windows7x64位上进行了测试。

doublepulsar_check.cpp -该程序发送了4个SMB数据包、1个协商、1个会话设置、1个树连接和Trans2 SESSION_SETUP数据包。通过发送Trans2 SESSION_SETUP数据包,可以将Trans2响应数据包中发送的Multiplex ID(41)与Multiplex ID:0x51或81进行比较。如果响应,则表示计算机上存在DoublePulsar后门。DoublePulsar后门读取发送的Multiplex ID,在ping命令成功后,在Trans2 SESSION_SETUP响应中为Multiplex ID返回十六进制的+10。之后,发送命令(在SMB数据包的超时参数中发送)以刻录DoublePulsar后门。DoublePulsar后门处于休眠状态,没有被移除。已在Windows7x64位上进行了测试。

EternalBlue.cp**p** 该程序发送了多个SMB数据包。有协商、会话设置、树连接和多个NT trans和Trans2数据包。这些NT传输包的格式不正确,这会在受害者计算机的内存中存在漏洞。更多的空白或空SMB数据包通过多个套接字发送到受害者的同一端口。大部分EternalBlue的base64有效负载都是通过socket1发送的,其中negotiation、SessionSetup和TreeConnect数据包都是在这个套接字上发送的。然后创建20个其他套接字,并将数据发送到这些套接字(套接字3到套接字21)中。之后,DoublePulsar后门通过插座3发送到插座21。然后由程序关闭插座,引爆受害者电脑上的EternalBlue 和DoublePulsar后门。然后发送SMB断开连接和SMB注销请求并关闭连接。此漏洞攻击有效,并在Windows 7 x64位上进行了测试。花了大约5秒的时间让后门完全运行,正如互联网上报道的永恒之蓝那样。可能需要更多的利用尝试。然而,目前有一个错误,TreeID和UserID没有被正确的设置在数据包中,这将在以后的版本中修复。当TreeID和UserID设置为默认值时,这将适用于最近打开的计算机。当默认值设置为“0800”时,这些数据包必须更新其TreeID和UserID值。

DoublePulsarXORKeyCalculator.cpp - 该程序发送了4个SMB数据包。1个协商数据包和3个请求。最后一个请求是Trans2 SESSION_SETUP请求。然后,接收Trans2 SESSION_SETUP响应,并在(Recvbuff[18]->Recvbuff[22])处提取SMB签名。SMB签名从十六进制字符转换为无符号整数。该无符号整数通过DoublePulsar  XOR key calculator函数运行,该函数生成一个XOR密钥,该密钥可用于加密将上传到DoublePulsar后门的shellcode或DLL有效负载中。

注意:SESSION_SETUP数据参数必须包含此存储库的有效负载上传部分中计算出的DoublePulsar XOR密钥的char版本。在Windows7x64位上测试。屏幕截图示例:

doublepulsar_upload.cpp -该程序发送了4个SMB数据包。1个协商、1个会话设置、1个树连接和1个Trans2 SESSION_SETUP数据包。发送Trans2 SESSION SETUP请求数据包以获取Trans2 SESSION_SETUP响应数据包中的SMB签名。该签名通过从DoublePulsar后门二进制文件中提取的DoublePulsar XOR密钥计算器进行处理。然后,程序读取一个DLL文件(示例:payload.dll),并将其与userland shellcode(userland_shellcode.bin)相结合,然后用我们根据SMB签名计算出的DoublePulsar XOR密钥对缓冲区进行XOR运算。数据包通过分配内存,复制Trans2数据包,编辑SMB事务正常工作所需的值(UserID, ProcessID, TreeID, MultiplexID),然后将XORed数据(shellcode + DLL)复制到末尾,并通过它循环发送到DoublePulsar后门。每次发送的总数据包长度为4096字节。数据包总长度=4178。4096一般用于XOR加密数据。这项工作仍在进行中,无法正常工作。

TODO:可能需要使用TRANS2数据包结构实现Trans2上传函数,而不是使用十六进制编辑Trans2数据包进行捕获。

Doublepulsar_UploadShellcode.cpp - 该程序发送了5个SMB数据包。1个协商,1个会话设置,1个树连接,1个ping Trans2 SESSION_SETUP数据包和1个exec Trans2 SESSION_SETUP数据包。发送Trans2 SESSION SETUP请求数据包以获取TRANS2 SESSION_SETUP响应数据包中的SMB签名。该签名通过从DoublePulsar后门二进制文件中提取的DoublePulsar XOR密钥计算器进行处理。然后,程序从一个缓冲区(NOPs/x90)复制示例shellcode,它使用我们根据SMB签名计算出的DoublePulsar XOR密钥对缓冲区进行XORs运算。数据包是通过分配内存,复制Trans2 exec数据包(来自Wannacry),编辑SMB事务正常工作所需的值(UserID,TreeID),然后将SESSION_PARAMETERS和4096字节的XORed数据(shellcode)复制到末尾,并以4178字节的数据包总长度将其发送到DoublePulsar后门。数据包总长度=4178,SESSION_PARAMETERS为12。4096一般用于XOR加密数据。这项工作仍在进行中,无法正常工作。

TODO:可能需要使用结构来实现Trans2上载函数,而不是使用十六进制编辑Trans2数据包进行捕获。

项目地址

EternalBlueC:

https://github.com/bhassani/EternalBlueC

精彩推荐

大家好,我是老盖,感谢观看本篇文章,本文做的也有视频,也可以看我发布的视频。

今天给大家讲一下电脑自带的计算器,我们可以点开始附件中打开它,也可以点开始运行,输入命令calc,打开计算器,这个就是默认的计算器的界面。

计算器左上方菜单中有个查看,我们点一下,选择菜单中的科学型。

这个是科学型的计算器,它可以进行一些数学的运算,还有一些函数的运算,比如sin,cos等一些函数计算。

菜单中的程序员选项我们点一下。

在程序员这个计算器中,可以对一些数据进行逻辑运算,比如与,或,异或等一些运算,还可以进行十六进制八进制二进制等一些转换。

我们再看一下菜单中的单位转换,点一下。

单位转换包含了很多单位转换,右侧有一个菜单,点一下可以根据需要计算的单位进行转换。

菜单中左下方还有一个日期计算,点一下。

这个就是日期计算,计算器中还有一些其他的选项,大家可以也可以测试查看一下。

感谢观看本篇文章,希望对你有所帮助,本文由老盖聊技术原创,本文是出于爱心帮助人的目的所写,请随意分享转载,感谢正能量传播。

转载请注明出处:十六进制计算器, IoT漏洞研究(一)固件基础 :http://www.720weixin.com/marketing/315564.html
  • 登录

    使用微信帐号直接登录,无需注册