栈迁移简介
当程序输入不足以构造rop链,但是又能覆盖rbp(ebp)就可以考虑栈迁移,让程序换个地方执行rop链
原理
leave指令
leave 相当于
- mov esp ebp
- pop ebp
1 | 1. 栈初始状态 |
1 | 2. 执行 mov esp ebp |
1 | 3. 执行 pop ebp |
具体实现
填充完buf后,覆盖ebp,让ebp处的值位新的地址
在0x0003的位置填上所要跳转的地址,再次执行leave,则程序就会跳转到目标地址
注意栈迁移时第一次的return addr为下一次leave的地址
跳转完成后,新地址的下一处地址即为esp的位置
另外
如果没有PIE保护或者可以直到PIEbase的话可以让栈迁移到bss段上,修改返回地址为read再次栈溢出读到bss段上,再次leave即可
实例
题目来源:BUUCTF在线评测 (buuoj.cn) ciscn_2019_es_2
分析
main函数没什么好看的,初始化后就来到了这里
程序有两次read,显然0x30的长度不足以构造rop链,因此可以考虑栈迁移
首先第一个read把s填满后让printf读,只要没有\x00,printf就会一直读下去,因此可以让printf读出ebp泄露栈地址
第二个read就可以考虑栈迁移了,这里把ROP写到s中,也就是栈上,栈迁移后程序继续在栈上执行
第二次的payload
1 | payload=p32(1)+p32(0x8048400)+p32(0)+p32(addr-0x18)+b'/bin/sh\x00' |
首先p32(1)是填充垃圾数据,让esp跳转到这里之后pop ebp让esp指向第二个p32()也就是system
p(0)是因为32位下system和参数间要填充四个字节
后面的addr-0x18是为了构造指向binsh的指针,因为system的参数要是指针才可以
之后填充s,覆盖ebp让ebp的地址为s里,后面返回地址为下一个leave ret处
exp
1 | from pwn import* |