NepCTF2025 WP
NepCTF2025 WP
这次逆向的题目质量都挺好的,各个考点都不错,拿下三个一血和一个三血。
Crypto
Nepsign
Gemini 2.5Pro一把梭。
1 | import socket |
Web
easyGooGooVVVV
Gemini 2.5Pro
exp:this.getClass().getClassLoader().getResource("file:///proc/self/environ").text
RevengeGooGooVVVY
同上EXP。
exp:this.getClass().getClassLoader().getResource("file:///proc/self/environ").text
Misc
NepBotEvent
从题目以及文件结构可分析出是input_event结构体数据。
1 | struct input_event { |
解析代码:
1 | import struct |
SpeedMino
游玩游戏的可以发现背景是一直在变换,猜测是正在解密的flag,需要解密一定次数才行,直接CheatEngine齿轮加速100000000倍,让他加速解密,游戏结束前就会解密完。
MoewBle喵泡
CE附加,启用mono注入功能。
单击Lookup instances找到实例,将角色血量改高就不会死。
走遍地图接触每个点就可以得到各个部分flag,这边要吃一下最顶上的这个,才能解锁其中几个隐藏的。
提示缺失的第七段flag在GM面板获取。
找到GmManager,同样查找实例,然后调用这个OnKonamiCodeActivaied就可以激活GM面板。
点击右上角齿轮打开,getflag 7即可得到最后部分flag。
NepCTF{94721248-773d-0b25-0e2d-db9cac299389}
Reverse
RealMe
发现main函数下面有个函数,也是变体RC4加密,不过没被调用到,猜测是有反调试。
x32dbg动调,使用Scyllahide插件一键过反调试,断点该处代码,发现被调用,edx也就是sbox,直接提取出来,对main函数的密文解密即可得到flag。
1 |
|
NepCTF{Y0u_FiN1sH_Th1s_E3sy_Smc!!!}
CrackMe
字符串定位到核心函数。
判断Password是否符合正则。
将Password直接unhex,判断转成的字节长度是否等于16。说明我们要输入32个字符。
主流程如下:
1.MD5_Custom(Username)的值作为密钥。
2.MD5_Custom(Username+”Showmaker11”)作为密文在下面判断
3.AES_Custom(Password, Key)
4.判断AES结果是否等于第二步的密文
解密就是MD5获取密钥,将第二步MD5值作为密文进行AES解密即可得到对应Password十六进制。
MD5是libcrypto.dll里面的函数,可以直接调用,不需要逆向。
AES也是libcrypto.dll的函数,但是存在代码流混淆和魔改,将代码块打乱到不同的位置,不过还是可以通过观察知道那些代码块对应AES流程中的哪一步。
可以找到Sbox,发现是没有魔改的值。
通过对比AES标准代码,去找一些关键点进行断点,且将密文密钥同步到标准AES代码调用,对此时程序的密文数据与标准AES的密文数据进行对比,看看是哪一步开始对不上,就是魔改的地方。
发现在MixColumns处出现数据对不上,可以看到对比标准代码多异或上了0x55。
经过修改标准AES代码,进行加密验证,发现和该程序的AES加密结果能对上,说明就只有MixColumns一处进行了魔改,这样就可以写注册机了。
注册机代码:
1 |
|
最后将网页上的名称都存到本地文件,修改注册机代码,一键获取Password,然后手动填写到网页提交即可得到Flag。
QRS
运行,发现启动了一个服务,网页打开提示这个,应该是用input参数提交Flag进行Check。
IDA字符串搜索missing field,查看交叉调用找到调用方函数,断点在返回处。
调试运行程序,网页打开触发断点,往上走两层,走到如下函数。
在该函数下面会看到这边有个函数调用,如果网页访问带input参数传入文本,mark2函数就会传入input的明文,以及获取的一串key。
加密完,下面也从xmm获取数据,进行cmp比对,那么下面的那两串xmm数据就应该是密文,mark2是加密函数。
mark2核心加密代码如下,是一个魔改的Tea加密,而Delta使用GetTickCount,应该是未知的,但是实际调试发现不管什么时候这边获取到的TickCount都是固定的,值为:0x68547369。
提取密文和密钥,编写解密即可得到Flag。
解密代码:
1 |
|
SpeedMino-Warlock
可以从文件结构知道使用了Love框架,右键exe用压缩包打开就可以得到lua源码。
main.lua里面解密出来是fake flag。
IDA加载version.dll,发现字符串存在lovely-injector,那么这个version.dll就是lovely-injector项目的,用于劫持注入修改lua代码。
由于该lovely-injector源码被出题人修改过,并没有输出任何文件或log到本地,只能通过其他方法得到即将被注入的lua代码,下面提供两种方法。
法一:CE直接搜索
原程序main.lua这部分代码是获取剪贴板内容,然后进行calcData加密与下面的假flag密文比对,尝试在CE搜索result_table,看看有没有其他的调用代码。
可以搜到两处。
可以看到其中一个是在一个没见过的lua代码里面调用的,这边从NEURO,也就是上面那些字节加载了一个luajit函数,传入result_table进行了Check,然后输出”PERFECT!”,那么上面这个luajit函数就是真实的Check函数。
法二:启用dump_all参数
查阅lovely-injector项目源码,发现init这边有个dump_all参数,用于dump即将要被注入的代码,
在version.dll的dllmain函数中,可以看到这边有判断是否有”–dump-all”参数,来启用dump_all功能。
断点给dump_all_bool赋值0的地方,步过执行一步,将rsi改成1,然后运行,就会弹出dump字节,也就是luajit代码。
luajit分析
将上面的得到的luajit字节输出到文件,使用https://github.com/weaweawe01/luajit_decompile项目进行luajit反编译,可以得到如下lua代码。
1 | slot1 = function(slot0, slot1) |
直接对应编写解密即可,然后还需要再解密一层CalcData加密。
解密代码:
1 | import math |
NepCTF{Y0u_c4n_M0dDing_LOVE2D_g@mE_By_l0vely_iNjector!}