Misc1
[LitCTF2026] lit_lsb_base64
题目描述
组委会发来一张花花绿绿的 PNG,标题里写着三个字母:LSB。这和「崂山煲」可没关系——它指的是 最低有效位(Least Significant Bit) 隐写。

从 stego.png 的 R 通道 LSB 按正常 bit 顺序提取,开头得到:TGl0Q1RGe2xzYl8xc19mdW5fdzF0aF9iNHMzXzY0fQ==
Base64 解码后是:LitCTF{lsb_1s_fun_w1th_b4s3_64}
Misc2
[LitCTF2026] lit_rush_qr
题目描述
附件里有一个「闪得很快」的 GIF。有人说自己好像瞥见了 二维码 的一角,但怎么也扫不出来……
这个 GIF 的闪现帧里二维码缺了两个定位块,把第 2 帧按 QR 网格采样,确认是 33x33 模块,补回三个标准定位块和静区后成功解码。
LitCTF{qr_h1gh_3rr_c0r_r3c0v3ry}
Misc3
[LitCTF2026] lit_sstv
题目描述
你收到一段奇怪的 WAV:听起来像老式调制解调器或短波噪声。它其实不是「坏掉的音频」,而是一种把 图片编码进声音 的协议——SSTV(慢扫描电视)。

WAV 开头的 VIS 码是 44,对应 Martin M1 SSTV。按 Martin M1 解调后图片里显示的 flag 是:LitCTF{sstv_p4t13nc3}
Misc4
[LitCTF2026] lit_welcome
题目描述
组委会给你发了一张「欢迎」图片,说 flag 就在图里——可你一眼望去好像只有几行普普通通的欢迎语?
图片里只有两种颜色:#ffffff 和几乎看不出的 #feffff。

Misc5
[LitCTF2026] lit_pyjail_reader
题目描述
ez_jail
程序核心读取函数:
def safe_read(path: str) -> str:
p = path.strip()
if not p or p.startswith("-") or "\x00" in p:
raise ValueError("invalid path")
with open(p, "r", errors="replace") as f:
return f.read(MAX_FILE)
过滤只有路径不能为空,不能以-开头,不能包含null字节
但最终仍然直接调用 Python 内置 open() 打开用户提供的路径进行读取,而 open() 本身就是标准文件读取接口。存在任意文件读取

Misc6
[LitCTF2026] lit_pyjail_unicode
题目描述
ezjail_2
先看关键逻辑:
if banned(line):
conn.sendall(b"disallowed pattern in source\n")
return
out = eval(line, {"__builtins__": __builtins__})
题目会接受一行python代码,先做黑名单过滤,直接eval(),并且给了完整__builtins__
黑名单检测的是原始源码字符串,这里BANNED.search(raw)检测的是输入的文本,Python 解释器处理 Unicode 标识符时,会做 NFKC normalization(兼容规范化)。Python 文档说明 identifiers 会在解析时做 NFKC 归一化。所以可以换成全角来进行输入

RE1
[LitCTF2026] lit_rc4_variant
题目描述
程序实现一种 64 字节状态 的流密码式异或:初始化与 RC4 类似但模 64,且输出字节与 S[v7] + S[v10](模 256)异或——与「标准 RC4 输出 S[(S[i]+S[j])&255]」不同。密钥为 ASCII 字符串,放在 .rdata。

发现密钥已经明文存放key 为:lit_rc4_key!
在伪代码中发现程序将用户输入加密后与固定数据比较:
![]()
g_cipher 为目标密文,长度为 0x1D = 29 字节。

