逆向分析转战IoT安全合集(一)

安全入门
2022-05-18 16:39
28462

逆向分析转战IoT安全合集(一)

技术背景: 无固件分析经验, 无IoT安全经验, 从CTF逆向手转向IoT二进制研究.

(零) 写在前面

逆向手的入门优势在于对汇编, 对Linux操作系统的了解. 这里选择从分析固件入手, 对固件有足够了解之后再进行漏洞挖掘的研究.

第一周计划的学习路线是固件模拟分析. 并且可以按照已有的文章复现一些漏洞.

(壹) 固件分析环境配置

1. 虚拟机安装

我想从其他方向安全转战IoT安全的师傅应该对虚拟机都不陌生, 这里只提一点自己的想法.

由于IoT设备的特殊性, 往往仿真不需要在最新的Linux上, 我们在选择一些镜像和工具的时候, 不必追求版本最新.

我这里选择通过Docker起一个最基础的实验环境

2. 一些基础应用

​ 打算以qemu为基础, 尝试使用FirmAE实战, 并且学习Qiling框架.

  1. Qemu(Mips)

    其实Qemu应该是一个老生常谈的模拟器了, 逆向手如果有遇到Mips, Arm等架构的二进制文件, 往往需要Qemu/Unicorn进行动态调试. 虽然我没有很多经验, 但是不建议总是编译安装最新版, 我们用apt来的比较旧

    apt update
    apt-get install qemu-system
    
  2. Binwalk

    固件分析解包用, 从Misc来的师傅大概已经很熟练了

    apt-get install binwalk
    
    1. IDA

​ IDA是很重要的逆向分析工具, 静态动态集一身, 网上有很多教程了. 逆向手应该没有不熟悉IDA的

​ 此外一些基础工具如Docker, Python一类的, 我就不再赘述了. 后面也有一些非环境级别的工具, 在使用的时候都会附上下载链接

(贰) 环境测试与漏洞分析

尝试模拟该固件

Dlink的一些固件可以在这里下载到

直接binwalk可以看到我们想要的固件

binwalk -Me dcs930l_v108_b4.bin

binwalk -Me一把梭, 可以发现cpio-root文件夹

应该是我们感兴趣的文件系统内容


可以看到二进制的目标系统是Mipsel 32bit.

我们利用mips的Debian搭建一个qemu的虚拟机, 然后替换对应文件尝试

这里找到需要的mips debian文件


在等待下载镜像的时候我们要配置一个虚拟网卡, 以便于使用的Ubuntu和Qemu虚拟机的互联.

apt-get install bridge-utils uml-utilities

我在使用tunctl的时候出现了找不到/dev/net/tun的问题. 原因是docker没有开特权等级, 需要加上privileged权限.

tunctl -t tap0
ifconfig tap0 192.168.100.1/24

之后就可以尝试启动虚拟机了: 默认的用户名密码均为root

qemu-system-mipsel -M malta -kernel vmlinux-3.2.0-4-4kc-malta -hda debian_wheezy_mipsel_standard.qcow2 -append "root=/dev/sda1 console=ttys0" -nographic -net tap,ifname=tap0,script=no,downscript=on

发现里面没有eth0网卡, 没有办法和主机链接, 询问了一下没有得到可行的结果, 遂看了看大家都怎么做的

加上-device选项后配置上了网卡-device rtl8139,netdev=tapnet, 后来的实践中发现, 可以通过-net nic选项配置

顺便解释一下这里用到的参数:

-M : 设备类型
-netdev/net/nic : 网络配置
-kernel : 内核镜像
-hda/hdb/hdc/hdd : 虚拟机系统安装文件
-append : 给内核的附加选项

进入到qemu虚拟机后, 配置一下网卡

root@debian-mipsel:~# ifconfig eth0 192.168.100.2
# 采用这种方法配置的, 每次重启qemu的时候都要重新配置一下ip

通过scp把固件拷入

