ctfshow--Crypto(funnyrsa1-密码2)解题步骤

前言

相关密码学内容专栏:CTF密码学综合教学指南_其实防守也摸鱼的博客-CSDN博客

前面内容:

ctfshow--Crypto(crypto1-14)解题步骤-CSDN博客

ctfshow--Crypto(萌新_密码5-easyrsa)解题步骤-CSDN博客

密码学强力工具:要学使用 CyberChef 吗?这一篇就够了-CSDN博客

密码学综合教学指南(共11章,已更完):CTF密码学综合教学指南--第一章-CSDN博客

其他内容可看我主页自行了解

本章内容

funnyrsa1

解题步骤

下载附件

一、题目背景(一眼看懂)

给出两组 RSA:

  • RSA1:(e1, p1, q1, c1)
  • RSA2:(e2, p2, q2, c2)

特点:

  • p1 = p2 = p(同一个大质数)
  • q1、q2 不同
  • e1、e2 都很大,且 gcd(e1, e2) = b(公共因子)
  • 目标:求明文 m,最后开平方得到 flag

属于 共享质数 + 共模 + 公共指数因子 + CRT + 开平方 的经典 RSA 变形题。


二、涉及知识点(完整清单)

1. RSA 基础

  • n = p·q
  • φ(n) = (p−1)(q−1)
  • 加密:c = m^e mod n
  • 解密:m = c^d mod n,d = e⁻¹ mod φ(n)

2. 共享质数(Common Prime)

  • p1=p2=p
  • n1 = p·q1,n2 = p·q2

3. 公共指数因子(e1、e2 有公因子 b)

  • e1 = b·a1
  • e2 = b·a2
  • gcd(a1, φ(n1)) = 1,gcd(a2, φ(n2)) = 1

4. 逆元(modular inverse)

  • a 在模 m 下可逆 ⇔ gcd (a,m)=1
  • 用 gmpy2.invert ()

5. 中国剩余定理 CRT

  • 解同余方程组:x ≡ a1 mod m1x ≡ a2 mod m2...
  • 模数两两互质时有唯一解 mod M = m1・m2・...

6. 开平方(整数平方根)

  • gmpy2.iroot (m, 2):返回 (s, True/False),s²=m

7. long_to_bytes

  • 大整数 → bytes 字符串

代码

复制代码
from Crypto.Util.number import long_to_bytes,bytes_to_long
import gmpy2
from libnum import *
 
e1 = 14606334023791426
p1 = 121009772735460235364940622989433807619211926015494087453674747614331295040063679722422298286549493698150690694965106103822315378461970129912436074962111424616439032849788953648286506433464358834178903821069564798378666159882090757625817745990230736982709059859613843100974349380542982235135982530318438330859
q1 = 130968576816900149996914427770826228884925960001279609559095138835900329492765336419489982304805369724685145941218640504262821549441728192761733409684831633194346504685627189375724517070780334885673563409259345291959439026700006694655545512308390416859315892447092639503318475587220630455745460309886030186593
c1 = 11402389955595766056824801105373550411371729054679429421548608725777586555536302409478824585455648944737304660137306241012321255955693234304201530700362069004620531537922710568821152217381257446478619320278993539785699090234418603086426252498046106436360959622415398647198014716351359752734123844386459925553497427680448633869522591650121047156082228109421246662020164222925272078687550896012363926358633323439494967417041681357707006545728719651494384317497942177993032739778398001952201667284323691607312819796036779374423837576479275454953999865750584684592993292347483309178232523897058253412878901324740104919248
n1 = p1 * q1
 
e2 = 13813369129257838
p2 = 121009772735460235364940622989433807619211926015494087453674747614331295040063679722422298286549493698150690694965106103822315378461970129912436074962111424616439032849788953648286506433464358834178903821069564798378666159882090757625817745990230736982709059859613843100974349380542982235135982530318438330859
q2 = 94582257784130735233174402362819395926641026753071039760251190444144495369829487705195913337502962816079184062352678128843179586054535283861793827497892600954650126991213176547276006780610945133603745974181504975165082485845571788686928859549252522952174376071500707863379238688200493621993937563296490615649
c2 = 7984888899827615209197324489527982755561403577403539988687419233579203660429542197972867526015619223510964699107198708420785278262082902359114040327940253582108364104049849773108799812000586446829979564395322118616382603675257162995702363051699403525169767736410365076696890117813211614468971386159587698853722658492385717150691206731593509168262529568464496911821756352254486299361607604338523750318977620039669792468240086472218586697386948479265417452517073901655900118259488507311321060895347770921790483894095085039802955700146474474606794444308825840221205073230671387989412399673375520605000270180367035526919
n2 = p2 * q2
 
p = p1
phi1 = (p - 1) * (q1 - 1)
phi2 = (p - 1) * (q2 - 1)
b = gmpy2.gcd(e1, phi1)
a1 = e1 // b
a2 = e2 // b
bd1 = gmpy2.invert(a1, phi1)
bd2 = gmpy2.invert(a2, phi2)
 
mb1 = pow(c1, bd1, n1)
mb2 = pow(c2, bd2, n2)
c3 = mb1 * mb2 % p
c2 = mb2 % q2
c1 = mb1 % q1
 
res = solve_crt([c1, c2, c3], [q1, q2, p])   #crt中国同余定理,求出特解作为
# print(res)
 
n = q1 * q2
f = (q1 - 1) * (q2 - 1)
m = res % n
d2 = gmpy2.invert(7, f)
m = pow(m, d2, n)
msg = gmpy2.iroot(m, 2)[0]
print(long_to_bytes(msg))

中间的\xcf\x86为utf-8编码,转换得到字符:

flag{gcd_e&φ_isn't_1}

代码整体功能解析

这段代码是针对多组 RSA 加密参数 的解密脚本,核心利用了 RSA 的数学原理(欧拉函数、模逆、中国剩余定理 CRT),结合多组加密参数的公共因子(共享的大素数p)来还原明文,最终解码出字节形式的消息。

分步逐行解释

1. 导入依赖库

复制代码
from Crypto.Util.number import long_to_bytes,bytes_to_long
import gmpy2
from libnum import *
  • Crypto.Util.number:PyCryptodome 库的工具模块,提供long_to_bytes(大整数转字节)、bytes_to_long(字节转大整数),用于最终明文的字节转换。
  • gmpy2:高性能数论库,支持大整数运算、模逆、最大公约数(gcd)、整数开方等核心操作。
  • libnum:数论工具库,提供solve_crt(中国剩余定理求解)等便捷函数。

2. 定义 RSA 加密参数(两组)

复制代码
# 第一组RSA参数
e1 = 14606334023791426  # 公钥指数1
p1 = 121009772735460235364940622989433807619211926015494087453674747614331295040063679722422298286549493698150690694965106103822315378461970129912436074962111424616439032849788953648286506433464358834178903821069564798378666159882090757625817745990230736982709059859613843100974349380542982235135982530318438330859  # 大素数1(两组共享)
q1 = 130968576816900149996914427770826228884925960001279609559095138835900329492765336419489982304805369724685145941218640504262821549441728192761733409684831633194346504685627189375724517070780334885673563409259345291959439026700006694655545512308390416859315892447092639503318475587220630455745460309886030186593  # 大素数2
c1 = 11402389955595766056824801105373550411371729054679429421548608725777586555536302409478824585455648944737304660137306241012321255955693234304201530700362069004620531537922710568821152217381257446478619320278993539785699090234418603086426252498046106436360959622415398647198014716351359752734123844386459925553497427680448633869522591650121047156082228109421246662020164222925272078687550896012363926358633323439494967417041681357707006545728719651494384317497942177993032739778398001952201667284323691607312819796036779374423837576479275454953999865750584684592993292347483309178232523897058253412878901324740104919248  # 密文1
n1 = p1 * q1  # 模数1(RSA核心,n=pq)

# 第二组RSA参数
e2 = 13813369129257838  # 公钥指数2
p2 = p1  # 注意:p2和p1是同一个数(共享素数)
q2 = 94582257784130735233174402362819395926641026753071039760251190444144495369829487705195913337502962816079184062352678128843179586054535283861793827497892600954650126991213176547276006780610945133603745974181504975165082485845571788686928859549252522952174376071500707863379238688200493621993937563296490615649  # 大素数3
c2 = 7984888899827615209197324489527982755561403577403539988687419233579203660429542197972867526015619223510964699107198708420785278262082902359114040327940253582108364104049849773108799812000586446829979564395322118616382603675257162995702363051699403525169767736410365076696890117813211614468971386159587698853722658492385717150691206731593509168262529568464496911821756352254486299361607604338523750318977620039669792468240086472218586697386948479265417452517073901655900118259488507311321060895347770921790483894095085039802955700146474474606794444308825840221205073230671387989412399673375520605000270180367035526919  # 密文2
n2 = p2 * q2  # 模数2
  • 核心关键点:p1 = p2(两组 RSA 共享同一个大素数p),这是破解的核心前提(RSA 的 "共模攻击"/"共享素数攻击" 基础)。
  • RSA 加密公式:c = m^e mod nc密文,m明文,e公钥指数,n模数)。

3. 计算欧拉函数与模逆(破解第一 / 二组 RSA 的中间步骤)

复制代码
p = p1  # 提取共享素数p
phi1 = (p - 1) * (q1 - 1)  # 第一组的欧拉函数φ(n1)=φ(pq)=(p-1)(q-1)(RSA核心,用于求私钥)
phi2 = (p - 1) * (q2 - 1)  # 第二组的欧拉函数φ(n2)

b = gmpy2.gcd(e1, phi1)  # 计算e1和phi1的最大公约数(处理e与phi不互质的情况)
a1 = e1 // b  # 约简e1:e1 = b * a1
a2 = e2 // b  # 约简e2:e2 = b * a2(这里b是e1和phi1的gcd,代码中复用了b,实际逻辑是对e2做同比例约简)

bd1 = gmpy2.invert(a1, phi1)  # 求a1在模phi1下的模逆(即a1^-1 mod phi1)
bd2 = gmpy2.invert(a2, phi2)  # 求a2在模phi2下的模逆
  • 欧拉函数 φ(n):对于素数 p、q,φ(pq)=(p-1)(q-1),RSA 中私钥d满足e*d ≡ 1 mod φ(n)(即de的模逆)。
  • 模逆存在条件:gcd(e, φ(n))=1;若不互质(这里b=gcd(e1,phi1)≠1),则先约简ea1=e1/b,再求a1的模逆。

4. 还原部分明文(模幂运算)

复制代码
mb1 = pow(c1, bd1, n1)  # 模幂:c1^bd1 mod n1 → 得到m^b mod n1(因为 (m^e1)^bd1 = m^(b*a1*bd1) ≡ m^b mod n1)
mb2 = pow(c2, bd2, n2)  # 同理:c2^bd2 mod n2 → m^b mod n2
  • 模幂运算pow(base, exp, mod)是 Python 高效计算大整数幂取模的方式,核心是利用 RSA 解密的逆运算:m ≡ c^d mod n(这里是约简后的m^b)。

5. 拆分部分明文到不同模数下

复制代码
c3 = mb1 * mb2 % p  # 结合mb1和mb2,取模共享素数p,得到m^b mod p
c2 = mb2 % q2       # 提取mb2在模q2下的结果:m^b mod q2
c1 = mb1 % q1       # 提取mb1在模q1下的结果:m^b mod q1
  • 由于p是共享素数,mb1 ≡ m^b mod pmb2 ≡ m^b mod p,因此mb1*mb2 mod p等价于(m^b)*(m^b) mod p,但代码中此处逻辑是提取m^b在三个互质模数(p、q1、q2)下的结果,为后续 CRT 做准备。

6. 中国剩余定理(CRT)合并结果

复制代码
res = solve_crt([c1, c2, c3], [q1, q2, p])  # 用CRT求解:x ≡ c1 mod q1、x ≡ c2 mod q2、x ≡ c3 mod p 的最小正整数解
  • 中国剩余定理(CRT):若模数q1、q2、p两两互质,则存在唯一的x mod (q1*q2*p)满足上述同余式;这里x就是m^b(因为c1=m^b mod q1c2=m^b mod q2c3=m^b mod p)。
  • libnum.solve_crt(余数列表, 模数列表):直接返回 CRT 的解。

7. 最终还原明文

复制代码
n = q1 * q2  # 新模数(q1和q2的乘积)
f = (q1 - 1) * (q2 - 1)  # 新的欧拉函数φ(n)=φ(q1*q2)=(q1-1)(q2-1)
m = res % n  # 提取res在模n下的结果(即m^b mod n)
d2 = gmpy2.invert(7, f)  # 求7在模f下的模逆(7是新的公钥指数,此处是自定义的解密步骤)
m = pow(m, d2, n)  # 模幂:(m^b)^d2 mod n → 还原出m^(b*d2),此处b*d2≡1 mod f,最终得到m^2(结合后续开方)
msg = gmpy2.iroot(m, 2)[0]  # 对m开平方(因为上一步得到的是m²),取整数根
print(long_to_bytes(msg))  # 大整数转字节,输出明文
  • 核心逻辑:
    1. 前序步骤得到res = m^b(b 是约简因子),此处通过模n=q1*q2缩小范围;
    2. 自定义公钥指数7,求其模逆d2,通过pow(m, d2, n)还原出
    3. 开平方得到原始明文msg,再转字节输出。

关键数学原理总结

  1. RSA 核心c = m^e mod n,解密需求d=e^-1 mod φ(n),则m = c^d mod n
  2. 共享素数攻击 :两组 RSA 共享p,可拆分模数为p*q1p*q2,分别还原部分明文;
  3. 中国剩余定理:将多个模数下的同余结果合并为统一的明文;
  4. 模逆与约简 :处理eφ(n)不互质的情况,通过约简e后求逆,再还原明文。

代码执行结果

运行代码后,long_to_bytes(msg)会输出最终的明文(通常是字符串 / 字节流),这也是该脚本的最终目标 ------ 破解多组共享素数的 RSA 加密,还原原始消息。


funnyrsa2

解题步骤

下载附件,py代码

题目考点分析

这道题是RSA 加密算法的变体(3 素数 RSA),核心考点包括:

  1. RSA 基本原理:理解 RSA 的加密 / 解密公式、公钥 / 私钥的生成逻辑;
  2. RSA 扩展(多素数 RSA):普通 RSA 是 2 个素数,本题是 3 个素数,需掌握多素数场景下欧拉函数 φ(n) 的计算(φ(pqr)=(p-1)(q-1)(r-1),而非普通的 (p-1)(q-1));
  3. 大整数分解 :已知 n=pqr,解题的核心瓶颈是将 n 分解为 3 个 80 位的素数(80 位素数分解是本题的关键操作);
  4. 模逆元计算:通过 e 和 φ(n) 计算私钥 d(模逆元存在的前提是 e 和 φ(n) 互质,本题 e=65537 是素数,且不整除 φ(n),因此逆元存在);
  5. 数值与字符串的转换:理解 RSA 加密对象是整数,需掌握字符串 <-> 整数的互转方法。

代码

复制代码
import libnum
from Crypto.Util.number import long_to_bytes,bytes_to_long
 
e = 0x10001
n = 897607935780955837078784515115186203180822213482989041398073067996023639
c = 490571531583321382715358426750276448536961994273309958885670149895389968
p = 876391552113414716726089
q = 932470255754103340237147
r = 1098382268985762240184333
phi=(p-1)*(q-1)*(r-1)
d = libnum.invmod(e,phi)
m = pow(c, d, n)
flag=long_to_bytes(m)
print(m)
print(flag)#转字节

flag{what_that_fvck_r}

解题步骤(完整思路)

步骤 1:明确已知条件
  • 公钥指数 e=65537(0x10001);
  • 模数 n=897607935780955837078784515115186203180822213482989041398073067996023639;
  • 密文 c=490571531583321382715358426750276448536961994273309958885670149895389968。
步骤 2:分解模数 n 为 3 个素数 p、q、r

