TP-Link XDR-5430-V2 研究分享 – 第一章

IoT 2年前 (2021) admin
2,014 0 0

作者简介 /Profile/

朱文哲,平安科技银河实验室高级安全研究员,Blackhat Asia 2019 演讲者,专注于工业物联网设备及软件的漏洞挖掘。曾发现多款知名品牌工业物联网设备及软件的安全漏洞。

0x01 前言

本文仅对TP-Link XDR-5430-V2路由器研究的过程及发现进行一些记录,部分描述可能会存在错误,在后续研究更深入后会进行修正,截止目前该设备的研究尚未有明显的突破。

0x02 焊接UART串口

和往常一样在拿到设备后首先第一件要干的事情,就是拆开设备寻找UART或其他调试接口。在拆卸设备后,可以在主板正面看到很清晰的4个UART PIN,在PIN的边上也对每个PIN的用途进行了标注。

 

TP-Link XDR-5430-V2 研究分享 - 第一章

UART串口焊接后的效果如下图所示。

 

TP-Link XDR-5430-V2 研究分享 - 第一章

此时我们就可以通过串口客户端来获取设备的串口信息,设备的引导日志如下所示:

Format: Log Type - Time(microsec) - Message - Optional InfoLog Type: B - Since Boot(Power On Reset),  D - Delta,  S - StatisticS - QC_IMAGE_VERSION_STRING=BOOT.BF.3.3.1.1-00041S - IMAGE_VARIANT_STRING=MAABANBZAS - OEM_IMAGE_VERSION_STRING=WINCE-SDC-101S - Boot Config, 0x000002c1B -       127 - PBL, StartB -      1559 - bootable_media_detect_entry, StartB -      1736 - bootable_media_detect_success, StartB -      1740 - elf_loader_entry, StartB -      2736 - auth_hash_seg_entry, StartB -      3109 - auth_hash_seg_exit, StartB -    147859 - elf_segs_hash_verify_entry, StartB -    202709 - PBL, EndB -    165432 - SBL1, StartB -    226157 - GCC [RstStat:0x10, RstDbg:0x600000] WDog Stat : 0x4B -    235338 - clock_init, StartD -      7228 - clock_init, DeltaB -    243603 - boot_flash_init, StartS - SBL last page flash: 38.S - SBL last page flash: 1.S - SBL last page flash: 0.S - SBL last page flash: 0.S - First  couple:0x26:0x1.S - Second couple:0x0:0x0.D -     17110 - boot_flash_init, DeltaB -    262879 - boot_config_data_table_init, StartD -         0 - boot_config_data_table_init, Delta - (0 Bytes)B -    274744 - Boot Setting :  0x00000618B -    277062 - CDT version:2,Platform ID:8,Major ID:4,Minor ID:0,Subtype:1B -    288194 - sbl1_ddr_set_params, StartB -    294020 - Pre_DDR_clock_init, StartB -    295118 - Pre_DDR_clock_init, EndB -    845978 - Image Load, StartD -    438590 - QSEE Image Loaded, Delta - (383613 Bytes)B -   1285453 - QSEE Execution, StartD -        61 - QSEE Execution, DeltaB -   1292102 - SBL1, EndD -   1126853 - SBL1, DeltaS - Flash Throughput, 4732 KB/s  (383613 Bytes,  81066 us)S - DDR Frequency, 800 MHzS - Core 0 Frequency, 800 MHz

