Hack Your Heart

Back

堆#

太复杂了,我操

就听在那说,也没有构成完整的体系。还是自己找资料吧

第10集不用看了,直接看陈老师的:https://www.yuque.com/hxfqg9/bin/nvrgrs

实际上堆先学的是:malloc内存管理

推荐阅读:malloc内存管理总结 ↗

鸡老师的:malloc源码分析 ↗ 夯爆了

大致有了个雏形,但是还是混乱。看了張元的视频,重新整理一下,讲得很不错,ppt和视频我都上传到B站了。后面由于时间问题很多也省略了。

堆调试#

 1❯ cat heap.c
 2#include<stdio.h>
 3#include<stdlib.h>
 4
 5int main(){
 6	void *p1, *p2;
 7	p1 = malloc(0x30);
 8	p2 = malloc(0x30);
 9	free(p1);
10	free(p2);
11	return 0;
12}
13❯ gcc -m32 -g heap.c -o heap_32
c

执行完两个malloc后,看堆:

可以看到第一个Allocated chunk是系统初始化的chunk,p默认是1。

第二、三个chunk是我们创建的,pwndbg为了方便调试,这里显示的是header的地址,其理论空间为:0x30(手动创建的空间) + 0x4(size) + 0x4(prev_size)=0x38,由于16字节对齐,所以向上取整,是0x40空间,加上flag是0x41

Q:1是存在size末尾的,为什么算到总共的空间中?

A:由于对齐,所以空间只能是16字节倍数,那个1实际上是用来标记p的,堆管理器在读取大小的时候会将这个1抹去。

看一下p1的chunk空间:

 1pwndbg> x/20x 0x5655a198
 20x5655a198:	0x00000000	0x00000041	0x00000000	0x00000000
 30x5655a1a8:	0x00000000	0x00000000	0x00000000	0x00000000
 40x5655a1b8:	0x00000000	0x00000000	0x00000000	0x00000000
 50x5655a1c8:	0x00000000	0x00000000	0x00000000	0x00000000
 60x5655a1d8:	0x00000000	0x00000041	0x00000000	0x00000000
 7pwndbg> x/20x p1
 80x5655a1a0:	0x00000000	0x00000000	0x00000000	0x00000000
 90x5655a1b0:	0x00000000	0x00000000	0x00000000	0x00000000
100x5655a1c0:	0x00000000	0x00000000	0x00000000	0x00000000
110x5655a1d0:	0x00000000	0x00000000	0x00000000	0x00000041
120x5655a1e0:	0x00000000	0x00000000	0x00000000	0x00000000
text

98到c8都是p1 chunk,d8刚好是p2 chunk的地址。可以知晓是连续的,上面也能看出代码里p1地址是userdata的起始地址,pwndbg显示的是header起始地址。

free后:

可以看到p1的chunk被放到了tcache bins里面,而且多了fd的值,在第一行的第三个单元,也就是之前的用户空间。

这里超纲了,还没学tcache,而且这里有指针保护,fd是运算过的,第四个单元是key字段貌似是tcache的地址。

如果是老版本没有tcache,这里应该显示fastbins。

p2的chunk 里面的fd也是运算的,老版本应该是p1的地址

fd、bk是free后才有,把userdata前几个字节拿来保存,形成链表,像 tcache 和 fastbin 这种单向链表,通常只有 fd,而unsort bins都有,它是双向循环链表。

张元讲得真好

UAF#

1pwndbg> checksec
2File:     /home/pwn/Desktop/NTU-Computer-Security/week3/uaf/share/uaf
3Arch:     amd64
4RELRO:      Full RELRO
5Stack:      Canary found
6NX:         NX enabled
7PIE:        PIE enabled
8FORTIFY:    Enabled
9Stripped:   No
text

伪代码:

nbytes_4开辟了16字节空间,定义了俩函数。ida里面点中nbytes_4,发现while里面没有高亮,只有倒数第二行有,漏洞很明显,倒数第二行,调用了free过的函数。

