2025lrtctf 决赛 部分wp

这是我和PwnBaby师傅打的所有题目。

WEB

the-moment-of-token

看源码,有注释:

不难看出这是一道时间口令的题目,int(time.time())返回服务器当前时间的时间戳,password需要我们写入和时间戳相等的数(误差不超过五秒)。

去网上直接搜当前时间戳,然后写入password,例如在这里我写入1762678735.

显示loginsuccess,但是就没有任何东西了。

这时候我们看到token,在网上可以找到其他师傅的资料:

从0学习CTF-从ctfhub来了解JWT - 知乎

我们查看cookie,

cookie里有token,放到解码工具里:

发现有gift,base32解码得到flag。


嘉豪的文件管理系统

根据hint,在getFileList.php处可以对order使用get传参

猜出/?order=id,然后我们就可以直接通过sqlmap,

python sqlmap.py -u http://10.3.37.51:33083/getFileList.php/?order=id --os-shell --batch -v 3

直接进入shell。

得到shell以后,我们ls ../../../,可以看到根目录下的文件,其中有一个readFlag,刚好是题目附件给的c语言文件名。

这时候我们直接输入:

1
/readFlag

就能直接得到flag。

Crypto

哈基米喔

网上找哈基米语翻译器,然后设置方言lrtctf,就可以得到flag。

ez_RSA

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
from Crypto.Util.number import long_to_bytes, bytes_to_long, inverse
from Crypto.Cipher import AES
from itertools import product

# ==== 题面给定常量 ====
e = 65537
n1 = 3555087919989098257857834211749744824870239043650546563151780797486335436386001133312933656366557213148289559331691151443978421360808772013642281878942757
n2 = 4345900529827777003797094945560455642537763399598054811539798178904374025913276893502212113082283464694428541337068771706146665310988593260568079353689961
out = b'"f\x89S=FS\x1c\x1eS\xfd\rK\xb4!\xd2h\xd1\xef\xbf\xa0\xe6*y\t\x99\xadu\xe9V\xce\xee'
c1 = 166299793440675310861219950396885269502382069930359224840179689610508541015159486546503897776299474335015611243766205672085632531343085227394978200964153
c2 = bytes.fromhex("a963d1fa0a8d9ebaed03f569297f1bab")
r_high = 56049175294030724512817953407773124612093657209

# ==== 工具函数 ====
def is_pkcs7_block(b: bytes) -> bool:
if not b: return False
pad = b[-1]
return 1 <= pad <= 16 and b.endswith(bytes([pad]) * pad)

def unpad_pkcs7(b: bytes) -> bytes:
pad = b[-1]
return b[:-pad]

def reconstruct_p_with_keystream(keystream32: bytes) -> int:
obf = out[::-1]
P = bytes([obf[i] ^ keystream32[i] ^ i for i in range(32)])
return int.from_bytes(P, 'big')

