ctfshow:堆前置pwn145~pwn149
本文最后更新于150 天前,其中的信息可能已经过时,如有错误请发送邮件到506742773@qq.com

1、pwn145

没钱了孩子们,只能本地玩玩了

继续学习堆,这道题的提示是:glibc的一种分配规则

首先查查glibc分配规则是什么:

glibc 堆内存整体架构

glibc 使用 ptmalloc2 管理堆,基于 bins + tcache(2.26+)

主要结构:

malloc → 从 tcache 拿 → fastbin → smallbin → unsortedbin → largebin → top chunk

free → 先 tcache → fastbin → unsortedbin → smallbin/largebin

各种 Bin 的作用与特点
tcache(2.26+ 新特性)
  • 每个线程有一组 tcache bins
  • 每个 bin 最多存 7 个 chunk
  • 链表为 单链表
  • free 时优先放入 tcache
  • malloc 时优先从 tcache 拿

用途:加速频繁的小块分配
安全性:比 fastbin 更弱,是很多攻击(tcache poisoning)的重点。

fastbin
  • chunk size 范围:0x20 ~ 0x70(不同版本略不同)
  • 单链表
  • FREE 时不合并(no consolidation)
  • MALLOC 时从对应的 fastbin 链表取第一个

主要漏洞利用点:

  • double free
  • fastbin dup → 任意写
unsorted bin
  • free 的大多数 chunk 最开始都会进入 unsorted bin
  • 一个双向链表
  • 在下一次 malloc 时会分割
  • 经常用于 leak libc 地址(因为里面的 bk / fd 带着 libc 链表指针)

smallbin

  • chunk size:固定大小(0x20 ~ 0x400 一些区间)
  • 双向循环链表
  • free 时放入 smallbin
  • malloc 时必须 EXACT SIZE 匹配
largebin
  • chunk 比 smallbin 大
  • 按大小排序
  • malloc 时会在 bin 中找“最合适块”(best fit)

这部分一般用于:

  • unlink attack
  • largebin attack
glibc malloc 的核心流程 :
MALLOC(size) 简化流程:
  1. tcache 有?直接取
  2. fastbin 有?取
  3. unsorted bin 找可用 chunk
  4. smallbin / largebin 查找
  5. 不够则从 top chunk 切割
  6. top chunk 不够 → 触发 brk 或 mmap
glibc free 流程 :
FREE(ptr) 流程:
  1. 目标 chunk 大小 ≤ tcache?
    放入 tcache
  2. fastbin 大小?
    → 放入 fastbin
  3. 否则:
    → 放入 unsorted bin
    → 可能和前后 chunk 合并(consolidation)

合并行为:

  • fastbin chunk 不合并
  • small/large 会合并
  • 通常放入 unsorted bin 后会合并,或等待下一次 malloc 再处理

大概就记录这些,然后开始分析这个程序

checksec:

64位开了canary和NX,运行看看情况(自己在本地写了flag)

是一个演示过程,演示完输入sh就可以查看flag

faetong@faetong-virtual-machine:~/pwnit$ ./pwn145
    ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                           
  ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                           
 ██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
 ██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
 ██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██ 
  ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀ 
    ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
    * *************************************                           
    * Classify: CTFshow --- PWN --- 入门                              
    * Type  : Heap_Exploitation                                       
    * Site  : https://ctf.show/                                       
    * Hint  : Why it can UAF(use after free) ?                        
    * *************************************                           
演示glibc 的分配机制
glibc 使用首次适应算法选择空闲的堆块
如果有一个空闲堆块且足够大,那么 malloc 将选择它
如果存在 use-after-free 的情况那可以利用这一特性
首先申请两个比较大的 chunk
第一个 a = malloc(0x512) 在: 0x2a80b2a0
第二个 b = malloc(0x256) 在: 0x2a80b7c0
我们可以继续分配它
现在我们把 "AAAAAAAA" 这个字符串写到 a 那里 
第一次申请的 0x2a80b2a0 指向 AAAAAAAA
接下来 free 掉第一个...
接下来只要我们申请一块小于 0x512 的 chunk,那就会分配到原本 a 那里: 0x2a80b2a0
第三次 c = malloc(0x500) 在: 0x2a80b2a0
我们这次往里写一串 "CCCCCCCC" 到刚申请的 c 中
第三次申请的 c 0x2a80b2a0 指向 CCCCCCCC
第一次申请的 a 0x2a80b2a0 指向 CCCCCCCC
可以看到,虽然我们刚刚看的是 a 的,但它的内容却是 "CCCCCCCC"
sh
$ ls
flag  pwn145
$ cat flag
flag{Inoue_Takina}
$ 

演示的是一种glibc分配规则,纯演示,直接下一道题吧

2、pwn146(为什么会产生UAF漏洞?)

题目提示:为什么会产生UAF漏洞?

UAF漏洞:

英文的话更好理解UAF是什么意思: Use-After-Free

概念:程序 已经通过 free/delete 释放了某块堆内存,但之后仍然通过 悬挂指针(dangling pointer) 对这块已释放的内存进行访问(读、写或执行)。

大白话讲就是: 内存已经被 free 掉了,程序却还继续把它当正常数据来用。

人机给了一个幽默的: 就像你把出租屋退租了,但你还偷偷拿着钥匙进去住一样(危险得很)。

通常出现的情况

free之后没有把指针清空

char *p = malloc(0x20);
free(p);
printf("%s\n", p); // p 还在用,炸!
//这里 p 指向的内存已经被标记为可重用,但程序还以为它有效。


一个对象被多出引用

Node* a = malloc(sizeof(Node));
Node* b = a;
free(a);
b->value = 123; // b 还在写,其实对象已经 free

接下来看看这个题吧

checksec:

开了canary和nx

运行一下


结合反汇编:

void __cdecl demo()
{
  ptr *p1; // [rsp+0h] [rbp-20h]
  ptr *p2; // [rsp+8h] [rbp-18h]
  char c[8]; // [rsp+10h] [rbp-10h] BYREF
  unsigned __int64 v3; // [rsp+18h] [rbp-8h]

  v3 = __readfsqword(0x28u);
  printf(&format_);
  p1 = (ptr *)malloc(0x20u);
  printf(aP1, p1);
  p1[1] = (ptr)Printf;
  puts(&s_);
  p1[1]("Hello CTFshow\n\n");
  puts(aFree_0);
  free(p1);
  puts(&s__0);
  p1[1]("Hello CTFshow again\n");
  puts(&s__1);
  p2 = (ptr *)malloc(0x20u);
  printf(aP2, p2);
  printf(aP1, p1);
  puts(&s__2);
  puts("Then get the flag && enjoy it !\n");
  p2[1] = (ptr)demoflag;
  putchar(36);
  __isoc99_scanf("%2s", c);
  p1[1](c);
}


开始是分配了堆空间->p1[1]=printf->让p1[1]输出Hello CTFshow->释放p1但并没有置零,其实后面应该置零之后仍然可以打印Hello CTFshow

    ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                           
  ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                           
 ██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
 ██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
 ██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██ 
  ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀ 
    ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
    * *************************************                           
    * Classify: CTFshow --- PWN --- 入门                              
    * Type  : Heap_Exploitation                                       
    * Site  : https://ctf.show/                                       
    * Hint  : Why it can UAF(use after free) ?                        
    * *************************************                           
申请0x20大小的内存p1 的地址: 0x13ba010
把p1[1]赋值为Printf函数,然后打印出"Hello CTFshow"
Hello CTFshow

free 掉 p1
因为并没有置为null,所以p1[1]仍然是Printf函数,仍然可以输出打印了"Hello CTFshow again"
Hello CTFshow again
接下来再去malloc一个p2,会把释放掉的p1给分配出来,可以看到他俩是同一地址的
p2 的地址: 0x13ba010
p1 的地址: 0x13ba010
然后把p2[1]给改成demoflag也就是system函数