目标密文为cipher = bytes.fromhex(
"7b3d38774e72427d4537760f53534f66371775375f495872747f791f3a"
)
Exp: key = b"lit_rc4_key!"
cipher = bytes.fromhex(
"7b3d38774e72427d4537760f53534f66371775375f495872747f791f3a"
)
def decrypt(data):
S = list(range(64))
# KSA
j = 0
for i in range(64):
j = (j + S[i] + key[i % len(key)]) & 0x3f
S[i], S[j] = S[j], S[i]
out = bytearray(data)
# PRGA
i = 0
j = 0
for n in range(len(out)):
i = (i + 1) % 64
j = (j + S[i]) % 64
S[i], S[j] = S[j], S[i]
k = (S[(S[i] + S[j]) & 0x3f] + S[i]) & 0xff
out[n] ^= k
return bytes(out)
print(decrypt(cipher))
RE2
[LitCTF2026] lit_tea_standard
题目描述
程序把你输入的 flag 做 PKCS#7 填充 到 8 字节倍数,再按 8 字节一块 做 标准 TEA(32 轮,delta = 0x9E3779B9),与内存中的密文逐字节比较。密钥 k[0..3] 以 4×uint32 形式存放在 .rdata。
![]()
填充后的长度必须是32字节
循环里有sub r8d, 0x61c88647
cmp r8d, 0xc6ef3720其中0x61c88647 == -0x9E3779B9 mod 2^32
0xc6ef3720 == 0x9E3779B9 * 32 mod 2^32这是标准的TEA 32轮加密
从加密公式反推四个常量
k0 = 0xA11CEFAC
k1 = 0xB00B1E00
k2 = 0xCAFEBABE
k3 = 0xDEADBEEF
提取密文cipher = bytes.fromhex(
"edef21feb79b3cb0"
"1e9372e2023e29bc"
"36f70c922e5aae46"
"44fa45251ae58c87"
)
解密脚本from struct import unpack, pack
cipher = bytes.fromhex(
"edef21feb79b3cb0"
"1e9372e2023e29bc"
"36f70c922e5aae46"
"44fa45251ae58c87"
)
key = [0xA11CEFAC, 0xB00B1E00, 0xCAFEBABE, 0xDEADBEEF]
delta = 0x9E3779B9
def tea_decrypt_block(block):
v0, v1 = unpack("<2I", block)
s = (delta * 32) & 0xffffffff
for _ in range(32):
v1 = (v1 - ((((v0 << 4) + key[2]) ^ (v0 + s) ^ ((v0 >> 5) + key[3])) & 0xffffffff)) & 0xffffffff
v0 = (v0 - ((((v1 << 4) + key[0]) ^ (v1 + s) ^ ((v1 >> 5) + key[1])) & 0xffffffff)) & 0xffffffff
s = (s - delta) & 0xffffffff
return pack("<2I", v0, v1)
pt = b"".join(tea_decrypt_block(cipher[i:i+8]) for i in range(0, len(cipher), 8))
pad = pt[-1]
flag = pt[:-pad]
print(flag.decode())
RE3
[LitCTF2026] lit_b64_alphabet
题目描述
程序把你输入的 flag 按标准 Base64 的分组方式编码,但 64 个输出字符的字母表 不是 RFC 默认顺序,而是程序里保存的一串置换。比对对象是内存中的 期望密文字符串。
![]()
期望密文与自定义字母表
标准的base64表是ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/
把自定义密文里的字符,按自定义字母表的下标映射回标准 Base64 表。
import base64
cipher = "zjA5lToj9PUAGn2O+v6TRPosgYWB6noyGjhBgjfwyl=="
custom = "2KuEphj84USZF67iloxzfYd+MrDgRG9yLwBnHAXcJq3eCN/s1bOQ5TvPa0tVkWmI"
standard = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
table = str.maketrans(custom, standard)
standard_b64 = cipher.translate(table)
flag = base64.b64decode(standard_b64)
print(flag.decode())
RE4
[LitCTF2026] lit_xor_chain
题目描述
程序读入一行字符串,对 每个字节 先做异或常数、再加常数,再与内存里的 期望数组 逐字节比较。逻辑与常见逆向入门讲义中的「循环 + xor + add + 数组比对」一致。
140001491: lea rcx, [rsp+20h] ; 输入
140001496: call strlen
14000149b: cmp rax, 1Eh
14000149f: jne Wrong
输入长度必须是0x1E = 30
1400014c0: movzx edx, byte ptr [rcx]
1400014c3: xor edx, 52h
1400014c6: add edx, 5
1400014c9: cmp dl, byte ptr [r8]
1400014cc: jne Wrong核心循环的逻辑是输入 → XOR 0x52 → +5 → 和数组比较
提取期望数组

expected = bytes.fromhex(
"23 40 2B 16 0B 19 2E 25"
"3C 29 67 68 12 2F 42 25"
"12 2B 3F 3C 41 12 38 3B"
"3B 12 42 3E 78 34"
)
脚本:expected = bytes.fromhex(
"23 40 2B 16 0B 19 2E 25"
"3C 29 67 68 12 2F 42 25"
"12 2B 3F 3C 41 12 38 3B"
"3B 12 42 3E 78 34"
)
flag = bytes(((b - 5) & 0xff) ^ 0x52 for b in expected)
print(flag.decode())
RE5
[LitCTF2026] lit_xtea_tweak
题目描述
流程与「分组 + 填充 + 分块加密」类似 XTEA,但轮常数 delta 被换成 0xDEADBEEF(标准文献里常见的是 0x9E3779B9)。若直接套用网上搜到的 XTEA 脚本而不改 delta,解密会失败。
密钥是key = [
0x11111111,
0x22222222,
0x33333333,
0x44444444
]
密文是cipher = bytes.fromhex(
"e3ee1ee7d3a7966f"
"c6a7b9e1b94e6786"
"5f0304a6dbbbb940"
"563af79eee64d406"
)
解密脚本
import struct
cipher = bytes.fromhex(
"e3ee1ee7d3a7966f"
"c6a7b9e1b94e6786"
"5f0304a6dbbbb940"
"563af79eee64d406"
)
key = [0x11111111, 0x22222222, 0x33333333, 0x44444444]
delta = 0xDEADBEEF
MASK = 0xffffffff
def xtea_decrypt_block(block):
v0, v1 = struct.unpack("<2I", block)
s = (delta * 32) & MASK
for _ in range(32):
v1 = (v1 - (((((v0 << 4) ^ (v0 >> 5)) + v0) & MASK) ^ ((s + key[(s >> 11) & 3]) & MASK))) & MASK
s = (s - delta) & MASK
v0 = (v0 - (((((v1 << 4) ^ (v1 >> 5)) + v1) & MASK) ^ ((s + key[s & 3]) & MASK))) & MASK
return struct.pack("<2I", v0, v1)
pt = b"".join(
xtea_decrypt_block(cipher[i:i+8])
for i in range(0, len(cipher), 8)
)
pad = pt[-1]
flag = pt[:-pad]
print(flag.decode())