3

记一次RTEMS系统的固件解密

 1 year ago
source link: https://www.51cto.com/article/756985.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

记一次RTEMS系统的固件解密

作者:LightSec 2023-06-07 07:31:04
本篇我们着重讨论了一款固件加密的门禁考勤机设备,并借鉴PC端app脱壳的技巧,解密解压了固件中的kernel以及app。

近期翻阅之前的工作,发现在考勤机系列的安全研究里,有一篇很早就整理好但是没发出来的文章,考虑到相关设备的漏洞已经提交了快两年了,想必该修复的也修复了,设备可能都已经不再生产了。这篇文章我们主要聊一下这个设备的分析过程,着重描述一下这个设备的固件解密。

2、IoT设备启动概述

IoT设备的安全分析中,分析人员的主要工作是分析设备的逻辑功能及其代码实现,从而挖掘设备中的漏洞。大多数分析工作是从解析固件开始的,如果这一步就不顺利的话,后续可能会更加麻烦。不巧的是,这次的设备在起步阶段就很曲折:固件被加密了,并不能直接开始分析。

在此我们再简单总结一下那些常见IoT设备的启动流程,如下图所示:

f56227d4630bc169dcc094a05ce266a1e0001b.jpg

图2-1 IoT设备大致的启动流程

上图大致可以分为4个阶段:

(1)首先,芯片上电后首先运行的是boot rom,执行完毕后会跳转到bootloader,如果设备启用了固件签名,那么此处会对固件进行校验;

(2)然后,bootloader的作用类似于PC启动过程的引导,主要功能就是为操作系统的运行做准备工作,在复杂的设备中,bootloader会进一步分成多个阶段;

(3)此时,操作系统就开始接管对MCU的控制,上图中把操作系统和app分成了两个部分,是为了对标PC方便大家理解,实际上在有些设备里跑的操作系统和app是混合在一起的,甚至可能没有操作系统;

(4)最后,各个app被加载运行,开始执行设备的逻辑功能,此处往往是我们想要着重分析的地方。

上图中,bootloader、kernel和app这三个阶段的代码通常是由开发者编写并烧录到Flash中的,我们所说的固件指的就是这部分内容。请将此图放在脑海中,后续所有工作都将以此图为模板展开分析和叙述。

PS:本次分享讨论的是固件可以提取但是无法分析的情况,一些由于读保护机制导致固件不被提取的情况,暂不在本篇的讨论范畴中。

3、情况简介

通过热风枪可以取下Flash芯片,然后通过编程器可以拿到Flash中的固件,这些基本功各位一定已经非常熟悉了,这里就不再赘述。固件文件包含一个FAT12格式的文件系统,可以通过7z工具直接提取出文件系统中的所有内容,提取内容截图如下:

489ba0b39b222e5ad89681821ae282dc3dd0ca.jpg

图3-1 固件文件系统内容

显而易见,上图中的App文件夹中肯定包含了这款门禁考勤机的主程序。不过,事情肯定没有那么顺利,该文件夹内只有一个文件是可以解析的ELF格式,其他文件均不可解析,仅存在可解析文件叫main.bin,用IDA加载之后,内容看起来也有些过于简单,截图如下:

45bf9f61377bdaca7fd573a59fd07ac40c2377.jpg

图3-2 main.bin程序内容

上图中,虽然函数符号都还在,可以大概推断出函数的作用,但是函数看起来很古怪,比如RT_CreateProcess函数内容是下图中的样子:

图3-3 RT_CreateProcess函数内容

上图中的gFuncEntryKern看起来有点像虚表指针,指向一个函数地址表,而这个指针在程序启动时被赋值。

程序main.bin很短,很快就逆完了,看起来最后创建了一个新的进程,但除了main.bin程序之外,其他程序全都无法解析。即便是main.bin自身,看起来也不太正常,很多函数只有跳转stub,缺少实质内容,看起来像是PC中加了保护导入表的壳。那么,接下来还是从头开始啃一下这个设备固件吧。

4、kernel分析与解密

