2021 TCTF 题目类型涉及到了平常不会接触的题型,通过这次比赛初次接触了unicorn和musl-gcc。
Listbook
程序分析
1 | __int64 __fastcall sub_134C(__int64 name, int size) |
在计算chunk
的 idx
时,会将堆块所有内容相加,然后取其绝对值,再模16。最终得到 chunk
的编号。
这里,如果输入数据为 128,会导致计算的编号为 ff
,即为 -1,向上溢出。
利用分析
能向上溢出,说明能向上修改 used_list
或者 chunk_list
。
EXP
1 | from pwn import * |
uc_masteer
程序分析
利用 unicorn
实现了对一个程序的 hook
。主要有两个重要的 hook
。当访问了 CODE + 0x6b - 5
的指令时,会调用 admin_hook
,将全局变量is_admin=True
,并且调用写函数,覆盖code+0x1000
的代码:
1 | def admin_hook(uc, address, size, user_data): |
第二是对 0xbabecafe233
地址的访问时,也会触发一个hook
。会将全局变量is_admin=False
,并执行写入的数据:
1 | def hook_mem_access(uc, access, address, size, value, user_data): |
利用分析
这里基本的想法是,触发 hook_mem_access
,去执行命令,但是将改命令code+0x1053
改为自己的数据。
这里首先就要保证 is_admin=True
。但是,如果想要使其为True
,就得执行admin_hook
。但执行了该hook
,又回将我们修改的 code+0x1053
的数据覆写回去。
但是,这里关注到这两个部分的跳转地址都位于栈上,所以通过修改跳转地址。可以实现先 触发admin_hook
,但不紧接着执行hook_mem_access
。然后修改了 code+0x1053
的命令,再用 test
去执行。
EXP
1 | from pwn import * |
babyheap2021
程序分析
1 | int __fastcall get_input(chunk_struct *a1, int a2, int a3) |
edit
读取用户输入时,size
的类型由int64
变为了int32
。导致我们可以输入 0xffffffff
来绕过对size
的检查,最终实现堆溢出。
利用分析
是 musl-gcc
,版本是 1.1.24
。很老的一个版本,所以有很多漏洞。这里如果想具体学习musl
中的堆管理机制,可以参考这篇文章。
泄漏地址
由于musl
对堆块的管理都是通过mal.bins
来管理,类似glibc
的 smallbin
,是一个双向链表。所以我们只要申请一个堆块,其都会残留一个libc
地址。
但是,这里对申请的堆块,都会执行memset
操作。所以,这里需要使用堆重叠,来泄漏地址。
先申请4个大小为0x20的堆块,利用chunk1
的堆溢出,修改chunk2
的size
为0x41
,和修改chunk4
的prv_size=0x21
。然后释放 chunk2
,再申请一个0x20
的chunk
。此时会切割chunk2
,那么刚好就会在chunk3
处残留libc
地址。输出即可泄漏。
任意堆块分配
这里可以利用堆溢出来实现任意堆块分配。
musl
的堆块malloc
时,也会有一个类似的unlink
的操作。会将进行如下解链操作:
1 | static void unbin(struct chunk *c, int i) |
如果能够控制当前空闲chunk
的prev
和next
指针,就能向任意地址写入一个堆地址。
而前面提到musl
对于空闲堆块的管理,采用类似smallbin
的双链表。而这里musl
更不安全,在malloc
时并没有对双链表的完整性进行检查。如果我们能够修改一个mal.bins
的head
指针指向我们想要的地址,后面就可以分配该bins
的空闲堆块,就会把我们想要的地址分配给我们。
这里只需要保证我们伪造的fake_chunk
的next
和prev
指针可写即可。
这里的思路是先利用修改一个0x20
的chunk
的next
和prev
指针分别指向mal.bins[37]-0x10
和stdin-0x10
的地址。
然后分配该0x20
的chunk
,由于该chunk
位于mal.bins[0]
的head
,此时进行解链。即会将 (mal.bins[37]-0x10)->prev = mal.bins[37]+8
的位置写上prev=fake_chunk-0x10
,即将mal.bins[37].head = fake_chunk-0x10
。将(fake_chunk-0x10)->next = fake_chunk
的位置写上next=mal.bins[37]-0x10
的值。
此时,对于mal.bins[37]
的head
被指向了fake_chunk-0x10
。而mal.bins[37]
存储的是 top_chunk
的地址,也就是后续分配堆块,如果没有空闲堆块匹配,那么就会直接从mal.bins[37].head
中去分配堆块。这样就实现了向任意地址分配堆块。
随后,还需要再利用一次上面的步骤,不过是修改同样的0x20
的chunk
的next
和prev
指针都指向fake_chunk-0x10
的地址。目的是 向fake_chunk
的next
和prev
处写入一个可写的地址。
最后,即可分配 任意大小的堆块,分配出fake_chunk
。
getshell
实现了任意堆块分配后,由于musl-gcc
没有__free_hook
或__malloc_hook
这一类指针。所以比较稳定的利用方法是利用FSOP
。
将任意堆块分配到stdin
结构体处,修改stdin.write
指针,触发 exit
最终即会调用如下函数:
1 | static void close_file(FILE *f) |
只要保证f->wpos
不等于f->wbase
,就能保证去触发write
指针。
由于开启了沙箱,所以需要找一个gadget
,来执行orw
。
EXP
1 | from pwn import * |
uc_goood
程序分析
和前一题区别在于,这道题不能再去修改跳转地址了。需要通过字节写shellcode
来控制程序执行流。
EXP
1 | from pwn import * |
- 本文作者: A1ex
- 本文链接: http://yoursite.com/2021/07/06/2021-TCTF题解/
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!