赤道企鹅的博客 | Eqqie Blog - 2022年12月
type: channel
link: https://eqqie.cn/index.php/2022/12/
description: Binary Security Researcher, IoT/NFV Sec, Static Program Analysis, Fuzzing Test, CTFer
items: [DataCon 2022] 大数据安全分析竞赛 物联网赛道writeup

[DataCon 2022] 大数据安全分析竞赛 物联网赛道writeup
type: item
date: 2022-12-12T23:25:00+08:00
description: 一、固件基地址识别1.1 题目要求1.2 思路1.3 实现1.3.1 相关工具1.3.2 脚本二、函数符号恢复2.1 题目要求2.2 思路2.2.1 Binary Match2.2.2 Emulated Match2.2.2.1 概要2.3 实现2.3.1 基本架构2.3.2 BinaryMatch 类2.3.3 Solver 类2.3.4 减少 False Positive 思路2.3.4.1 巧妙设计测试用例2.3.4.2 增加额外的检查2.3.5 运行效果2.4 不足与改进三、整数溢出检测3.1 题目要求3.2 思路3.3 实现3.3.1 修改 CWE190 Checker3.3.1.1 查找到足够的 Sink3.3.1.2 使用 High-Pcode3.3.1.3 污点指令识别3.3.1.4 CALL 指令参数检查3.3.2 修改 PcodeVisitor3.3.2.1 标记潜在整数溢出指令3.3.2.2 检查四种运算指令的整数溢出3.3.3 运行效果3.4 不足与改进无经验新手队伍的writeup，轻喷一、固件基地址识别1.1 题目要求1.2 思路一般对于一个完整的 RTOS 设备固件而言，通常可以通过解压固件包并在某个偏移上搜索到内核加载基址的信息，参考：[RTOS] 基于VxWorks的TP-Link路由器固件的通用解压与修复思路 。但是赛题1给的是若干个不同厂商工具链编译的 RTOS 内核 Image，无法直接搜索到基址信息；内核 Image 中虽然没有基址信息，但是有很多的绝对地址指针（pointer）和 ASCII 字符串（string），而字符串相对于 Image Base 的偏移量是固定的，所以只有选取正确的基址值时，指针减去基址才能得到正确的 ASCII 字符串偏移；即需要满足如下关系：pointer_value - image_base = string_offset所以实现方式大致为：检索所有的字符串信息，并搜集string_offset按照目标架构的size_t长度搜集所有的pointer_value按照一定步长遍历image_base，计算所有image_base 取值下string_offset的正确数量，并统计出正确数量最多的前几个候选image_base输出在此基础上可以增加一些优化措施，比如可以像 rbasefind2 一样通过比较子字符串差异以获得image_base候选值，这样就不需要从头遍历所有的image_base，速度更快1.3 实现1.3.1 相关工具基于 soyersoyer/basefind2 和 sgayou/rbasefind 项目以及 ReFirmLabs/binwalk 工具实现rbasefind 主要提供了3个控制参数：搜索步长，最小有效字符串长度以及端序binwalk 用于通过指令比较的方式检查 Image 文件的架构和端序通过多次调整步长和字符串长度参数进行 rbasefind，可以得到可信度最高的 Image Base 值，将其作为答案提交1.3.2 脚本import os import sys import subprocess chall_1_data_path = "../dataset/1" file_list = os.listdir(chall_1_data_path) vxworks = {15, 21, 36, 37, 44, 45, 49} ecos = {4, 2, 30, 49, 18, 45, 33, 5, 20, 32, 43} answer = {} def get_default_answer(data_i): if int(data_i) in vxworks: return hex(0x40205000) elif int(data_i) in ecos: return hex(0x80040000) else: return hex(0x80000000) def check_endian(path): out, err = subprocess.Popen( f"binwalk -Y \'{path}\'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE).communicate() # print(out) if b", little endian, " in out: return "little" elif b", big endian, " in out: return "big" else: return "unknown" if __name__ == "__main__": #file_list = ["2", "5"] cnt =…