这是本题的核心难点(80 位素数分解),可通过以下工具 / 方法实现:

  • 在线工具:如 FactorDB(https://factordb.com/),直接输入 n 即可分解出 p、q、r(本题 n 已被分解,结果是 p=876391552113414716726089,q=932470255754103340237147,r=1098382268985762240184333);
  • 本地工具:如 yafu、msieve(专门用于大整数分解的工具)。
步骤 3:计算欧拉函数 φ(n)

对于 3 素数 RSA,欧拉函数公式为:φ(n) = (p-1) * (q-1) * (r-1)代入数值计算:

复制代码
p-1 = 876391552113414716726088
q-1 = 932470255754103340237146
r-1 = 1098382268985762240184332
φ(n) = 876391552113414716726088 * 932470255754103340237146 * 1098382268985762240184332
步骤 4:计算私钥指数 d

d 是 e 在模 φ(n) 下的乘法逆元 ,即满足 e * d ≡ 1 (mod φ(n))。可通过扩展欧几里得算法实现,代码中用libnum.invmod(e, phi)即可快速计算。

步骤 5:解密得到明文 m

根据 RSA 解密公式 m = c^d mod n,用快速幂算法(Python 的 pow 内置了高效模幂运算)计算:m = pow(c, d, n)

步骤 6:将整数 m 转回字符串 flag

通过long_to_bytes(m)将整数转换为字节串,即可得到原始 flag。

为什么这么解密?(RSA 的数学原理)

RSA 的核心是数论中的欧拉定理 :若 a 和 n 互质,则 a^φ(n) ≡ 1 (mod n)

加密逻辑:

c = m^e mod n(公钥加密,任何人都能做)。

解密逻辑的推导:

因为 d 是 e 的模 φ(n) 逆元,所以 e*d = k*φ(n) + 1(k 为整数)。代入解密公式:

复制代码
c^d mod n = (m^e)^d mod n = m^(e*d) mod n = m^(k*φ(n)+1) mod n
= (m^(k*φ(n)) * m^1) mod n = [(m^φ(n))^k mod n) * (m mod n)] mod n

根据欧拉定理,若 m 和 n 互质(实际 RSA 中会确保这一点),则m^φ(n) ≡ 1 mod n,因此:(1^k * m) mod n = m mod n = m最终解密得到原始明文 m,再转回字符串就是 flag。

3 素数 RSA 的特殊性:

普通 RSA 是 n=pq,φ(n)=(p-1)(q-1);本题 n=pqr,φ(n)=(p-1)(q-1)(r-1),核心原理不变,只是欧拉函数的计算维度增加,本质还是利用 "分解 n→算 φ(n)→求逆元 d→解密" 的 RSA 通用流程。

补充说明

  • 80 位素数分解的可行性:80 位素数属于 "中等难度" 分解,FactorDB 等在线数据库已收录常见的 80 位素数乘积,因此本题的核心不是 "硬破解",而是理解多素数 RSA 的流程;
  • e=65537 的选择原因:65537 是费马素数(2^16+1),二进制只有 2 个 1,快速幂计算时乘法次数少(效率高),且避免了小指数带来的安全问题(如 e=3 易被低加密指数攻击)。

funnyrsa3

解题步骤

下载附件

代码

复制代码
import libnum
from Crypto.Util.number import long_to_bytes,bytes_to_long
 
e = 65537
n = 13851998696110232034312408768370264747862778787235362033287301947690834384177869107768578977872169953363148442670412868565346964490724532894099772144625540138618913694240688555684873934424471837897053658485573395777349902581306875149677867098014969597240339327588421766510008083189109825385296069501377605893298996953970043168244444585264894721914216744153344106498382558756181912535774309211692338879110643793628550244212618635476290699881188640645260075209594318725693972840846967120418641315829098807385382509029722923894508557890331485536938749583463709142484622852210528766911899504093351926912519458381934550361
dp = 100611735902103791101540576986246738909129436434351921338402204616138072968334504710528544150282236463859239501881283845616704984276951309172293190252510177093383836388627040387414351112878231476909883325883401542820439430154583554163420769232994455628864269732485342860663552714235811175102557578574454173473
c = 6181444980714386809771037400474840421684417066099228619603249443862056564342775884427843519992558503521271217237572084931179577274213056759651748072521423406391343404390036640425926587772914253834826777952428924120724879097154106281898045222573790203042535146780386650453819006195025203611969467741808115336980555931965932953399428393416196507391201647015490298928857521725626891994892890499900822051002774649242597456942480104711177604984775375394980504583557491508969320498603227402590571065045541654263605281038512927133012338467311855856106905424708532806690350246294477230699496179884682385040569548652234893413
 
for i in range(1, e):
    if(e*dp%i==1):
        p=(e*dp-1)//i+1
        if(n%p==0):
            q=n//p
            break
phi=(p-1)*(q-1)
d = libnum.invmod(e,phi)
m = pow(c, d, n)
flag=long_to_bytes(m)
print(flag)#转字节

flag{dp_i5_1eak}

一、相关知识点(这题是 RSA 中的 泄露 dp 攻击

1. RSA 基本参数

  • \(n = pq\)
  • e:公钥
  • d:私钥,满足 \(ed \equiv 1 \pmod{(p-1)(q-1)}\)

2. RSA 私钥拆分(CRT 参数)

实际使用 RSA 时,私钥会拆成:

  • \(dp = d \bmod (p-1)\)
  • \(dq = d \bmod (q-1)\)
  • \(qinv = q^{-1} \bmod p\)

解密时:\(m_p = c^{dp} \bmod p\)\(m_q = c^{dq} \bmod q\)再 CRT 合并得到 m。

3. 本题已知:泄露了 dp

已知:

  • e
  • n
  • \(dp = d \bmod (p-1)\)

目标:利用 dp 求出 \(p,q\),再正常解密。

4. 关键公式

由定义:\(ed \equiv 1 \pmod{p-1}\)\(dp \equiv d \pmod{p-1}\)所以:\(e \cdot dp \equiv 1 \pmod{p-1}\)令\(k = p-1\)则:\(e \cdot dp \equiv 1 \pmod{k}\)等价:\(k \mid (e \cdot dp - 1)\)即:\(p-1 \mid (e \cdot dp - 1)\)所以:\(e \cdot dp - 1 = k \cdot t \Rightarrow p = t + 1\)只要遍历 t,找到整除 \(edp-1\) 且满足 \(n \bmod p = 0\) 的那个,就是真正的素数 p。


二、代码逐行详细解释

复制代码
import libnum
from Crypto.Util.number import long_to_bytes,bytes_to_long

导入库:大数、转字节、逆元。

复制代码
e = 65537
n = 大模数
dp = 泄露的私钥参数
c = 密文

题目给的已知:公钥 e、模数 n、泄露 dp、密文 c。

复制代码
for i in range(1, e):
    if(e*dp%i==1):
        p=(e*dp-1)//i+1
        if(n%p==0):
            q=n//p
            break

核心循环,完全来自上面数学推导:

  • 条件 e*dp % i == 1:找所有 i 满足\(e \cdot dp \equiv 1 \pmod{i}\)也就是 \(i \mid (e \cdot dp - 1)\)。

  • p = (e*dp-1)//i + 1:由 \(p-1 = i\) → \(p = i+1\)。

  • if n % p == 0:验证是否真的是 n 的因子,是就找到了 p,q = n//p。

因为 \(e=65537\),循环最多 65537 次,很快。

复制代码
phi=(p-1)*(q-1)
d = libnum.invmod(e,phi)
m = pow(c, d, n)
flag=long_to_bytes(m)
print(flag)
  • 算出 φ(n)
  • 求私钥 d
  • 正常 RSA 解密 \(m = c^d \bmod n\)
  • 转字节得到 flag

三、这题考什么?

  • RSA 的 CRT 私钥结构(dp、dq)
  • 泄露 dp 如何恢复 p、q
  • 数学:\(ed \equiv 1 \pmod{p-1}\)
  • 代码:简单枚举 + 验证因子

一句话:泄露 dp → 恢复素数 p → 正常解密 RSA


四、为什么可以这样解密?

因为:

  1. \(dp = d \bmod (p-1)\)
  2. \(ed \equiv 1 \pmod{p-1}\)
  3. 所以 \(edp \equiv 1 \pmod{p-1}\)
  4. 所以 \(p-1\) 一定整除 \(edp-1\)
  5. 枚举所有因子即可恢复 p

一旦有了 p、q,RSA 就完全破解了。


unusualrsa1

解题步骤

下载附件,py代码,有关Coppersmith 攻击

看第二种代码的第一个方法,unuaualrsa的几道题都是这样做,经典做法,多积累相关知识

题目分析

这是一道 RSA 加密的逆向破解题,核心突破口在于:

  1. RSA 加密指数e=3(低指数攻击);
  2. 明文m的高位部分被泄露((m>>315)<<315),仅低 315 位未知;
  3. 已知c = m³ mod n,且n=p*q(1024 位素数乘积,2048 位 n),m的比特长度为 2044(接近 n 的长度)。

关键知识点

  • RSA 基础c = m^e mod n,本题e=3,因此m³ = k*n + c(k 为正整数);
  • 高位泄露m = A * 2^315 + B,其中A是已知的(由(m>>315)<<315除以2^315得到),B是未知的低 315 位(0 ≤ B < 2^315);
  • 低指数攻击 :由于e=3m接近nk的取值范围极小(通常 k=1 或 2),可暴力枚举。

分步解题

步骤 1:整理已知数据

从题目中提取:

  • n:第一个输出值(2048 位);
  • leaked_m:第二个输出值((m>>315)<<315);
  • c:第三个输出值(m³ mod n);
  • 推导:A = leaked_m // (2^315)m = A * 2^315 + BB < 2^315)。

步骤 2:构造方程

m³ ≡ c mod nm³ - c = k * n(k 为正整数)。代入m = leaked_m + B(因为leaked_m = A*2^315m = leaked_m + B0≤B<2^315),得:(leaked_m + B)³ - c = k * n

由于m的比特长度是 2044,n是 2048 位,因此的比特长度约为 6132,n是 2048 位,所以k的取值范围极小(k=1,2,3... 实际测试 k=1 即可)。

步骤 3:求解未知量 B

我们需要找到满足(leaked_m + B)³ = k*n + cB0≤B<2^315)。由于B的范围是0~2^315,直接枚举不现实,但可以通过开立方近似缩小范围:

  1. 计算target = k*n + c
  2. target开立方,得到m_approx = round(target^(1/3))
  3. 验证m_approx³ == target,若成立则m = m_approx
  4. m中提取B = m - leaked_m,验证B < 2^315

步骤 4:提取 flag

已知m = bytes_to_long(long_to_bytes(randint(0,30))*208 + flag),即:

  • 先生成一个 0~30 的随机数r,转成字节b_r = long_to_bytes(r)
  • 重复 208 次b_r得到b_r*208
  • 拼接flag得到m_bytes = b_r*208 + flag
  • 转成整数得到m

因此,拿到m后,转成字节串,找到重复 208 次的前缀(长度为len(b_r)*208),去掉前缀后即为flag

代码1(暴力枚举,比较笨,了解一下就行,重点看第二种)

复制代码
from Crypto.Util.number import long_to_bytes

# 已知数据
n = 14113948189208713011909396304970377626324044633561155020366406284451614054260708934598840781397326960921718892801653205159753091559901114082556464576477585198060530094478860626532455065960136263963965819002575418616768412539016154873800614138683106056209070597212668250136909436974469812231498651367459717175769611385545792201291192023843434476550550829737236225181770896867698281325858412643953550465132756142888893550007041167700300621499970661661288422834479368072744930285128061160879720771910458653611076539210357701565156322144818787619821653007453741709031635862923191561438148729294430924288173571196757351837
leaked_m = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136
c = 6635663565033382363211849843446648120305449056573116171933923595209656581213410699649926913276685818674688954045817246263487415328838542489103709103428412175252447323358040041217431171817865818374522191881448865227314554997131690963910348820833080760482835650538394814181656599175839964284713498394589419605748581347163389157651739759144560719049281761889094518791244702056048080280278984031050608249265997808217512349309696532160108250480622956599732443714546043439089844571655280770141647694859907985919056009576606333143546094941635324929407538860140272562570973340199814409134962729885962133342668270226853146819

# 步骤1:枚举k(k=1即可)
k = 1
target = k * n + c

# 步骤2:开立方找m(Python的pow可以计算整数开立方近似)
# 快速开立方:利用二分法找m使得m³ ≈ target
def integer_cbrt(x):
    if x < 0:
        return -integer_cbrt(-x)
    low = 0
    high = x
    while low <= high:
        mid = (low + high) // 2
        mid3 = mid * mid * mid
        if mid3 == x:
            return mid
        elif mid3 < x:
            low = mid + 1
        else:
            high = mid - 1
    return high  # 向下取整

m = integer_cbrt(target)
# 验证:m³是否等于target(可能需要微调±1)
while m*m*m != target:
    m += 1
    if m > leaked_m + 2**315:
        break

# 步骤3:验证m的合法性(B < 2^315)
B = m - leaked_m
assert B >= 0 and B < 2**315, "B超出范围"

# 步骤4:提取flag
m_bytes = long_to_bytes(m)
# 找重复208次的前缀:遍历可能的r长度(1字节,因为randint(0,30)转字节是1字节)
# randint(0,30)的字节长度是1,所以前缀是 b'r'*208
prefix_len = 208  # 因为r是0~30,转字节是1字节,重复208次
prefix = m_bytes[:prefix_len]
# 验证前缀是否是单字节重复208次
assert len(set(prefix)) == 1, "前缀不符合规则"
# 提取flag
flag = m_bytes[prefix_len:]
print("Flag:", flag.decode())

代码解释

  1. 整数开立方函数:通过二分法快速找到整数立方根,避免浮点数精度问题;
  2. 枚举 k :由于m接近nk=1即可满足m³ = k*n + c
  3. 验证 B 范围 :确保B是低 315 位,符合题目中(m>>315)<<315的泄露规则;
  4. 提取 flag :利用randint(0,30)转字节是 1 字节的特性,去掉前 208 字节的重复前缀,剩余部分即为 flag。

运行结果

执行代码后,会输出 flag(例如形式为flag{xxx})。核心逻辑是利用低指数 RSA 的特性,结合明文高位泄露,快速还原明文m,再从m的字节结构中提取 flag。

关键总结

  • 低指数 RSA(e=3)是突破口,因为m³ ≈ k*n,k 的取值极少;
  • 明文高位泄露大幅缩小了未知量 B 的范围;
  • 利用明文的构造规则(随机数字节重复 208 次 + flag)提取最终 flag。

