UCSC_CTF_CRYPTO_WP

题目:

from Crypto.Util.number import *
import sympy
from flag import flag
a=getPrime(512)
p=sympy.nextprime(13*a)
q=sympy.prevprime(25*a)
number2=p*q

def crypto01(number1, number2, number3):
    number4 = 1
    while number2 > 0:
        if number2 % 2: 
            number4 = (number4 * number1) % number3
        number1 = number1 ** 2 % number3
        number2 //= 2
    return number4

def crypto02(number1, number2):
    number3 = number1
    number4 = number2
    giao = 1
    giaogiao = 0
    while number4 > 0:
        number7 = number3 // number4
        giao, giaogiao = giaogiao, giao - giaogiao*number7
        number3, number4 = number4, number3 - number4*number7
    while giao<0:
        giao = giao + number2
    return giao

def crypto03(number1, number2, number3):
    number4 = crypto01(number3, number1, number2)
    return number4



def crypto05(number1,number2):
    return pow(number1,0xe18e,number2)






number1 = 6035830951309638186877554194461701691293718312181839424149825035972373443231514869488117139554688905904333169357086297500189578624512573983935412622898726797379658795547168254487169419193859102095920229216279737921183786260128443133977458414094572688077140538467216150378641116223616640713960883880973572260683
number2 = 20163906788220322201451577848491140709934459544530540491496316478863216041602438391240885798072944983762763612154204258364582429930908603435291338810293235475910630277814171079127000082991765275778402968190793371421104016122994314171387648385459262396767639666659583363742368765758097301899441819527512879933947

number3 = int.from_bytes(flag[0:19].encode("utf-8"), "big")
number4 = int.from_bytes(flag[19:39].encode("utf-8"), "big")

print(crypto03(number1, number2, number3))
print(crypto05(number4,number2))
#6624758244437183700228793390575387439910775985543869953485120951825790403986028668723069396276896827302706342862776605008038149721097476152863529945095435498809442643082504012461883786296234960634593997098236558840899107452647003306820097771301898479134315680273315445282673421302058215601162967617943836306076
#204384474875628990804496315735508023717499220909413449050868658084284187670628949761107184746708810539920536825856744947995442111688188562682921193868294477052992835394998910706435735040133361347697720913541458302074252626700854595868437809272878960638744881154520946183933043843588964174947340240510756356766

1. 题目分析

题目给出了以下参数:

  • number1:可能是第一个 RSA 的加密指数 e1 或解密指数 d1

  • number2:模数 n(由 p 和 q 组成)。

  • c1:第一段密文(使用 number1 加密)。

  • c2:第二段密文(使用 e2 = 0xe18e 加密)。

目标

  1. 解密 c1 得到第一部分明文。

  2. 解密 c2 得到第二部分明文(由于 gcd(e2, φ(n)) = 2,需要特殊处理)。

  3. 组合两段明文得到最终 flag。


2. 解题步骤

(1)分解 n 并计算 φ(n)

先将n分解出p,q

p = 102397419546952293033860597727650152144175130286102358700580521651161981691864932442389800376284315897109792547767071136122457986326994452907466660551539601
q = 196918114513369794295885764860865677200336789011735305193424080098388426330509485466134231492854453648288062591859752184850880742936527794052820501060652747

验证 n = p * q

n = p * q
assert n == number2, "分解正确!"

计算欧拉函数:

phi = (p - 1) * (q - 1)

(2)解密第一部分(标准 RSA)

假设 number1 是 e1,计算私钥 d1

d1 = inverse(number1, phi)
m1 = pow(c1, d1, n)
part1 = long_to_bytes(m1).decode('utf-8', errors='ignore').strip('\x00')

输出