U-Boot 2016.01 (May 26 2021 - 22:32:57 +0800)
DRAM:  smem ram ptable found: ver: 1 len: 4256 MiBUsing default environment
In:    serial@78AF000Out:   serial@78AF000Err:   serial@78AF000machid: 8040001No ART partition foundSPI_ADDR_LEN=3SF: Detected XM25QU128C with page size 256 Bytes, erase size 4 KiB, total 16 MiBinput <abort key> to stop autoboot in 500 msdevice 0 offset 0xe0000, size 0xd0SF: 208 bytes @ 0xe0000 Read: OKverifying uboot partition...copy the full partition to ram ...device 0 offset 0xc0000, size 0x20000SF: 131072 bytes @ 0xc0000 Read: OKokverifying kernel and romfs partition...copy the full partition to ram ...device 0 offset 0xe0200, size 0xb9fe00SF: 12189184 bytes @ 0xe0200 Read: OKokdevice 0 offset 0xe0000, size 0x200SF: 512 bytes @ 0xe0000 Read: OKdevice 0 offset 0xc0000, size 0x20000SF: 131072 bytes @ 0xc0000 Read: OK

U-Boot 2016.01 (May 26 2021 - 22:44:05 +0800)
DRAM:  smem ram ptable found: ver: 1 len: 4256 MiBUsing default environment
In:    serial@78AF000Out:   serial@78AF000Err:   serial@78AF000machid: 8040001No ART partition foundSPI_ADDR_LEN=3SF: Detected XM25QU128C with page size 256 Bytes, erase size 4 KiB, total 16 MiBdevice 0 offset 0xe0000, size 0x200SF: 512 bytes @ 0xe0000 Read: OKset_fs_bootargs flash_type = 6SPI_ADDR_LEN=3SF: Detected XM25QU128C with page size 256 Bytes, erase size 4 KiB, total 16 MiBdevice 0 offset 0xe0200, size 0x300000SF: 3145728 bytes @ 0xe0200 Read: OK## Loading kernel from FIT Image at 44000000 ...   Using '[email protected]' configuration   Trying 'kernel@1' kernel subimage     Description:  ARM OpenWrt Linux-4.4.60     Type:         Kernel Image     Compression:  lzma compressed     Data Start:   0x440000e4     Data Size:    2265062 Bytes = 2.2 MiB     Architecture: ARM     OS:           Linux     Load Address: 0x41208000     Entry Point:  0x41208000     Hash algo:    crc32     Hash value:   e147d172     Hash algo:    sha1     Hash value:   9da1dce4bf2675cc6d62d7d23eb3482f38bfc60d## Loading fdt from FIT Image at 44000000 ...   Using '[email protected]' configuration   Trying '[email protected]' fdt subimage     Description:  ARM OpenWrt qcom-ipq50xx-mpxx device tree blob     Type:         Flat Device Tree     Compression:  uncompressed     Data Start:   0x4422920c     Data Size:    54170 Bytes = 52.9 KiB     Architecture: ARM     Hash algo:    crc32     Hash value:   d5137844     Hash algo:    sha1     Hash value:   ab96aac48f897beb852b9960e577904023881b28   Booting using the fdt blob at 0x4422920c   Uncompressing Kernel Image ... OK   Loading Device Tree to 497f1000, end 49801399 ... OKUsing machid 0x8040001 from environment
Starting kernel ...
- preinit -- regular preinit -switching to jffs2tmp/virtual_art.bin- init -
Please press Enter to activate this console. dev.nss.general.redirect = 100.00.23.782318 MCSD plugin   info : Initializing plugin managermcastds: Entering evloopRun        checkOrResetFactoryInfo(913). Factory Info verified <ucconv> usrconfPathPrepare(37). /tmp/etc/config doesn't exit, Now create it.00.00.24.673944 MCSD netd     ERR  : netdBridgeLinkNLCB Read error, Create it again        deviceInfoInit(785). softver:1.0.15 Build 210526 Rel.80830n        br_scan_ifaces(47). Scanning bridge netif...        event_sock_enable(1018). enabled netlink socket at 23 with own pid 433
lanv6LocalIpv6: fe80::36f7:16ff:fe9f:bfbf[wireless]ioctlCmdSetAlmac[791]Set almac 34:f7:16:9f:bf:bf.........

