-
-
[推荐][原创]某二进制固件漏洞挖掘
-
发表于: 2天前 493
-
最近做了二进制固件挖掘的题目,题目比较简单,做出来和大家分享一下。
使用的反编译工具IDA Pro 9.2
一、确定硬件架构以及入口点
首先告知固件二进制文件的固件运行平台为STM32F4xxxx,根据查找资料,其中断向量表SP_main在0x8000000处,入口点在Reset_handler处,如下
图所示:

一、找到主程序
如下图所示,在入口点Reset_handler处,初始化了一些data段的数据:

由于本人在写分析过程时,固件已经分析完毕,大部分符号基本上是基于我自己的理解打上去的,除了Reset_Handler、NMI_Handler这种与中断相关自带符号,其他函数基本上不带符号,Reset_handler大部分符号我已经还原。首先进入SystemInit,如下图:

只有一些写内存的操作,属于是一些初始化的操作。其次进入__libc_init_arrary中:

sub_8005A08无实际操作,而在__libc_init_arrary循环调用了init_arrary里边的函数,看起来像是linux加载so时,会循环调用init_arrary里边的初始话函数。进入到init_arrary数组里边去,里边有许多初始化函数,目前尚不知道作用是什么。先进入main_loop里边查看。
发现main_loop(符号我自己定义的)里边有个循环函数,业务逻辑应该都在main_loop里边了,至此已经发现主要业务逻辑代码,main_loop如下图所示:

一、分析主程序
在nullsub_17中无实际操作,就是一个空函数。进入Protocol_Manager_Init中去,如下图:

进入到sub_80005E4中,发现函数如下图所示:

如果dword_20000120为0,则将会执行svc指令,但是dword_20000120除了在Reset_handler中被初始化为0,无论实在init_arrary函数中,都未曾有过修改,故syscall_3f必然执行,而SVC的处理函数对应的是NMI_handler,这是一个死循环函数,程序执行到这一步将会卡死。至此发现了第一个漏洞点。
回到Protocol_Manager_Init中,进入到下一个要执行的函数,如下图所示:

都是进行一些赋值的操作。进入HardwareSerial_Begin里面去,如下图:

根据断言处的字符串提示,可知该固件所使用的框架基于基于 Arduino STM32 Core (v1.3.0),在github上下载其源码,找到Reset_handler里边的源码,如下图所示:

基本上可以确定出Reset_handler函数里边的符号。从而也确定了HardwareSerial_Begin的符号,可以知道HardwareSerial_Begin是一个初始函数,属于开源框架部分。剩下的就好分析了。
在Protocol_Manager_Init中有一个叫g_ModbusContext(0x20000124)的全局变量,在main_loop里边的操作后续都围绕着g_ModbusContext,需要对g_ModbusContext的结构进行还原。通过交叉引用,发现在init_arrary中的init_gModbusContext对结构体进行了初始化,如下图所示:

返回到_Protocol_Manager_Init中,发现g_ModbusContext第一个成员对应的就是g_Serial2_Object的地址,而通过交叉引用和对init_arrary里边的函数进行进一步的分析,发现g_Serial2_Object里边有一个虚表函数表,对应的是off_8005EFC,因为在后边的业务逻辑中大量地使用了该虚表中的函数,所以需要对虚表中的函数进行进一步的分析。由于本人对STM32开发只了解过一点,我把二进制固件的整体框架提供给AI,叫AI分析虚表中的函数,最终通过AI的分析加上源码的对比,成功还原了虚表中函数对应的函数:

最终确定了g_Serial2_Object的对象是HardwareSerial。最终也根据AI发现该固件使用的协议栈是Modbus。
根据g_ModuleBus初始化函数,如下图:

经过其上层函数确定,a2=1,而结合Modbus协议标准,确认此处为从站地址,默认值为1,从而确定Offset 4处的成员为Slave ID,观察到该偏移量Offset 6被传入pinMode和digitalWrite函数,推断其为控制某个部件收发切换的硬件引脚号。Offset 86应该为某个频率或者时间。Offset 10如下图控制着switch case中跳转,符合Modbus PDU格式中功能码的位置,如下图所示:

Offset 73在Modbus_Read_Holding_Registers_Response等ModBus相关的函数中高频使用,发现该变量充当了响应包构建过程中的动态写指针,如下图:

基于上述逆向证据,最终还原出的 ModbusContext 内存布局如下(省略了部分结构体成员的还原过程)如下图所示:

至此ModbusContext的重要结构体成员已经还原完毕,接下来可以分析通信了。
一、通信协议
通信协议其实非常简单,根据所查资料,得知在IRQ38_handler处接收数据,主程序通过Serial2.read()(虚表中的函数,对应的是图片中的HardwareSer_Read)从64 字节的小池子中提取字节,并根据索引变量ctx[73]将其存入g_ModbusContext的缓冲区中去。至此已经了解了程序的大致运行过程,可以进行进一步的漏洞分析了。

五、漏洞挖掘
继续回到main_loop中,进入到Modbus_Protocol_Dispatcher中去,不断升入,最终发现在如下图中使用Serial2.read()获取到传输过来的数据,然后进行处理,这里应该是漏洞集合的大本营了。

1.在doModbusCheck(sub_80007F0)中发现了对寄存器地址的校验存在漏洞,如下图:


2.在Modbus_Read_Holding_Registers_Response(sub_8000998)中发现了存在陷入死循环、内存越界读取、索引劫持与缓冲区溢出的风险(漏洞),如下图:

3.Modbus_Write_Single_Coil_Handler(sub_8000A00)发现了任意内存比特位篡改漏洞:

4.在Modbus_FC06_WriteSingleRegister(sub_8000A4E)中存在越界写入内存的漏洞(缓冲区溢出),如下图:

5.Modbus_FC15_WriteMultipleCoils依然存在缓冲区溢出的漏洞:

6.Modbus_FC16_WriteMultipleRegisters也存在缓冲区溢出漏洞

回到main_loop中:
7.使用函数地址作为while条件,如下图:

8.isAvailable_Data无实际功能:

9.最初发现的因为syscall_3f导致程序陷入死循环:

这就是我目前挖到的所有漏洞了,如果有错误请给位大佬指正。
赞赏
- [推荐][原创]某二进制固件漏洞挖掘 494
- [原创]使用调试器调试某UE4手游 1328
- [原创]某手游外挂驱动分析 9532
- [讨论][求助]android studio断点问题 3680
- [讨论]loadlibraryA与rsp的问题 3078