第一部分明文: flag{75811c6d95770d

(3)解密第二部分(gcd(e2, φ(n)) = 2 的特殊情况)

由于 e2 = 57742,计算 gcd(e2, phi)

g = math.gcd(e2, phi)  # g = 2

解决方法

  1. 计算调整后的 e2' = e2 // g = 28871 和 φ' = φ(n) // g

  2. 计算 d2' = inverse(e2', φ')

  3. 计算部分解密结果 c2_prime = pow(c2, d2', n)

  4. 由于 g = 2,需要对 c2_prime 求模平方根才能得到最终明文。

代码

e2_prime = e2 // g
phi_prime = phi // g
d2_prime = inverse(e2_prime, phi_prime)
c2_prime = pow(c2, d2_prime, n)

(4)求解模平方根

由于 n = p * q,分别在 GF(p) 和 GF(q) 下求平方根,再用 CRT(中国剩余定理) 组合所有可能的解:

def get_square_roots(c, p):
    try:
        return sympy.ntheory.sqrt_mod(c, p, all_roots=True)
    except ValueError:
        return []

roots_p = get_square_roots(c2_prime, p)  # 在模 p 下的解
roots_q = get_square_roots(c2_prime, q)  # 在模 q 下的解

输出

模 p 的平方根: [root1_p, root2_p]
模 q 的平方根: [root1_q, root2_q]

(5)用 CRT 组合所有可能的解

遍历所有可能的 (root_p, root_q) 组合,用 CRT 计算 m

from sympy.ntheory.modular import crt

valid_part2 = None
for mp in roots_p:
    for mq in roots_q:
        m, _ = crt([p, q], [mp, mq])
        candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
        if candidate.isprintable() and len(candidate) == 32:  # 假设第二部分是 32 位哈希
            valid_part2 = candidate
            break
    if valid_part2:
        break

输出

第二部分明文: 56092817b75f15df05}

(6)组合两段明文得到 flag

flag = part1 + valid_part2 + "}"  # 补全 flag 的闭合括号
print("Final Flag:", flag)

最终 Flag

flag{75811c6d95770d56092817b75f15df05}

3. 总结

  1. 标准 RSA 解密(第一部分):

    • 直接计算 d1 = e1^{-1} mod φ(n),然后解密 c1

  2. 非互素 RSA 解密(第二部分):

    • 由于 gcd(e2, φ(n)) = 2,需要:

      • 调整 e2' = e2 // 2,计算 d2'

      • 解密后求模平方根。

  3. 模平方根计算

    • 分别在 GF(p) 和 GF(q) 下计算,再用 CRT 组合。

  4. 验证明文

    • 检查解密结果是否为可打印字符(如 32 位 MD5 哈希)。

    • 检查解密结果是否为可打印字符(如 32 位 MD5 哈希)。                            ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​        ​​​​​​​     

 4.完整代码

import sympy
from Crypto.Util.number import inverse, long_to_bytes
import math

# =============================================
# 给定参数(题目提供的数据)
# =============================================
number1 = 6035830951309638186877554194461701691293718312181839424149825035972373443231514869488117139554688905904333169357086297500189578624512573983935412622898726797379658795547168254487169419193859102095920229216279737921183786260128443133977458414094572688077140538467216150378641116223616640713960883880973572260683  # 可能是e1或d1
number2 = 20163906788220322201451577848491140709934459544530540491496316478863216041602438391240885798072944983762763612154204258364582429930908603435291338810293235475910630277814171079127000082991765275778402968190793371421104016122994314171387648385459262396767639666659583363742368765758097301899441819527512879933947  # 模数n
c1 = 6624758244437183700228793390575387439910775985543869953485120951825790403986028668723069396276896827302706342862776605008038149721097476152863529945095435498809442643082504012461883786296234960634593997098236558840899107452647003306820097771301898479134315680273315445282673421302058215601162967617943836306076   # 第一部分密文
c2 = 204384474875628990804496315735508023717499220909413449050868658084284187670628949761107184746708810539920536825856744947995442111688188562682921193868294477052992835394998910706435735040133361347697720913541458302074252626700854595868437809272878960638744881154520946183933043843588964174947340240510756356766    # 第二部分密文

# =============================================
# 预计算的质因数(通过分解n得到)
# =============================================
p = 102397419546952293033860597727650152144175130286102358700580521651161981691864932442389800376284315897109792547767071136122457986326994452907466660551539601
q = 196918114513369794295885764860865677200336789011735305193424080098388426330509485466134231492854453648288062591859752184850880742936527794052820501060652747

# 验证n = p*q
assert p * q == number2, "质因数分解验证失败!"

# =============================================
# 计算欧拉函数 φ(n) = (p-1)*(q-1)
# =============================================
phi = (p - 1) * (q - 1)

# =============================================
# 解密第一部分(标准RSA解密)
# =============================================
# 计算解密指数d1 = e1^(-1) mod φ(n)
d1 = inverse(number1, phi)  
# 解密得到明文 m1 = c1^d1 mod n
m1 = pow(c1, d1, number2)  
# 将长整数转换为字节并解码为字符串
part1 = long_to_bytes(m1).decode('utf-8', errors='ignore').strip('\x00')  
print(f"[*] 第一部分解密结果: {part1}")

# =============================================
# 解密第二部分(e2与φ(n)不互质时的特殊情况)
# =============================================
e2 = 0xe18e  # 57742(十六进制表示)

# 检查gcd(e2, φ(n)),如果不为1需要特殊处理
g = math.gcd(e2, phi)
print(f"[*] GCD(e2, phi) = {g} (需要特殊处理)")

# 计算调整后的参数
e2_prime = e2 // g          # 使e2'与φ(n)'互质
phi_prime = phi // g        # 缩减后的φ(n)'
d2_prime = inverse(e2_prime, phi_prime)  # 计算部分解密指数

# 部分解密:c2_prime = c2^d2_prime mod n
# 注意:此时得到的不是最终明文,还需要解g次方根
c2_prime = pow(c2, d2_prime, number2)

# =============================================
# 求解模平方根(使用中国剩余定理)
# =============================================
def get_square_roots(c, modulus):
    """计算c的模平方根(支持质数模数)"""
    try:
        roots = sympy.ntheory.residue_ntheory.sqrt_mod(c, modulus, all_roots=True)
        return roots
    except ValueError:
        return []  # 无解时返回空列表

# 分别在模p和模q下计算平方根
roots_p = get_square_roots(c2_prime, p)  # 在模p下的解
roots_q = get_square_roots(c2_prime, q)  # 在模q下的解
print(f"[*] 找到 {len(roots_p)} 个模p的根, {len(roots_q)} 个模q的根")

# 用中国剩余定理组合所有可能的解
from sympy.ntheory.modular import crt

valid_part2 = None
for mp in roots_p:
    for mq in roots_q:
        # 组合解:m ≡ mp mod p, m ≡ mq mod q
        m, _ = crt([p, q], [mp, mq])  
        # 转换为字节并尝试解码
        candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
        
        # 验证候选明文是否有效(可打印字符或flag格式)
        if all(ord(c) < 128 and (c.isprintable() or c == '\x00') for c in candidate):
            valid_part2 = candidate
            print(f"[+] 找到有效的第二部分: {valid_part2}")
            break
    if valid_part2:
        break

# =============================================
# 结果输出
# =============================================
if valid_part2:
    flag = part1 + valid_part2
    print("\n=================================")
    print(f"[★] 最终Flag: {flag}")
    print("=================================")
else:
    print("\n[!] 未能找到有效的第二部分明文")
    print("尝试所有可能的组合:")
    for i, mp in enumerate(roots_p):
        for j, mq in enumerate(roots_q):
            m, _ = crt([p, q], [mp, mq])
            candidate = long_to_bytes(m).decode('utf-8', errors='ignore').strip('\x00')
            print(f"  组合 {i}-{j}: {repr(candidate)}")  # 使用repr显示不可见字符

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值