ROP骚操作总结

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