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() |
找一下漏洞