利用UART串口调试提取360路由器固件

百家 作者:Chamd5安全团队 2021-01-12 09:46:21
1
概括
在智能设备漏洞挖掘过程中,固件提取是分析的第一步,也是迈向成功的第一步。目前,已有多种关于固件提取的思路和技术,具体可参考看雪2018峰会回顾[1]
我以前分析的设备,固件存储于tsop8角封装的nor flash,可以直接通过芯片夹夹取,或者直接焊下flash利用芯片座提取,然后无脑binwalk获得整个文件系统。但最近分析的设备,固件存储于tsop48角封装的nand flash,没有对应的芯片夹,而且要求有一定的焊接技术。此外,由于nandflash存储的特性[2](如OOB数据的干扰),即使成功进行了物理提取,后续的数据处理也相当复杂。(在这里也希望有师傅能出一篇物理提取nand flash固件过程的教程,我实在不会TAT)
接下来,我将结合最近的实验,与各位分享下本人在nand flash固件提取过程中,摸索和总结出来的一种利用UART串口调试提取固件的方法。由于本人能力有限,如有错误,还请各位师傅提出指正。
2
前提条件
UART串口调试,可进入linux系统命令行。
3
基本概念
MTD(MemoryTechnology Device),存储技术设备,用于访问存储设备(ROM,flash)的Linux子系统。

通过proc文件系统查看mtd设备的分区情况,可以发现mtdN和mtdblockN描述的是同一个MTD分区,对应同一个硬件分区。两者是同一个MTD分区的两种不同描述:mtdN是实现MTD分区所对应的字符设备,而mtdblockN设备则是在对应生成的块设备,两者内容一致,但具体的ioctl命令操作是不同的。

4
实例

出于保密性,在这里不透露具体型号。通过简要分析和丝印查阅,可以得知固件存储于红色框的flash中,如下图所示。

但幸运的是,电路板上留存了UART调试串口,并且标注了各个接口的属性(GND、RX、TX等)。接下来就可以通过连接UART进行串口调试的操作。

连接后,我使用的是SecureCRT与其进行通信。选择好正确的串口号和波特率(波特率的测试一般是从9600开始,可参照SecureCRT提供的常见波特率进行测试,如下图所示)。当界面有输出,且为正常字符,则连接正确。

但是值得注意的是,系统在启动成功后,是关闭了UART调试shell的,此时只能看到系统日志的输出,而无法输入相关的命令对其进行操作,如下图所示。

仔细观察设备启动的全过程,我们发现其bootloader启动时,给我们提供了一次进入系统的机会,即在某个时候按下f可进入failsafe mode,而在该模式下,我们可获取其中的linux shell并进行命令输入,即可“看”到整个设备的文件系统,如下图所示。

然而,由于此时设备并没有完全启动成功,所以其与外界是断联的,无法与外界进行通信。而这显然不能很好地帮助我们进行分析。那么如何在断网的情况下,导出整个文件系统,则是接下来需要解决的问题。

通过查看/proc/mtd,可以得知各个mtd分区的基本信息描述,其中mtd10,mtd11,mtd13则有可能是我们所需分析的文件系统,如下图所示。

由于此时设备与我们的交互只能停留在命令行的回显中,一个很巧妙的方法则是将对应的mtd分区内容转换为可见字符(如hexdump,base64),我们记录命令行的输出回显则可以获取整个文件系统。但是这里需要注意的是,由于mtd分区内容较多,且嵌入式设备处理性能有限,只能通过结合SecureCRT脚本编写,分段记录mtd分区。

关键代码如下:

def send_cmd(fp,offset,step): 
    crt.Screen.Synchronous = True  

    # Send the initial command then throw out the first linefeed that we  
    # see by waiting for it.  
   
    crt.Screen.Send("hexdump {} -s {} -n {}\n".format("/dev/mtdN",offset,step))  
    crt.Screen.WaitForString("\n")  
  
    # Create an array of strings to wait for.  
    promptStr = "root"   
    waitStrs = ["\n", promptStr]  
  
    row = 1  
 
    while True:  

        # Wait for the linefeed at the end of each line, or the shell  
        # prompt that indicates we're done.  
        result = crt.Screen.WaitForStrings( waitStrs )  
 
        # If we saw the prompt, we're done.  
        if result == 2:  
            break  
        # The result was 1 (we got a linefeed, indicating that we  
        # received another line of of output). Fetch current row number  
        # of the cursor and read the first 20 characters from the screen  
        # on that row.   
        #   
        # This shows how the 'Get' function can be used to read  
        # line-oriented output from a command, Subtract 1 from the  
        # currentRow to since the linefeed moved currentRow down by one.  
        #    
        screenrow = crt.Screen.CurrentRow - 1  
        readline = crt.Screen.Get(screenrow, 1, screenrow, 48)  
        readline = readline.strip()  
        
        NOTE: We read 48 characters from the screen 'readline' may  
        # contain trailing whitespace if the data was less than 48  
        # characters wide.  
 
        # Write the line out with an appended end-of-line sequence  
        fp.write(readline)  
   
        crt.Screen.Synchronous = False  
offset与step则是对应hexdump的偏移以及显示字节数,可根据实际需要进行设置。
5
总结

本文总结了一种利用UART串口调试获取设备的文件系统的方法,其关键在于通过hexdump或base64等将不可见字符转换为终端可回显的字符。

本方法的最大限制则是电路板中需要有UART调试串口并且设备存在相关的转换命令(虽然hexdump是很基本的命令了)。
6
参考链接

[1] https://bbs.pediy.com/thread-230095.htm

[2] https://zhuanlan.zhihu.com/p/26745577


结束


招新小广告

ChaMd5 Venom 招收大佬入圈

新成立组IOT+工控+样本分析 长期招新

欢迎联系admin@chamd5.org



关注公众号:拾黑(shiheibook)了解更多

[广告]赞助链接:

四季很好,只要有你,文娱排行榜:https://www.yaopaiming.com/
让资讯触达的更精准有趣:https://www.0xu.cn/

公众号 关注网络尖刀微信公众号
随时掌握互联网精彩
赞助链接