ctfshow-做题笔记-堆利用·前置基础
为防止题目难度跨度太大,135-140为演示题目阶段,你可以轻松获取flag,但是希望你能一步步去调试,而不是仅仅去拿到flag。 如何申请堆?
pwn135
没啥说的,ida反编译进去看一下源码就知道怎么做了。
简单了解一下堆的知识
malloc函数:
作用:从堆内存中分配指定大小的内存块
1 | void* malloc(size_t size); |
参数:size 是需要分配的字节数。
返回值:成功时返回指向已分配内存的指针;若分配失败,则返回 NULL。
特点:分配的内存不会被初始化,可能包含垃圾数据,需要手动清零。
calloc函数:
作用:分配并初始化内存块(每个字节都会杯初始化为0)。
1 | void* calloc(size_t num, size_t size); |
参数:
num:需要分配的元素个数。size:每个元素的大小(字节)。
返回值:成功时返回指向已分配内存的指针;若分配失败,则返回 NULL。
特点:分配的内存会被初始化为 0。
realloc函数:
作用:重新调整已分配内存的大小
1 | void* realloc(void* ptr, size_t new_size); |
参数:
ptr:指向已分配内存的指针。如果传入NULL,realloc的行为与malloc类似。new_size:重新分配的字节数。
返回值:成功时返回指向调整后内存块的指针;若失败,返回 NULL。
特点:
- 如果新的大小大于原内存块,可能会移动数据到新的位置。
- 如果新的大小小于原内存块,超出的部分将会被释放
pwn136
也是一样的
在我看了来,这道题就是简单讲了一下free函数
free函数:
作用:它是与malloc函数配套使用的内存释放函数,用于将malloc或去哦他分配内存函数申请的内存还给系统
1 | void free(void *ptr); |
参数 ptr: 指向由 malloc 或其他分配函数申请的内存块的地址。
效果: 将这块内存归还给系统,释放其占用的资源。
pwn137
2024.10.25
还是了解一些知识点
getpid():
作用:获取当前进程的PID
1 | pid_t getpid(void); |
返回值:返回调用进程的 进程 ID,数据类型为 pid_t(通常是一个整数类型)。
sbrk()函数:
作用:于获取当前进程的 数据段 (也称为堆)的末尾地址。
1 | void *sbrk(intptr_t increment); |
返回值:这个函数返回的是一个指针,指向当前堆的末尾。
brk函数:
作用:设置进程的数据段(堆)的末尾地址。它通常与 sbrk 一起使用,但它的功能更为直接,主要用于控制堆的大小。
1 | int brk(void *end_data_segment); |
该函数尝试将数据段的结束地址设置为 end_data_segment,如果成功则返回 0;如果失败则返回 -1。
pwn138
mmap函数:
作用:mmap() 是一个 Linux 系统调用,用于将文件或匿名内存区域映射到进程的地址空间。
1 | void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); |
参数 含义
addr 指定映射的内存地址(通常传 NULL,让内核决定)
length 需要映射的内存大小
prot 保护标志:可读、可写、可执行等(`PROT_READ
flags 标志:匿名映射、共享映射等
fd 文件描述符(-1 表示匿名映射)
offset 文件偏移量(匿名映射传 0)
这里做过一道用mmap函数进行分配内存写入shellcode的题
但是我觉得最多出道mprotect函数
pwn139
fseek()函数:
作用:fseek() 用于将文件指针移动到文件的指定位置。
1 | int fseek(FILE *stream, long offset, int whence); |
参数说明:
**stream**:表示文件指针,即目标文件流。
**offset**:相对于 whence 的偏移量。
**whence**:位置基准,可以为以下 3 种:
- **
SEEK_SET**:从文件开头开始偏移。 - **
SEEK_CUR**:从当前指针位置偏移。 - **
SEEK_END**:从文件末尾开始偏移。
返回值:
- 成功返回
0,失败返回非零值。
ftell函数:
ftell() 用于获取当前文件指针的位置(以字节为单位)。
1 | long ftell(FILE *stream); |
参数说明:
- **
stream**:表示文件指针。
返回值:
- 返回当前文件指针的偏移量,从文件开头算起。
- 若出错,返回
-1L。
fread函数:
fread() 是 C 标准库中的一个函数,用于从文件流中读取数据。
1 | size_t fread(void *ptr, size_t size, size_t count, FILE *stream); |
参数说明:
- **
ptr**:指向存储读取数据的内存区域的指针。 - **
size**:每个数据项的字节数。 - **
count**:要读取的数据项的数量。 - **
stream**:指向目标文件流的指针。
返回值:
- 返回实际读取到的项数(可能小于
count),可通过与count比较来检查是否成功读取。
pwn140
pthread_create()函数:
用于创建一个新线程
1 | pthread_create(&newthread, 0LL, threadFunc, 0LL) |
参数:
- **
&newthread**:指向新线程的线程标识符的指针(类型为pthread_t)。 - **
0LL**:线程的属性,通常可以传NULL(或0),表示使用默认属性。 - **
threadFunc**:指向线程要执行的函数(线程的起始点),该函数必须符合特定的函数原型。 - **
0LL**:传递给线程函数的参数,通常可以传NULL。
返回值:
- 成功返回
0,失败返回错误码。
pthread_join()函数:
pthread_join() 用于等待指定线程终止,并可以获取该线程的返回值。
1 | pthread_join(newthread, &thread_return) |
参数:
- **
newthread**:要等待的线程的标识符。 - **
&thread_return**:用于接收线程的返回值的指针,类型通常为void**。
返回值:
- 成功返回
0,失败返回错误码。
pwn141
日常检查
1 | bbq@ubuntu:~$ checksec pwn |
丢进ida里面看看
1 | int __cdecl __noreturn main(int argc, const char **argv, const char **envp) |
看着还是很经典的
也是看见后门函数了哈
1 | .text:08049684 use proc near |
怎么利用呢???
首先找一下漏洞在哪???
UAF漏洞
ok
1 | if ( *(¬elist + v1) ) |
这里只是单纯进行了 free,而没 有设置为 NULL,那么显然,这里是存在 UAF漏洞。
正确的写法
1 | if (*(¬elist + v1)) { |
有后门,我们只需要修改 note 的 put 字段为use函数的地址,从⽽实现在执行print note 的时候执行后门函数。
exp:
1 | from pwn import * |
简单了解一下UAF漏洞
pwn142
先放着
off-by-one漏洞
pwn143
样子都差不多
ida打开也是看见后门了
1 | void __noreturn fffffffffffffffffffffffffffffffffflag() |
找一下漏洞