def build_keystream32_from_key1(key1: bytes) -> bytes:
# 重复到至少 32 字节,然后截断
t = (key1 * ((32 + len(key1) - 1) // len(key1)))[:32]
assert len(t) == 32
return t

# 已知 r_high = r >> 100,r 是 256-bit prime
# 因此 r 的前 128bit 等于 (r_high >> 28)
r_prefix16 = (r_high >> (156 - 128)).to_bytes(16, 'big')

# 先用 AES 侧做筛选:尝试 key1 的周期长度 L,构造 key1 的前16字节,解出 aes_key,要求 PKCS#7 合法
candidates_key1 = [] # (key1_period_bytes,)
for L in range(1, 5): # 可按需把上界改成 8,但 1~4 通常就够
# key1 是任意 L 字节;我们先枚举 L 字节,再扩展出前16字节
for tup in product(range(256), repeat=L):
key1_period = bytes(tup)
# 生成 key1 的前16字节(周期重复)
key1_prefix16 = (key1_period * ((16 + L - 1)//L))[:16]
# aes_key = key1_prefix16 ^ r_prefix16
aes_key = bytes(a ^ b for a, b in zip(key1_prefix16, r_prefix16))
pt_block = AES.new(aes_key, AES.MODE_ECB).decrypt(c2)

if not is_pkcs7_block(pt_block):
continue # 先用严格的 PKCS#7 过滤
# 进一步可选:去填充后的数据应基本可打印(CTF flag 一般 ASCII)
try:
m = unpad_pkcs7(pt_block)
if all(32 <= x <= 126 for x in m):
candidates_key1.append(key1_period)
except Exception:
pass

if candidates_key1:
# 找到就先不扩大 L,先去 RSA 那边验证
break

# 用 RSA 侧验证候选 key1:要求从 out 还原出的 p 能整除 n1
found = None
for key1_period in candidates_key1:
keystream32 = build_keystream32_from_key1(key1_period)
p = reconstruct_p_with_keystream(keystream32)
if p > 1 and n1 % p == 0:
found = (key1_period, p)
break

if not found:
print("[!] 没在 L<=4 的范围内命中。可以把上面的 L 上限改到 8 再试,或在 AES 过滤处放宽“可打印”条件。")
raise SystemExit()

key1_period, p = found
q = n1 // p
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m1 = pow(c1, d, n1)
flag1 = long_to_bytes(m1)

# 还原 AES 部分
key1_prefix16 = (key1_period * ((16 + len(key1_period) - 1)//len(key1_period)))[:16]
aes_key = bytes(a ^ b for a, b in zip(key1_prefix16, r_prefix16))
flag2_padded = AES.new(aes_key, AES.MODE_ECB).decrypt(c2)
flag2 = unpad_pkcs7(flag2_padded)

print("[+] key1(period) =", key1_period)
print("[+] p =", p)
print("[+] q =", q)
print("[+] flag1 =", flag1)
print("[+] flag2 =", flag2)
print("[+] flag =", flag1 + flag2)

GPT一把梭。

这里有一串神秘代码

pwnw{vti_bbs_xce_zn_etdrr}

前四个对应flag,试试凯撒偏移。

  • p->f:10
  • w->l:11
  • n->a:13
  • w->g:16

可以发现增量是+1,+2,+3的规律,后面的偏移依然递增。

{_}使偏移递增,但是不动,只对字母做移位。

按上述规则整体解密:

1
pwnw{vti_bbs_xce_zn_etdrr}  →  flag{wow_you_are_so_smart}

Misc

Forensics1

快速提取pcap文件中所有的HTTP请求,找到(形式:lrtctf{xxxx.co.xxxx/w0ks//?YO=xxxxxxx})

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
  ─[pwn@pwn-VVP] - [~/Desktop/work/Irtctf/misc1] - [Sun Nov 09, 18:50]
└─[$]> tshark -r 7.pcap -Y "http.request" -T fields -e http.host -e http.request.uri

tsdandassociates.co.sz /w0ks/?92444881
tsdandassociates.co.sz /favicon.ico
tsdandassociates.co.sz /w0ks//?YO=1702920835
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
** (tshark:124963) 18:50:30.129767 [Epan WARNING] -- Dissector bug, protocol CLDAP, in packet 6265: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
** (tshark:124963) 18:50:30.130471 [Epan WARNING] -- Dissector bug, protocol CLDAP, in packet 6279: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
** (tshark:124963) 18:50:30.132774 [Epan WARNING] -- Dissector bug, protocol CLDAP, in packet 6332: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
** (tshark:124963) 18:50:30.133400 [Epan WARNING] -- Dissector bug, protocol LDAP, in packet 6346: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
** (tshark:124963) 18:50:30.144990 [Epan WARNING] -- Dissector bug, protocol CLDAP, in packet 6630: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
** (tshark:124963) 18:50:30.147594 [Epan WARNING] -- Dissector bug, protocol LDAP, in packet 6689: ./epan/dissectors/packet-ldap.c:2180: failed assertion "recursion_depth <= 100"
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *
239.255.255.250:1900 *