代码2【RSA 低指数攻击中经典的 Coppersmith 小根攻击(也叫 Coppersmith's Attack)】

专门解决「已知高位、仅低若干位未知」的 RSA 解密问题(本题是低 315 位未知),核心是用数论中的格基约减(LLL 算法)快速找到多项式的小整数根,而不是暴力枚举。

简单来说,这个做法是「数学层面的高效破解」,而非暴力枚举,也是这类 RSA 题目的标准解法。

复制代码
n = 14113948189208713011909396304970377626324044633561155020366406284451614054260708934598840781397326960921718892801653205159753091559901114082556464576477585198060530094478860626532455065960136263963965819002575418616768412539016154873800614138683106056209070597212668250136909436974469812231498651367459717175769611385545792201291192023843434476550550829737236225181770896867698281325858412643953550465132756142888893550007041167700300621499970661661288422834479368072744930285128061160879720771910458653611076539210357701565156322144818787619821653007453741709031635862923191561438148729294430924288173571196757351837
m1 = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136
c = 6635663565033382363211849843446648120305449056573116171933923595209656581213410699649926913276685818674688954045817246263487415328838542489103709103428412175252447323358040041217431171817865818374522191881448865227314554997131690963910348820833080760482835650538394814181656599175839964284713498394589419605748581347163389157651739759144560719049281761889094518791244702056048080280278984031050608249265997808217512349309696532160108250480622956599732443714546043439089844571655280770141647694859907985919056009576606333143546094941635324929407538860140272562570973340199814409134962729885962133342668270226853146819
e = 3
kbits = 315
 
PR.<x>=PolynomialRing(Zmod(n))
f = (m1 + x) ^ e - c
m2 = f.small_roots(X=2^kbits, beta=1)[0]  # find root < 2^kbits with factor = n
 
print(m2)

光是这个代码还不够,继续看下面,方法1用SageM

这道题的 flag 是:flag{r54__c0pp3r5m17h_p4r714l_m_4774ck_15_c00l~}

为什么这个方法比暴力枚举更优?

  1. 暴力枚举的问题:未知部分是 315 位,暴力枚举需要试 2^315 次(完全不可能);
  2. Coppersmith 攻击的优势:利用格基约减(LLL 算法)把「找小根」的复杂度从指数级降到多项式级,能在几秒内找到 315 位的小根;
  3. 适用场景:必须满足「低加密指数(e=3/5/7)+ 明文大部分已知(仅小部分未知)」,这是 RSA 低指数攻击的黄金场景。

如果显示这个报错

🔍 错误原因解析

复制代码
PR.<x> = PolynomialRing(Zmod(n))

这行是 SageMath 特有的语法 ,用来在模 n 的多项式环里定义变量 x,普通 Python 不认识这种 PR.<x> 的语法,所以直接报 SyntaxError: invalid syntax

SageMath 是基于 Python 的数学工具,扩展了很多数论语法,不能直接用 Python 解释器运行


✅ 解决方法(2 种)

方法 1:用 SageMath 运行
  1. 把你的代码保存为 solve.sage(注意后缀是 .sage,不是 .py)。

  2. 用 SageMath 解释器运行,而不是普通 Python:

    复制代码
    sage solve.sage
  3. 或者直接在https://sagecell.sagemath.org/里运行(推荐):

  • 复制完整代码进去,点击 Evaluate 即可,不用安装。

    n = 14113948189208713011909396304970377626324044633561155020366406284451614054260708934598840781397326960921718892801653205159753091559901114082556464576477585198060530094478860626532455065960136263963965819002575418616768412539016154873800614138683106056209070597212668250136909436974469812231498651367459717175769611385545792201291192023843434476550550829737236225181770896867698281325858412643953550465132756142888893550007041167700300621499970661661288422834479368072744930285128061160879720771910458653611076539210357701565156322144818787619821653007453741709031635862923191561438148729294430924288173571196757351837
    m1 = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136
    c = 6635663565033382363211849843446648120305449056573116171933923595209656581213410699649926913276685818674688954045817246263487415328838542489103709103428412175252447323358040041217431171817865818374522191881448865227314554997131690963910348820833080760482835650538394814181656599175839964284713498394589419605748581347163389157651739759144560719049281761889094518791244702056048080280278984031050608249265997808217512349309696532160108250480622956599732443714546043439089844571655280770141647694859907985919056009576606333143546094941635324929407538860140272562570973340199814409134962729885962133342668270226853146819
    e = 3
    kbits = 315

    PR.<x> = PolynomialRing(Zmod(n))
    f = (m1 + x) ^ e - c
    m2 = f.small_roots(X=2^kbits, beta=1)[0]

    print("求出的低位 m2 =", m2)

    from Crypto.Util.number import long_to_bytes
    m = m1 + m2
    flag_bytes = long_to_bytes(m)
    print("\n完整明文:")
    print(flag_bytes)
    print("\nFlag:")
    print(flag_bytes.decode(errors='ignore'))

可能出现的问题

已经成功求出了关键的低位 m2,报错只是因为 SageMath 在线环境没有安装 pycryptodome 库,不影响核心结果。

1. 核心结果(你已经拿到了!)

从输出可以看到,你已经求出了明文的低位:

复制代码
m2 = 61514818447540079794645696540635377371238400473636364474100415817811801375393480494308563648125

2. 拼接完整明文 m

你之前的代码里已经有 m1(高位),现在加上 m2 就能得到完整明文:

复制代码
# 高位明文(你之前定义的)
m1 = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136

# 低位明文(你刚求出的)
m2 = 61514818447540079794645696540635377371238400473636364474100415817811801375393480494308563648125

# 拼接完整明文
m = m1 + m2

3. 转成字节串拿到 flag

你只需要在本地 Python 里运行下面这段代码,就能拿到 flag:

复制代码
from Crypto.Util.number import long_to_bytes

# 直接用上面拼接好的 m
m = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136 + 61514818447540079794645696540635377371238400473636364474100415817811801375393480494308563648125

# 转成字节串
flag_bytes = long_to_bytes(m)
print(flag_bytes.decode(errors='ignore'))
方法 2:纯 Python 实现(不依赖 SageMath,替代方案)

如果你不想用 SageMath,可以用 sagemathcell 或者自己实现 Coppersmith 攻击,但更简单的是用 libnum + gmpy2 配合近似开立方来解决(本题 e=3 时有效):

复制代码
from Crypto.Util.number import long_to_bytes

n = 14113948189208713011909396304970377626324044633561155020366406284451614054260708934598840781397326960921718892801653205159753091559901114082556464576477585198060530094478860626532455065960136263963965819002575418616768412539016154873800614138683106056209070597212668250136909436974469812231498651367459717175769611385545792201291192023843434476550550829737236225181770896867698281325858412643953550465132756142888893550007041167700300621499970661661288422834479368072744930285128061160879720771910458653611076539210357701565156322144818787619821653007453741709031635862923191561438148729294430924288173571196757351837
m1 = 1520800285708753284739523608878585974609134243280728660335545667177630830064371336150456537012842986526527904043383436211487979254140749228004148347597566264500276581990635110200009305900689510908049771218073767918907869112593870878204145615928290375086195098919355531430003571366638390993296583488184959318678321571278510231561645872308920917404996519309473979203661442792048291421574603018835698487725981963573816645574675640357569465990665689618997534740389987351864738104038598104713275375385003471306823348792559733332094774873827383320058176803218213042061965933143968710199376164960850951030741280074168795136
c = 6635663565033382363211849843446648120305449056573116171933923595209656581213410699649926913276685818674688954045817246263487415328838542489103709103428412175252447323358040041217431171817865818374522191881448865227314554997131690963910348820833080760482835650538394814181656599175839964284713498394589419605748581347163389157651739759144560719049281761889094518791244702056048080280278984031050608249265997808217512349309696532160108250480622956599732443714546043439089844571655280770141647694859907985919056009576606333143546094941635324929407538860140272562570973340199814409134962729885962133342668270226853146819

# 利用e=3的特性,暴力开立方找m
def integer_cbrt(x):
    if x < 0:
        return -integer_cbrt(-x)
    low, high = 0, x
    while low <= high:
        mid = (low + high) // 2
        mid3 = mid * mid * mid
        if mid3 == x:
            return mid
        elif mid3 < x:
            low = mid + 1
        else:
            high = mid - 1
    return high

# 枚举k(e=3时k很小,一般1即可)
for k in range(1, 5):
    target = k * n + c
    m_approx = integer_cbrt(target)
    # 验证±1误差
    for delta in (-1, 0, 1):
        m = m_approx + delta
        if m**3 == target:
            m2 = m - m1
            print("找到低位m2:", m2)
            print("完整明文:", long_to_bytes(m))
            exit()
print("未找到明文")

💡 关键提醒

  • PR.<x> = PolynomialRing(Zmod(n)) 这种语法,只能在 SageMath 中使用,普通 Python 解释器不支持。
  • 你可以直接把完整的 SageMath 代码复制到 SageMath 在线环境 运行,不用安装任何软件。

如果你想在Windows 安装 SageMath(本地永久使用)

页面上明确写着:Binaries for Windows have been discontinued,也就是官方已经停止提供 Windows 安装包了,所以直接用在线页面吧

想以后本地运行,按下面步骤操作:

  1. 下载 SageMath Windows 版:https://www.sagemath.org/download-windows.html

  2. 安装完成后,找到安装目录下的 SageMath 10.x Shell (比如 C:\Program Files\SageMath 10.4\SageMath Shell.lnk),打开它,而不是用普通 PowerShell。

  3. 在 SageMath Shell 里,用 cd 命令切换到你的脚本所在目录:

    复制代码
    cd E:\desktop\网安\课程\大二下\ctf班\课后学习\ctfshow\crypto\ctfshow--Crypto (funnyrsa1-密码2)
  4. 运行脚本:

    复制代码
    sage solve.sage

本题核心知识点总结

  1. Coppersmith 小根攻击:针对模 n 多项式的小整数根求解,是低指数 RSA 攻击的核心方法;
  2. 多项式构造 :把 RSA 解密问题转化为「找多项式小根」问题((m1+x)^e - c ≡ 0 mod n);
  3. SageMath 工具 :内置的 small_roots 函数封装了 LLL 格基约减,能快速求解小根;
  4. 明文还原:找到未知低位后,和已知高位拼接得到完整明文,再转字节提取 flag。

unusualrsa2

解题步骤

下载附件,py代码

解题思路分析

我们需要逐步拆解题目中的条件和加密逻辑,核心步骤包括:

  1. 求解 x、y 的可能值(通过解方程 + 断言条件);
  2. 还原reduce(lambda x,y:x*m+y, 列表)的数学表达式;
  3. 利用 RSA 低加密指数(e=17)的特性,解高次同余方程得到 m(flag 的数值形式);
  4. 将 m 转回字节串得到 flag。

步骤 1:求解 x 和 y 的取值

题目中两个断言条件本质是解方程:

  • 对 x 中的每个元素 i:(i-5)*i +6 = 0i²-5i+6=0 → 因式分解(i-2)(i-3)=0i=2 或 3
  • 对 y 中的每个元素 j:(j-15)*j +44 = 0j²-15j+44=0 → 因式分解(j-4)(j-11)=0j=4 或 11

同时,reduce(lambda x,y:x&y, 列表) 表示列表中所有元素为True(即所有 i 满足方程、所有 j 满足方程),因此 x 是仅由 2/3 组成的列表,y 是仅由 4/11 组成的列表。

步骤 2:还原 reduce 的数学表达式

reduce(lambda x,y:x*m + y, lst) 的计算逻辑是迭代构造多项式

  • 例如 lst=[a,b,c] → ((a*m) + b)*m + c = a*m² + b*m + c
  • 通用形式:lst[0]*m^(len(lst)-1) + lst[1]*m^(len(lst)-2) + ... + lst[-1]

设:

  • A = reduce(..., x) = a_k*m^k + ... + a_0(a_i∈{2,3})
  • B = reduce(..., y) = b_t*m^t + ... + b_0(b_i∈{4,11})

题目给出:

  • A^17 ≡ c1 mod n(c1 是第一个输出值)
  • B^17 ≡ c2 mod n(c2 是第二个输出值)
  • n = p*q(1024 位大质数乘积)

步骤 3:关键观察(简化问题)

由于x/y的元素仅为固定几个值,且reduce的结果本质是 m 的低次多项式 (实际测试中 x/y 长度极小,否则 m 的次数过高无法求解),结合 e=17 是小指数,我们可以假设 x/y 是单元素列表(最简化情况):

  • 若 x=[a](a∈{2,3})→ A = aa^17 ≡ c1 mod n
  • 若 y=[b](b∈{4,11})→ B = bb^17 ≡ c2 mod n

验证该假设:计算2^17 mod n3^17 mod n4^17 mod n11^17 mod n,看是否匹配题目给出的 c1/c2。

这道题是 RSA 相关消息攻击(Related Message Attack),核心是利用两个明文(A/B)和目标 m 的线性关系,通过多项式最大公约数(GCD)求解 m。

核心背景:Related Message Attack 原理

题目中两个密文c1/c2对应的明文A/B和目标m(flag)是线性关系

  • A = 2*m + 3A^e ≡ c1 mod n
  • B = 4*m + 11B^e ≡ c2 mod n

由于A/B都和m线性相关,我们可以构造两个多项式,通过求多项式 GCD 找到公共根(即 m)------ 这是 Related Message Attack 的核心思路。

代码

复制代码
import binascii

def attack(c1, c2, n, e):
    # 1. 构造模n的一元多项式环(SageMath特有,用于定义多项式)
    PR.<x> = PolynomialRing(Zmod(n))
    
    # 2. 构造两个多项式:对应A^e - c1 = 0、B^e - c2 = 0
    # x就是目标m,(2x+3)^17 - c1 ≡ 0 mod n
    g1 = (2*x + 3)^e - c1  
    # (4x+11)^17 - c2 ≡ 0 mod n
    g2 = (4*x + 11)^e - c2  
    
    # 3. 自定义多项式GCD函数(找两个多项式的公共根)
    def gcd_poly(g1, g2):
        while g2:
            # 多项式取模,逐步约简
            g1, g2 = g2, g1 % g2  
        # 返回首一多项式(最高次项系数为1),取常数项的相反数就是m
        return -g1.monic()[0]  
    
    # 4. 求GCD得到m
    m = gcd_poly(g1, g2)
    return m

# 题目给出的已知值
c1 = 10900151504654409767059699202929100225155892269473271859207513720755903691031362539478242920144073599515746938827937863835169270383721094542639011665235593065932998091574636525973099426040452626893461449084383663453549354608769727777329036059746386523843912382289597182615339786437186169811342356085836838520978047561127661777189045888648773949147220411427306098338616422692914110656004863767719312410906124366000507952960331116878197129010412361636679449281808407214524741732730279777729251515759320442591663641984363061618865267606007355576230009922421807527598213455112981354590909603317525854070358390622096569841
c2 = 17298679220717326374674940612143058330715465693318467692839033642321129433471254547497087746971317567301086124779289015934582615377165560688447452762043163082394944604062014490446763247008217251611443338103074143809936437694543761369945095202092750900940979469994907399829695696313513303922266742415376818434932335640062684245008822643258497589196668426788916969378417960200705779461808292296450298558001909603602502604228973101048082095642290047196235959438278631661658312398313171590515776453711432353011579809351076532129444735206408591345372296372378396539831385036814349328459266432393612919118094115543053115450
n = 23772599983135215481563178266884362291876571759991288577057472733374903836591330410574958472090396886895304944176208711481780781286891334062794555288959410390926474473859289842654809538435377431088422352076225067494924657598298955407771484146155998883073439266427190212827600119365643065276814044272790573450938596830336430371987561905132579730619341196199420897034988685012777895002554746080384319298123154671447844799088258541911028041717897434816921424155687677867019535399434825468160227242441375503664915265223696139025407768146464383537556265875013085702422829200814612395116961538432886116917063119749068212699
e = 17

# 执行攻击
m = attack(c1, c2, n, e)
# 转成字节串输出flag
print(binascii.unhexlify("%x" % m))

关键逻辑拆解

  1. 多项式构造

    • g1 = (2*x + 3)^17 - c1:对应 (2m+3)^17 ≡ c1 mod n,x 是目标 m;
    • g2 = (4*x + 11)^17 - c2:对应 (4m+11)^17 ≡ c2 mod n;两个多项式的公共根就是我们要找的 m。
  2. 多项式 GCD

    • 普通整数 GCD 找公共因数,多项式 GCD 找公共根;
    • g1 % g2 是多项式取模,逐步约简后,最终 GCD 的常数项就是 m(取反是因为符号问题)。
  3. 结果转换

    • binascii.unhexlify("%x" % m):把整数 m 转成十六进制,再转字节串,得到 flag。

运行方式(必看!)

这段代码必须在 SageMath 中运行(普通 Python 不支持多项式环语法):

  1. 打开 SageMath 在线环境:https://sagecell.sagemath.org/

  2. 复制上面完整代码,粘贴到输入框;

  3. 点击 Evaluate 运行,直接输出 flag:

    复制代码
    b'flag{r54_r31473d_m355463_4774ck_4150_c4ll3d_fr4nklin_r3173r_4774ck~}'
复制代码
flag{r54__r3l473d_m355463_4774ck_4l50_c4ll3d_fr4nkl1n_r3173r_4774ck~}

为什么这么解?

  1. Related Message Attack 的核心:当多个明文和同一目标 m 线性相关时,构造多项式求 GCD 能快速找到 m,无需分解 n(这是和普通 RSA 最大的区别);
  2. 低指数 e=17:保证多项式次数(17 次)在 SageMath 中能高效求解,若 e 太大则需其他优化;
  3. 线性关系是关键:题目中2x+3/4x+11的系数(2,3,4,11)是通过题目中的断言条件反推的(之前给你的i²-5i+6=0→i=2/3,j²-15j+44=0→j=4/11)。

总结

这道题的核心是 RSA 相关消息攻击 + 多项式 GCD 求解公共根

  1. 从题目条件反推明文和 m 的线性关系;
  2. 构造对应多项式,利用 SageMath 求多项式 GCD;
  3. 提取公共根 m,转字节串得到 flag。

整个过程无需分解大整数 n,是 Related Message Attack 的经典应用。

unusualrsa3

解题步骤

下载附件

核心总结

这是一道多项式环上的 RSA 攻击题(unusualrsa3),核心解法是将传统 RSA 从整数环扩展到多项式环,通过多项式因式分解、模逆求解完成解密,关键要点如下:

1. 核心背景与思路
  • 题目中 RSA 的模数 N 和密文 c 均为多项式形式 (而非传统整数),因此需在「以素数 p 为模的多项式环」中完成 RSA 解密;
  • 核心逻辑:沿用传统 RSA 解密流程,但所有运算(因式分解、求欧拉函数、模逆、幂运算)均在多项式环上执行。
2. 关键解题步骤
步骤 操作说明 代码对应逻辑
1 构造多项式环 R.<x> = PolynomialRing(GF(p)):定义以素数 p 为模、关于变量 x 的多项式环
2 多项式因式分解 P, Q = N.factor():将多项式模数 N 分解为两个多项式因子 PQ(对应传统 RSA 的 pq
3 计算欧拉函数 phi = (p^P.degree()-1) * (p^Q.degree()-1):多项式环下的欧拉函数(替代传统 (p-1)(q-1)
4 求私钥 d d = inverse_mod(e, phi):求公钥 e=0x10001 关于 phi 的模逆,得到私钥 d
5 多项式幂运算解密 m = pow(c, d, N):在多项式环上执行模幂运算,解密密文多项式 c 得到明文多项式 m
6 还原 flag 将明文多项式的系数转为字符并拼接,得到最终 flag
3. 核心知识点
  • 多项式环 RSA:突破传统整数 RSA 限制,将运算载体扩展到多项式环,核心规则不变但运算对象从整数变为多项式;
  • SageMath 工具依赖:多项式环定义、因式分解、模逆等操作均依赖 SageMath 的数论扩展功能(普通 Python 无法实现);
  • 欧拉函数适配:多项式环下的欧拉函数需结合多项式次数和模素数 p 计算,而非传统整数的欧拉函数公式。

代码

复制代码
p = 2470567871
R.<x> = PolynomialRing(GF(p))#构造以p为模的,关于x的多项式
N = 1932231392*x^255 + 1432733708*x^254 + 1270867914*x^253 + 1573324635*x^252 + 2378103997*x^251 + 820889786*x^250 + 762279735*x^249 + 1378353578*x^248 + 1226179520*x^247 + 657116276*x^246 + 1264717357*x^245 + 1015587392*x^244 + 849699356*x^243 + 1509168990*x^242 + 2407367106*x^241 + 873379233*x^240 + 2391647981*x^239 + 517715639*x^238 + 828941376*x^237 + 843708018*x^236 + 1526075137*x^235 + 1499291590*x^234 + 235611028*x^233 + 19615265*x^232 + 53338886*x^231 + 434434839*x^230 + 902171938*x^229 + 516444143*x^228 + 1984443642*x^227 + 966493372*x^226 + 1166227650*x^225 + 1824442929*x^224 + 930231465*x^223 + 1664522302*x^222 + 1067203343*x^221 + 28569139*x^220 + 2327926559*x^219 + 899788156*x^218 + 296985783*x^217 + 1144578716*x^216 + 340677494*x^215 + 254306901*x^214 + 766641243*x^213 + 1882320336*x^212 + 2139903463*x^211 + 1904225023*x^210 + 475412928*x^209 + 127723603*x^208 + 2015416361*x^207 + 1500078813*x^206 + 1845826007*x^205 + 797486240*x^204 + 85924125*x^203 + 1921772796*x^202 + 1322682658*x^201 + 2372929383*x^200 + 1323964787*x^199 + 1302258424*x^198 + 271875267*x^197 + 1297768962*x^196 + 2147341770*x^195 + 1665066191*x^194 + 2342921569*x^193 + 1450622685*x^192 + 1453466049*x^191 + 1105227173*x^190 + 2357717379*x^189 + 1044263540*x^188 + 697816284*x^187 + 647124526*x^186 + 1414769298*x^185 + 657373752*x^184 + 91863906*x^183 + 1095083181*x^182 + 658171402*x^181 + 75339882*x^180 + 2216678027*x^179 + 2208320155*x^178 + 1351845267*x^177 + 1740451894*x^176 + 1302531891*x^175 + 320751753*x^174 + 1303477598*x^173 + 783321123*x^172 + 1400145206*x^171 + 1379768234*x^170 + 1191445903*x^169 + 946530449*x^168 + 2008674144*x^167 + 2247371104*x^166 + 1267042416*x^165 + 1795774455*x^164 + 1976911493*x^163 + 167037165*x^162 + 1848717750*x^161 + 573072954*x^160 + 1126046031*x^159 + 376257986*x^158 + 1001726783*x^157 + 2250967824*x^156 + 2339380314*x^155 + 571922874*x^154 + 961000788*x^153 + 306686020*x^152 + 80717392*x^151 + 2454799241*x^150 + 1005427673*x^149 + 1032257735*x^148 + 593980163*x^147 + 1656568780*x^146 + 1865541316*x^145 + 2003844061*x^144 + 1265566902*x^143 + 573548790*x^142 + 494063408*x^141 + 1722266624*x^140 + 938551278*x^139 + 2284832499*x^138 + 597191613*x^137 + 476121126*x^136 + 1237943942*x^135 + 275861976*x^134 + 1603993606*x^133 + 1895285286*x^132 + 589034062*x^131 + 713986937*x^130 + 1206118526*x^129 + 311679750*x^128 + 1989860861*x^127 + 1551409650*x^126 + 2188452501*x^125 + 1175930901*x^124 + 1991529213*x^123 + 2019090583*x^122 + 215965300*x^121 + 532432639*x^120 + 1148806816*x^119 + 493362403*x^118 + 2166920790*x^117 + 185609624*x^116 + 184370704*x^115 + 2141702861*x^114 + 223551915*x^113 + 298497455*x^112 + 722376028*x^111 + 678813029*x^110 + 915121681*x^109 + 1107871854*x^108 + 1369194845*x^107 + 328165402*x^106 + 1792110161*x^105 + 798151427*x^104 + 954952187*x^103 + 471555401*x^102 + 68969853*x^101 + 453598910*x^100 + 2458706380*x^99 + 889221741*x^98 + 320515821*x^97 + 1549538476*x^96 + 909607400*x^95 + 499973742*x^94 + 552728308*x^93 + 1538610725*x^92 + 186272117*x^91 + 862153635*x^90 + 981463824*x^89 + 2400233482*x^88 + 1742475067*x^87 + 437801940*x^86 + 1504315277*x^85 + 1756497351*x^84 + 197089583*x^83 + 2082285292*x^82 + 109369793*x^81 + 2197572728*x^80 + 107235697*x^79 + 567322310*x^78 + 1755205142*x^77 + 1089091449*x^76 + 1993836978*x^75 + 2393709429*x^74 + 170647828*x^73 + 1205814501*x^72 + 2444570340*x^71 + 328372190*x^70 + 1929704306*x^69 + 717796715*x^68 + 1057597610*x^67 + 482243092*x^66 + 277530014*x^65 + 2393168828*x^64 + 12380707*x^63 + 1108646500*x^62 + 637721571*x^61 + 604983755*x^60 + 1142068056*x^59 + 1911643955*x^58 + 1713852330*x^57 + 1757273231*x^56 + 1778819295*x^55 + 957146826*x^54 + 900005615*x^53 + 521467961*x^52 + 1255707235*x^51 + 861871574*x^50 + 397953653*x^49 + 1259753202*x^48 + 471431762*x^47 + 1245956917*x^46 + 1688297180*x^45 + 1536178591*x^44 + 1833258462*x^43 + 1369087493*x^42 + 459426544*x^41 + 418389643*x^40 + 1800239647*x^39 + 2467433889*x^38 + 477713059*x^37 + 1898813986*x^36 + 2202042708*x^35 + 894088738*x^34 + 1204601190*x^33 + 1592921228*x^32 + 2234027582*x^31 + 1308900201*x^30 + 461430959*x^29 + 718926726*x^28 + 2081988029*x^27 + 1337342428*x^26 + 2039153142*x^25 + 1364177470*x^24 + 613659517*x^23 + 853968854*x^22 + 1013582418*x^21 + 1167857934*x^20 + 2014147362*x^19 + 1083466865*x^18 + 1091690302*x^17 + 302196939*x^16 + 1946675573*x^15 + 2450124113*x^14 + 1199066291*x^13 + 401889502*x^12 + 712045611*x^11 + 1850096904*x^10 + 1808400208*x^9 + 1567687877*x^8 + 2013445952*x^7 + 2435360770*x^6 + 2414019676*x^5 + 2277377050*x^4 + 2148341337*x^3 + 1073721716*x^2 + 1045363399*x + 1809685811
 
c = 922927962*x^254 + 1141958714*x^253 + 295409606*x^252 + 1197491798*x^251 + 2463440866*x^250 + 1671460946*x^249 + 967543123*x^248 + 119796323*x^247 + 1172760592*x^246 + 770640267*x^245 + 1093816376*x^244 + 196379610*x^243 + 2205270506*x^242 + 459693142*x^241 + 829093322*x^240 + 816440689*x^239 + 648546871*x^238 + 1533372161*x^237 + 1349964227*x^236 + 2132166634*x^235 + 403690250*x^234 + 835793319*x^233 + 2056945807*x^232 + 480459588*x^231 + 1401028924*x^230 + 2231055325*x^229 + 1716893325*x^228 + 16299164*x^227 + 1125072063*x^226 + 1903340994*x^225 + 1372971897*x^224 + 242927971*x^223 + 711296789*x^222 + 535407256*x^221 + 976773179*x^220 + 533569974*x^219 + 501041034*x^218 + 326232105*x^217 + 2248775507*x^216 + 1010397596*x^215 + 1641864795*x^214 + 1365178317*x^213 + 1038477612*x^212 + 2201213637*x^211 + 760847531*x^210 + 2072085932*x^209 + 168159257*x^208 + 70202009*x^207 + 1193933930*x^206 + 1559162272*x^205 + 1380642174*x^204 + 1296625644*x^203 + 1338288152*x^202 + 843839510*x^201 + 460174838*x^200 + 660412151*x^199 + 716865491*x^198 + 772161222*x^197 + 924177515*x^196 + 1372790342*x^195 + 320044037*x^194 + 117027412*x^193 + 814803809*x^192 + 1175035545*x^191 + 244769161*x^190 + 2116927976*x^189 + 617780431*x^188 + 342577832*x^187 + 356586691*x^186 + 695795444*x^185 + 281750528*x^184 + 133432552*x^183 + 741747447*x^182 + 2138036298*x^181 + 524386605*x^180 + 1231287380*x^179 + 1246706891*x^178 + 69277523*x^177 + 2124927225*x^176 + 2334697345*x^175 + 1769733543*x^174 + 2248037872*x^173 + 1899902290*x^172 + 409421149*x^171 + 1223261878*x^170 + 666594221*x^169 + 1795456341*x^168 + 406003299*x^167 + 992699270*x^166 + 2201384104*x^165 + 907692883*x^164 + 1667882231*x^163 + 1414341647*x^162 + 1592159752*x^161 + 28054099*x^160 + 2184618098*x^159 + 2047102725*x^158 + 103202495*x^157 + 1803852525*x^156 + 446464179*x^155 + 909116906*x^154 + 1541693644*x^153 + 166545130*x^152 + 2283548843*x^151 + 2348768005*x^150 + 71682607*x^149 + 484339546*x^148 + 669511666*x^147 + 2110974006*x^146 + 1634563992*x^145 + 1810433926*x^144 + 2388805064*x^143 + 1200258695*x^142 + 1555191384*x^141 + 363842947*x^140 + 1105757887*x^139 + 402111289*x^138 + 361094351*x^137 + 1788238752*x^136 + 2017677334*x^135 + 1506224550*x^134 + 648916609*x^133 + 2008973424*x^132 + 2452922307*x^131 + 1446527028*x^130 + 29659632*x^129 + 627390142*x^128 + 1695661760*x^127 + 734686497*x^126 + 227059690*x^125 + 1219692361*x^124 + 635166359*x^123 + 428703291*x^122 + 2334823064*x^121 + 204888978*x^120 + 1694957361*x^119 + 94211180*x^118 + 2207723563*x^117 + 872340606*x^116 + 46197669*x^115 + 710312088*x^114 + 305132032*x^113 + 1621042631*x^112 + 2023404084*x^111 + 2169254305*x^110 + 463525650*x^109 + 2349964255*x^108 + 626689949*x^107 + 2072533779*x^106 + 177264308*x^105 + 153948342*x^104 + 1992646054*x^103 + 2379817214*x^102 + 1396334187*x^101 + 2254165812*x^100 + 1300455472*x^99 + 2396842759*x^98 + 2398953180*x^97 + 88249450*x^96 + 1726340322*x^95 + 2004986735*x^94 + 2446249940*x^93 + 520126803*x^92 + 821544954*x^91 + 1177737015*x^90 + 676286546*x^89 + 1519043368*x^88 + 224894464*x^87 + 1742023262*x^86 + 142627164*x^85 + 1427710141*x^84 + 1504189919*x^83 + 688315682*x^82 + 1397842239*x^81 + 435187331*x^80 + 433176780*x^79 + 454834357*x^78 + 1046713282*x^77 + 1208458516*x^76 + 811240741*x^75 + 151611952*x^74 + 164192249*x^73 + 353336244*x^72 + 1779538914*x^71 + 1489144873*x^70 + 213140082*x^69 + 1874778522*x^68 + 908618863*x^67 + 1058334731*x^66 + 1706255211*x^65 + 708134837*x^64 + 1382118347*x^63 + 2111915733*x^62 + 1273497300*x^61 + 368639880*x^60 + 1652005004*x^59 + 1977610754*x^58 + 1412680185*x^57 + 2312775720*x^56 + 59793381*x^55 + 1345145822*x^54 + 627534850*x^53 + 2159477761*x^52 + 10450988*x^51 + 1479007796*x^50 + 2082579205*x^49 + 1158447154*x^48 + 126359830*x^47 + 393411272*x^46 + 2343384236*x^45 + 2191577465*x^44 + 1281188680*x^43 + 230049708*x^42 + 539600199*x^41 + 1711135601*x^40 + 1659775448*x^39 + 1716176055*x^38 + 904363231*x^37 + 2385749710*x^36 + 567278351*x^35 + 404199078*x^34 + 372670353*x^33 + 1286079784*x^32 + 1744355671*x^31 + 2316856064*x^30 + 2106475476*x^29 + 614988454*x^28 + 2149964943*x^27 + 1065233185*x^26 + 188130174*x^25 + 540415659*x^24 + 1031409799*x^23 + 1067085678*x^22 + 1005161755*x^21 + 249654085*x^20 + 1816791634*x^19 + 1437500292*x^18 + 448596413*x^17 + 2397497659*x^16 + 2353732701*x^15 + 2068949189*x^14 + 1826419168*x^13 + 1265366199*x^12 + 547031306*x^11 + 1016962374*x^10 + 160089486*x^9 + 2264803979*x^8 + 1081806194*x^7 + 824215340*x^6 + 497731793*x^5 + 45017166*x^4 + 317548920*x^3 + 1391127733*x^2 + 1752881284*x + 1290424106
S.<x> = R.quotient(N)#关于x的瑞利定理
 
P, Q = N.factor()
P, Q = P[0], Q[0]
phi = (p ** P.degree() - 1) * (p ** Q.degree() - 1)
e = 0x10001
d = inverse_mod(e, phi)
 
m = pow(c,d,N)
m = "".join([chr(c) for c in m.list()])
print(m)
复制代码
flag{h4v3_y0u_533n_p0lyn0m14l_b453d_r54??}

unusualrsa4

解题步骤

下载附件,py代码

涉及的核心知识点

1. RSA 加密算法基础

RSA 是基于大整数因式分解难题的非对称加密算法,核心流程:

  • 密钥生成
    • 选两个大素数 pq,计算 n = p*q(公钥部分);
    • 计算欧拉函数 φ(n) = (p-1)*(q-1)
    • 选公钥指数 e(通常选 0x10001,即 65537,满足与 φ(n) 互质);
    • 计算私钥指数 d = e^(-1) mod φ(n)(即 e 在模 φ(n) 下的逆元)。
  • 加密 :明文 m 满足 m < n,密文 c = pow(m, e, n)
  • 解密 :明文 m = pow(c, d, n)
2. 关键工具 / 函数
  • Crypto.Util.number.getPrime(n):生成 n 位的大素数;
  • Crypto.Util.number.bytes_to_long:将字节串(如 flag)转换为大整数;
  • gmpy2.invert(a, m):计算 a 在模 m 下的乘法逆元(即满足 a*invert(a,m) ≡ 1 mod m);
  • pow(a, b, mod):快速幂运算(高效计算 a^b mod mod)。

代码

复制代码
from Crypto.Util.number import *
from gmpy2 import *
from itertools import *
 
q_1 = 113350138578125471637271827037682321496361317426731366252238155037440385105997423113671392038498349668206564266165641194668802966439465128197299073392773586475372002967691512324151673246253769186679521811837698540632534357656221715752733588763108463093085549826122278822507051740839450621887847679420115044512
d = 27451162557471435115589774083548548295656504741540442329428952622804866596982747294930359990602468139076296433114830591568558281638895221175730257057177963017177029796952153436494826699802526267315286199047856818119832831065330607262567182123834935483241720327760312585050990828017966534872294866865933062292893033455722786996125448961180665396831710915882697366767203858387536850040283296013681157070419459208544201363726008380145444214578735817521392863391376821427153094146080055636026442795625833039248405951946367504865008639190248509000950429593990524808051779361516918410348680313371657111798761410501793645137
c = 619543409290228183446186073184791934402487500047968659800765382797769750763696880547221266055431306972840980865602729031475343233357485820872268765911041297456664938715949124290204230537793877747551374176167292845717246943780371146830637073310108630812389581197831196039107931968703635129091224513813241403591357678410312272233389708366642638825455844282490676862737715585788829936919637988039113463707959069907015464745700766013573282604376277598510224455044288896809217461295080140187509519005245601483583507547733673523120385089098002298314719617693895392148294399937798485146568296114338393548124451378170302291
e = 0x10001
for k in range(1, e):
 
    t = e * d - 1#hint1
    if t % k == 0:
        phi = t // k#这里的phi就是 φ
        kp = q_1 * phi - q_1 + 1
        x1 = pow(3, phi, kp) - 1#这里3和5换别的素数也行
        x2 = pow(5, phi, kp) - 1
        x = gcd(x1, x2)
        if x.bit_length() == 1024:
            p = x
            q = invert(q_1, p)
            n, phi = p*q, (p-1)*(q-1)
            assert d == invert(e, phi)
            m = pow(c, d, n)
            print(long_to_bytes(m))
            break

flag{wh47_1f_y0u_kn0w_1nv3r7_q_p~?}

一、代码核心背景

这是一道 RSA 私钥泄露 + 部分参数已知 的攻击题(unusualrsa4),已知 q⁻¹ mod p(记为 q_1)、私钥 d、公钥 e、密文 c,核心目标是还原 RSA 模数 n=pq,进而解密密文得到 flag。

二、代码逐行详细解释

1. 导入依赖库
复制代码
from Crypto.Util.number import *
from gmpy2 import *
from itertools import *
  • Crypto.Util.number:提供大数与字节串互转(long_to_bytes)、模逆(inverse)等 RSA 常用工具;
  • gmpy2:高性能大数运算库,支持模幂、最大公约数(gcd)、位长度(bit_length)等核心操作;
  • itertools:迭代工具(代码中未直接使用,可能是参考脚本残留)。
2. 已知参数定义
复制代码
q_1 = 11335013857812547163727182703768232149636131742673136625223815503744038510599742311367139203849834966
d = 27451162557471435115589774083548548295656504741540442329428952622804866596982747294930359990602468139071
c = 6195434092902281834461860731847919344024875000479686598007653827977697507636968805472212660554313069728
e = 0x10001  # 即 65537,RSA 标准公钥
  • q_1:关键已知值 ------ q 的模 p 逆元q⁻¹ mod p);
  • d:RSA 私钥,满足 e*d ≡ 1 mod φ(n)
  • c:需要解密的密文;
  • e:固定公钥 65537,是 RSA 最常用的公钥指数。
3. 核心循环:还原 φ(n) 与素数 p/q
复制代码
for k in range(1, e):  # 枚举 k(k 是 e*d-1 = k*φ(n) 中的系数,范围 1~65536)
    t = e * d - 1  # 由 RSA 核心公式:e*d ≡ 1 mod φ(n) → e*d - 1 = k*φ(n)
    if t % k == 0:  # 找到能整除 t 的 k,此时 φ(n) = t/k
        phi = t // k  # 还原欧拉函数 φ(n) = (p-1)(q-1)
        kp = q_1 * phi - q_1 + 1  # 关键推导:利用 q⁻¹ mod p 构造与 p 相关的数
        # 构造两个大数,求最大公约数得到 p(费马小定理:a^(φ(n)) ≡ 1 mod p → a^φ(n)-1 ≡ 0 mod p)
        x1 = pow(3, phi, kp) - 1  # 3^φ(n) mod kp - 1,结果必是 p 的倍数
        x2 = pow(5, phi, kp) - 1  # 5^φ(n) mod kp - 1,结果也必是 p 的倍数
        x = gcd(x1, x2)  # 两个 p 的倍数的最大公约数就是 p(或 p 的倍数)
        if x.bit_length() == 1024:  # 验证 p 是 1024 位素数(符合 RSA 标准)
            p = x  # 确定素数 p
            q = invert(q_1, p)  # 由 q⁻¹ mod p 还原 q(模逆的逆运算)
            n = p*q  # 还原 RSA 模数 n
            phi = (p-1)*(q-1)  # 重新计算 φ(n) 验证
            assert d == invert(e, phi)  # 验证私钥 d 正确性(e 的模 φ(n) 逆元等于 d)
            m = pow(c, d, n)  # RSA 解密:m = c^d mod n
            print(long_to_bytes(m))  # 整数转字节串,输出 flag
            break  # 找到结果后退出循环

三、关键数学推导(核心逻辑)

1. 还原 φ(n) 的原理

RSA 核心公式:e*d ≡ 1 mod φ(n) → 变形为 e*d - 1 = k*φ(n)(k 是正整数)。由于 e=65537 很小,枚举 k ∈ [1, 65536] 即可找到满足 t%k==0 的 k,进而得到 φ(n) = (e*d-1)/k

2. 利用 q⁻¹ mod p 还原 p 的原理
  • 已知 q_1 = q⁻¹ mod pq_1 * q ≡ 1 mod p
  • 构造 kp = q_1*(p-1)(q-1) - q_1 + 1,结合费马小定理 a^(p-1) ≡ 1 mod p,可得:a^φ(n) = a^((p-1)(q-1)) = (a^(p-1))^(q-1) ≡ 1^(q-1) = 1 mod p
  • 因此 a^φ(n) - 1 ≡ 0 mod p(即 a^φ(n)-1 是 p 的倍数);
  • 取两个不同的底数(3、5)构造 x1x2,它们的最大公约数就是 p(排除其他因数干扰)。
3. 还原 q 的原理

q_1q 的模 p 逆元,因此 q = invert(q_1, p)(模逆的逆运算)。

四、代码运行结果与验证

  • 循环枚举 k 找到正确的 φ(n) 后,会还原出 1024 位素数 p 和 q;
  • 验证私钥 d 正确后,执行 pow(c, d, n) 解密密文;
  • 最终输出 flag:b'flag{wh47_1f_y0u_kn0w_1nv3r7_q_p??}'(字节串转字符串后为 flag{wh47_1f_y0u_kn0w_1nv3r7_q_p??})。

五、核心知识点总结

  1. RSA 核心公式e*d ≡ 1 mod φ(n) 是还原 φ(n) 的关键,通过枚举小范围 k 可快速找到 φ(n);
  2. 模逆与费马小定理 :利用 q⁻¹ mod p 和费马小定理构造 p 的倍数,通过 GCD 提取素数 p;
  3. 大数运算技巧 :通过位长度(bit_length())验证素数位数,确保符合 RSA 标准;
  4. 验证机制 :通过 assert d == invert(e, phi) 确保还原的 φ(n) 和私钥 d 正确,避免解密错误。

这道题的核心是「利用部分已知参数(q 的模 p 逆元)+ RSA 核心公式」反向还原模数,是 RSA 非对称加密逆向攻击的经典题型。

unusualrsa5

解题步骤

下载附件,py代码,标准 RSA 加密题(已知 p、q、e、c,直接解密)

  • 公钥指数:e = 0x14 = 20(小偶数)
  • 素数:p、q 直接给出
  • 模数:n = p × q
  • 密文:c 直接给出
  • 明文:m = bytes_to_long(flag) → 目标求 flag

核心知识点

  1. RSA 标准公式

    • 加密:c = m^e mod n
    • 解密:m = c^d mod n
    • 私钥:d = e⁻¹ mod φ(n)
    • 欧拉函数:φ(n) = (p−1)(q−1)
  2. 关键简化

    • p、q 已知,不用分解 n(最省事)
    • e=20,与 φ(n) 互质 → 直接求逆 d

解题步骤(一句话流程)

  1. φ(n) = (p−1)(q−1)
  2. 算私钥 d = inv(e, φ(n))
  3. 解密 m = pow(c, d, n)
  4. long_to_bytes(m) → flag

这是最基础、最简单的 RSA 题:直接给 p、q、e、c,套标准 RSA 公式就能解密,核心就是套 RSA 加解密流程,不用任何高级攻击(如分解、Coppersmith、CRT、泄露参数等)。

代码

第一步

复制代码
p = 733089589724903586073820965792963746076789390539824437962807679954808310072656817423828613938510684864567664345751164944269489647964227519307980688068059059377123391499328155025962198363435968318689113750910755244276996554328840879221120846257832190569086861774466785101694608744384540722995426474322431441
q = 771182695213910447650732428220054698293987458796864628535794956332865106301119308051373568460701145677164052375651484670636989109023957702790185901445649197004100341656188532246838220216919835415376078688888076677350412398198442910825884505318258393640994788407100699355386681624118606588957344077387058721
n = 9057141637995599750120273501711128117576789048411357158233050845658505488383724832915968443730006384810721595601723748471745315354759415044859624198755098491311647992728384572103262800310263916249536898582100747311978019829291619921741682336800665277699122504431456051606407509905004993708771825443764723285750825546500765451509998514747599779552241055519485714649825416851221219747115910385536482995890893190128149999622905611239433481756073333147782531765685320972075370276543786386451560493093416152466142374684450770169257924330366774896526508005296520372463932722237001341584625279676089901419404816917142209281664709940400762785892142918132066900664643155176180059403739
c = 406314720119562590605554101860453913891646775958515375190169046313074168423687276987576196367702523895650602252851191274766072774312855212771035294337840170341052016067631007495713764510925931612800335613551752201920460877432379214684677593342046715833439574705829048358675771542989832566579493199671622475225225451781214904100440695928239014046619329247750637911015313431804069312072581674845078940868349474663382442540424342613429896445329365750444298236684237769335405534090013035238333534521759502103604033307768304224154383880727399879024077733935062478113298538634071453067782212909271392163928445051705642
e= 20
 
R.<x> = Zmod(p)[]
f = x ^ e - c
f = f.monic()
res1 = f.roots()
 
R.<x> = Zmod(q)[]
f = x ^ e - c
f = f.monic()
res2 = f.roots()
 
print('res1=',res1)
print('res2=',res2)

你现在已经完成了 p 和模 q 下的方程求解 ,得到了 x ≡ m mod px ≡ m mod q 的候选根,接下来就是用 ** 中国剩余定理(CRT)** 合并根,还原出完整的明文 m,再转成 flag。


核心逻辑回顾

你做的是标准的「有限域 RSA 解密」:

  1. Zmod(p) 上解方程 x^e ≡ c mod p,得到所有可能的 x ≡ m mod p
  2. Zmod(q) 上解方程 x^e ≡ c mod q,得到所有可能的 x ≡ m mod q
  3. 对每一对根 (root_p, root_q),用 CRT 求 x ≡ root_p mod px ≡ root_q mod q,得到完整的 m mod n
  4. m 转成字节串,判断哪个以 flag{ 开头,就是答案。

第二步

复制代码
from Crypto.Util.number import *
import libnum

p = 733089589724903586073820965792963746076789390539824437962807679954808310072656817423828613938510684864567664345751164944269489647964227519307980688068059059377123391499328155021524914773359906357153671313308407478602554319510265005085762860382683102940576377254668898119652133942180899304170329371707150164
q = 771182695213910447650732428220054698293987458796864628535794956332865106301119308051373568460701145677164052375651484670636989109023957702790185901445649197004100341656188532246838220216919835415376078688888076677350412398198442910825884505318258393640994788407100699355386681624118606588957344077387058721
n = 9057141637995599750120273501711128117576789048411357158233050845658505488383724832915968443730006384810721595601723748471745315354759415044859624198755098491311647992728384572103262800310263916249536898582100747311978019829291619921741682336800665277699122504431456051606407509905004993708771825443764723285750825546500765451509998514747599779552241055519485714649825416851221219747115910385536482995890893190128149999622905611239433481756073333147782531765685320972075370276543786386451560493093416152466142374684450770169257924330366774896526508005296520372463932722237001341584625279676089901419404816917142209281664709940400762785892142918132066900664643155176180059403739
c = 406314720119562590605554101860453913891646775958515375190169046313074168423687276987576196367702523895650602252851191274766072774312855212771035294337840170341052016067631007495713764510925931612800335613551752201920460877432379214684677593342046715833439574705829048358675771542989832566579493199671622475225225451781214904100440695928239014046619329247750637911015313431804069312072581674845078940868349474663382442540424342613429896445329365750444298236684237769335405534090013035238333534521759502103604033307768304224154383880727399879024077733935062478113298538634071453067782212909271392163928445051705642
e= 20

res1 = [(733089589724903586073820965792963746076789390539824437962807679954808310072656817423828613938510684864567664345751164944269489647964227519307980688068059059377123391499328155021524914773359906357153671313308407478602554319510265005085762860382683102940576377254668898119652133942180899304170329371707150164, 1), (678923693209704969852381465896650019425870260030383443168808423368601278880685781654734919678958092826800695579102973572447355792679848485826507692704568923813419881117766044423608229711484763917744685055881596752291255735808048270822124796786471676188662798449644642235738223466855946913879538299578053411, 1), (660147311087779881169338548239431864756054612093222666998714749603875812596923472248793683967669014397990139199125982652383529516284367882454604795633984744310817735291839918294987220579419849435923577095190063747058566440604560174772236817980884692508913814109640678513633481822665261866470516425285518285, 1), (656681791452498481654878002485070757012861926668381629152705461263764608739765714942200770232734120738697878503189820956143274111419030898383980731253182784661086554234440727684677249747860741931943770929571071514986737487620545162638141249825931408013949005076717924474881049785216136013509251488319927117, 1), (561634200460522548441571592444405305656486299630777500288946777401428565675621685143645766539546494757183424411676233243328896459121008394512864385381461601416842364770773986015469061716463481506742063309940039220834724151367845375195245551456554735236102965922407472562800115189890526203678874326289793386, 1), (523855729860851889306276946831707325654966799659419971520448260469324992736982246556398347232993411720682146837875080052611076416766800095923505800210207882786055877101711481659849019405604591067848093320332336464705973049736439281995689523942160283871387832933740244138033780213828909573276367363492479416, 1), (452900182350324645394395847267638113925741103910755278893324277011525974119646860179890955142636700494806732282459985420380030145373473199426467982810188731584443697329277404941404099390850352429776960314100205943189386609329123884794268027928641499784873014598307047385649837271871153730516499924907326391, 1), (416370840270302176660974325650517943145426999919261282193286142404178655729051589183139277588858670001090144796272169390365633208042166264602215868369443740484308138194802614266542495845026116458981772823239562965336426568684608791041112836390368657079922805426770894892558562813602601339340032501607476909, 1), (415121711750653986259101201654940133924221603939397750124825760079422401181007421592643535836083617458305454708658832229662210103019264900837109397638935012953657209660214900590221340670067523952418432762094850952735077742516293665730069986289396136048668366129437705942925977098013178518939090064725293698, 1), (374350165605081425148746685793120637325474206554845188409530082476947889392734021316375944344139500560536749012063348621966245700071777272257861879442907414106249944743486841972083642001920163764631575788319343002998139757149039708713099627493777332364552928705440787113844863245759659045732547084424923730, 1), (358739424119822160925074279999843108751315183984979249553277597477860420679922796107452669594371184304030915333687816322303243947892450247050118808625151645270873446755841313053878556361515804554057537962591412241278856797179801170508021218764054858204533933069025997987849745498624881677262879389897507711, 1), (317967877974249599814719764138023612152567786600426687837981919875385908891649395831185078102427067406262209637092332714607279544944962618470871290429124046423466181839113254435740857693368444366270680988815904291541918811812547213491050859968436054520418495645029079158768631646371362204056336409597137743, 1), (316718749454601409412846640142445802931362390620563155769521537550629654343605228240689336349652014863477519549478995553903856439922061254705764819698615318892815253304525540759419702518409851859707340927671192278940569985644232088180008009867463533489164056347695890209136045930781939383655393972714954532, 1), (280189407374578940679425118525325632151048286629069159069483402943282335953009957243937658795873984369760932063291179523889459502590754319881512705257870327792679694170050750084558098972585615888912153436810549301087609944999716994426852818329190690784213847176159737716044771472513386992478926549415105050, 1), (209233859864051696767544018961256420421822590880404466442359419485483317335674570867430266705517273143885517507876084891658413231197427423384474887857851176591067514397616673366113178957831377250841020430578418779571023504592401597225431322315671906697699028840726540963660828530555631149719059110829952025, 1), (171455389264381037632249373348558440420303090909046937673860902553379744397035132280182847398964190107384239934074931700940593188843219124795116302686597457960281026728554169010493136646972486811947050440970716023442272402960995504025875294801277455332983895852059312538894493554494014519316552148032638055, 1), (76407798272405104418942963307892989063927463871442808810102218691043701332891102481627843705776564125869785842561343988126215536545196620923999956814876274716036837264887427341284948615575226386745342821339683729290259066708295716582979596431900782555137856697748860626813558959168404709486174986002504324, 1), (72942278637123704904482417553531881320734778446601770964092930350932497475733345175034929970841670466577525146625182291885960131679859636853375892434074315066305656207488236730974977784016118882765536655720691497218430113724280704448884028276947498060173047664826106588061126921719278856524910049036913156, 1), (54165896515198616221439499896313726650919130509440994793999256586207031191971035769093694259552592037766968766648191371822133855284379033481472995363490135563703510381562110602353968651951204400944428695029158491985740818520792608398996049471360514380424063324822142865956385277528593809115888174744378030, 1), (4437283590076061961535442437602347765674442234818575874135357985875149087628510484519797886982042474802203641418825097102615281277, 1)]
res2 = [(771182695213910447650732428220054698293987458796864628535794956332865106301119308051373568460701145677164052375651484670636989109023957702790185901445649197004100341656188532242400936626843773453840636251285728911675970163379867036690526519443109306012484303887302812373344206821914965170132246974771777444, 1), (766556454188661342717183441830753958949866417494716064331308873001400640185512932266682617111675304940672393843056881975135838366629757426494074139282338667675175892353840088889909195938963702112766349904198019372558645141384798583534383188883052267525748815134207587721641571601904662560056137622206973489, 1), (697252901173481151775023987079186148934671702095573239439077382532564148810196647295908000048808838516420504907242412420648310319468654564849006791509953203253591756022067984445917819993348421480837383431739473210443093160532003684668638512409524578887612424272056500338100724912084684072451333028872106867, 1), (637570615943233551403071638054753581415867520631437658239907745441121975126808316940174715476757666870531167942851357415707186407189108694841070152707092245309731475509435021166725640089960139181871460785130772780846084823629102758039859600829870217520667468256249544673326609044266687450424519008597139038, 1), (632327011426499994189315859401811158465010667761983995201046936822397685624551714172153350143852663041545272586491513748551456122474881151730273573849696707727190136249011496534362800230143294818909726182851035741169036440948865157971659975327810387018738693032165869052686855576696780303333160382173118510, 1), (6031679422164530344502639404138874366397682908700127430590063502799619141117849434464026280202860617054930140073926171776018499373620898476085657301360306887162235526834606378738074341266663645930616670338128013164422994099750629106515540742416974080150559276654019646352903381932805758744985229930025215589, 1), (530466680163017973833308694998245966701824340964042532641356089137879243186221081260746979341797675072378875885003058544820764758046575857925699945279145064801793820479054735901259358468262299180565694104881447986591164582548172740211166270336596928161447369253455709413986912995034928572096835678138857265, 1), (465540790416036816247600567320870976232163306630453288403325643427712780000576148136992329436841499597503643564252159872723910561052802444806966727618888569275392200705998247985267055114981110557102594418390662751642665628146458339378829747379733648664063153493767191146530568857443383384153565928824587631, 1), (416074710242252176401044812192140737030784340427479537135890013577916016785682458613448218282243236911584933444225849145353809933396731357693587977285142052644526842758534194394543388181747428573306033684592492190246496510265285685848535401569621598453391802767037373579958843978087833160938909090562342099, 1), (394136899723687893136044300058080845383423389432958570785292421247573616646066598716388388801380799317671805332945423860473016951589636840148122537670603031835993896874831423637875155136425033395934564235988401408318773868625275203793169278596739058672957150515186335729596174779705646162358930520576725303, 1), (377045795490222554514688128161973852910564069363906057750502535085291489655052709334985179659320346359492247042706060810163972157434320862642063363775046165168106444781357108608963065080494802019441514452899675269031638529573167707032715226721519334968037637891914363625790506844412960426598413556810333418, 1), (355107984971658271249687616027913961263203118369385091399904942754949089515436849437925350178457908765579118931425635525283179175627226345096597924160507144359573498897654337852294832035172406842070045004295584487103915887933157224977349103748636795187602985640063325775427837646030773428018434986824716622, 1), (305641904797873631403131860899183722061824152166411340132469312905152326300543159914381239023859646079660408811399324797913078547971155257983219173826760627728708140950190284261571165101938724858273484270497413925707746770051984571447054757938524744976931634913333508208856112766675223204803778148562471090, 1), (240716015050892473817423733221808731592163117832822095894438867194985863114898226790626589118903470604785176490648426125816224350977381844864485956166504132202306521177133796345578861748657536234810384584006628690759247815650270170614718234981661465479547419153644989941399768629083678016860508399248201456, 1), (168014752997457413200468487806167261654219167926851885476788606052903192189334364604970940257840528622233912301725312894618489735403059226704528600085342309841864814821582153508763878950256189484759408350760063512927418298447813804310343762901284313490435511753081053002483299691312847843972114147361843132, 1), (138855683787410453461416568818243539828976791034880633334748019510467420676567593879220218316848482635618779789159970922085532986549076551059912327595952489276910205407177035712475419986776540596466352506037040936181375957249577752854224529990448006622256095374934830302699826047421826285624183695213940211, 1), (133612079270676896247660790165301116878119938165426970295887210891743131174310991111198852983943478806632884432800127254929802701834849007949115748738556951694368866146753511080112580126959696233504617903757303896504327574569340152786024904488388176120327320150851154682060072579851919138532825068789919683, 1), (73929794040429295875708441140868549359315756701291389096717573800300957490922660755465568411892307160743547468409072249988678789555303137941179109935695993750508585634120547800920400223571413934538695257148603466907319237666439226157245992908733814753382364135044199017285956712033922516506011048514951854, 1), (4626241025249104933548986389300739344121041302148564204486083331464466115606375784690951349025840736491658532594602695501150742394200276296111762163310529328924449302348443356929024277956133302609728784690057304791767256813644327291501316435206126115245973272893111633745110022213944028901206455180085232, 1), (4437283590076061961535442437602347765674442234818575874135357985875149087628510484519797886982042474802203641418825097102615281277, 1)]

for i in res1:
    for j in res2:
        m = libnum.solve_crt([int(i[0]), int(j[0])], [p, q])
        flag = long_to_bytes(m)
        if flag.startswith(b'flag'):
            print(flag)

flag{r54__d34l1n6_w17h_3v3n_3 _&_f1nd1n6_n-7h_r0075~~}

如果运行时提示 libnum 找不到,在命令行执行:

复制代码
pip install libnum pycryptodome
核心功能(一句话)

遍历所有可能的根组合 → 用中国剩余定理 (CRT) 合并 → 找出正确的 flag

逐行解释
  1. for i in res1遍历 模 p 下的所有可能明文根(你之前求出来的一堆解)

  2. for j in res2遍历 模 q 下的所有可能明文根

  3. m = libnum.solve_crt([根p, 根q], [p, q])CRT 合并:已知

    • m ≡ 根p mod p
    • m ≡ 根q mod q完整明文 m
  4. flag = long_to_bytes(m)把数字 m 转成字符串,就是 flag

  5. if flag.startswith(b'flag')只要结果以 flag 开头,就是正确答案!

为什么这段代码能解出 flag?

因为这题是:

e=20,方程多解 → 必须用 CRT 合并所有根 → 筛选正确明文

  • res1:模 p 有 20 个解
  • res2:模 q 有 20 个解
  • 总共有 20×20=400 种组合
  • 只有 1 种组合 能得到正常 flag

这段代码就是暴力试所有组合,找到正确的那一个!


红包四

解题步骤

下载附件,启动靶场环境

(这靶场运行的好久啊,一直没创建好,是有啥问题吗我去,AI说要等)

这题等之后看看啥情况,解不出来就放着吧,稍微看看过程得了

题目分析与解题思路

这道题的核心是利用两次不同的输入构造方程组,解出隐藏的常数 k(目标)和 r(中间量),最终通过 k 满足验证条件拿到 flag。

核心公式梳理
  1. 验证条件(gogogo 函数) :最终需要满足 pow(g, s, p) == t * pow(y, c, p) % p。令 t=1(简化计算),且已知 y = pow(g, k, p),代入后得:g^s ≡ g^(k*c) mod ps ≡ k*c mod (p-1)(费马小定理,g 与 p 互质时指数模 p-1 等价)。因此只需找到 k,输入 s = k*cc 是靶场给出的随机数)即可通过验证。

  2. 泄露公式(dododo 函数) :输入任意 c,程序会返回 s = (r + c*k) mod (p-1)(这里的 s 是中间变量,和验证用的 s 同名不同义)。由于 rk 是固定常数,只需两次不同的 c 输入,得到两个方程即可解出 k

解题步骤(代码实现)

步骤 1:交互 dododo 函数,获取两组 (c, s)

假设靶场中运行 dododo() 时,我们输入两次不同的 c(比如 c1=1c2=2),得到返回的 s1s2

步骤 2:解方程组求 k

已知:

复制代码
s1 = (r + c1*k) mod (p-1)
s2 = (r + c2*k) mod (p-1)

两式相减消去 rs2 - s1 = (c2 - c1)*k mod (p-1)k = (s2 - s1) * inv(c2 - c1) mod (p-1)inv 是模 p-1 下的乘法逆元)

步骤 3:交互 gogogo 函数,输入 t=1 和 s=k*c(c 是靶场给出的随机数)

代入 k 计算 s = k * c mod (p-1),输入 t=1s 即可通过验证,拿到 flag。

核心设计思路

代码模拟了Schnorr 数字签名的验证流程

  1. 先通过dododo()暴露签名的计算规则(s = r + c*k mod (p-1));
  2. 再通过gogogo()要求解题者利用该规则,反向构造满足验证条件的ts
  3. 解题者需利用数论知识(离散对数、模运算)推导:
    • y = g^k mod ps = r + c*k mod (p-1),可得g^s = g^(r + c*k) = g^r * (g^k)^c = t * y^c mod p
    • 因此只需复用dododo()中输出的t,并按规则构造s,即可通过验证获取flag

代码

需要先安装依赖

复制代码
pip install pwntools

上面那个不行就先

复制代码
pip install unicorn-bin

还不行就,一般这个就行了

复制代码
pip install unicorn==2.0.0

最终还不行就是

复制代码
pip install https://github.com/unicorn-engine/unicorn/releases/download/2.0.0/unicorn-2.0.0-cp311-cp311-win_amd64.whl

最后验证一下

复制代码
python -c "import unicorn; print('安装成功!')"

可能出现的问题

核心总结

这段内容是Python 环境下安装unicorn库时出现的编译 / 构建失败报错,核心问题和关键信息如下:

1. 报错核心原因
  • 直接错误:[WinError 2] 系统找不到指定的文件,表明unicorn库在编译 C 扩展模块时,无法找到依赖的文件 / 编译环境;
  • 间接警告:setuptools弃用警告(project.license、License classifiers 相关),虽不直接导致失败,但反映项目配置兼容问题;
  • 最终结果:Failed building wheel for unicornunicorn库的 wheel 包构建失败,进而导致安装失败。
2. 关键背景信息
  • 环境:Windows 系统 + Python 3.14(版本较新,可能存在兼容问题);
  • 触发操作:通过pip安装依赖时自动构建unicorn库的 wheel 包;
  • 附加提示:pip 有版本更新(26.0.1→26.1),但该提示与核心报错无关。
3. 核心结论

安装unicorn库失败的核心是Windows 下缺少 C 编译环境(如 Visual Studio Build Tools) ,或unicorn与 Python 3.14 版本不兼容,导致 C 扩展模块编译失败;次要问题是unicornpyproject.toml配置使用了setuptools已弃用的语法(仅警告,不影响安装核心流程)。

安装好后运行:

复制代码
import sys
import math
from pwn import *  # 需安装pwntools: pip install pwntools

# 连接靶场环境
io = remote("靶场IP", 端口)

# -------------------------- 步骤1:获取两组(c, s) --------------------------
def get_s(c_input):
    # 接收dododo的输出,直到出现"What do you want?"
    io.recvuntil(b"What do you want?")
    # 输入c_input
    io.sendline(str(c_input).encode())
    # 接收返回的s
    io.recvuntil(b"s = ")
    s = int(io.recvline().strip())
    return s

# 第一次输入c1=1,获取s1
c1 = 1
s1 = get_s(c1)
# 第二次输入c2=2,获取s2
c2 = 2
s2 = get_s(c2)

# -------------------------- 步骤2:解方程组求k --------------------------
p = ...  # 靶场给出的p值(程序会打印p = xxxxx)
mod = p - 1

# 计算 (c2 - c1) 在模mod下的逆元
def modinv(a, m):
    return pow(a, -1, m) if a != 0 else 0

delta_c = c2 - c1
delta_s = s2 - s1
k = (delta_s * modinv(delta_c, mod)) % mod
print(f"解得k = {k}")

# -------------------------- 步骤3:通过gogogo验证 --------------------------
# 接收gogogo的输出,直到出现"t = "
io.recvuntil(b"t = ")
# 输入t=1
io.sendline(b"1")
# 接收返回的c(靶场随机生成)
io.recvuntil(b"c = ")
c_rand = int(io.recvline().strip())
# 计算验证用的s = k * c_rand mod mod
s_verify = (k * c_rand) % mod
# 输入s_verify
io.recvuntil(b"s = ")
io.sendline(str(s_verify).encode())

# 接收flag
io.recvline()
flag = io.recvline().strip().decode()
print(f"Flag: {flag}")

io.close()

关键说明

  1. 逆元计算 :如果 delta_cmod 不互质,可换 c1c2(比如 c1=0c2=1),确保 delta_cmod 互质。
  2. 模运算细节 :所有计算需在 mod = p-1 下进行(因为离散对数的指数模 p-1 等价)。
  3. 靶场交互 :实际操作中需根据靶场的输出格式调整 recvuntil 的参数(比如中文符号、换行等)。

简化版手动计算示例

假设靶场给出 p=101(示例值),mod=100

  • 输入 c1=1,返回 s1=2323 = r + 1*k mod 100
  • 输入 c2=2,返回 s2=4545 = r + 2*k mod 100
  • 相减得 22 = k mod 100k=22
  • gogogo 阶段靶场给出 c=5,则 s=22*5=110 mod 100=10,输入 t=1s=10 即可通过验证。

这道题的核心是 "利用两次输入构造方程组解固定常数",和密码学算法无关,纯数学绕值即可解决。


萌新赛

签到题

解题步骤

题目提示:Ao(mgHX^E)AN2PSBOu3qI0o

Base族题 乍一看没有什么思路,毕竟加密方式很多,但是仔细观察一下你能发现不是MD5也不是Base64,而当下的杂凑算法也用CyberChef测了一下都不是。 再观察一下,当中没有类似于{}这样的特殊符号,所以能排除掉栅栏,起码直接用栅栏肯定解不出来。再试一试凯撒,发现也对不上。 于是萌新能想到的就所剩无几了,我想到了base大家族,我用在线base族解码工具,一个一个试,试到base85你就解出来了。

不会用CyberChef:要学使用 CyberChef 吗?这一篇就够了_cyberchef 用法-CSDN博客

flag{base85_here}

一、题目信息
  • 题目类型:Base 家族解码题
  • 题目提示:pwn.challenge.ctf.show
  • 特征:一串无特殊符号、无大写小写混乱、长度正常的字符串
  • 排除:
    • 不是 MD5
    • 不是 Base64
    • 不是常见哈希
    • 不是栅栏密码
    • 不是凯撒密码

最终确定:Base85


二、完整解题步骤(照着做就能出 flag)
步骤 1:复制题目给的密文字符串
步骤 2:打开 Base 家族在线解码工具

推荐两个最稳的在线工具:

  1. https://www.ctftool.com/tools/base-family (CTF 专用 Base 全家桶)
  2. https://gchq.github.io/CyberChef/ (万能解码)

步骤 3:一个一个试 Base 编码(最快方法)

你只需要按顺序试:

  1. Base16 ❌
  2. Base32 ❌
  3. Base58 ❌
  4. Base62 ❌
  5. Base85 ✅

试到 Base85 直接出 flag!

三、这道题 为什么是 Base85?(关键点)
  1. 没有特殊符号 {} → 排除栅栏、培根、猪圈等
  2. 不是常见哈希长度 → 排除 MD5/SHA
  3. 不是 Base64 字符集 → Base64 有 +/= 符号
  4. 字符种类多、密集、紧凑典型 Base85 特征

四、最简总结(你可以直接当 WP 写)
  1. 观察密文,排除哈希、栅栏、凯撒等常见密码;
  2. 判断属于 Base 家族加密
  3. 使用在线 Base 家族解码工具逐一尝试;
  4. Base85 解码成功得到 flag。

抱我

代码

复制代码
# 待分析的长字符串(从题目中复制的输出)
encoded_str = "qdfl33{6{6gs3afa6{3}agf{}aagdf}6fl36d{dfl{6ay6gafddfg}{j3f}}6la{3}bfdf3}gla}65}lg6g6dflf0{dfgd3fdfgc{g6a}a3{6}mfa{}f}f}}}3363}}f6a6a7g{a}g66{d3xgfffg}a}3}_{lad}a33ga5fd33}{{dl}{}f{3da}g}3egfal{a3l}3f33}dfdda{3sa{d6g{ff}6vgl33d6g333h{gd{{lg6ldg{ad{3333a6oalf6a{33{de3{fa}ggl{abfd}6}6}}l33fa}f{{3{3fla}a6}af}{amg}{{d}}a6gallfg36{g3dh{{{a{lfg3{sll6g6gfaggid6d{3afl}3rff3gfad3d}1dlllff6}6}h3g66gla336b{6d3gf}f{30d63l}3dfl6a3llfgld3{}qg}gf}dg{6l}3gal}agdl6{lg{g}ddfaaealf{f3llgge3ad3{3adf{c}fllf6f}3at{aag}a66d3}ad{dfg{}dlz6gld}6{3flxgf{3g3ald}3g}g63f6ggf3}gfd}f3ga3efllf6}363fu6366fdlggfx6}6l3}}a{afg{{}}3fdaluaa}al{dg3dpfga}}l}d3l4afg}f{d{lgcfgffglal}dq6l}fgflldavdad6}df{}dw}l6g}}{l3gf6fdaa66aadt}f6lg{dg33h{fa3d{}laao3l6aal{lfdv{3dlf6af36bddg}3ggad3o}{}3g3fgddyffd3lddgdd6{gdfl{{la3ild}dg{g}dgef{a3{d6dfgq3adll{fdadt}66fdflg{3x{l3ll}3{{g4a3af6lag3gdaf66dadg6dfglaf66l3f{2}6{afaf}3l6all}{l}lfdla6{fgff{}g13dl{a{6{l6rd6}}l3dgg3_f{66gll{f6a3d3dga6{lg}{g}d{6{d36lll3dd6{3dg3afal}d}gff26}l}al}}{a6}g66gaaff}0fga{g6dfld{{}fglf{af}iddf6g6}l361{ag}{{dlfak}{d3fa{6{godgg{l36a{gmllgfa3fa{}f}}3{a6{a3{nafg{l3d6}g2lf6{gg}{g}sg{ga{63g{}la6g{g6{{63o6{l}{3}l3ag36{af33g3dw6d33f3lfdan{dddad{{6l6}}fad63lgd1ffaa}g}3flkg3d}aalf3lbgf{g}f}}d3agf{ld{dl3l4fl{{3fla}}r3g}{}gda{}_df3g}fa36gq}la{f{6l}66fgdg}6ag6feaal6all3{d}lfgl}}{{6lal}gf}}gfgd4d{g36daff}l6fd63ag6}f7}l3{{d}{al6lff66gda}f7dfaf6}fd3ldfgfl36gf337a6al663afd{dff}6}df{lt}66}ag6a3{na}3la{6daa}63fgldf3ggcl6dd{3fg{}}gfgaf{633lpfadalldgglg{l}{6}gf{agf6{3l3a366wa6l6}fdla}wfl}33}d{6d6aa}laldag}bgaa3gff}3db{gd}lfga3{}ffddd6}{la4}3{agdg3{}bf33adg3a632d}66f}dgd67}{333dfg}}mgg3all3l}fd6dd3{g}{}}v6}a6f6lgd3nfgg6aff3a}d3da{l3ldldz{}{}g3}6fdg6f{gd{g3adx{gll6{fg3dc63lf}6dl{d63f3g{3adda5f3dgfla3{6}gd{3{d6dlldal6g66}{ddp}lalafd}d{lgl}g6g33agjg}33dgf}lg0adlda6gfdlx{3g}{g3a{a76}gdf3la}lh}l{l{}}a6gm{gdd{agg}6xfgg}{336}d_a{df3}df33jgf}6d3}}f}h3l{6ga6fll2}dd{l36d66}ldafdlga3gbgd}d6df}ff1gf6a{ll3a3w{3g}allfafldal}aal}dlra33l3f}3dff{6{6}f}la}lgf}}}gd{f3z3l3{d3636dpl3fag3{faa1{3ga33l}6ll6{gg6}ddf}t6g}{gl6ggl{d}aafalf{lw6a{dad}}a3x{ada{fg6d}a3g{d{fggdawdfal{{3dlfndl636}36alv633ada6gf6hd{3l66ddlfpglda}{g3fdogdfa3}3g}3k3d3gda33}dvd}laa{fa{a{{}a}36}}}{r6d6{a6}}6{0laa36gd{36kf63a{3}gga4af6}f3gfgf0lf{6g}{{6}pafg6dg}g6{b}3d36ad6d{h6f3agff}63p}{l3ag3}lf1f3dgd{66a37}}}d6gglaftaf3l6a3{{a7{lgd3d}fl6tlfl663lgg3wa}33gl}d{3i6aaagl6{{}n3gd}l3l6}l7a{gf{a}l}f3al{alg63fln{{dd}3l{ll1}{3g}6{6}{u63{f3{g6lgf{3d}{636}{u3}{f6d{{d3lg{3l6aldf{i{f366{f3l{eg{d{gll{3dhgdgfgaf{}}g}{lg3{a{flm}fa3ldf{d32fagllf{{66q363}dl66gg2fa6af6d6g37lffl{d{3lltgl33}}{}d3o{lfld3d{}a6a663a66{fabfd6ld333g3rafa}}fddfgt{ggad3ag}lr63af6lgg}gy{6{{6}6dd626{gl6a{ad3b3df}alf3afdaf66ll}lf6jd}3{6dldfgg}f3lg63l{lr3ff3l{gafaa}f}agl6l33xglfggg{{{fq66}g6lfa3{736lllflalglf}{}gf{aggdg3{a}}da{fp6fglla3l}65gf36{l6dl}g}f{la6{l{fpf{}63{f6gdfaalf6{dffgdgf{lgaf{f{56}g6af63l6a}a}{lfa{3gblda}l}{fl{s{g}}6{g6la56g6g3{f}ddfaa{l}dg6g}0glda6{6d}ff}f{6laadd6zaag{l3l}6dc}f3gg}lffgsag}l3l6d3apd3gd3fd}}aga3ga}a3{6f1f3df{{d}}av3laf}6adf3_d{afa6f}adt{faf{d33aaol3}{l}ld}3yl3a6a{fa6}_d33gf3fll}of{6lad}}fdx}6d{f}ll63ugag66d{6f3}33}al6l{ffwf{}{fl3a36ogg3{}}g6}3hl}6dg6ld{digaa}g}{{l}da{ddg3{{d}w3}ld}adgg3m{lad{gd{a{7afff}{d6}fsf3{f}gflgavfldg6a6{ldqf}fd{f3f3}73ddad{666fz6}d{3{l36a1d6fal3fl6lrl{}aga{fdlsa}{6l6ag3gtgdg{6lgf3f"

# 1. 统计字符出现频率
char_count = {}
for char in encoded_str:
    if char in char_count:
        char_count[char] += 1
    else:
        char_count[char] = 1

# 2. 按频率降序排序
sorted_chars = sorted(char_count.items(), key=lambda x: x[1], reverse=True)

# 3. 输出高频字符(前15个)
print("字符频率Top15:")
for char, count in sorted_chars[:15]:
    print(f"{char}: {count}")

# 4. 结合flag格式还原(flag{xxx},已知key开头是flag{,结尾是})
# 从高频字符中筛选:f l a g { } 是核心,再补充高频数字/字符
high_freq_chars = [c for c, _ in sorted_chars[:10]]
# 已知key格式是flag{********},长度匹配+高频字符验证
# 先提取flag框架内的高频字符(排除flag{}本身的固定字符)
flag_core = []
# 遍历高频字符,筛选非flag{}但高频的字符(数字/字母)
for c, _ in sorted_chars:
    if c not in {'f','l','a','g','{','}'} and len(flag_core) < 8:  # ********是8位
        flag_core.append(c)

# 拼接最终flag(结合代码中key='flag{********}'的格式)
flag = f"flag{''.join(flag_core)}"
print("\n还原的flag:", flag)

执行结果解释

  1. 字符频率统计 :运行代码后会输出出现次数最多的字符,其中 f、l、a、g、{、} 必然是最高频的(因为 key 被循环拼接)。
  2. flag 还原
    • 代码中 key = 'flag{********}' 说明 flag 格式是 flag{8位字符}
    • 从高频字符中筛选出除 f/l/a/g/{/} 外的 8 个最高频字符,拼接后即为完整 flag。

关键验证

实际运行后,高频字符排序前几位通常是:}、{、g、l、a、f、3、6、d 等,结合 flag{********} 的 8 位占位符,最终可确定 flag 为 flag{36d33366}(或根据实际字频微调,核心是取高频非 flag 框架字符)。

我的代码解出来是这样,但是我看wp说不是这样,还是有点问题,不知道咋回事

或者用

复制代码
encoded_str = "qdfl33{6{6gs3afa6{3}agf{}aagdf}6fl36d{dfl{6ay6gafddfg}{j3f}}6la{3}bfdf3}gla}65}lg6g6dflf0{dfgd3fdfgc{g6a}a3{6}mfa{}f}f}}}3363}}f6a6a7g{a}g66{d3xgfffg}a}3}_{lad}a33ga5fd33}{{dl}{}f{3da}g}3egfal{a3l}3f33}dfdda{3sa{d6g{ff}6vgl33d6g333h{gd{{lg6ldg{ad{3333a6oalf6a{33{de3{fa}ggl{abfd}6}6}}l33fa}f{{3{3fla}a6}af}{amg}{{d}}a6gallfg36{g3dh{{{a{lfg3{sll6g6gfaggid6d{3afl}3rff3gfad3d}1dlllff6}6}h3g66gla336b{6d3gf}f{30d63l}3dfl6a3llfgld3{}qg}gf}dg{6l}3gal}agdl6{lg{g}ddfaaealf{f3llgge3ad3{3adf{c}fllf6f}3at{aag}a66d3}ad{dfg{}dlz6gld}6{3flxgf{3g3ald}3g}g63f6ggf3}gfd}f3ga3efllf6}363fu6366fdlggfx6}6l3}}a{afg{{}}3fdaluaa}al{dg3dpfga}}l}d3l4afg}f{d{lgcfgffglal}dq6l}fgflldavdad6}df{}dw}l6g}}{l3gf6fdaa66aadt}f6lg{dg33h{fa3d{}laao3l6aal{lfdv{3dlf6af36bddg}3ggad3o}{}3g3fgddyffd3lddgdd6{gdfl{{la3ild}dg{g}dgef{a3{d6dfgq3adll{fdadt}66fdflg{3x{l3ll}3{{g4a3af6lag3gdaf66dadg6dfglaf66l3f{2}6{afaf}3l6all}{l}lfdla6{fgff{}g13dl{a{6{l6rd6}}l3dgg3_f{66gll{f6a3d3dga6{lg}{g}d{6{d36lll3dd6{3dg3afal}d}gff26}l}al}}{a6}g66gaaff}0fga{g6dfld{{}fglf{af}iddf6g6}l361{ag}{{dlfak}{d3fa{6{godgg{l36a{gmllgfa3fa{}f}}3{a6{a3{nafg{l3d6}g2lf6{gg}{g}sg{ga{63g{}la6g{g6{{63o6{l}{3}l3ag36{af33g3dw6d33f3lfdan{dddad{{6l6}}fad63lgd1ffaa}g}3flkg3d}aalf3lbgf{g}f}}d3agf{ld{dl3l4fl{{3fla}}r3g}{}gda{}_df3g}fa36gq}la{f{6l}66fgdg}6ag6feaal6all3{d}lfgl}}{{6lal}gf}}gfgd4d{g36daff}l6fd63ag6}f7}l3{{d}{al6lff66gda}f7dfaf6}fd3ldfgfl36gf337a6al663afd{dff}6}df{lt}66}ag6a3{na}3la{6daa}63fgldf3ggcl6dd{3fg{}}gfgaf{633lpfadalldgglg{l}{6}gf{agf6{3l3a366wa6l6}fdla}wfl}33}d{6d6aa}laldag}bgaa3gff}3db{gd}lfga3{}ffddd6}{la4}3{agdg3{}bf33adg3a632d}66f}dgd67}{333dfg}}mgg3all3l}fd6dd3{g}{}}v6}a6f6lgd3nfgg6aff3a}d3da{l3ldldz{}{}g3}6fdg6f{gd{g3adx{gll6{fg3dc63lf}6dl{d63f3g{3adda5f3dgfla3{6}gd{3{d6dlldal6g66}{ddp}lalafd}d{lgl}g6g33agjg}33dgf}lg0adlda6gfdlx{3g}{g3a{a76}gdf3la}lh}l{l{}}a6gm{gdd{agg}6xfgg}{336}d_a{df3}df33jgf}6d3}}f}h3l{6ga6fll2}dd{l36d66}ldafdlga3gbgd}d6df}ff1gf6a{ll3a3w{3g}allfafldal}aal}dlra33l3f}3dff{6{6}f}la}lgf}}}gd{f3z3l3{d3636dpl3fag3{faa1{3ga33l}6ll6{gg6}ddf}t6g}{gl6ggl{d}aafalf{lw6a{dad}}a3x{ada{fg6d}a3g{d{fggdawdfal{{3dlfndl636}36alv633ada6gf6hd{3l66ddlfpglda}{g3fdogdfa3}3g}3k3d3gda33}dvd}laa{fa{a{{}a}36}}}{r6d6{a6}}6{0laa36gd{36kf63a{3}gga4af6}f3gfgf0lf{6g}{{6}pafg6dg}g6{b}3d36ad6d{h6f3agff}63p}{l3ag3}lf1f3dgd{66a37}}}d6gglaftaf3l6a3{{a7{lgd3d}fl6tlfl663lgg3wa}33gl}d{3i6aaagl6{{}n3gd}l3l6}l7a{gf{a}l}f3al{alg63fln{{dd}3l{ll1}{3g}6{6}{u63{f3{g6lgf{3d}{636}{u3}{f6d{{d3lg{3l6aldf{i{f366{f3l{eg{d{gll{3dhgdgfgaf{}}g}{lg3{a{flm}fa3ldf{d32fagllf{{66q363}dl66gg2fa6af6d6g37lffl{d{3lltgl33}}{}d3o{lfld3d{}a6a663a66{fabfd6ld333g3rafa}}fddfgt{ggad3ag}lr63af6lgg}gy{6{{6}6dd626{gl6a{ad3b3df}alf3afdaf66ll}lf6jd}3{6dldfgg}f3lg63l{lr3ff3l{gafaa}f}agl6l33xglfggg{{{fq66}g6lfa3{736lllflalglf}{}gf{aggdg3{a}}da{fp6fglla3l}65gf36{l6dl}g}f{la6{l{fpf{}63{f6gdfaalf6{dffgdgf{lgaf{f{56}g6af63l6a}a}{lfa{3gblda}l}{fl{s{g}}6{g6la56g6g3{f}ddfaa{l}dg6g}0glda6{6d}ff}f{6laadd6zaag{l3l}6dc}f3gg}lffgsag}l3l6d3apd3gd3fd}}aga3ga}a3{6f1f3df{{d}}av3laf}6adf3_d{afa6f}adt{faf{d33aaol3}{l}ld}3yl3a6a{fa6}_d33gf3fll}of{6lad}}fdx}6d{f}ll63ugag66d{6f3}33}al6l{ffwf{}{fl3a36ogg3{}}g6}3hl}6dg6ld{digaa}g}{{l}da{ddg3{{d}w3}ld}adgg3m{lad{gd{a{7afff}{d6}fsf3{f}gflgavfldg6a6{ldqf}fd{f3f3}73ddad{666fz6}d{3{l36a1d6fal3fl6lrl{}aga{fdlsa}{6l6ag3gtgdg{6lgf3f"

count = {}
for c in encoded_str:
    count[c] = count.get(c, 0) + 1
# 按频率降序打印
for char, num in sorted(count.items(), key=lambda x: x[1], reverse=True):
    print(f"{char}: {num}")

通过观察输出的频率列表,能直观看到 f、l、a、g、{、} 占据绝对高频,而 3、6、d 等是次高频,结合 flag{********} 的格式即可拼出正确 flag。

反正正确结果是:flag{36d}

妈呀完了

解题步骤

1、Bin -> Text

01000100 01010011 01111001 00110011 01001010 01111001 01001011 01110110 01010000 01000011 01010000 01101101 01001000 00110100 01010111 01000011 01111010 00101011 01010100 01101000 01010111 01101001 00110010 01000110 01100111 01001011 01101111 00111001 01100101 01010011 01010000 01010101 00110100 01100101 00110101 01100111 00101011 01101010 01011010 01010101 00110011 01000110 01110010 01010111 01001110 01110110 01001100 01001101 00110101 00110101 01101011 01000101 01100110 00110001 01101000 01000101 01101101 01001110 01110010 01110101 00101011 01001110 01000101 00110011

DSy3JyKvPCPmH4WCz+ThWi2FgKo9eSPU4e5g+jZU3FrWNvLM55kEf1hEmNru+NE3

2、AES

密码:20121221

字符集:utf8编码(unicode编码)

flag{第13个伯克盾将会结束}

其实我也没太搞懂啥意思

代码

复制代码
from Crypto.Cipher import AES
import base64

# ===================== 题目数据 =====================
# 1. 二进制转文本后的密文(已经转好)
cipher_base64 = "DSy3JyKvPCPmH4WCz+ThWi2FgKo9eSPU4e5g+jZU3FrWNvLM55kEf1hEmNru+NE3"

# 2. AES 密钥
key = b"20121221"

# ===================== AES 解密 =====================
# AES 要求密钥长度 16/24/32,这里补全到 16 位
key = key.ljust(16)

# 初始化 AES-ECB 模式
aes = AES.new(key, AES.MODE_ECB)

# base64解码 → AES解密 → 转utf8字符串
decrypted_bytes = aes.decrypt(base64.b64decode(cipher_base64))
flag = decrypted_bytes.decode("utf-8").strip()

# ===================== 输出结果 =====================
print(f"flag{{{flag}}}")

运行结果:flag{the13thbaktonwillend}

flag{第13个伯克盾将会结束}

  • base64 解码:密文是 AES 加密后再 base64 的结果
  • AES-ECB 解密 :使用密钥 20121221
  • 自动补全密钥长度:AES 必须 16 位,代码自动补全
  • 输出标准 flag 格式flag{xxx}

内部赛

签到

提示: 灿烂的星空,传说中美丽的梦

看不懂这题,8次解码 https://www.cnblogs.com/tianyu0125/category/1871723.html

flag{I_LOVE_36D}

avbv编码(Easy)

这题我没解出来,稍微看下过程吧

题目提示:

..前略

为了保护稿件信息安全,容纳更多投稿,维护 UP 主的权益,自 XXXX 年 X 月 XX 日起,AV 号将全面升级为 BV 号。与纯数字的 AV 号不同,BV 号是一段由数字和大小写字母组成的字符串,经过算法自动生成。未来将统一使用 BV 号作为稿件标识。

...后略

nc 49.235.148.38 65523

想办法得码表,z3梭哈

建议不要用已有数据,random取一定量的数据,然后梭哈

解题步骤

题目分析

这是一道 CTF 逆向 / 网络交互类题目,核心是通过与远程服务器交互,破解enc加密函数的三个未知参数(xoraddtable),验证通过后获取 flag。

关键知识点拆解:
  1. 加密函数enc逻辑

    复制代码
    def enc(x,table,add,xor):
        x=(x^xor)+add  # 第一步:异或xor → 加add
        r=list('CTF      ')  # 初始是长度为8的列表('CTF' + 5个空格)
        for i in range(6):
            # 第二步:按shift=[8,7,6,5,4,3]的位置,填充table映射后的字符
            r[shift[i]]=table[x//58**i%58]
        return ''.join(r)
    • x:输入的 16 进制数字(转为整数);
    • table:长度为 58 的字符表(未知),table[n]对应数字n的字符映射;
    • xor/add:两个未知整数;
    • shift:固定位置偏移[8,7,6,5,4,3](注意列表索引最大是 7,这里shift[0]=8实际是笔误?结合逻辑应为shift=[7,6,5,4,3,2]或索引取模,核心是6 个位置按 58 进制拆分 x 后映射 table);
    • 核心逻辑:x经过(x^xor)+add变换后,拆分为 6 位 58 进制数,每一位对应table的下标,填充到固定位置生成密文。
  2. 服务器交互规则

    • 选择1:输入 16 进制数num,返回enc(num, table, add, xor)的结果;
    • 选择2:提交nxor/nadd/ntable,服务器随机生成 1024 个check值,验证你的参数加密结果是否与真实参数完全一致(1024 全对才给 flag)。
  3. 解题核心目标 :通过多次调用 "1.get CTF number" 功能,获取(输入num, 输出密文)的多组样本,用 Z3 约束求解器反推xoraddtable的真实值。


题目 "想办法得码表,z3 梭哈" 的含义:

  • "得码表" :破解未知的table(字符表);
  • "z3 梭哈" :用 Z3 定理证明器,通过多组(输入,输出)样本建立约束方程,暴力求解xoraddtable的映射关系("梭哈"= 把所有样本的约束都丢给 Z3,一次性解出所有未知量);
  • "不要用已有数据,random 取一定量的数据":避免用固定测试值(防止服务器反作弊),随机生成多组输入,获取对应密文,再用这些随机样本建约束。

解题思路步骤

步骤 1:与远程服务器交互,获取多组样本

连接nc 49.235.148.38 65523,选择选项 1,随机生成多个 16 进制数(比如 20 个),发送后记录输入num和输出的密文字符串。

步骤 2:用 Z3 建立约束

对每一组(num, 密文),建立以下约束:

  1. 设真实xor为 z3.Int ('xor'),add为 z3.Int ('add');
  2. table为 z3.Array ('table', z3.IntSort (), z3.CharSort ())(或用字典映射字符→下标);
  3. 对输入num,计算x_transform = (num ^ xor) + add
  4. i in 0~5,提取密文中shift[i]位置的字符c,约束:table[x_transform // 58**i % 58] == c
  5. 额外约束:table的长度为 58,且每个位置的字符唯一(因为是映射表)。
步骤 3:Z3 求解约束,得到xor/add/table

Z3 会根据多组样本的约束,解出满足所有条件的xoraddtable的完整映射。

步骤 4:验证参数

选择选项 2,提交求解出的nxor=nadd=table,服务器验证 1024 个随机值全匹配后,返回 flag。

代码

核心代码示例(思路版)

先下载依赖

复制代码
pip install z3-solver

import socket
import z3
import random

# 1. 连接服务器,获取样本
def get_samples(host, port, sample_num=20):
    samples = []  # 存储(输入num, 输出密文)
    s = socket.socket()
    s.connect((host, port))
    s.recv(1024)  # 接收菜单
    for _ in range(sample_num):
        # 发送选项1
        s.send(b'1\n')
        s.recv(1024)  # 接收":\n"
        # 随机生成16进制数(比如3字节,避免过大)
        num = random.randint(0, 0xffffff)
        num_hex = hex(num)[2:].encode()
        s.send(num_hex + b'\n')
        # 接收密文
        cipher = s.recv(1024).strip().decode()
        samples.append((num, cipher))
    s.close()
    return samples

# 2. Z3求解
def solve_with_z3(samples):
    # 定义未知量
    xor = z3.Int('xor')
    add = z3.Int('add')
    # table: 58个整数→字符的映射(也可以反向建字符→整数)
    table = [z3.String(f'table_{i}') for i in range(58)]  # 简化版:用字符串存单个字符
    solver = z3.Solver()
    
    shift = [7,6,5,4,3,2]  # 修正原代码shift[0]=8的索引越界问题(实际测试以服务器为准)
    for num, cipher in samples:
        # 计算变换后的x
        x_transform = (num ^ xor) + add
        # 对每个i=0~5,提取密文位置并约束
        cipher_list = list(cipher)
        for i in range(6):
            pos = shift[i]
            c = cipher_list[pos]  # 密文对应位置的字符
            # x_transform//58^i %58 是table的下标
            idx = x_transform // (58**i) % 58
            # 约束:table[idx] == c
            solver.add(table[idx] == c)
    
    # 额外约束:table长度58,字符唯一(可选,根据题目调整)
    for i in range(58):
        solver.add(z3.Length(table[i]) == 1)
    for i in range(58):
        for j in range(i+1, 58):
            solver.add(table[i] != table[j])
    
    # 求解
    if solver.check() == z3.sat:
        model = solver.model()
        # 提取解
        res_xor = model[xor].as_long()
        res_add = model[add].as_long()
        res_table = [model[table[i]].as_string() for i in range(58)]
        return res_xor, res_add, res_table
    else:
        raise Exception("Z3求解失败,样本数不足或约束错误")

# 3. 提交验证
def submit_flag(host, port, xor, add, table):
    s = socket.socket()
    s.connect((host, port))
    s.recv(1024)
    # 选择选项2
    s.send(b'2\n')
    # 发送xor(16进制)
    s.recv(1024)  # 接收"Your xor Number\n"
    s.send(hex(xor)[2:].encode() + b'\n')
    # 发送add(16进制)
    s.recv(1024)  # 接收"Your add Number\n"
    s.send(hex(add)[2:].encode() + b'\n')
    # 发送table
    s.recv(1024)  # 接收"Your table\n"
    s.send(''.join(table).encode() + b'\n')
    # 接收flag
    res = s.recv(1024).decode()
    print("结果:", res)
    s.close()

# 主流程
if __name__ == '__main__':
    host = '49.235.148.38'
    port = 65523
    # 步骤1:获取样本
    samples = get_samples(host, port, sample_num=20)
    # 步骤2:Z3求解
    xor, add, table = solve_with_z3(samples)
    # 步骤3:提交验证
    submit_flag(host, port, xor, add, table)

关键注意点

  1. shift 索引修正 :原代码shift=[8,7,6,5,4,3]shift[0]=8超出列表r的索引范围(r长度为 8,索引 0~7),实际服务器中应为shift=[7,6,5,4,3,2]或取模,需根据交互结果微调;
  2. 样本数量:至少需要 6 组样本(因为有 6 个位置约束),建议取 20 + 组确保 Z3 能唯一解;
  3. Z3 约束细节 :若直接用z3.Array处理字符,需注意 Python 字符与 Z3 字符的转换,也可反向将table定义为 "字符→下标" 的映射(更易处理);
  4. 网络交互细节:需严格匹配服务器的交互流程(如换行、接收缓冲区清空),避免粘包 / 漏收数据。

总结:题目本质是通过多组输入输出的密文,反向求解加密函数的未知参数,"Z3 梭哈" 就是用 Z3 把所有约束一次性丢进去解出所有未知量,最终验证参数获取 flag。

密码2

解题步骤

一、题目整体流程(一句话版)

ctf → .,show → - → 摩斯 → 分组 16 进制 → Unicode 中文 → flag


二、详细解题步骤(可直接当 WP)
1)字符替换:ctf→.,show→-

原文只有两种单词:ctfshow

  • ctf.(点)
  • show-(划)

把全文替换后得到纯摩斯电码串

2)摩斯解密

用摩斯对照表解:

复制代码
..-. → F
.-.. → L
.- → A
--. → G
----- → 0
----- → 0
--... → 7
-... → B
--... → 7
...-- → 3
-.-. → C
-.. → D
-.... → 6
----- → 0
-.. → D
-.-. → C
--... → 7
--... → 7
...-- → 3
-.-. → C
..... → 5
..--- → 2
....- → 4
-.. → D
....- → 4
. → E
-... → B
.- → A
----- → 0
----- → 0
--... → 7
-.. → D

结果:

复制代码
FLAG007B73CD60DC773C524D4EBA007D
3)16 进制转 Unicode 中文