不同于早期的D-Link及小米路由器,在终端输入回车后提示需要输入账号密码才能够登陆路由器的终端shell,此时就需要去分析设备的固件来分析此处的账号密码。

0x03 固件提取及分析

在设备主板上,我们可以看到一枚型号为XMC25QU128CH10的flash芯片,我们可以使用热风枪将该芯片拆卸后,使用专用的编程器对Flash中的数据进行读写。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

使用专用工具导出该FLASH芯片内的数据如下图所示。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

使用Binwalk对固件文件进行分析的结果如下图所示,我们可以在固件中找到squashfs文件系统。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

使用binwalk自动解压后即可查看到具体的设备文件。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

在passwd文件中发现了root账号的密码hash,但遗憾的是无法通过在线平台对该hash进行破解。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

考虑到我们可以通过编程器直接修改已拆卸FLASH芯片中的内容,下一步的计划就是直接重新打包一个修改过密码的rootfs到flash中尝试绕过路由器自带的系统升级校验。

0x04 尝试重打包固件

首先我们需要修改原始rootfs的passwd文件,将hash替换后在进行重打包为squashfs文件。

# 使用mksquashfs命令将修改后的rootfs打包为hacked.squashfs文件mksquashfs squashfs-root hacked.squashfs -b 256k -comp xz使用python脚本重打包# !/usr/bin/env python3# coding=utf-8import struct
squashfs_base_offset = 0x324594squashfs_end_offset = 0xC80000
src_file_path = "./TP_XDR_5430_XMC25QU128CH10_20210628_104736.BIN"dst_file_path = "./TP_XDR_5430_XMC25QU128CH10_20210628_104736_harked.BIN"squashfs_file_path = "./hacked.squashfs"

src_file_data = open(src_file_path, 'rb').read()squashfs_file_data = open(squashfs_file_path, 'rb').read()
squashfs_file_data += (squashfs_end_offset - squashfs_base_offset - len(squashfs_file_data)) * b'xff'new_dst_file_data = src_file_data[:squashfs_base_offset] + squashfs_file_data + src_file_data[squashfs_end_offset:]


new_dst_file = open(dst_file_path, 'wb')new_dst_file.write(new_dst_file_data)new_dst_file.close()

将重打包后的固件重新写入FLASH芯片,装回设备上开机后发现,TP-Link的bootloader会对rootfs进行校验,直接修改rootfs的方法无效。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

Recover mode的界面如下。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

尝试了一下在Revocer界面直接更新打过patch的固件升级包,则会提示如下的RSA签名校验错误信息,可见TP-Link在这一步也做了签名校验。

TP-Link XDR-5430-V2 研究分享 - 第一章

目前想到的方法就是分析bootloader代码,找到校验的逻辑后patch掉。

0x05 bootloader分析

从最初的引导日志中可以发现firmware check failed报错是在第一个u-boot中,因此我们就需要先从固件中提取第一个u-boot。继续对binwalk分析出的几个gzip包解压进行字符串搜索后,最终定位到第一个u-boot就在偏移为0x69000处。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

解压后发现uboot是一个纯二进制文件,没有使用任何封装。在Ghidra中加载该文件进行分析。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

直接查看uboot头部的代码可以发现在0x00000004处PC寄存器指向了0x4a920000附近,然后又由于uboot头部代码存储的一般是中断向量表,因此大概率0x4a920000就是这个uboot的加载地址。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

重新使用0x4a920000加载后进行分析,在0x4a952e3c处发现了目标关键字的引用。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

在0x4a925000处发现了用于校验固件的函数。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

在0x4a9451ac函数中找到校验逻辑,发现有一个循环会逐个对kernel和rootfs等进行校验,随即进行patch。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

打完patch后使用如下gzip命令压缩。