root@e59e1ac34385:/ctf/work/dlink# scp cpio.tar root@192.168.100.2:~/
root@debian-mipsel:~# mount -o bind /dev cpio-root/dev/
root@debian-mipsel:~# mount -t proc /proc/ cpio-root/proc/
root@debian-mipsel:~# chroot cpio-root sh

之后在qemu中解包, 重挂载..

以上大概就是挂载流程, 我们接下来尝试复现一些漏洞和尝试仿真一个固件看看.

固件失败了, 需要hook的地方太多了..., 直接找到了漏洞作者作者说他也模拟不起来, 还是需要实机复现. 下面记录了我的一些尝试, 包括漏洞的分析.

https://support.dlink.com/resource/PRODUCTS/DIR-882/REVA/

在dlink上找到对应固件

直接binwalk可以发现固件应该是被加密了, binwalk无法分析出有用的信息.

在这里其实可以发现SHRS头的加密固件已经有了解密方案, 通过对作者0xricksanchez的查询, 我们可以在这里找到一篇来自两年前的固件解密脚本.

不妨先从简单的来, 我们从漏洞分析先开始吧. 利用在github上找到的固件解密脚本, 先把固件解密并解包

PS E:\ctf\work\routes\dlink> python3 dec.py -i .\DIR882A1_FW130B06.bin -o .\dec.bin
[*] Calculating key...
        [+] OK!
[*] Checking magic bytes...
        [+] OK!
[*] Verifying SHA512 message digest of encrypted payload...
        [+] OK!
[*] Verifying SHA512 message digests of decrypted payload...
        [+] OK!
        [+] OK!
[+] Successfully decrypted "DIR882A1_FW130B06.bin"!

同样的cpio-root, MIPS LSB 32, 老样子, 继续起一个qemu

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" -nographic -net nic -net tap,ifname=tap0,script=no,downscript=no

lighttpd下的http_parse函数下可以发现可以直接通过访问/start_telnet开启telnetd服务, 但是这个telnetd不是真的shell, 成功登陆之后指向的是/usr/bin/cli

直接去往漏洞点/usr/bin/cli

可以发现注入成功. 尝试分析一下

cli注册了这些命令, 需要去看看cli如何调用这些callback函数的

libcli

cmd函数的原型有四个参数, 大概是(cli *cli, char *command, char* argv, int argc)

这样几乎可以认为就是把我们的命令做了长度限定之后打印到v11里, 再传给SystemCmd函数

systemCmd直接使用了popen函数, 没有进行应有的限定, 所以出现了被绕过的攻击点. 我们可以简单的用 管道符& 实现命令注入

我们继续仿真复现前端环境, 在/etc_ro下

发现rcS, 一般来讲rcs, init.d这类是初始化的服务, 我们cat一下看看

#!/bin/sh
mount -a
mkdir -p /var/run
makedevlinks.sh
cat /etc_ro/motd > /dev/console
nvram_daemon&
#goahead&
init_system start

#for telnet debugging
telnetd

#for syslogd
mkdir -p /var/log

直接执行发现没有nvram, 经过查找资料询问其他师傅得知这个东西和硬件相关, 我们要hook掉相关函数才能启动服务, 这个点应该是固件模拟中很基础并且最普遍的点了.

尝试不启动nvram_daemon直接进行init_system, 发现lightttpd在等待nvram_daemon的执行, 这样看来我们绕不开nvram

先了解一下nvram :

简单的查找和询问之后得知这东西就是一个可擦的rom一样的东西, 里面会放一些键值对, 断电也可以留存数据. 这些键值对用来基础配置或者存其他信息, 是一个单独的硬件芯片, 不同的设备这里面的信息都会不同, 所以对于不同的固件模拟, 我们几乎是都要模拟出这个nvram, 否则系统就无法正常初始化.

github上可以找到相关通用hook代码, 可能需要我们自己改一下这里..虽然都是给arm写的吧, 但是总得试试.