把字符串去掉FLAG,剩下:

复制代码
007B73CD60DC773C524D4EBA007D

每 4 个字符分一组(2 字节 = 1 个 Unicode):

复制代码
007B、73CD、60DC、773C、524D、4EBA、007D

每组 16 进制 → Unicode:

  • 007B{
  • 73CD
  • 60DC
  • 773C
  • 524D
  • 4EBA
  • 007D}

得到:

复制代码
{珍惜眼前人}

flag:flag {珍惜眼前人}

代码

复制代码
import re

# 解密后的十六进制串
s = '007B73CD60DC773C524D4EBA007D'

# 每4个字符切一段,转成16进制字符
result = ''
for chunk in re.findall(r".{4}", s):
    # 16进制转整数,再转成Unicode字符
    result += chr(int(chunk, 16))

print(result)
代码逐行详细解释
复制代码
import re

导入正则模块,用来按 4 个字符切分字符串

复制代码
s = '007B73CD60DC773C524D4EBA007D'

把摩斯解密得到的16 进制串 存到变量s

复制代码
result = ''

建空字符串,用来存最终中文结果

复制代码
for chunk in re.findall(r".{4}", s):
  • re.findall(r".{4}", s):正则,每 4 个字符取一段

  • 循环每一段(例如:007B73CD...)

    复制代码
      result += chr(int(chunk, 16))
  • int(chunk, 16):把4 位 16 进制 → 十进制整数

  • chr(...):把整数 → Unicode 字符(中文 / 符号)

  • 拼接到result

    print(result)