# 一定要使用 -n 去除header中的文件名 gzip -n uboot_1_harked.bin使用python脚本再次重打包# !/usr/bin/env python3# coding=utf-8import struct
squashfs_base_offset = 0x324594squashfs_end_offset = 0xC80000uboot_1_start_offset = 0x69000uboot_1_end_offset = 0xA0000

src_file_path = "./TP_XDR_5430_XMC25QU128CH10_20210628_104736.BIN"dst_file_path = "./TP_XDR_5430_XMC25QU128CH10_20210628_104736_harked.BIN"squashfs_file_path = "./hacked.squashfs"uboot_1_file_path = "./uboot_1_harked.bin.gz"

src_file_data = open(src_file_path, 'rb').read()squashfs_file_data = open(squashfs_file_path, 'rb').read()
squashfs_file_data += (squashfs_end_offset - squashfs_base_offset - len(squashfs_file_data)) * b'xff'new_dst_file_data = src_file_data[:squashfs_base_offset] + squashfs_file_data + src_file_data[squashfs_end_offset:]

uboot_1_size_offset_in_elf = 0x31000 + 0x3e0uboot_1_size_in_firmware = struct.unpack("<Q", src_file_data[uboot_1_size_offset_in_elf: uboot_1_size_offset_in_elf + 8])[0]print("uboot_1_size_in_firmware: {}".format(uboot_1_size_in_firmware))
# uboot_1uboot_1_file_data = open(uboot_1_file_path, 'rb').read()uboot_1_file_length = len(uboot_1_file_data) print("len(uboot_1_file_data): {}".format(len(uboot_1_file_data)))# Padding b'xff'uboot_1_file_data += (uboot_1_end_offset - uboot_1_start_offset - len(uboot_1_file_data)) * b'xff'print("len(uboot_1_file_data): {}".format(len(uboot_1_file_data)))new_dst_file_data_2 = new_dst_file_data[:uboot_1_start_offset] + uboot_1_file_data + new_dst_file_data[uboot_1_end_offset:]

new_dst_file = open(dst_file_path, 'wb')new_dst_file.write(new_dst_file_data_2)new_dst_file.close()

这次重新打包后又遇到了新的问题,引导程序报错。

TP-Link XDR-5430-V2 研究分享 - 第一章

针对这个情况,有两种可能:

一种是bootloader没有能够自动识别到gzip的文件长度导致解析错误;

另一种可能是gzip的不同版本之间压缩算法存在区别,之前在研究D-Link路由器时遇到过类似的问题,原因是xz压缩库版本不兼容导致的。

针对第一种情况,由于gzip的header自身是不带文件长度信息的,因此在bootloader中必然会有某些地方用于存储gzip的长度信息。在对固件中第二个elf文件进行提取分析时发现,uboot实际是由第二个elf文件加载的。

TP-Link XDR-5430-V2 研究分享 - 第一章

第二个ELF中的Program Header中可以找到uboot.bin.gz相关的文件大小等各类信息。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

从上图中可以得知,gzip文件的大小是写在第二个ELF的Header信息中的,不过当手动修改ELF Header中的gzip文件大小后烧录Flash引导会导致新的报错,BootLoader在做ELF Header校验时出错(详见第6章节高通Secure boot描述)。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

此时还有另一个方法,就是使用高压缩比的gzip,然后按照原来的FLASH ROM特征,使用x00填充gzip文件,使gzip文件满足Header中的长度要求,后续的FLASH段内容以xFF填充。

在进行了如下操作后烧录至FLASH引导后的日志如下所示,从日志中看至少校验和解压部分应该顺利完成了,但是在Core 0 Frequency, 800MHz后没有能够跳转到我们修改后的uboot执行,这个可能就是压缩算法的差异导致解压后的二进制文件异常。

TP-Link XDR-5430-V2 研究分享 - 第一章

然而在尝试了几个版本,甚至是直接使用qemu调用路由器rootfs中的gzip命令进行压缩都会报同样的错误,那么下一步只能想办法通过模拟执行去分析TP-LInk到底是如何去解压gzip文件的。