按照图2-1所示,我们决定先看看bootloader,在解压获得的完整文件系统中,可以看到Product.ini文件,从里面能够找到设备的分区表,如下图所示:

82be33471d9a40d2b18592ff788fbbc9a84786.jpg

图4-1固件分区表

我们此前解压缩得到的FAT12文件系统即为0x20000偏移处,顺便吐槽一下binwalk,为啥binwalk识别不出FAT12文件系统,因为太古老了吗?

结合设备上电后串口的输出字符串,可以推断出0x20000之前的固件内容即为程序的bootloader,接下来用IDA载入bootloader部分代码进行分析,并结合串口的输出日志,可以定位到关键位置,如下图:

669c18919115d7943c118515b39608f689d7b2.jpg

图4-2 bootloader加载kernel文件的过程

上图中,我们根据串口打印出来的字符串来定位程序的关键位置,找到了kernel文件其实就是rootfs中的Rtbio_3760_R4502文件,且串口打印出来的信息包含了kernel的加载基址和入口地址,根据这些信息,可以分析kernel文件了。

使用IDA加载kernel文件之后,IDA导航条如下图所示:

036ee2d38b987a1c0b3674297e47104845831c.jpg

图4-3 直接解析Rtbio文件时的IDA导航条

可以看到此时kernel只被解析出了一小部分,绝大多数内容都是unexplored状态,实际上这部分内容还是处于加密或压缩状态。由于执行流程已经离开bootloader而进入kernel阶段,那么kernel代码肯定是自解密或自解压的,这很正常,一般情况下linux kernel都是自解压的,但我们没办法直接解压就很奇怪。通过逆向分析,可以在入口点附近看到多处异或解密代码,如下图所示:

844c085077538d01a5520184b1b0586396cea0.jpg

图4-4 异或解密代码

上图中,0x79E1即为异或解密使用的密钥,类似上图中的异或解密代码在多个位置都有出现,原来再自解压之前还有一步自解密过程,所以没法直接解压。

耐心分析这些已经解析的代码,可以将kernel的启动流程整理为下图所示内容:

e1c7d6b77046e8c760a66189f924b1fe7363f6.jpg

图4-5 kernel文件的解密过程分析

可以看到,kernel既包含自解密部分,也包含自解压部分。将我们在PC端脱壳的经验用到此处,待程序完成自解密和自解压之后,dump完整的内存,就可以获得可以逆向阅读的kernel二进制文件。

然而我们并没有找到设备调试接口,写脱机解密脚本又太繁琐,最终选择用QEMU加载kernel,运行其自解密和自解压代码,待所有工作完毕之后dump内存。最后,用ida加载dump下来的内核文件,就可以看到很多代码和明文的字符串,如下图:

178aba034350d8f6e3f892b9559337ee16b916.jpg

图4-6 载入并分析dump kernel

在众多字符串中,我们还注意到了一个版权信息,如下图:

892bcba5417521a58bf683056e20615daa42e9.jpg

图4-7 版权信息字符串

上图中的版权信息表明此固件可能使用了RTEMS系统。该系统一款开源的RTOS系统,在github上就可以找到源码,代码参考链接:https://github.com/RTEMS/rtems

与我们此前分析云丁鹿客智能门锁那篇很类似,当确定了固件使用的操作系统,那么逆向工作就变得相对简单,毕竟有源码可以参考。通过对比源码,我们可以分辨出大量函数,如open、close、read等。查找这些函数的交叉引用,我们发现了一张很大的函数表,如下图右侧所示:

f4d180214052353bc07763e7b5b6faf907c493.jpg

图4-8 导出函数表

上图左侧中,我们展示了第3章main.bin的几个函数调用,并与右侧kernel中的函数表相互对应。还记得gFuncEntryKern指针吗?看起来这个指针所指向的函数表就是上图右侧中的函数表。到此,依靠这张函数表,我们就可以正常分析main.bin程序中的所有函数了。