Then get the flag && enjoy it !


调试看看,首先执行到malloc


我们创建的是那个大小为0x30的chunk,执行完call free


看到有一个free chunk

但是到这儿程序段错误了,只能看看师傅们的调试了

推荐文章:https://blog.csdn.net/2502_91269216/article/details/146376715?fromshare=blogdetail&sharetype=blogdetail&sharerId=146376715&sharerefer=PC&sharesource=Yilanchia&sharefrom=from_link


3、pwn147

提示是fastbin_dup

我们来看看checksec


然后运行一下程序

faetong@faetong-virtual-machine:~/pwnit$ nc pwn.challenge.ctf.show 28225
    ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                           
  ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                           
 ██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
 ██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
 ██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██ 
  ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀ 
    ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
    * *************************************                           
    * Classify: CTFshow --- PWN --- 入门                              
    * Type  : Heap_Exploitation                                       
    * Site  : https://ctf.show/                                       
    * Hint  : Fastbin_dup -- Double free                              
    * *************************************                           
演示 fastbin 的 double free
首先申请 3 个 chunk
第一个 malloc(8): 0xd08010
第二个 malloc(8): 0xd08030
第三个 malloc(8): 0xd08050
free 掉第一个
当我们再次 free 0xd08010 的时候, 程序将会崩溃因为 0xd08010 在 free 链表的第一个位置上
我们先 free 0xd08030.
现在我们就可以再次 free 0xd08010 了, 因为他现在不在 free 链表的第一个位置上
现在空闲链表是这样的 [ 0xd08010, 0xd08030, 0xd08010 ]. 如果我们 malloc 三次, 我们会得到两次 0xd08010 
第一次 malloc(8): 0xd08010
第二次 malloc(8): 0xd08030
第三次 malloc(8): 0xd08010
$


演示的是fastbin的double free,其实之前遇到过,通过演示,double free就变得直观了。

sh
ls
cat flag


我们来试着调试一下,先看看代码

void __cdecl demo()
{
  char *AAAAAAAA; // [rsp+0h] [rbp-30h]
  char *b; // [rsp+8h] [rbp-28h]
  char *c; // [rsp+10h] [rbp-20h]
  char *d; // [rsp+18h] [rbp-18h]
  char *e; // [rsp+20h] [rbp-10h]
  char *f; // [rsp+28h] [rbp-8h]

  fwrite(&ptr_, 1u, 0x1Fu, stderr);
  fwrite(&ptr__0, 1u, 0x19u, stderr);
  AAAAAAAA = (char *)malloc(8u);
  strcpy(AAAAAAAA, "AAAAAAAA");
  b = (char *)malloc(8u);
  strcpy(b, "BBBBBBBB");
  c = (char *)malloc(8u);
  strcpy(c, "CCCCCCCC");
  fprintf(stderr, &format_, AAAAAAAA);
  fprintf(stderr, &format__0, b);
  fprintf(stderr, &format__1, c);
  fwrite(&ptr__1, 1u, 0x12u, stderr);
  free(AAAAAAAA);
  fprintf(stderr, &format__2, AAAAAAAA, AAAAAAAA);
  fprintf(stderr, &format__3, b);
  free(b);
  fprintf(stderr, &format__4, AAAAAAAA);
  free(AAAAAAAA);
  fprintf(stderr, &format__5, AAAAAAAA, b, AAAAAAAA, AAAAAAAA);
  d = (char *)malloc(8u);
  e = (char *)malloc(8u);
  f = (char *)malloc(8u);
  strcpy(d, "DDDDDDDD");
  strcpy(e, "EEEEEEEE");
  strcpy(f, "FFFFFFFF");
  fprintf(stderr, &format__6, d);
  fprintf(stderr, &format__7, e);
  fprintf(stderr, &format__8, f);
}


我们进入demo这个函数进行细调


我们运行到call malloc


创建完(下面换了libc-2.23.so和ld-2.23.so)