0x06 使用Qiling框架模拟执行bootloader

提取Bootloader

通过引导日志我们可以发现TP-Link的bootloader至少有三层,第一层负责引导第二层的uboot,第二层的uboot会对第三层的uboot及rootfs进行校验,确认数据完整性后再移交给第三层的uboot进行系统引导。

那么在进行模拟执行之前,我们首选需要解决的一个问题是,把第一层的bootloader提取出来。再来看一下之前binwalk的信息,我们可以发现flash的头部是一个elf文件。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

直接使用readelf命令可以看到elf header的信息,通过该信息,我们就可以准确提取该elf文件。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

将提取后的文件拖到ida中直接分析(懒得提取也可以直接把整个flash备份拖到ida)。在0x070057d2处可以看到对应的报错日志代码。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

使用Qiling框架进行模拟执行

那么接下来的目的就是通过模拟执行,想办法让程序执行到解压gzip的代码段。先使用如下python脚本,尝试进行模拟执行。

# !/usr/bin/env python3# coding=utf-8import os as oosimport sysimport shutilfrom qiling import *from qiling.const import QL_VERBOSE

def memory_fix(ql, access, addr, size, value):    ql.log.info("[_] Mapping " + str(size) + " bytes at " + hex(addr) + " | access: " + str(access) + " | value: " + str(value))    print(        "[_] Mapping " + str(size) + " bytes at " + hex(addr) + " | access: " + str(access) + " | value: " + str(value))
    ql.mem.map(addr//4096*4096, 4096*4096)    packer = ql.pack32    if ql.archbit == 64:        packer = ql.pack64    ql.mem.write(addr, packer(value))  # memory packing is OS dependant    return

def my_sandbox(path, rootfs):
    ql = Qiling(path,                rootfs=rootfs,                log_file='./xdr5430-qiling.log',                verbose=QL_VERBOSE.DEBUG,                # verbose=QL_VERBOSE.DISASM,                )    # 发现没有mapped的数据就进行map    ql.hook_mem_unmapped(memory_fix)    ql.run()


if __name__ == "__main__":    running_env_path = "./running_env/"    if oos.path.exists(running_env_path):        shutil.rmtree(running_env_path)        oos.mkdir(running_env_path)    else:        oos.mkdir(running_env_path)    shutil.copy("TP_XDR_5430_harked.BIN", running_env_path)    main_app_path = "{}/TP_XDR_5430_harked.BIN".format(running_env_path)
    my_sandbox(path=[main_app_path], rootfs=running_env_path)

直接运行时会出现很多unmapped内存地址以及各类奇怪的报错。

 

TP-Link XDR-5430-V2 研究分享 - 第一章

由于此时我们模拟的是bootloader,因此相当于是在内核态进行执行,很多内存地址其实是一些特殊的寄存器地址或是外部设备地址。

通过启动日志我们可以得知这款路由器使用的处理器是qcom-ipq50xx-mpxx系列,在网上查阅了一些资料后得知具体的型号是高通IPQ5018 Cortex-A53。

在Binwalk分析的FLASH的结果中我们可以看到几个Flattened device tree文件,但是binwalk并没有能够自动提取这几个FDT文件,FDT文件相关的资料

 

TP-Link XDR-5430-V2 研究分享 - 第一章

我们可以使用如下命令手工对FDT文件进行提取并将提取后的dtb文件转成dts文件。

dd if=TP_XDR_5430_XMC25QU128CH10_20210628_104736.BIN of=0xE0200.dtb bs=1 skip=918016 count=2376596
# 提取完整的 ARM OpenWrt FIT (Flattened Image Tree)dtc -I dtb -O dts 0xE0200.dtb >0xE0200.dts
# 提取 ARM OpenWrt qcom-ipq50xx-mpxx device tree blob Qualcomm Technologies, Inc. IPQ5018/AP-MP03.1dd if=TP_XDR_5430_XMC25QU128CH10_20210628_104736.BIN of=0x30940C.dtb bs=1 skip=3183628 count=54170dtc -I dtb -O dts 0x30940C.dtb >0x30940C.dts
# 提取 ARM OpenWrt qcom-ipq50xx-mpxx device tree blob Qualcomm Technologies, Inc. IPQ5018/AP-MP02.1dd if=TP_XDR_5430_XMC25QU128CH10_20210628_104736.BIN of=0x3168B4.dtb bs=1 skip=3238068 count=55681dtc -I dtb -O dts 0x3168B4.dtb >0x3168B4.dts

转换完成的ARM OpenWrt FIT (Flattened Image Tree)文件如下所示,这个文件中包含了3个image,分别是Linux kernel和两个fdt文件, 而这2个fdt文件就是前面我们通过dd命令提取的0x30940C.dts和0x3168B4.dts。

 

TP-Link XDR-5430-V2 研究分享 - 第一章

在dts文件中,我们可以看到CPU相关的一些如UART设备相关的内存基地址。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

通过分析高通CPU的dts文件可以在一定程度上帮助我们分析及模拟执行bootloader的代码,然而在实际模拟中我们还是会遇到很多涉及到主板外部设备的内存地址,这时只能通过在网上搜索近似型号处理器相关的dts描述文件进行查看。最终在相似型号的描述文件中找到了0x4a3000地址应该是高通的sleep counter。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

此时我们可以通过在python脚本中追加如下代码ql.mem.map(0x004a3000, 0x00001000)手动map这个内存地址。同时我们也可以通过debug调试来查访问目标unmapped内存的代码位置。

TP-Link XDR-5430-V2 研究分享 - 第一章

同样如果我们想要查看是哪边对这个内存地址进行了读写操作也可以使用Qiling的hook api进行判断。

接下来,就可以从elf文件的entry_point处0x07000FB0结合模拟执行开始逐步分析。此时我们的线索就是如下所示设备正常情况下的引导日志,通过模拟执行时输出的文本可以初步判断我们执行的情况。

Format: Log Type - Time(microsec) - Message - Optional InfoLog Type: B - Since Boot(Power On Reset),  D - Delta,  S - StatisticS - QC_IMAGE_VERSION_STRING=BOOT.BF.3.3.1.1-00041S - IMAGE_VARIANT_STRING=MAABANBZAS - OEM_IMAGE_VERSION_STRING=WINCE-SDC-101S - Boot Config, 0x000002c1B -       127 - PBL, StartB -      1559 - bootable_media_detect_entry, StartB -      1736 - bootable_media_detect_success, StartB -      1740 - elf_loader_entry, StartB -      2736 - auth_hash_seg_entry, StartB -      3109 - auth_hash_seg_exit, StartB -    147859 - elf_segs_hash_verify_entry, StartB -    202709 - PBL, EndB -    165432 - SBL1, StartB -    226157 - GCC [RstStat:0x10, RstDbg:0x600000] WDog Stat : 0x4B -    235338 - clock_init, StartD -      7228 - clock_init, DeltaB -    243603 - boot_flash_init, StartS - SBL last page flash: 38.S - SBL last page flash: 1.S - SBL last page flash: 0.S - SBL last page flash: 0.S - First  couple:0x26:0x1.S - Second couple:0x0:0x0.D -     17110 - boot_flash_init, DeltaB -    262879 - boot_config_data_table_init, StartD -         0 - boot_config_data_table_init, Delta - (0 Bytes)B -    274744 - Boot Setting :  0x00000618B -    277062 - CDT version:2,Platform ID:8,Major ID:4,Minor ID:0,Subtype:1B -    288194 - sbl1_ddr_set_params, StartB -    294020 - Pre_DDR_clock_init, StartB -    295118 - Pre_DDR_clock_init, EndB -    845978 - Image Load, StartD -    438590 - QSEE Image Loaded, Delta - (383613 Bytes)B -   1285453 - QSEE Execution, StartD -        61 - QSEE Execution, DeltaB -   1292102 - SBL1, EndD -   1126853 - SBL1, DeltaS - Flash Throughput, 4732 KB/s  (383613 Bytes,  81066 us)S - DDR Frequency, 800 MHzS - Core 0 Frequency, 800 MHz

U-Boot 2016.01 (May 26 2021 - 22:32:57 +0800)
DRAM:  smem ram ptable found: ver: 1 len: 4256 MiBUsing default environment
In:    serial@78AF000Out:   serial@78AF000Err:   serial@78AF000machid: 8040001

为了模拟执行输出日志,我们首先需要解决一个问题,就是如何让模拟执行输出日志。因为这些日志实际是通过读写uart接口来实现的,因此我们可以通过定位到具体的打印函数,进行hook来直接输出写入到uart的内容。

通过分析打印文本的交叉引用可以很轻松的定位到0x07009EF0及0x07009C14处的两个打印相关的函数。

TP-Link XDR-5430-V2 研究分享 - 第一章

相关hook代码如下:

def hook_print_07009CF8(ql, *args, **kw):    print_string = ql.mem.string(ql.reg.r0)    print("Hook print(0x07009CF8): {}".format(print_string))    return
ql.hook_address(hook_print_07009CF8, 0x07009CF8)

Hook后就可以在模拟执行时查看如下图所示的uart输出内容了。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

在模拟过程中还会遇到各种如下所示的逻辑问题,从代码来看如果0xE1140处内存数据不满足条件就会一直死循环。

TP-Link XDR-5430-V2 研究分享 - 第一章

此时我们就需要分析整体代码的逻辑分支手动对一些外部设备内存地址进行赋值。

ql.mem.map(0x000E1000, 0x00010000)ql.mem.write(0xE1140, ql.pack32(0x02000000))  # use at 0x7004977

如果长时间程序没有任何反应也没有报错,还可以把verbose级别调到QL_VERBOSE.DISASM,这样就可以查看是否哪里陷入了死循环等情况,会遇到很多如下图所示的情况需要定位并对内存进行处理。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

通过hook unmapped内存,我们可以进一步识别出memcpy(0x070117CC)及内存初始化函数0x07000EAC,在上述两个函数下hook可以帮助我们更好的理解代码逻辑。

通过分析交叉引用结合Qiling调试分析,大致确认了需要执行到的解压gzip函数的调用栈情况如下:

0x07000FB0(Entrypoint)->0x07012030->0x07007AD8->0x07007B2C->0x070098A4 ->0x07007EB4 ->0x07008254 ->0x070085A8 ->0x07005640(zlib_decompress)

接下来就是需要逐步分析业务逻辑和处理各种内存异常。

在漫长的分析、调试和内存patch后,最终在0x70120E6处遇到了一个无法处理的问题。

通过调试分析sub_7011FE4得知该函数的参数r0实际是程序自身ELF文件加载时的第一个参数,然而ELF加载时的代码在SOC芯片中,我们无法获取,因此在无法知晓这部分代码实际输入的情况下也就很难进行处理,此外这部分代码也会影响后续的业务处理逻辑,强行进行patch绕过这段代码也会对实际分析产生影响。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

通过Qiling模拟执行,去分析bootloader的话,会遇到很多复杂的问题,需要投入大量的时候去分析和处理,目前时间有限,只能暂时放一下,等后续有时间时再进一步分析该bootloader的相关代码。

0x07 总结

通过对TP-Link XDR-5430-V2路由器进行研究后,对高通的固件格式和引导流程有了一定得了解,下图为高通Secure boot的描述。

从描述中看,Secure boot会首先加载Primary BootLoader (PBL) 进行预处理后加载Secondary BootLoader (SBL) 对下一层bootloader进行校验并最终跳转执行。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

如下图所示,高通信任链中会对所有ELF image Header及segment内容进行校验。

TP-Link XDR-5430-V2 研究分享 - 第一章

 

按照高通的信任链,我们通过修改rootfs和uboot的方法应该是无法成功的,但是在实际测试中我们在未修改ELF Header的情况下修改gzip信息会报zlib报错,这应该代表已经通过了高通的信任链校验,这个显然并不科学,其中的一种可能就是TP-LInk 并未严格按照高通的信任链去开发,具体原因如何,只能等后续有时间的情况下进一步的分析来确认。

在这次研究过程中,使用Qiling框架模拟Bootloader也面临了很多的挑战,Qiling框架虽然有模拟执行的能力,但框架自身并没有模拟外部设备(时钟、FLASH存储等)的功能。

此外分析bootloader的难度和复杂度也较高,需要投入大量的时间和精力去研究处理不同CPU及外部设备之间是如何工作的,在缺乏描述文件的情况下这部分的分析会很困难。相信这些问题在积累分析各类bootloader的经验后,处理起来应该会相对更轻松一些。

0x08 引用

  • Qcom IPQ40xx Device Tree Overview
http://wiki.dreamrunner.org/public_html/Embedded-System/Qcom-ipq40xx/ipq40xx-device-tree-overview.html
  • 高通入门缩写词意思 – 知乎
https://zhuanlan.zhihu.com/p/62247840
  • Msm8998 Cold Boot Flow – Evsio0n
https://evsio0n.com/archives/307/
  • 高通平台Android源码bootloader分析之sbl1(一) | Andy.Lee’s Blog
http://huaqianlee.github.io/2015/08/15/Android/%E9%AB%98%E9%80%9A%E5%B9%B3%E5%8F%B0Android%E6%BA%90%E7%A0%81bootloader%E5%88%86%E6%9E%90%E4%B9%8Bsbl1-%E4%B8%80/
  • Analysis of Qualcomm Secure Boot Chains
https://blog.quarkslab.com/analysis-of-qualcomm-secure-boot-chains.html

银河实验室

TP-Link XDR-5430-V2 研究分享 - 第一章

银河实验室(GalaxyLab)是平安集团信息安全部下一个相对独立的安全实验室,主要从事安全技术研究和安全测试工作。团队内现在覆盖逆向、物联网、Web、Android、iOS、云平台区块链安全等多个安全方向。
官网:http://galaxylab.pingan.com.cn/

TP-Link XDR-5430-V2 研究分享 - 第一章

#PSRC 2021年度评选# 四大奖项正在评选中,丰厚奖金等你来拿!

往期回顾

技术

【平安CTF赛题】解题思路总结

技术

D-Link 816-A2 路由器研究分享

技术

使用Ghidra P-Code对OLLVM控制流平坦化进行反混淆

技术

家用路由器D-LINK DIR-81漏洞挖掘实例分析

公告

防守方视角看漏扫——手把手教你定制自己的漏扫框架(POC部分)

TP-Link XDR-5430-V2 研究分享 - 第一章
TP-Link XDR-5430-V2 研究分享 - 第一章
TP-Link XDR-5430-V2 研究分享 - 第一章

长按识别二维码关注我们

微信号:PSRC_Team

TP-Link XDR-5430-V2 研究分享 - 第一章

球分享

TP-Link XDR-5430-V2 研究分享 - 第一章

球点赞

TP-Link XDR-5430-V2 研究分享 - 第一章

球在看

 

 

原文始发于微信公众号(平安集团安全应急响应中心):TP-Link XDR-5430-V2 研究分享 – 第一章

版权声明:admin 发表于 2021年12月23日 上午9:59。
转载请注明:TP-Link XDR-5430-V2 研究分享 – 第一章 | CTF导航

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
暂无评论...