到此,bootloader和kernel部分可以告一段落了,在开始分析APP之前,还有一点需要提一下,与传统PC操作系统不同的是,该设备并不存在用户态和内核态,kernel和app处于相同的特权级别,所以两者之间可以直接相互调用,不需要上下文的切换。

5、app分析与解密

在这个设备固件中,除了kernel需要解密解压之外,大部分app也是需要解密解压的。除main.bin之外的程序,我们都无法用IDA直接分析,就是因为这些程序尚未解密解压,而解密解压的关键就在RT_UnzipFile函数中,将该函数的关键位置截图如下:

53dd81f016eb4abf3f3510d161df5f0ffc83da.jpg

图5-1 RT_UnzipFile函数的代码片段

上图中可以看到read、xor、write等几个关键点,进一步分析可以判定RT_UnzipFile函数同样包含解密和解压这两个过程,异或密钥也没有变化,同样是0x79E1。

根据以上结论我们可以写一个用于解密和解压每个bin文件的脚本,将这些文件恢复成可以分析的ELF格式。通过脚本,我们将Root_00.bin解密解压,root_00.bin就是main.bin在执行最后创建的新进程,用IDA载入解密解压后的ELF文件,截图如下:

a66aebc69122008f40044346995709f505fb55.jpg

图5-2 通过IDA载入解密后的Root_00.bin文件

可以看到,Root_00.bin已经可以分析了。至此,我们已经完全理解了这个设备和固件,可以随意分析其中的文件了。

6、固件程序的编写

门禁考勤机作为一个企业的门户,往往承担了保卫企业的任务,如果门禁考勤机自身变成了打入企业内网的潜伏者,想必是件很搞笑的事情。当我们发现了门禁考勤机的漏洞,想植入后门时,就可以来阅读一下这第6章的内容。

虽然与开发传统的Linux程序比较类似,但由于我们没有与之匹配的SDK,而只能选择公版ARM编译器,所以在开发过程中,需要额外注意两点:

(1)系统调用。通过逆向分析被解密解压的多个固件程序,我们可以确定程序是没有导入表的,系统调用是通过gFuncEntryKern和gFuncEntryLibc等几个指针实现。由于我们没有官方SDK,这就导致没办法像常规正向开发一样进行各种系统调用,但是按照shellcode开发思路,直接写汇编肯定是没问题的,最终结果如下图所示:

332f98999ffa6d0b02575581fc060a78456494.jpg

图6-1 模拟系统调用

上图中,我们参考IDA的逆向代码用汇编实现socket、bind等系统调用,具体的逻辑功能代码还是可以照常用C来编写。

(2)生成文件格式。通过查看其他已经解密解压的多个固件程序,我们可以确定ELF文件是relocatable格式,而不是executable格式的,如下图所示:

13c62c26622cf4cef464029a15551d79848905.jpg

图6-2 查看main.bin文件格式

因此,使用gcc编译程序时,需要指定-c参数,用于生成仅编译和汇编的文件;同时,使用ld链接程序时,指定-r参数,用于生成relocatable文件。

完成以上工作后,将编译的程序,按照第三章的分析做好压缩、加密处理后,并通过某些方式植入设备中,设备上电后即可看到程序开始执行,tcp回连nc监听的42240端口,如下图:

967311e279e56f2c9ec952abfb34d0ab27b1d9.jpg

图6-3 反弹socket通信的进程

上图中,我们以发送hello world字符串来说明问题。

当然,我们也可以修改固件中已有的文件,例如开机界面的图片,更多内容我们这里不再展示。

e7b27a75111d7ad64075151923c28ef57df395.jpg

图6-4 修改考勤机开机界面

本篇我们着重讨论了一款固件加密的门禁考勤机设备,并借鉴PC端app脱壳的技巧,解密解压了固件中的kernel以及app。其实,本篇分享的固件加密只是各种加密方式的一小类,更常见的情况应该是固件文件完整加密,如果以后有机会我们也会分享那些完整加密的情况,感兴趣的可以等我们后续的分享。

责任编辑:武晓燕 来源: FreeBuf.COM

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK