WHUCTF2025 WP
WP
MISC
[签到]益智游戏
1.数独
数独还没做完,直接爆破就出了。

得到WHUCTF{Little_games_reall
2.数织
队友很强,直接手搓了。

得到y_train_your_brain_&_play
3.鬼脚图
直接白给了。。。
得到_more_in_cn.puzzle_website}
哪里有文档
附件是一个docx文档,而且直接给出了part1。
part2:ctrl+A一下,发现在文章最后,改一下字体颜色就看到了。
part3:ctrl+F一下,搜索part,发现一个非常隐秘的文本框,放大一下,找到part3
part4: 由于docx文档本质是压缩包,可以解压出一个文件夹,在docprops文件夹下边的custom文件中发现part4.
part5: 在word文件夹下边的vbaProject.bin文件中发现非疑似part5的字符串,后来试了一下还真是。
解密器: 在文档页脚的地方发现有东西,换一下字体颜色便发现,by Abracadabra encoder,上网搜索之后发现是魔曰。
解密步骤: 将5段part拼接后进行base解码,得到一段文字,
鹏无局,无以致天,光有能听,竹有善然,虽无楼鸢之彰,亦报以畅看瀚水,求将与其家,而任想买以星者,星也,后曲之雨,选之霞而称之茶也,寒冰悠见,写者旅之,叶流而清梦看也,报在莹鹤。
放在魔曰解码器中解码得到flag
WHUCTF{always_follow_your_heart}
行星防御理事会
附件为一个wav音频,放到Audacity中查看波形图,觉得类似sstv,放入RX-SSTV工具中
发现有密码2024-YR4,联想需要密码的wav音频隐写,在Deepsound中输入密码后得到附件,解压后是一个变形过的二维码和一个变形脚本。编写恢复脚本
1 | from PIL import Image |