此时分配了3个chunk

接下来是free,注意观察fastbin

第一个free了chunk0

可以看到它被列入了fastbin

接下来free chunk1


现在我们可以看到bins里面的情况

chunk[0]-chunk[1]-chunk[0]

接下来又把chunk申请回来


现在我们要看看,我们写入的的东西是不是会出现在chunk[0]中

ok,就到这里

4、pwn148

fastbin_dup_into_stack,继续学习新东西,先看一下checkseck

设置了终端背景颜色和透明度hh,

然后我们看看演示

设置了终端背景颜色和透明度hh,

然后我们看看演示

faetong@faetong-virtual-machine:~/pwnit$ nc pwn.challenge.ctf.show 28163
    ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                           
  ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                           
 ██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
 ██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
 ██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██ 
  ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀ 
    ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
    * *************************************                           
    * Classify: CTFshow --- PWN --- 入门                              
    * Type  : Heap_Exploitation                                       
    * Site  : https://ctf.show/                                       
    * Hint  : Fastbin_dup_into_stack -- Double free                   
    * *************************************                           
通过欺骗 malloc 使得返回一个指向受控位置的指针(本例为栈上)
通过 malloc 申请到 0x7ffed3cf1770.
先申请3 个 chunk
chunk a: 0x2362010
chunk b: 0x2362030
chunk c: 0x2362050
free 掉 chunk a
如果还对 0x2362010 进行 free, 程序会崩溃。因为 0x2362010 现在是 fastbin 的第一个
先对 b 0x2362030 进行 free
接下来就可以对 0x2362010 再次进行 free 了, 现在已经不是它在 fastbin 的第一个了
现在 fastbin 的链表是 [ 0x2362010, 0x2362030, 0x2362010 ] 接下来通过修改 0x2362010 上的内容来进行攻击.
第一次 malloc(8): 0x2362010
第二次 malloc(8): 0x2362030
现在 fastbin 表中只剩 [ 0x2362010 ] 了
接下来往 0x2362010 栈上写一个假的 size,这样 malloc 会误以为那里有一个空闲的 chunk,从而申请到栈上去
现在覆盖 0x2362010 前面的 8 字节,修改 fd 指针指向 stack_var 前面 0x20 的位置
第三次 malloc(8): 0x2362010, 把栈地址放到 fastbin 链表中
这一次 malloc(8) 就申请到了栈上去: 0x7ffed3cf1770

还是运用了double free,然后往fastbin0x2362030的栈上写了一个假size欺骗了malloc

double free之后的fastbin:

main_arena->chunk[0]->chunk[1]->chunk[0]->0

现在针对0进行攻击,现在把chunk[0]和chunk[1]申请走

main_arena->chunk[0]->0

接下来的操作把fastbin变为:

main_arena->chunk[0]->stack_addr

然后把chunk[0]和stack_addr所在chunk申请走,获得了在栈上写的权限

这里的操作其实不是太懂,我们看看代码才能知道

