『ROP Emporium』ret2win
0x01 ret2win32
x86架构的ret2text,非常简单,程序有栈溢出,没有canary保护,所以只要溢出覆盖ebp后,将ret地址覆盖为ret2win函数的地址即可。
1 | from pwn import * |
0x02 ret2win
x86_64架构的ret2text,一样的非常简单,栈溢出完了之后覆盖ret地址为后门地址即可。但是这里需要注意一个问题,如果直接选取ret2win的函数地址,puts函数可以被正常执行,但是发现flag不会显示出来。不用调试也能知道这是64位下栈平衡的问题,并且前面的函数可以执行但唯独system执行不了的话,是卡在了do_system这个函数,这里不展开叙述。解决方案是,跳过ret2win函数中push rbp的语句即可。
1 | from pwn import * |
0x03 ret2win_armv5
虽然不是第一次见到异构pwn题,但是是第一次做,所以这里记录一下基本的环境配置步骤和arm汇编相关的知识,但不会深入展开,主打一个够用就行。
环境配置
1 | sudo apt-get update |
运行程序
如果想要在终端运行arm架构的程序,那就用以下命令
1 | qemu-arm-static ./your_exec_file |
如果找不到动态链接库,就加个-L /usr/mipsel-linux-gnu/
在中间就行,调试同理。
调试程序
如果想要调试arm架构的程序,使用以下命令
1 | qemu-arm-static -g 9999 ./your_exec_file |
然后打开另一个终端,输入
1 | gdb-multiarch -q ./your_exec_file |
上面的9999是端口,数字可以自己定。第三行命令是为了让gdb找到动态链接库。如果是aarch64架构的,也是一样的操作,换一下动态链接库路径为/usr/aarch64-linux-gnu/lib/即可。
题目分析
首先先看看arm寄存器方面和x86的不同之处。
- 32位的arm有13个通用寄存器,分别是R0-R12。
- R0在常规操作中可用于存储临时值,也可以用于存储函数的第一个参数或返回结果。
- 在ARM架构中约定指定函数前四个参数存储在R0~R3寄存器中。
- R7寄存器在函数调用中负责存储系统调用号。
- R11寄存器,又称FP,可以用来记录回溯信息,也可以当做局部变量来使用。
- R13是栈指针寄存器,又称SP,相当于esp。
- R14为链接寄存器,又称LR,用于保存调用函数的下一条指令地址,用于被调用函数(子函数)结束工作后返回调用函数(父函数)。有点像ret地址。
- R15为程序计数器,又称PC,类似于X86架构下的EIP寄存器负责保存目标地址,与x86不同的点在于PC在ARM状态下存储当前指令+4的地址。这个寄存器可读可写,对PC进行写操作可以改变程序执行流,而且是立马就变。
- 还有一些特殊的标志寄存器,这里不做介绍。
现在我们来看代码。首先把LR和R11压入栈,然后将SP+4存到R11,SP-0x20开辟栈空间。这和x86调用函数如出一辙。注意此时R11就指向返回地址了。
调用read的时候把第一个参数放到了R0,第二个参数是R11-0x24,放到了R1,第三个参数在R2。从这里可以确认的一点是我们输入的地方距离存放LR的地方隔了0x24。所以填充0x24padding再写个ret2win的地址,我们就算是利用栈溢出劫持了程序执行流。
函数结束之后先将R11-4赋给了SP,再分别将R11和PC出栈。那么这时,PC寄存器里地址就是我们写入的ret2win的函数地址。
调试
因为是第一次做arm的pwn题,我们还是详细调试一下看看,加深理解。首先我们开启gdb-multiarch之后,连接qemu端口,设置好动态链接库。之后断点在pwnme函数,再往后就单步执行看栈和寄存器变化就可以了。
EXP
1 | from pwn import * |
0x04 ret2win_mipsel
也是第一次接触的mips的pwn,和arm一样,先讲环境配置。
环境配置
1 | sudo apt install qemu-user |
运行程序
1 | qemu-mipsel-static ./your_exec_file |
如果找不到动态链接库,就加个-L /usr/mipsel-linux-gnu/
在中间就行,调试同理。
调试程序
1 | qemu-mipsel-static -g 9999 ./your_exec_file |
然后打开另一个终端,输入
1 | gdb-multiarch -q ./your_exec_file |
和arm调试是一样的。
题目分析
mips的指令和x86、arm的指令长得差很远。先来看一些基础的知识。
- MIPS32 架构中是没有 EBP 寄存器的,程序函数调用的时候是将当前栈指针向下移动 n 比特到该函数的 stack frame 存储组空间,函数返回的时候再加上偏移量恢复栈
- 传参过程中,前四个参数a0−a3,多余的会保存在调用函数的预留的栈顶空间内
- MIPS 调用函数时会把函数的返回地址直接存入 $RA 寄存器
可以注意到函数初始时RA寄存器的内容被入栈到了距离SP寄存器0x38+4的位置,函数退出时RA出栈。所以我们只要栈溢出到SP+0x60处写入ret2win函数地址即可成功劫持程序执行流。
EXP
1 | from pwn import * |