原文始发于京东探索研究院信息安全实验室:使用Binary Ninja进行IoT设备漏洞挖掘
Binary Ninja是一款简单易用的二进制分析平台,它提供了丰富的API接口,可以帮助安全研究人员进行自动化的分析。
最近我在进行各种IoT设备的安全研究工作,这些主要是一些路由器、NAS、NVR、IP摄像头之类的产品。通常情况下我会首先尝试去在网上下载这些产品的固件,然后使用binwalk进行解包分析,他们绝大多数都是Linux嵌入式系统,获得了它们的rootfs以后,会对里面的ELF程序进行静态分析。
根据经验,我会重点关注厂商在实现一些特定协议时自己写的代码,如http 、cgi、upnp、netatalk、sslvpn等。我下面列出了静态分析时自己会审计的一些漏洞原型,虽然下面列出的危险函数在现代化的软件开发中需要被禁止使用,但由于历史原因,很多IoT设备上还是会遗留这些古老的代码,存在被攻击者利用的机会。
在理清楚需要做的事情以后,漏洞挖掘的工作就变得比较重复。最费时的任务就是依次去审计每个函数被引用时的上下文,然后去判断:
- 当前的函数调用是否安全(尤其需要检查是否存在缓冲区溢出的可能)
- 攻击者的输入是否可以到达这个路径
如果以上两个条件都满足,那么很有可能这就是一个可被利用的漏洞。不过,一个ELF比较复杂时,这些高危函数的调用关系会变得异常多,依靠人力去肉眼审计会变得相当费力。比如厂商的开发人员特别喜欢在cgi里直接调用system函数来实现某些任务,比如重启机器、检查更新等,绝大多数system函数的参数都不是攻击者能够控制的,那么如何能够快速找到那么可以被攻击者控制进行命令注入的漏洞呢?
这显然涉及到了静态分析的范畴,我可以根据控制流、数据流对整个程序进行比较完备的分析,但是这需要耗费巨大的算力,很有可能导致分析时间呈现指数级的增长。作为一名漏洞挖掘人员,我更关心如何找到可以利用的漏洞,而不是绝对地依靠计算机去自动化发现bug。我可以接受自动化分析产生的误报,只要是比起我在IDA里面按下X键进行人肉扫描方便,那么这就可以被认为是自动化分析。至于如何降低它的误报率,可以先把这个东西做出来,再考虑这个问题。
经过调查,我尝试了使用angr、IDAPython、Ghrida进行分析工作,但结果并不太满意(也许是我对这些软件的使用并不太熟悉),最后我使用Binary Ninja完成了一个简单的自动化分析工具
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
from binaryninja import * from enum import Enum, auto class Error(Enum): FORMAT_UNCONSTANT = auto() FORMAT_OVERFLOW = auto() STACKOVERFLOW = auto() COMMANDINJECT = auto() def check_system(bv,symbol="system"): addr = get_function_addr(bv,symbol) if addr == None: return [] refs = bv.get_code_refs(addr) ret = [] for ref in refs: func = ref.function cmd = func.get_parameter_at(ref.address,None,0) if is_constant(cmd): continue ret.append((symbol,func.name,ref.address,Error.COMMANDINJECT)) return ret def get_function_addr(bv,symbol): syms = [] if symbol in bv.symbols: syms = bv.symbols[symbol] if "_%s"%symbol in bv.symbols: syms = bv.symbols["_%s"%symbol] for i in syms: if "mips32" == bv.arch.name or "mipsel32" == bv.arch.name: if i.type == SymbolType.ImportAddressSymbol: return i.address else: if i.type == SymbolType.ImportedFunctionSymbol: return i.address return None def is_constant(a): return a.type == RegisterValueType.ConstantPointerValue or a.type == RegisterValueType.ConstantValue if __name__ == "__main__": input_file = sys.argv[1] if os.path.exists(input_file + ".bndb"): bv = open_view(input_file + ".bndb") else: bv = open_view(input_file) settings = SaveSettings() bv.create_database(input_file + ".bndb", None, settings) ret = check_system(bv) for i in ret: print("%-15s function: %-20s addr: 0x%x %s"%(i[0],i[1],i[2],i[3])) |
上面的代码的主要逻辑在check_system函数,我这里只是把调用system时参数不为常量的调用过滤掉,最后将所有的结果输出出来。这样就能大大减少我需要检查的system调用数量。同样,我还可以通过Binary Ninja的API检查sprintf的调用是否存在buffer越界的风险。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
def check_sprintf(bv,symbol = "sprintf"): addr = get_function_addr(bv,symbol) if addr == None: return [] refs = bv.get_code_refs(addr) ret = [] for ref in refs: func = ref.function fmt = func.get_parameter_at(ref.address,None,1) if not is_constant(fmt): ret.append((symbol,func.name,ref.address,Error.FORMAT_UNCONSTANT)) continue asc = bv.get_ascii_string_at(fmt.value,min_length = 2) if asc == None: continue fmt_value = asc.value cidx = 0 arg_idx = 1 while True: idx = fmt_value.find("%",cidx) if idx < 0: break arg_idx += 1 cidx = idx + 1 if fmt_value[idx:].startswith("%s"): arg = func.get_parameter_at(ref.address,None,arg_idx) if not is_constant(arg): ret.append((symbol,func.name,ref.address,Error.FORMAT_OVERFLOW)) return ret |
上述这些漏洞分析的误报率仍然会比较高,根据经验,我往往会额外地添加一些看起来不是特别正确的约束条件:
- strcpy strcat
要求dst参数为栈空间(更有可能出现stackoverflow) - system
要求调用system的同时调用snprintf或sprintf(更有可能发生命令注入) - sscanf
要求调用sscanf的同时没有调用fopen(开发者会使用sscanf来读取配置文件里的信息)
通过上述的约束,自动化分析输出的结果已经大体上可以被作为静态分析的参考来使用了,不过想要进行批量化的固件分析,还需要再进一步地优化。目前我正在研究如何通过使用Binary Ninja的中间语言进行比较完整的数据流分析,进一步降低误报率,如果有其他的更新我会在这里进行同步,也欢迎有兴趣的同学来和我联系。
参考资料
- https://www.zerodayinitiative.com/blog/2022/2/14/static-taint-analysis-using-binary-ninja-a-case-study-of-mysql-cluster-vulnerabilities
- https://blog.trailofbits.com/2017/01/31/breaking-down-binary-ninjas-low-level-il/
- https://blog.trailofbits.com/2018/04/04/vulnerability-modeling-with-binary-ninja/