void __cdecl demo()
{
  __int64 v0; // [rsp+0h] [rbp-50h] BYREF
  unsigned __int64 stack_var; // [rsp+8h] [rbp-48h]
  char *AAAAAAAA; // [rsp+10h] [rbp-40h] BYREF
  char *b; // [rsp+18h] [rbp-38h]
  char *c; // [rsp+20h] [rbp-30h]
  unsigned __int64 *d; // [rsp+28h] [rbp-28h]
  char *e; // [rsp+30h] [rbp-20h]
  char *f; // [rsp+38h] [rbp-18h]
  char *g; // [rsp+40h] [rbp-10h]
  unsigned __int64 v9; // [rsp+48h] [rbp-8h]

  v9 = __readfsqword(0x28u);
  fwrite(&ptr_, 1u, 0x57u, stderr);
  fprintf(stderr, &format_, &AAAAAAAA);
  fwrite(&ptr__0, 1u, 0x15u, stderr);
  //创建chunk[0]
  AAAAAAAA = (char *)malloc(8u);   
  strcpy(AAAAAAAA, "AAAAAAAA");
  //创建chunk[1]
  b = (char *)malloc(8u);
  strcpy(b, "BBBBBBBB");
  c = (char *)malloc(8u);
  //创建chunk[2]
  strcpy(c, "CCCCCCCC");
  fprintf(stderr, "chunk a: %p\n", AAAAAAAA);
  fprintf(stderr, "chunk b: %p\n", b);
  fprintf(stderr, "chunk c: %p\n", c);
  fwrite(&ptr__1, 1u, 0x11u, stderr);
  //释放chunk[0]
  free(AAAAAAAA);
  fprintf(stderr, &format__0, AAAAAAAA, AAAAAAAA);
  fprintf(stderr, &format__1, b);
  //释放chunk[1]
  free(b);
  fprintf(stderr, &format__2, AAAAAAAA);
  //再次释放chunk[0],glibc不会检查第二次free同一个指针
  free(AAAAAAAA);
  fprintf(stderr, &format__3, AAAAAAAA, b, AAAAAAAA, AAAAAAAA);
  //申请回chunnk[0],d来控制malloc的返回值
  d = (unsigned __int64 *)malloc(8u);
  fprintf(stderr, &format__4, d);
  //申请回chunk[1]
  e = (char *)malloc(8u);
  strcpy(e, "EEEEEEEE");
  fprintf(stderr, &format__5, e);
  fprintf(stderr, &format__6, AAAAAAAA);
  fprintf(stderr, &format__7, AAAAAAAA);
  stack_var = 32;
  fprintf(stderr, &format__8, AAAAAAAA);
  //下面写入了fake fd,是栈地址
  *d = (unsigned __int64)&v0;
  f = (char *)malloc(8u);
  strcpy(f, "FFFFFFFF");
  fprintf(stderr, &format__9, f);
  g = (char *)malloc(8u);
  strcpy(g, "GGGGGGGG");
  fprintf(stderr, &format__10, g);
}

下面我们来试着调试看看这个过程

首先是三个对空间的分配


在三个malloc执行完以后我们看到chunk已经创建好了


接下来就是构成fastbin:main_arena->chunk[0]->chunk[1]->chunk[0]


这条链很明显:


接下来是申请回chunk[0]

main_arena->chunk[1]->chunk[0]

接下来申请回chunk[1]然后写入假fd


这个是写入F以后,马上要写入G


此时已经可以在栈上写入


5、pwn149(fastbin_dup_consolidate)


checksec


看看演示:

faetong@faetong-virtual-machine:~/pwnit$ nc pwn.challenge.ctf.show 28199
    ▄▄▄▄   ▄▄▄▄▄▄▄▄  ▄▄▄▄▄▄▄▄            ▄▄                           
  ██▀▀▀▀█  ▀▀▀██▀▀▀  ██▀▀▀▀▀▀            ██                           
 ██▀          ██     ██        ▄▄█████▄  ██▄████▄   ▄████▄  ██      ██
 ██           ██     ███████   ██▄▄▄▄ ▀  ██▀   ██  ██▀  ▀██ ▀█  ██  █▀
 ██▄          ██     ██         ▀▀▀▀██▄  ██    ██  ██    ██  ██▄██▄██ 
  ██▄▄▄▄█     ██     ██        █▄▄▄▄▄██  ██    ██  ▀██▄▄██▀  ▀██  ██▀ 
    ▀▀▀▀      ▀▀     ▀▀         ▀▀▀▀▀▀   ▀▀    ▀▀    ▀▀▀▀     ▀▀  ▀▀  
    * *************************************                           
    * Classify: CTFshow --- PWN --- 入门                              
    * Type  : Heap_Exploitation                                       
    * Site  : https://ctf.show/                                       
    * Hint  : Fastbin_dup_consolidate                                 
    * *************************************                           
