# GeekCTF2024 - WriteUp
# 得分总览
Rank 19/633
Category | 题目名称 | 得分 | 解题人数 |
---|---|---|---|
Misc | Welcome | 17 | 612 |
Misc | WhereIsMyFlag | 104 | 95 |
Misc | RealOrNot | 333 | 23 |
Misc | Boy's Bullet | 250 | 34 |
Pwn | shellcode | 261 | 32 |
Pwn | flat | 297 | 27 |
Pwn | CppGame | 392 | 18 |
Pwn | Memo0 | 98 | 102 |
Pwn | Memo1 | 174 | 53 |
Pwn | Memo2 | 500 | 12 |
Reverse | Peer-Trace | 289 | 28 |
总结:战斗,爽!抗压,爽!
# Update 2024/9/22
如果用一句话评价这场比赛,那就是 “这绝对是这学期我打过的质量最高的一场比赛,我受益颇丰。”
下图是前 80 免费送的 GEEKCON2024 ticket
原本是前 50 送的,为此我一周内不断掉到 50 名开外,又不断解出,最后直接干到 rank19,结果结束后主办方调整为前 80 送 ticket... ...
没事,打比赛是为了学习新技术是吧:(
可惜场地在新加披,如果是在北京我一定去捧场:(
# Misc
# Welcome
直接复制粘贴:Welcome to GEEKCTF 2024! Your first FLAG will be flag{welcome_geekers}
# WhereIsMyFlag
py 源码里面夹带私货:
1 | ;import gzip; import base64; gzip.decompress(base64.b64decode('H4sIAAAAAAACA5Pv5mAAASbmt3cNuf9EzT3+sN5nQrdr2jIOrcbXJmHROjnJAouEuzN5jcq4Fbf6bN1wVlfNYInA9KvHri/k2HjhUVbxzHOHlB5vNdhWdDOpzPyo0Yy7S+6LFzyoXBVc/0r/+ffe+TVfEr8u/dF93/3if9td8//+Ff//8WK4HQMUNL7+V9J/3fBA+2Ojea/lmaCiC7PLMzf1Mt3zjTvJCBU6+Pp00v6/Ah92xQpbQoUUKm7azN2meyBZkk/cFi52vlpmbXQD0LhshLq3er7XdB2+533y4oOKccTFi/1+63HgdZnvE6hQw4PUzyW3tjH0p1rEfIGL2b4v3JLH2He6Yt1TuNjW3SaR2xnu7j6pjbCiNvLNdmXG9bdNJzJDxZqmn72ceZvJZtrDgotwse97jl/cxWqh93jnNLjY9XeXUu4ylbxXW49wytfUjff7WPbkXXdBuNjMf3ku94eItsOu/DCxe5/l3F+LPdjR8zwKoW639+RS7gt7Z++ZhLBi+tE6a6HRwBsNvNHAGw280cAbDbzRwBsNPETgff/8c/3l6bfX1355+POl/P+f7P/n1n17/L7239/8ufs8Ztf/fWr+mP/P/rrvL+vrbP59m1/39Wf/vh/T///y/vb102R/u9/b4///3m4v9+/D9vof7+bv/zX7v2bdr375Xe//6DOe7GOObudnAAAdRZxfbAoAAA==')) |
base64 解码后是个嵌套压缩包,直接解压几次就出 flag 了
flag{760671da3ca23cae060262190c01e575873c72e6}
# RealOrNot
看 server.py 发现要判断 20 个图片是不是假的,人力判断太麻烦,而且回显也有指示哪个错误,直接每次交互都修正一次,多次交互之后就能得到正确答案。
因此写了个自动的 python 脚本。
1 | import hashlib |
一直跑就能拿 flag 了。
# Boy's Bullet
要求上传一张 jpeg 图片,应该是只检查的扩展名,要有时间戳,而且时间要满足要求。
时间戳好处理,直接手机拍一张图片就行。
修改时间就直接 WinHex 更改就行了。刚开始改到 10.1.1 发现都过不了,直接改成 9011.1.1 直接拿 flag 了
# Reverse
用一个程序去监视另一个进程,并且修改另一个进程的数据。
puppet 只是一个进行异或检查的东西,没啥大问题。但是 peer 把输入内容修改了而且把原本 flag 异或之后的数据修改了。
所以调试之后发现 peer 首先把每八个字节内部交换了顺序 (输入 12345678 之后好像是 68745132),之后减去了数值,而且把原本的结果数值都减去了 peer 中的数据。
因此直接搓个 cpp 就出 flag 了。
1 |
|
flag{tr@cE_TraC1ng_trAC3d_TRaces_z2CcT8SjWre0op}
# Pwn
# Shellcode
要求写一奇偶相间的 shellcode。
先搓一个 read 减少工作量,之后读入新的 shellcode 把老的覆盖掉,就能执行别的操作了。
但是这题开了沙箱,只能用 read open 的 syscall,直接输出 flag 是不行的了。
因此直接侧信道攻击拿 flag,一个字符一个字符爆破。
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
# flat
加了 OLLVM 混淆,直接跑脚本去掉混淆之后是个 2.23 的 heap,但是基本没有回显,而且读入比较刁钻,但是拿 shell 就很基础。不多讲述。
flag{learning_deflat_trick_to_defeat_ollvm}
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
# CppGame
漏洞在 display_card 函数里面,card 不是使用指针,导致函数结束执行析构函数会释放结构体的 description 堆块,但是并没有清空指针。
此外,随着 vector 容量增加,他需要新的、大的堆块,就会把析构函数 free 的 0x90 的 chunk 给申请过来,就可以直接更改 vector 的内容。
由于 card 结构体里面 name 之后紧跟的就是堆块地址,填 0x10 的 name 就能在输出 name 的时候把堆块地址带出来,于是直接把 vector 改成一半 Eruption,一半 Vigilance 加上一个 Rushdown,这样就能大概率实现在一轮 game 中循环扣血,直至磨死 monster。
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
# Memo0
签到题,简单小逆向,加了点小 trick 的 base64
密码是 CTF_is_interesting_isn0t_it?
直接输进去就有 flag 了
flag {U_r_th3_ma5ter_0f_ba5e64}
# Memo1
漏洞在 edit 函数里面,v3 是带符号的,导致可以写成负数,就可以过去大小判断的检查,直接栈溢出
但是保护全开,其实只要解决 canary 和 pie 就行了。
canary 就覆盖掉低位的 0x00,这里用 edit 函数,写 0xF000000000000109 的 size 就能写 0x109 个 a,而且不会添加 0x00 截断,之后输出就行了。
之后使用同样方法拿到 libc 基址,搓个 ROP 链子就能拿 flag 了
1 | # sudo sysctl -w kernel.randomize_va_space=0 |
# Memo2
相比较 Memo1,把 buf 移到了 mmap 的一段内存了,并且内存清零了,但是写满 0x2000 可以泄露 ld 基址。
sign 函数有个栈溢出,但是有 canary。RELRO 没有全开,考虑有可能是利用 RELRO 相关内容。
动态链接会用到 ld 里面的 linkmap,因此 buf 里面伪造一个 symtab,__stack_check_fail 就会链接为别的不会退出的函数,之后直接 ROP 就能拿 shell 了。
此外,动态链接会检查函数的版本啥的__stack_check_fail 是 GLIBC_2.4 的,所以要找一个相同版本的函数在伪造。因此选择 openat64 函数。
ld 里面函数 ROP 基本用不到,考虑直接 syscall
首先栈迁移,之后设置寄存器,在 ret 到 syscall,就能拿 shell 了。
1 | #coding=utf-8 |