0x01
如果给的二进制文件采取的是静态连接,就可以直接通过ROPgadget 来直接构造ropchain
命令为 ROPgadget --binary rop(文件名) --ropchain,比如hackme中pwn的rop那道题目就是这样,直接可以用这个命令生成rop链从而直接getshell,不过需要注意的是如果用这种方法则需要在py脚本中加上
from struct import pack
其实通过看它生成的ROP链大概也是能猜出是用了systemcall的方法
这个方法的好处是可以一梭子搞定exp不用自己想,但是局限性太大了,基本上只有弱智题才会这样用静态链接的,所以常规的操作还是自己撸代码好一点,下面就是自己用systemcall方法写的exp:
0x02
如果用rop进行systemcall的操作,那必定要用到系统中断号:int 0x80,所以我们在想要用这种方法的时候,先用ROPgadget查找一下有没有这个gadget,如果没有果断放弃,另想其他操作
0x03
最常规的rop,就是利用像pop。。。。ret这样的gadget都是用来写入数据到寄存器的
pop reg + ret
pop reg + pop reg + ret
pop reg + pop reg + pop reg + ret
写入数据到内存:
mov [reg],reg + ret
mov [reg+xxx],reg + ret
调用函数,跳转函数
jmp xxx + ret
call xxx + ret
int 0x08
leave + ret(在进行栈迁移的时候会用到的gadget)
算数,逻辑运算
add reg,reg + ret
and reg,reg + ret
xor reg1,reg2 + ret#reg1与reg2进行异或运算,结果存入reg1中
xchg reg1,reg2 + ret#交换reg1和reg2的内容
注意异或运算xor有个有趣的地方:
1、如果xor自身则是将寄存器清空为零,
2、仅仅xor就可以实现reg间的写入操作:
用一个xor自身清空寄存器A,接着让寄存器B去xor寄存器A,把结果存在寄存器A,就相当于把B赋值給A,这样不需要mov或者pop就实现了寄存器间的值传递
0x04
使用one_gadget 工具可以一梭子getshell
one-gadget RCE 是在 libc 中存在的一些执行 execve('/bin/sh', NULL, NULL)
的片段。当我们知道 libc 的版本,并且可以通过信息泄露得到 libc 的基址,则可以通过控制 EIP 执行该 gadget 来获得 shell。这个方法的优点是不需要控制调用函数的参数,在 64 位程序中,也就是 rdi、rsi、rdx 等寄存器的值。
可以使用工具 one_gadget 很方便地查找 one-gadget
如果给出了lib文件,就可以直接用命令:one_gagdet [libc库]
找到相对应的一梭子getshell函数
如果没有给出,则需要通过泄漏出的libc基址偏移,然后在https://libc.blukat.me/?q=puts%3Ae140&l=libc6-i386_2.23-0ubuntu10_amd64中找到对应的libc版本,从而使用one_gadget
0x05
libc_scu_init gadget
有的时候在64位的程序中,我们需要控制rdi,rsi,rdx寄存器去控制函数的参数,尤其是一些像write(),read()这样的三参数函数,我们就需要用到一波特殊的gadget:
libc_csu_init中我们主要利用了以下寄存器
利用尾部代码控制了rbx,rbp,r12,r13,r14,r15。
利用中间部分的代码控制了rdx,rsi,edi。
通常这样构造的rop链也特别的长,所以一般写成封装函数,调用的时候比较方便
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'a' * 0x80 + fakeebp #填充垃圾字符串
payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(
r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)
payload += 'a' * 0x38
payload += p64(last)
sh.send(payload)
sleep(1)
0x06
srop
如果想进行syscall的操作,但是又没有找到int 0x80 的系统中断gadget,那么就可能是需要用到srop这个操作了:
简单的说,srop是利用一个sigreturn系统调用进行操作的,进程在调用sigreturn的时候会保存现场(各个寄存器的值)到用户的栈里面,执行完后,又会从栈里面取出之前存入的那一份数据,从而恢复各个寄存器的值,这里我们可以构造一个假的数据扔进栈,然后再调用sigreturn,从而达到操控程序执行流程的目的
32位的sigreturn的调用号为77,64位的系统调用号为15。
原理理解不算很难,重在于实际的操作,下面一些的学习博客:
http://www.freebuf.com/articles/network/87447.html
https://ctf-wiki.github.io/ctf-wiki/pwn/stackoverflow/advanced_rop/#srop
网友评论