申请两个 fastbin 范围内的 chunk: p1=0xcdd010 p2=0xcdd030
先 free p1
去申请 largebin 大小的 chunk,触发 malloc_consolidate(): p3=0xcdd050
因为 malloc_consolidate(), p1 会被放到 unsorted bin 中
这时候 p1 不在 fastbin 链表的头部了,所以可以再次 free p1 造成 double free
现在 fastbin 和 unsortedbin 中都放着 p1 的指针,所以我们可以 malloc 两次都到 p1: 0xcdd010 0xcdd010
$

似乎也是去构造一个double free但是用的方法不同,先看看人机的讲解然后来调试一下程序

首先是Fastbin_dup_consolidate的目的

让同一个 fastbin chunk(例如 p1) 既出现在 fastbin 链表里,也出现在 unsorted bin 里,从而在后续 malloc() 时可以连续两次分配到同一块内存,实现 double malloc → double free → 任意写 / 堆布局控制

现在这个chunk不仅仅出现在fastbin里,还出现在unsorted bin里,malloc之后将会连续两次分配到同一片内存

因为需要出发 malloc_consolidate() ,这是什么功能呢

当触发:

  • malloc() 需要从 top chunk 拿空间但不够
  • 或者 malloc_trim
  • 或者申请一个 过大 chunk(触发整理)

会对 fastbin 进行合并(consolidate)
→ fastbin 里所有 chunk 会被取出并放进 unsorted bin。

ok,接下来看看代码是怎么实现的(带注释)

void __cdecl demo()
{
  char *p1; // [rsp+8h] [rbp-28h]
  char *p2; // [rsp+10h] [rbp-20h]
  void *p3; // [rsp+18h] [rbp-18h]
  _QWORD *p4; // [rsp+20h] [rbp-10h]
  char *p5; // [rsp+28h] [rbp-8h]

  //创建chunk[0]
  p1 = (char *)malloc(0x10u);	
  strcpy(p1, "AAAAAAAA");

  //创建chunk[1]
  p2 = (char *)malloc(0x10u);
  strcpy(p2, "BBBBBBBB");
  fprintf(stderr, &format_, p1, p2);
  fwrite(&ptr_, 1u, 0xCu, stderr);

  //free掉chunk[0]
  free(p1);

  //申请一个更大的chunk[2],触发 malloc_consolidate()
  // glibc 会把 fastbin 里的 chunk(包括 p1)全部搬到 unsorted bin
  p3 = malloc(0x400u);
  fprintf(stderr, &format__0, p3);
  fwrite(&ptr__0, 1u, 0x3Eu, stderr);

  // 此时 p1 已经在 unsorted bin 中,不在 fastbin 头部,
  // 因此 free(p1) 不会触发 double free 检测!
  // free(p1) 会把 p1 再次放入 fastbin
  free(p1);
  fwrite(&ptr__1, 1u, 0x5Fu, stderr);

  //申请小块,返回p1
  p4 = malloc(0x10u);
  *p4 = 0x43434343434343LL;

  //申请小块,同样返回p1
  p5 = (char *)malloc(0x10u);
  strcpy(p5, "DDDDDDDD");
  fprintf(stderr, &format__1, p4, p5);
}

现在来调试程序

在demo打个断点run一下,我们把两个malloc执行完


接下来free chunk[0]

chunk[0]已经进入fastbin

接下来是申请一个更大的chunk[3]


奇怪,为什么被归在smallbin里面了


但是总归是不在fastbin里面的,所以继续free(p1)


虽然是在smollbin但是也是形成了double free

接下来就是申请回两个小块

首先是收回fastbin中的chunk[0]


下方可以看到被收回了


接下来是收回smollbin(原本是unstored bin)


可以发现已经被申请回来了

可以看到里面的内容也是被覆盖了

ok,拿个flag


好了,fastbin就到这儿,要考概率论了TAT

TAT

评论

  1. nero
    Windows Edge
    1 月前
    2026-3-30 20:35:55

    师傅题解写的挺好的,网站也不错|´・ω・)ノ

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