W4terCTF 2024 Writeup(25题) ——GZTime的🐕
第一次参加校赛,reverse和pwn都是我做的(可惜时间不够,否则我都想re ak的),感觉这成绩还是不错的(两位队友带我起飞),本来是第二的,但有两支队不讲武德,藏flag最后四小时狠狠上分。之前没有什么逆向经验,这次比赛下来还是收获颇丰的,虽然做题做的有点慢,但是真切地能够感受到推理、查资料、求解的那种乐趣。Yring和Lilran两位师傅出的题目都很有含金量,能够学到蛮多东西的。下面的是我们队这次的wp。题外话:我们的队名GZTime的🐕是我起的,主要是我看到去年的队伍有叫做GZTime Fans的,就想取个比较搞的名字。
Crypto(4题)
Merciful ZMJ4396
多项式环上的扩展欧几里得算法得大因子
1 | from sage.all import * |
(HGCD没用)
n77
接收部分
1 | from pwn import * |
多项式插值
1 | from Crypto.Util.number import * |
sha256得key
1 | from sage.all import * |
Smoke hints
1 | from sage.all import * |
Wish
1 | from flask import Flask, request, jsonify, send_from_directory |
分析源码,可以一眼看出diff要足够的小才能最大限度地提高概率
然后生成伪随机数的方法是MT19937梅森旋转法,其中的随机数值是根据seed而定的
所以为了让diff更小,我遍历seed去选产生的第index个伪随机数最小的seed
寻找的脚本
1 | import random |
然后是队友的发包程序
1 | import requests |
然后就有每次十分之一的概率寻找flag了
Reverse(7题)
BruteforceMe
base64加cr4但是他加了一个能够判别多少位重合的机制,那么直接脚本爆破我是先报了一部分再进行调试再爆破
1 | #尝试某一位的爆破 |
1 | #尝试所有位数的爆破 |
1 | #W4terCTF{UNr3I47Ed_6ytE5_c4N_6e_ENuMerated} |
一开始本来想着三位三位爆破的,但是不知道哪里出了问题可能是debug操作失误,然后就让我以为后面的位数会影响前面的,导致了脚本不断修改尝试最后才出
box
box的加密实际上就两个函数,只是一个藏起来了不太好找。找到对照数组发现他在前面的函数中被引用过,直接就可以发现还有一层加密
1 | # 假设byte_41B000是一个字节数组 |
nor
嵌套函数太多了,一开始都看蒙了,然后一个一个分析从最小的开始其实就是异或门、与门和或门,构成的第一个函数的作用就是最关键的是这个函数,他的功能实际上就是加法器
接下来两个函数的功能就显而易见了
1 | enc=[15, 9, 16, 14, 16, 12, 63, 29, 23, 49, 23, 12, 63, 8, 55, 49, 39, 57, 35, 20, 13, 24, 43, 57, 17, 234, 229, 239, 52, 0, 5, 24, 9, 23, 227, 53, 14, 236, 227, 243, 62, 29] |
古老的语言
检测文件发现要用vb打开,然后没有提示一直不知道这个反编译竟然有问题,关掉优化后就可以看到完整代码。可以说vb的语言是真的有点恶心,参数又多又杂。翻译成正常的就是第一层将每个字符转成对应数值的16进制数,每四个依次构成一个八位十六进制数,共十二个。
接下来就是恶心的feistel网络加密,第一次接触不知道这个怎么逆运算,还有就是雷哥sublong函数的干扰导致一直有一堆错误。实际上就是加密过程和解密基本一样,key不用变,就是temp需要从尾巴开始然后一组三个部分得从后往前算。temp的值我一直算不对,查了才发现是sublong()的问题。
1 |
|
Crabs
第一部分简单异或:
1 | a='l2v\Q0YCzh}TMR~csegaQJA&LQEV]iL.x~cm@' |
第二部分分析出来是矩阵乘法,那就逆运算
矩阵求逆可以使用sagemath中的函数
即在整数域上进行求逆
1 | from sage.all import * |
根据得到的结果对照可得:
Shuffle Puts
签到题,打开ida,查看反编译代码即可找到flag
DouDou
很可惜比赛结束几分钟才做出来否则就第三了
先是js解混淆,用网站https://deobfuscate.relative.im
然后观察可以发现主要是用到e函数进行加密是个aes加密
1 | 87, 52, 116, 101, 114, 68, 114, 48, 112, 67, 84, 70, 50, 48, 50, 52, 82 |
python解aes的ecb模式,然后最后还有个异或的小加密
1 | from Crypto.Cipher import AES |
Web(5题)
Auto Unserialize
- 题目
1 |
|
- 找不到上传入口
- 尝试phar
- 脚本
1 |
|
伪装成图片的php脚本,[翻译]如何将 PHP Phar 包转化成图像以绕过文件类型检测-CSDN博客
GitZip
1 | https://gitzip.org/ |
- 以上信息为误导信息
代码审计 在routes.js下发现
1 | // ===================================== |
requestPath = path.resolve(__dirname, ‘../‘, ‘views/‘ + name);可以实现任意文件读取
构造payload发送 /../../../../tmp/flag 即可
注意这里 / 需要url编码发送
ASHBP
1 | // admin.php |
1 | 14775389255996250411216234491594398597225320426770762975422890469577828503535104091379415228489273198358046401943260876011710151302917584011251292020040281850364872539746024731573514408002450980166506176411170339067674764112346684536466446872682133703656532806131002005225331131138722629857593002147478979920 |
1 | 79666064669479503565757120118709286898444254899330310219058760922164429110197921573000847542660084392313142620174005901857458000882449229909519256589590863981625176399350264565473435443654608599321087768154746622544760578697587251951582132434004866717504828270979795734234752815558491495085331626005167632606 |
1 | from Crypto.Util.number import * |
1 |
|
1 | admin == IotOsOjglrGDbFkcILzBT/SCqkIAjJB14fv8L555NDT3ydDRVRCDIi7tDpUCBusfg2O3nmJTwt3EuPFcZoYVT05/fzKHieVDg/NPsL4Z6OjPA55fuzFsoxzxnAn3Lt3m6jwmNvRUiXiyqZ6Vc1iIAqsd2EtXN7qf7YOBM67kl44= |
1 |
|
背景/思路 :
通过admin.php POST发包实现 cre admin认证 + flag 任意文件包含
1 | if ($_POST['cre']) { |
PNG Server
nginx配置错误
上传小马 + /.php
其中小马因为检查的很严格 需要合并一张正常的图片带进去
1 |
|
成功访问到的话可进一步使用antsword快速定位flag
2min秒了 没什么可说的
User Manager
SQL 盲注
除了给定的几个排序方法外 , 使用 Secret acs也可以排序
通过发包
1 |
|
其中
- 根据ascii排序 : 1-9 < _ <A-Z < a-z < }
- 采用二分法查找
Misc(ak)
Revenge of Vigenere
这道题脑洞还挺大的,首先给了一段非常长的没法下手的明文,然后我们注意到毫不相关的题目:维吉尼亚与盖伊·福克斯,一搜发现是一部电影《V字仇杀队》,那么密文很有可能是台词或者是信件旁白之类的,搜了一下果然是
发现flag那一行不在里面,那就是根据已有的明文密文解码key,找了一句来解
1 | def extended_gcd(a, b): |
得到结果三十一循环,但是key是10-20位所以是30的因数且满足10-20,可以知道是15,然后将他拼起来就可以解码维吉尼亚了
1 | def decrypt_vigenere_variant(ciphertext, key): |
Spam 2024
垃圾邮件套娃
这题有四五关左右,也不知我的解法如何,但很有趣。
首先,与去年一样去spammimic去解第一层垃圾邮件
然后进行解十六进制,得出的字符串阅读性很差,观察得出是emoji的unnicode,使用html将unnicode直接转义得出emoji。
然后将emoji进行aes解码key为🔑
得到这个东西,很显然是base64+异或
用Cyberchef求解
看到了这个一看就很想flag的东东
剩的这一步很有趣,我是自己看出来奇数位都是对的
偶数位不是ACSII码值加4就是减4,然后后面根据“词频分析”进行手解的
以下是手解的图:
GZ GPT
观察输出,因为不是语言模型,所以输出的语句只是看起来也点区别,并不算完全随机
将gztime的话复制到txt文本之中
发现txt阅读器实际上读到的字符数比我们看到的要多
得出零宽字符隐写的结论
每一位的加密方式也有细微区别,但是除了正确解密基本上都是乱码 干扰较小
Sign In
找到W4terDr0p战队即可
Priv Escape
文件位置
1 | # 系统配置文件 |
写入个人文件内容
1 | echo 'user r00t; |
1 | sudo -u r00t /usr/sbin/nginx -c /home/W4terCTFPlayer/nginx.conf |
命令
1 | nginx -g 'nginx.conf /home/W4terCTFPlayer/nginx.conf;' |
问题
1 | -- 已解决 -- |
broken.mp4
根据视频内容我们可以发现刚好是一个修复视频的软件操作,直接找到并下载一把梭
Pwn(3题)
Python Wants Flags
玩多亿次就会发现这个有bug,好在我才用录频来玩才能抓住漏洞的产生:实际上就是当蛇吃到食物的时候尾巴会身长两格,然后就会覆盖掉储存的数组,这个是吃墙壁吃出来的,同理door也储存在同一个地方可以覆盖。然后撞墙的时候我发现我一次会被吃掉两格那么我们可以利用这个漏洞来打开墙壁。手操的过程极为痛苦,我后面直接暂停计算位置才艰难拿到flag
2048! 0x800 Edition!
一开始尝试了全是1输了很多,但是没啥效果,但我想想唯一有机会攻击的就是username那里,于是尝试了一下移动键输入,就碰对了。
Remember It 0
问就是一个一个截屏截出来的