NepCTF 2025 Writeup
记录一下NepCTF比赛MISC和Crypto方向的部分试题复现(有些暂时没看懂,后续有空再接着复现吧),题目还是很好的,覆盖面比较广,学到了许多知识(`・ω・´)
MISC
NepBotEvent
根据题目分析,该题所给的文件跟Keylogger有关,也就是跟键盘流量相关。
提到键盘流量分析,我们就会想到HID DATA的8字节的数据,而这个文件并不是流量数据包,因此不能够让wireshark帮我们提取出HID data,因此我们要通过二进制数据来分析出哪些数据代表着HID data。
通过搜索可以发现,其实该文件是 Linux 下常见的键盘事件数据,每组24个字节,对应 Linux input 子系统的 input_event 结构体。
该结构体定义在 /usr/include/linux/input.h:
1 | |
直接ai一个脚本
1 | |
输出:
通过分析我们便能够得到数据库名为NepCTF-20250725-114514。
SpeedMino
一个俄罗斯方块游戏,按照题目的意思来看只要玩够2600分就可以获得flag,但是试了一下发现还是比较费时间的,于是转换思路从源码入手。
解压SpeedMino.exe文件,得到源码,发现是lua源码,查看main.lua,搜索2600,发现了结果计算的逻辑。
通过函数名等关键词跳转,发现所有的数据计算方法都能够在该文件中找到,直接把代码丢给ai写一个最后输出flag的计算脚本:
1 | |
得到flag:NepCTF{You_ARE_SpeedMino_GRAND-MASTER_ROUNDS!_TGLKZ}
meowble猫泡
打开附件,发现是一个Unity游戏,打开游戏跑一下,发现有提示,跑一会儿就可以发现会给出部分flag,但是貌似并不会获得全部,我们这里可以使用一款Unity游戏数据解析工具:Downloads 来导出游戏数据。之后我们就可以使用Notepad++直接搜索提示信息Tiptext了。
但是发现flag也是少了第7段flag,提示给出让试试GM,应该是GameManager模式,直接在Assets\Scripts目录下查看CS脚本,发现可疑脚本,猜测应该是管理gm逻辑的:
分析CS脚本就知道是gm的启动方式是“上上下下左右左右BA”(貌似是魂斗罗30条命作弊代码),直接进入游戏按下Esc之后再按下对应按键进入GM模式,让输入命令,随便输入一些字母会发现提示:
因此我们可以输入“help”来寻找获取flag的方法,然后我们就可以发现能够getflag,然后我们结合缺失的flag和getflag的用法使用getflag 7来获取flag
1 | |
客服小美
题目: 一个流量包+一个内存镜像文件raw
使用ufs打开可以查看到用户名为JohnDoe,同时能够发现桌面上存在exe文件
也可以使用vol2来查看
1 | |
扫描结果:
1 | |
找到系统版本后,可以尝试继续扫描桌面信息
1 | |
扫描结果如下:
1 | |
我们也可以得出用户名为JohnDoe
然后追踪一下data

1 | |
找到Beacon对应进程号6492
使用Beta/cs-parse-http-traffic.py提取取流量中的raw data
1 | |
选取其中对44请求的回应数据
先使用vol2 -f DESKTOP.raw --profile=Win10x64_19041 memdump -p 6492 -D ./将对应Beacon进程dump下来,然后使用Beta/cs-extract-key.py来提取密钥
1 | |

可以获得Beacon会话的主密钥
1 | |
然后挨个对过滤出的data进行解密,找到flag。项目地址:CS_Decrypt/Beacon_Task_return_AES_Decrypt.py at main · WBGlIl/CS_Decrypt
1 | |
输出得到flag
easyshock
题目:
1 | |
分析:
故障注入攻击,分析题目所给代码,可以发现代码实现了一种比特翻转的漏洞,让Shocktime位置的汇编指令运行过程中所涉及的所有寄存器的最低位的一个字节随机翻转一个bit,这便是shock函数的逻辑。
接下来我们要分析获取FLAG的思路,通过代码
1 | |
我们可以知道明文是由text.txt文件和FLAG拼接成的,使用IDA逆向一下prog文件,可以看到该文件是RC4的加密算法的二进制文件,按理说将Shocktime设为-1就不会执行相关的Shock函数,且RC4是对称密码,连着加密两次按理说会输出原本的密文,然而实际测试结果为:
我们可以发现明文到了parameters.\n之后输出全是0x00,我们再结合题目所给的text.txt文件分析:
从这个0x00开始就不正常输出了,因此可以合理推测在prog的RC4解密逻辑中对0x00做了特殊的判断。
之后我们的思路就明确了,找到对0x00进行特殊判断的汇编指令,找到该指令执行时对应的CODE_COUNT,再将Shocktime设定为该CODE_COUNT值,通过shock使该指令失效。
通过IDA寻找,可以快速地找到一个十分可疑的指令,它和下边的jnz loc_169结合使用实现了当前eax寄存器值不为0时发生跳转,并且我们可以找到其的相对地址。
然后我们便能够通过在hook_code中添加下面指令来获取对应的CODE_COUNT:
1 | |
其中address表示当前指令的地址。将Shocktime设为-1后开始运行题目所给脚本,运行之后可以发现很多输出,由于我们要找到最后解密停止的地方,因此只需要最后一个CODE_COUNT即可。
得到对应的CODE_COUNT值为49239,然后连接服务器,设置Shocktime=49239,即可以向对应的汇编指令处进行故障注入,shock函数会在该指令执行前对寄存器进行修改,使对应的eax寄存器的值不再为0,代码会继续向下边进行解密,从而输出flag,脚本如下:
1 | |
结果如下:
Crypto
NepSign
题目
1 | |
分析
首先明确我们的目的,是要求出一个长度为48的qq列表,这个qq列表中的qq[i]都是sk[i]经过step[i]次SM3哈希之后得到的值。
关键点分析:
- 由于我们已经知道了明文就是
happy for NepCTF 2025,因此根据题目给出的算法我们能够求出step列表 - 其次,我们可以无限次地请求签名
- 最重要的一点是,我们可以手动对
qq[i]进行step[i]的增加,因为我们知道SM3的算法
因此,我们的思路已经明确,就是要找到一个qq序列,保证对应每个step[i]都小于原本明文的step[i],这样我们就可以计算出target_step[i]-step[i],从而手动恢复出正确的qq序列。
注意,我们并不需要这48个qq[i]由同一次签名得出,因为每个qq[i]和step[i]的对应关系跟其他的没有联系,也就是说,我们可以重复请求签名,知道每个i都有了对应的qq[i]、step[i]使得step[i]<target_step[i]。
解答
1 | |
LatticeBros
题目
1 | |
分析
已知$\alpha$的极小多项式是三次多项式,且$f(\alpha)=0$,即
$\alpha^3 + a_2\alpha^2 + a_1\alpha +a_0=0$
可以构造格:
$$ (1,a_2,a_1,a_0) \begin{pmatrix} 1 & 0 & 0 & \alpha^{3} C \\ 0 & 1 & 0 & \alpha^{2} C \\ 0 & 0 & 1 & \alpha C \\ 0 & 0 & 0 & C \end{pmatrix} \\ =(1,a_2,a_1,\epsilon C) $$其中$\epsilon$接近0,则$\epsilon C$接近0。格基规约就可以求出$a_2、a_1$,然后带回方程式求出$a_0$,之后就是正常的HNP问题。
解答
1 | |
ezrsa2
题目
1 | |
分析
题中给出了$d$的生成算法,我们可以知道$d\approx N^{0.33}$,和我们熟知的Boneh and Durfee attack的攻击上限$N^{0.292}$还有些差距,但是$hints$的暴露使得我们能够利用中国剩余定理求出$d$的部分低位$d_l$,可以表示为$d=d_hM+d_l$
因为我们已知了部分低位,因此我们的思路尽量往coppersmith上边靠,由于泄露的是$d_l$,我们选的的多项式为:$ed=1+k\phi$
稍微调整一下,
$e(d_hM+d_l)=1+k(N-p-q+1)$
=>$d_heM = 1+k(N-p-q+1)-ed_l$
两边同时模上$eM$,得$0\equiv 1+k(N-p-q+1)-ed_l \mod eM$
设$x=k,y=p+q$得$f(x,y)\equiv 1+x(N-y+1)-ed_l \mod eM$
并且我们可以算出$x\approx 2^{680},y\approx 2^{1024}$然后直接打二元coppersmith即可
解答
1 | |
unloopshock
题目
1 | |
分析
故障注入攻击,分析题目,跟MISC中的easyshock比较相似,其中的shock逻辑是一样的,这一题的交互逻辑和题目都在提示我们跟密钥泄露有关,只要求出key我们就能得到FLAG。
使用IDA逆向一下progaes4,发现是AES加密算法,逐个分析函数之后,发现函数sub_1BC控制着密钥扩展逻辑,根据我们了解的AES相关知识可以知道,扩展密钥由十一组组成,每组由4个4字节组成,其中第一组就是原始密钥。
由于我们可以自定义加密明文,也就是说,我们的目的就是通过修改密钥扩展算法,再结合明文和密钥的关系来获取密钥。通过分析我们可以通过修改i的大小,让i直接跳过do-while,这样扩展密钥就会变成除了第一组是原始密钥key,其他10组都默认为0,这样的话我们就能用来生成扩展密钥为全0进行解密,最后再异或一下明文m就能得到key。
首先我们要找到对应的注入点,通过查看该函数的汇编指令,找到扩展密钥初始化时++i的指令
找到相对地址为0x207,然后在hook_code中添加以下代码来获取对应的故障注入点:
1 | |
运行题目所给脚本,取最后一个CODE_COUNT作为Shocktime
得到Shocktime为90,之后便可以进行注入了,首先我们要有一个能够自定义轮密钥的AES解密脚本,在网上随便找了一个:AES 加解密 python手动实现 - wuuconix’s blog
稍微对脚本进行修改,写一个生成全0的轮密钥生成函数,并将解密的输出结果调整一下即可。
解答
aes.py:
1 | |
破解脚本ans.py:
1 | |
运行获取FLAG,提示一下,由于我们的故障注入并不能保证比特翻转后的i一定大于44,因此需要多尝试几次才能获得flag。