文章总结: ThisdocumentdetailsawriteupfortheXCTF-LilacCTF2026Ezpythonchallenge.TheauthordemonstrateshowtoextractanddecompileanencryptedPythonEXEusingPyInstallerandpycdctorecoverthesourcecode.ThecoreanalysisrevealsaBTEAencryptionalgorithmusedtovalidatetheflag.Theauthorexplainsthelogicinvolvingstructpackingandkeyderivation,thenprovidesaPythonexploitscripttoreversetheBTEAencryptionandsuccessfullyretrievetheflag.ThisservesasapracticalguideforreverseengineeringPythonexecutablesandsolvingcrypto-reversingtasksinCTFs. 综合评分: 89 文章分类: CTF,逆向分析,二进制安全
XCTF-LilacCTF 2026 Ezpython
Mystery Mystery
赛查查
2026年1月30日 10:09 北京
提示如下
Python is not as difficult as you think
flag format: LilacCTF{…}
下载附件不难看见这个exe已经被加密
接着我们去进行给他转成Pyc进行解密
解包工具
PyInstaller onefile
成功进行解密 接着我们去找他的原Pyc文件进行转成python、
这里给大家介绍两款实用工具
1.在线工具:python反编译 – 在线工具
2.离线工具:【免费下载】 pycdc:强大的Python反编译工具-CSDN博客
import structfrom crypto import *from sys import *import base64import myalgowelcome_msg = 'V2VsYzBtMyBUbyBUaGUgV29ybGQgb2YgTDFsYWMgPDM='input_msg = ':i(G#8T&KiF<F_)F`JToCggs;'right_msg = 'UmlnaHQsIGNvbmdyYXR1bGF0aW9ucyE='wrong_msg = 'V3JvbmcgRmxhZyE='print(b64decode(welcome_msg).decode())flag = input(a85decode(input_msg).decode())if not (flag.startswith('LilacCTF{') and flag.endswith('}') and (len(flag) == 26)): print(b64decode(wrong_msg).decode())else: flag = flag[9:25] res = [761104570, 1033127419, 3729026053, 795718415] key = struct.unpack('<IIII', b'1111222233334444') input = list(struct.unpack('<IIII', flag.encode())) myalgo.btea(input, 4, key) if input[0] == res[0] and input[1] == res[1] and (input[2] == res[2]) and (input[3] == res[3]): print(b64decode(right_msg).decode()) else: print(b64decode(wrong_msg).decode())
转成py文件进行代码审计
首先看见base解密以及ASCll85解密
接着我们进行一一解密首先的是
第一段解密welcome_msg = 'V2VsYzBtMyBUbyBUaGUgV29ybGQgb2YgTDFsYWMgPDM='解密:Welc0m3 To The World of L1lac <3第二段解密input_msg = ':i(G#8T&KiF<F_)F`JToCggs;'解密:Plz Input Your Flag:第三段解密right_msg = 'UmlnaHQsIGNvbmdyYXR1bGF0aW9ucyE='解密:Right, congratulations!第四段解密解密:Wrong Flag!
接着往下看
这里可以看出flag必须以 LilacCTF{ 开头,以} 结尾总长度必须等于 26
随后做切片flag = flag[9:25] ,因此实际参与加密校验的内容长度是
26 - 9 - 1 = 16个字符
也就是大括号内恰好16个字符并且由于flag.encode(),想要恰好16字节,基本意味着必须是 ASCII 单字节字符集
接着往下可关键
关键密码学点:把 16 字节当作 4×uint32
input = list(struct.unpack('<IIII', flag.encode()))关键点<IIII':小端序解析4个32-bit 无符号整数这一步强制 flag.encode() 必须 正好 16 字节,否则 struct.unpack 直接异常
同理,key固定 16 字节 key,解析成4个uint32小端字
key = struct.unpack('<IIII', b'1111222233334444')
核心校验:BTEA比较结果
myalgo.btea(input, 4, key)if input == res: print("Right")
btea(v, n, k) 常见约定:n>1 表示加密,n<-1 表示解密
这里 n=4,说明把 4-word(128-bit)块做 BTEA 运算后,结果必须精确等于
res = [761104570, 1033127419, 3729026053, 795718415]
因此考点为:逆向/还原myalgo.btea的实现细节,以及 32 位无符号溢出取模 2^32。
参考链接如下
XXTEA - Wikipediahttps://ctf.thaysan.com/ctf-and-writeups/2024-or-htb-cyber-apocalypse-challenges/cryptographie/iced-tea?utm_source=chatgpt.comhttps://ctftime.org/writeup/32328?utm_source=chatgpt.com
接着写一个EXP
import struct
DELTA = 0x45555254MASK = 0xFFFFFFFF
def _mx(y, z, s, k, p, e): a = ((z << 3) ^ (y >> 5)) + ((y << 4) ^ (z >> 2)) b = (s ^ y) + (k[(p & 3) ^ e] ^ z) return a ^ b
def btea_dec_4(block, key): v = list(block) # work on a copy rounds = 6 + 52 // 4 s = (rounds * DELTA) & MASK
for _ in range(rounds): e = (s >> 2) & 3 y = v[0]
for p in range(3, 0, -1): # 3,2,1 z = v[p - 1] v[p] = (v[p] - _mx(y, z, s, key, p, e)) & MASK y = v[p]
z = v[3] v[0] = (v[0] - _mx(y, z, s, key, 0, e)) & MASK s = (s - DELTA) & MASK
return v
def main(): ct = [761104570, 1033127419, 3729026053, 795718415] key = list(struct.unpack("<4I", b"1111222233334444")) pt = struct.pack("<4I", *btea_dec_4(ct, key)).decode("ascii") print(f"LilacCTF{{{pt}}}")
if __name__ == "__main__": main()
打印flag即可
来源:小M安全
精选阅读
交流分享
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:赛查查 Mystery Mystery《XCTF-LilacCTF 2026 Ezpython》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论