扫码得到flag。
青轴还是红轴
wireshark打开附件,发现全是USB协议流量,结合题目猜测是键盘流量。
在kali中使用tshark提取键盘流量
tshark -r raw_keyboard.pcapng -T fields -e usbhid.data > usbdata.txt
然后用脚本对提取出的流量进行处理。
1 | normalKeys = {"04": "a", "05": "b", "06": "c", "07": "d", "08": "e", "09": "f", "0a": "g", "0b": "h", "0c": "i", |
输出为output :ijniokj tfcfg fgtrdcv iuhbghj jkiuhnm ygvgh_ijnjkmn uhbn tgbnjujm ghytfvb_fghygvb ygbnju <DEL>_eszsd dfresxc hgvbhuhb_uyhbv dcfvg hbu hjkijnm uygbn eszsdx
低头看看键盘,就会发现每一组字母都是在键盘上拼出一个一个字母的形状。
最后得到prefer_blue_to_red_switch
Crypto
LFSR_Signin
题目:
1 | from Crypto.Util.number import * |
分析: 通过代码可以发现,生成时,每个最后边生成的二进制数字都是由前边的一些数字异或得到,逆向来分析,同理,每个靠前的数字也都可以由后边的一些数字得到。
由此,我们便可以从第255位开始,反向一个一个求出前255个数字。
解答:
1 | from Crypto.Util.number import long_to_bytes |
得到flagwhuctf{quit3_ea5y_Sign1n_R1ght?}
RSAASR
题目: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
26from Crypto.Util.number import getPrime, isPrime, bytes_to_long, long_to_bytes
def generate(bit):
while True:
p = getPrime(bit)
q = rev(p)
if isPrime(q):
break
return p, q
rev = lambda x: int(bin(x)[:1:-1], 2) # 二进制反转整数
flag = b"??????"
p, q = generate(512) #p,q相互反转
n = p * q
e = 65537
c = pow(bytes_to_long(flag), e, n)
print(f"n={n}")
print(f"e={e}")
print(f"c={c}")
"""
n=89260288112458610375700543707493254232809306221431627423709616690294586688526862549905410606087786699242563057156677052913617284849136716660502920085006747882186134482309361626185003661858419446057779826705477210404882478906671799290032009310469036065257789664458482249297907582602310789531951177426393110643
e=65537
c=34953739673730018843655174314108340461262205663805875643136393046216892771730195951086950749299233260612871271352091804579992550715616098448464010205976283620661044089962336249776561849400241337436006809354102892524119722533361144592982143227173415365371111087024439252557012289555411199194971295453523635612
"""
分析: 剪枝题,直接打板子,具体可参考Crypto趣题-剪枝 | 糖醋小鸡块的blog
解答: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
37from Crypto.Util.number import *
n = 89260288112458610375700543707493254232809306221431627423709616690294586688526862549905410606087786699242563057156677052913617284849136716660502920085006747882186134482309361626185003661858419446057779826705477210404882478906671799290032009310469036065257789664458482249297907582602310789531951177426393110643
c = 34953739673730018843655174314108340461262205663805875643136393046216892771730195951086950749299233260612871271352091804579992550715616098448464010205976283620661044089962336249776561849400241337436006809354102892524119722533361144592982143227173415365371111087024439252557012289555411199194971295453523635612
e = 65537
def find(ph,qh,pl,ql):
l = len(ph)
tmp0 = ph + (512-2*l)*"0" + pl
tmp1 = ph + (512-2*l)*"1" + pl
tmq0 = qh + (512-2*l)*"0" + ql
tmq1 = qh + (512-2*l)*"1" + ql
if(int(tmp0,2)*int(tmq0,2) > n):
return
if(int(tmp1,2)*int(tmq1,2) < n):
return
if(int(pl,2)*int(ql,2) % (2**(l-1)) != n % (2**(l-1))):
return
if(l == 256):
pp0 = int(tmp0,2)
if(n % pp0 == 0):
pf = pp0
qf = n//pp0
phi = (pf-1)*(qf-1)
d = inverse(e,phi)
m1 = pow(c,d,n)
print(long_to_bytes(m1))
exit()
else:
find(ph+"1",qh+"1","1"+pl,"1"+ql)
find(ph+"0",qh+"1","1"+pl,"0"+ql)
find(ph+"1",qh+"0","0"+pl,"1"+ql)
find(ph+"0",qh+"0","0"+pl,"0"+ql)
find("1","1","1","1")
Pollard & Williams
题目:
1 | from Crypto.Util.number import * |
分析: 可以看出,flag被分成了四部分,而且前两个之间存在关系,后两个之间存在关系。
重点在于p2 = (p1 - 1) // 2,p2 = (p1 + 1) // 2,我们来逐个分析。
Part1:
$\because p_2=(p_1-1)//2$
$\therefore 2\cdot N_2=2\cdot p_2\cdot q_2=(p_1-1)q_2$
即 $p_1-1|2\cdot N_2$
$\therefore a^{2\cdot N_2} \equiv a^{q_2\cdot (p1-1)}\equiv1 \mod p_1$
$\therefore a^{2\cdot N_2}-1 = kp_1$
$当然 a^{2\cdot N_2}-1 \mod N_1 = kp_1$
$\therefore p_1=gcd(a^{2\cdot N2}-1 \mod N_1,N_1)$
之后$q_1,p_2,q_2$便都能求出来,之后就是简单的RSA
Part2:
对于william p+1光滑算法中,p+1为光滑数,设B为p+1最大的光滑因子,要求得下标为B的阶乘B!卢卡斯序列,目的就是保证(p+1)|B!,而本题中能够看出$2N_4=(p_3+1)q_4$必然是$(p_3+1)$的整数倍。
之后就利用卢卡斯序列的性质来求$p_3。即p_3|(V_{2*N_4}-2)$
$\therefore p_3|gcd(V_{2*N_4}-2,N_3)$
$\therefore p_3=gcd(V_{2*N_4}-2\mod N_3,N_3)$
之后同上,脚本如下
解答:
1 | from sympy import Matrix |
ez_lattice
题目:
1 | from Crypto.Util.number import * |
参考: Wiener’s v.s Lattices —— Ax≡y(mod P)的方程解法笔记 | Tover’s Blog
分析: flag又被分成了两部分,感谢出题人总是先送我一半flag。
part1: 非常常规的格,构造
part2:
参考了大佬博客之后才有思路,总之就是要把X[2],X[3]分开来讨论,构造格
AM=B :
由于这个格并不满足Hermite定理,因此需要配平,配平系数可以参考大佬博客。
其中$\gamma=\frac12,\alpha=\frac{5}{14},\beta=0$
求出矩阵B之后再乘上M的逆,从而得到矩阵A,再利用a中关系求出$a_1,a_2$
解答:
1 | from Crypto.Util.number import long_to_bytes |
Siesta’s_revenge
题目:
1 | from Crypto.Util.Padding import pad |
分析: 怎么说呢,跟上题不能说十分类似吧,只能说一模一样,只是s变大了而已,但实践证明,它并没有影响我们格攻击成功。(所以说不理解出题人的预期效果是什么,希望能讲一讲)
那么攻击思路就同上
解答:
1 | from Crypto.Util.Padding import pad |
总结
打ctf以来最有感觉的一场比赛,酣畅淋漓,也是燃尽了,感谢队友的带飞。
总结自己的做题量还是太少,对于很多原本应该一眼看出来的问题还是犹犹豫豫的,甚至不敢相信自己思路是对的,后来被hint拉回原本的思路才坚持做出正确结果。
因此日后还要加倍努力,争取不拖队友后腿!