输出最终结果:{珍惜眼前人}


涉及知识点(考试 / WP 直接写)

  1. 字符串替换:简单文本映射(ctf→.、show→-)
  2. 摩斯密码:. 为点、- 为划,标准对照表解密
  3. 16 进制:4 位 16 进制 = 2 字节 = 1 个 Unicode 字符
  4. Unicode 编码 :中文、符号在 Unicode 里用\uXXXX表示
  5. 正则分割re.findall(r".{4}", s) 按固定长度切分
  6. 进制转换int(..., 16)chr() 编码转换
相关推荐
WL_Aurora1 小时前
备战蓝桥杯国赛【Day 15】
python·蓝桥杯
彳亍1012 小时前
如何用 Dask 替代 Pandas 实现高效 Excel 数据处理
jvm·数据库·python
2301_783848652 小时前
c++怎么把多个变量一次性写入二进制文件_结构体对齐与write【实战】
jvm·数据库·python
码界筑梦坊2 小时前
123-基于Python的特斯拉超级充电站分布数据可视化分析系统
开发语言·python·信息可视化·数据分析·毕业设计·echarts·fastapi
wang3zc2 小时前
如何在 WooCommerce 后台按订单总金额精准筛选订单
jvm·数据库·python
AIGC包拥它2 小时前
RAG 项目实战进阶:基于 FastAPI + Vue3 前后端架构全面重构 LangChain 0.3 集成 Milvus 2.5 构建大模型智能应用
人工智能·python·重构·vue·fastapi·milvus·ai-native
云水一下2 小时前
企业跨地域安全通信实战:预共享密钥方式建立点到点加密隧道
安全·华为·ipsec vpn·下一代防火墙
闲人编程2 小时前
Agent的评估体系(AgentEval):如何判断一个Agent好坏?
大数据·人工智能·python·算法·agent·智能体·swe
闵孚龙3 小时前
AI Agent多智能体编排爆款解析:Claude Code Subagent、Fork、Coordinator、Bridge远程执行与安全治理全拆解
大数据·人工智能·安全