这儿要配置一下交叉编译环境, 用现成编译好的, 自己尝试编译一下一个小时都没完事...这里能下到各种版本.

提示缺少libc.so.6, 一番查找后发现这个问题stackoverflow

重新编译一下so? 但是我确实是使用mips32el编译的, 直接运行nvram.so也没有提示load library错误, 说明libc.so.6不是nvram.so依赖的.... 但是没有PRELOAD它的话, 还不会出现这个问题. 疑惑住了, 打算拿出nvram_daemon分析一下

在这里, 习惯用glibc, 完全忘记了嵌入式比较常用的是uclibc, 这样说明还是交叉编译环境的问题, stackoverflow诚不欺我.

换一个交叉编译器重新编译nvram.so

mipsel-linux-gcc -Wall -fPIC -shared commonhook.c -o libnvram.so

发现动态库里函数不够, 我们还是要自己添加一些Hook

Firmadyne的github下能找到这个nvram的hook, 但是仍没有我们想要的nvram_bufget等函数, 但是有一个函数原型不同的nvram_get_buf, 我们尝试读一下源码看看是不是一个东西.

这里有一直困扰我们的打不开apsoc_nvram的调用, 看来这个地方应该是绕不开了...

我去github搜索了一些nvram_bufget和nvram_get_buf相关的信息, 其实从函数原型上可以很容易看出来这两个东西不太一样, 一个需要三个参数一个需要两个参数.

修改makefile

CFLAGS=-O2 -fPIC -Wall
LDFLAGS=-shared -nostdlib
CC=mipsel-linux-gcc
OBJECTS=$(SOURCES:.c=.o)
SOURCES=nvram.c
TARGET=libnvram.so

all: $(SOURCES) $(TARGET)

$(TARGET): $(OBJECTS)
        $(CC) $(LDFLAGS) $(OBJECTS) -o $@

.c.o:
        $(CC) -c $(CFLAGS) $< -o $@

clean:
        rm -f *.o libnvram.so test

.PHONY: clean

再次编译一个libnvram.so, 如果读了hook的动态链接库的代码, 会发现将很多调试信息输出到了stderr

LD_PRELOAD可以优先加载我们指定的动态链接库, 以达到Hook的效果. 我们暂时用不到更高级的系统调用劫持一类的hook

# LD_PRELOAD="./libnvram.so" /bin/nvram_daemon 2>tt.err

err里的行为比较正常, 就是在不断的初始化set数据, 但是在nvram_get_buf: Segmentation fault

出现了段错误

似乎可以发现都是set的部分, set之后关掉nvram, 然后再getbuf, 我们回IDA里看看怎么调用的.



尝试分析流程以找到报错点, 但是在这里查找相关资料后发现对Dir-882的Hook量级是比较庞大的, 除了libnvram, 还有大量的libc函数需要hook. 思路上是在hook的基础上通过gdbserver调试守护进程, 找到守护进程使动态链接库段错误的消息, 再决定是patch消息还是修改动态链接库以绕过这个错误. 决定止步于分析漏洞. 回头分析固件解密

这套固件是一套两三年前的固件了. 除了比较常见的通过过渡版本寻找解密程序解密之外, 还出现了0xricksanchez师傅对解密程序分析后带来的SHRS头Dlink固件通杀(882&3060), 本着学习的态度, 我们接下来对这两种方法都复现一下. 虽然也看到过类似的文章, 但是除了原作者的文章外描述的都不够详细, 对于我这样的新手来讲很难看懂或者是有细节被一笔带过. 无论如何, 自己深入一个固件解密都是学习分析的一小步.

首先回到固件下载的网站把几个固件都下载下来

可以发现FW101B02还没有进行过加密

从这儿可以看出来FW104B2没有加密, 你问我怎么知道的? 它文件名写了, 但是之后的110B02就已经经过加密了

这样可以看出来FW104B2是一个过渡版本, 针对它分析解包

在bin下直接发现了imgdecrypt, 猜测这个可能是解密

