Use After Free

题目:Hitcon Training lab10

文件信息

检查文件安全性:

1
2
3
4
5
6
7
$ checksec ./hacknote
[*] '/mnt/d/program/ctf/ctf-wiki/useafterfree/hacknote'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)

程序没有开启地址随机化,有栈保护和堆栈不可执行。

程序逻辑

这个可执行文件中存在函数的调式信息,main 函数就是打印菜单,然后执行几个选项的常规套路。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
int menu()
{
  puts("----------------------");
  puts("       HackNote       ");
  puts("----------------------");
  puts(" 1. Add note          ");
  puts(" 2. Delete note       ");
  puts(" 3. Print note        ");
  puts(" 4. Exit              ");
  puts("----------------------");
  return printf("Your choice :");
}

可见输出四个数字对应的功能。

add_note

简单的分配地址的功能。通过阅读源码实现逻辑,存储结构大致如下:

1
2
3
4
5
6
struct Note{
    void (*func_ptr)(int);	// 指向了 `print_note_content` 的函数地址
    char *buffer;			// 用户通过指定一个 `size` 读入的内容
};

Note notlist[5];

del_note

删除笔记:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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(*((void **)notelist[v1] + 1));
    free(notelist[v1]);
    puts("Success");
  }
  return __readgsdword(0x14u) ^ v3;
}

需要注意的是,这里的笔记内存被释放后,并没有清空指针。这有可能导致 UAF 漏洞或 Double Free 漏洞。

因为没有清空指针会导致的 UAF 漏洞即在此处发生。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
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] )
    (*(void (__cdecl **)(void *))notelist[v1])(notelist[v1]);
  return __readgsdword(0x14u) ^ v3;
}

它的程序逻辑是读入一个 index,调用对应位置第一个内容存储的 print_note_content 函数,将内容打印出来。

magic

比较有意思的是从函数表中还可以看到这样一个没有被调用的函数:

1
2
3
4
int magic()
{
  return system("cat flag");
}

据此我们就大概可以猜测到,破解的思路应该是执行 magic 函数即可。

漏洞利用

根据之前的阅读源码很显然这是一个 UAF 漏洞。我们