他while跑完就会崩:

 1❯ ./uaf
 2Hello ~~~
 3Size of your message: 100
 4Message: KKKKKKKKQQQQQQQQ
 5Saved message: KKKKKKKKQQQQQQQQ
 6
 7Size of your message: 100
 8Message: qwert
 9Saved message: qwert
10
11Size of your message: 100
12Message: qwert 
13Saved message: qwert
14
15zsh: segmentation fault (core dumped)  ./uaf
text

根据first fit原理,我让它开辟一个16字节的空间,它都会使用nbytes_4free 掉的空间。16是0x10,加上header已经是0x20,这已经刚刚好是16倍数了,精确使用16即可。

打印我输入的message后,会leak之前的bye_function地址,然后求出base地址,计算backdoor地址。

由于老师用的ubuntu 18,我是22,很多时候由于指针加密和tcache搞得很麻烦。不过原理是相通的

下面两道题目是星盟那个课的说实话,没太听懂,原理我知道,但是自己做不出来,总是在某些细节写不出来。

打算复习一下栈溢出先。

fmtstr_uaf#

 1❯ checksec echo2
 2[*] '/home/pwn/Desktop/pwn_learning/Exercises/fmtstr_uaf/echo2'
 3    Arch:       amd64-64-little
 4    RELRO:      Partial RELRO
 5    Stack:      No canary found
 6    NX:         NX unknown - GNU_STACK missing
 7    PIE:        No PIE (0x400000)
 8    Stack:      Executable
 9    RWX:        Has RWX segments
10    Stripped:   No
text

运行了一下,4个选项,1:不支持,2和3都是打印我输入的名字。4是问我要不要退出,第一次我写了n,取消退出。再次按4触发doublefree

ida:

可以看到,选项4执行了break跳出循环,然后执行cleanup,但是此时我并没有开始选择是否退出,他就cleanup了,选择否后,再次按4,又进行了一次cleanup,所以double free

echo2:

 1__int64 echo2()
 2{
 3  char format[32]; // [rsp+0h] [rbp-20h] BYREF
 4
 5  (*(o + 3))(o); // o[3](o) , 调用greetings函数
 6  get_input(format, 32); // 往 format 里读最多 32 字节
 7  printf(format); // 格式化字符串漏洞
 8  (*(o + 4))(o); // o[4](o) , 调用 byebye 函数
 9  return 0;
10}
c

echo3:

 1__int64 echo3()
 2{
 3  char *s; // [rsp+8h] [rbp-8h]
 4
 5  (*(o + 3))(o); // greetings(o)
 6  s = malloc(0x20u); // 分配 32 字节堆块
 7  get_input(s, 32); // 读入32字节内容到s
 8  puts(s); // 打印出来
 9  free(s); // 释放s
10  (*(o + 4))(o); // byebye(o)
11  return 0;
12}
c

cleanup:

1void cleanup()
2{
3  free(o); // 释放o堆块
4}
C

有点思路了,现释放o,再用s创建一下占用o的空间,再格式化字符串漏洞写数据到某个位置。

echo3有个UAF,可执行自定义函数,但是远程肯定有ASLR,所以用echo2的fmtstr漏洞泄露地址。

o的空间是0x28,在cleanup后被free。echo3里开辟s的空间是0x20,根据first fit原理,s用o的空间,0x20是3字长,所以会用o的前四字长,而o[3]是greeting,这里改写s的之后,即使s被释放,但不影响o调用数据。o在哪里调用呢?还是echo3的第一行,o[3](o)

exp要删去最后的hello:

pwnable.tw hacknote(heap1)#

1❯ checksec hacknote
2[*] '/home/pwn/Desktop/pwn_learning/Exercises/pwnable.tw hacknote/hacknote'
3    Arch:       i386-32-little
4    RELRO:      Partial RELRO
5    Stack:      Canary found
6    NX:         NX enabled
7    PIE:        No PIE (0x8048000)
text

ida,已重命名变量:

main没什么好说的,主要在每个分支函数。

Pwn学习日记-4
https://h4cker.zip/5b8a94/
Author Tajang
Published at April 21, 2026
Copyright CC BY-NC-SA 4.0