strings 一下, 霍, RSA, AES统统出现了, 丢进mipsel环境下使用imgdecrypt对固件进行解密, 执行格式是./imgdecrypt <binary>

这时候再binwalk被解密的固件就可以解包了

这样, 我们在遇到加密固件的时候, 往往可以通过考察同一产品线的相似固件, 找到旧版本或者过渡版本.

接下来是针对decrypt的分析, 这里参考了0xricksanchez师傅固件解密的分析

这两个函数名字一看就非常的美丽...

结合IDA的帮助,main的工作就是如果*argv中出现了decrypt字符串那就是用作解密, 否则用作加密, 我们之前的文件名中有decrypt字样,因此是解密. 它的参数仍是argc和argv

我们首先主要关注decrypt函数

如果存在第三个参数, 那么把第三个参数传进RSAHandle, 否则用默认路径传入RSAHandle, 把读出来的公钥放在全局变量里

关注第二个和AES有关的函数

这个函数只有一步, 就是进一步调用一个会调用cbc的函数, 其中的内容是写定的十六进制和传入的参数"0123-F"

这里有三个长0x10的串, 分别是98C9D8F0133D0695E2A709C8B69682D4, 358790034519F8C8235DB6492839A73FC8D32F409CACB347C8D26FDCB9090B3C 其中前两个分别是iv和key, 后面是密文, 解密得到的key是C05FBF1936C99429CE2A0781F08D6AD8, 这个key在之前解密的时候已经被打印过了

程序把key输出之后, 在sub_401780处真正处理了文件, 这个解密逻辑相当庞大, 但实际上并不是特别复杂. 我们一点点进去看一下.

欣赏一下逻辑图, 整体逻辑还是比较庞大的.

一直到第六个if都是在做准备工作, 清空两块内存, 并把参数I的文件映射到内存指针0处, 可读, 写回.

对'/tmp/.firmware.orig'的开启是读写方式(0x102), 成功后, 对file2文件指针重定位并把unk_402E8C写入到File2中, 并把file2也通过mmap映射到指定位置, prot是执行和写, 同样写回.

检查ROM的头是否是SHRS

在这里检测了文件头和倒序(LE转BE)SHA512的值

从ROM+0x1B7开始解密, 解密的len和iv都来自固件, 解密的key来自之前的预处理.

解密后再进行两次SHA512Check和一次RSA签名Check, 结束解密.

(叁) 总结

​ 对于逆向手来说, 入门IoT分析总体上讲还是比较容易的. 长期对和汇编打交道, 即使从x86转向Mips/ARM架构汇编也能快速得以上手. 实际上, 只需要大概形成一个对应体系, 汇编做的操作总是有限的.并且编译器产生汇编代码的结构大同小异, 对某一架构的汇编分析熟悉之后, 经验也适用于其他架构.

​ 逆向手一般也会对常接触的操作系统有比较丰富的了解, 我之前接触的Linux逆向更多一点, 到IoT固件分析时, 固件也大多基于Linux, 这让拿到文件系统后寻找有用的二进制程序也变得轻松不少. 总体来说, CTF本就是一门需要快速学习的竞赛, 扩展到更宽广的网络安全研究领域其实也是如此. 无论是哪个方向的选手, 除了分析题目带来的经验之外, 更宝贵的也有搜索解决问题的能力和快速学习的能力.

分享到

参与评论

0 / 200

全部评论 6

zebra的头像
学习大佬思路
2023-03-19 12:14
Hacking_Hui的头像
学习了
2023-02-01 14:20
tracert的头像
前排学习
2022-09-17 01:35
Tonyhappily的头像
爆赞
2022-08-03 16:22
顾的头像
大佬,我最近也想入门iot,有没有什么资料可以推荐一下的啊?
2022-06-01 16:56
iotstudy的头像
**量好大,得好好研习一下。
2022-05-19 08:47
投稿
签到
联系我们
关于我们