第四届美团网络安全高校挑战赛_hamburgerRSA

hamburgerRSA

题目描述
from Crypto.Util.number import *

flag = open('flag.txt').read()
nbit = 64

while True:
    p, q = getPrime(nbit), getPrime(nbit)
    PP = int(str(p) + str(p) + str(q) + str(q))
    QQ = int(str(q) + str(q) + str(p) + str(p))
    if isPrime(PP) and isPrime(QQ):
        break

n = PP * QQ
m = bytes_to_long(flag.encode())
c = pow(m, 65537, n)
print('n =', n)
print('c =', c)
程序分析

p,q是随机选取64位二进制的素数;

关键是生成的公私钥使用的是 P P , Q Q PP,QQ PP,QQ;

P P , Q Q PP,QQ PP,QQ是通过简单的字符串 p , q p,q p,q连接的形式组成

本题的关键就是如何去表示 P P , Q Q PP,QQ PP,QQ

由于是采用字符串相连接的形式,我们可以使用 1 0 x ∗ p + 1 0 y ∗ q 10^x*p+10^y*q 10xp+10yq这样的形式来表示 P P , Q Q PP,QQ PP,QQ

那么

x = l e n ( s t r ( p ) ) x = len(str(p)) x=len(str(p)) y = l e n ( s t r ( q ) ) y = len(str(q)) y=len(str(q))

P P = 1 0 x + 2 ∗ y ∗ p + 1 0 2 ∗ y ∗ p + 1 0 y ∗ q + q PP = 10^{x+2*y}*p+10^{2*y}*p+10^{y}*q+q PP=10x+2yp+102yp+10yq+q

Q Q = 1 0 2 ∗ x + y ∗ q + 1 0 2 ∗ x ∗ q + 1 0 x ∗ p + p QQ=10^{2*x+y}*q+10^{2*x}*q+10^x*p+p QQ=102x+yq+102xq+10xp+p

两者相乘表示为 n n n

n = P P ∗ Q Q = 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q + 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q + 1 0 2 ∗ x + 3 ∗ y ∗ p ∗ q + . . . + 1 0 x ∗ p ∗ q + 1 0 x ∗ p ∗ q + p ∗ q n = PP*QQ=10^{3*x+3*y}*p*q+10^{3*x+2*y}*p*q+10^{2*x+3*y}*p*q+...+10^{x}*p*q+10^{x}*p*q+p*q n=PPQQ=103x+3ypq+103x+2ypq+102x+3ypq+...+10xpq+10xpq+pq

中间的省略号部分是因为与之后的推导没有关系(也可以说是由于加法前后两项重合了数位无法简单表示),所以省略不考虑了

通过观察,已知的 n n n​实际上是由 p ∗ q p*q pq​的10的 k k k​次方组成的

到这里我们发现n的某一部分数位其实就是 p ∗ q p*q pq​​的数位(比如 15600000 15600000 15600000,它的数位是6-8),但是由于加法前后两项之间有重合,解释一下

比如 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103x+3ypq 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q 10^{3*x+2*y}*p*q 103x+2ypq,假设 p ∗ q p*q pq的最高位为 40 40 40, x = 19 , y = 20 x=19,y=20 x=19,y=20

那么前者的数位覆盖了117-157;后者的数位覆盖了97-137

这里就有重叠的地方了,由于重叠,加法运算之后,我们就没有办法直接用 n n n的这一部分直接等于 p ∗ q p*q pq的某一部分了

那么为了进一步表示 n n n,我们需要知道 x , y x,y x,y的确切大小


x , y x,y x,y的确切大小

通过 n n n的表达式,我们可以知道 n n n的最高位(10进制)等于 1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103x+3ypq的位数(10进制)

这里我做了一下测试:

while True:
    p = getPrime(64)
    q = getPrime(64)
    temp = p*q
    print(len(str(p)),end=" ")
    print(len(str(q)),end=" ")
    print(len(str(temp)))

可以观察到 p , q p,q p,q的十进制位数是 19 19 19或者 20 20 20​,也就是说 x = 19 , 20 x=19,20 x=19,20 y = 19 , 20 y=19,20 y=19,20

p ∗ q p*q pq的十进制位数为 39 39 39或者 38 38 38

n n n的十进制位数为 156 156 156

剩下的就是一个加法运算,如果 x = y = 20 x=y=20 x=y=20

那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3x+3y+len(pq)​至少等于 159 159 159​;

显然不满足条件;

如果 x = y = 19 x=y=19 x=y=19​;

那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3x+3y+len(pq)​最多等于 153 153 153

也不满足条件

如果 x = 19 , y = 20 x=19,y=20 x=19,y=20(这里 x , y x,y x,y的大小可以互调的)

那么 3 ∗ x + 3 ∗ y + l e n ( p ∗ q ) 3*x+3*y+len(p*q) 3x+3y+len(pq)​有等于 156 156 156​的可能性

综上, x = 19 , y = 20 x=19,y=20 x=19,y=20​;另外也可以知道 p ∗ q p*q pq​的十进制数位为 39 39 39


p ∗ q p*q pq的确切大小

根据之前重叠的说法,我们只需要看在 n n n​中没有重叠的部分,没有重叠的部分是直接等于对应的 p ∗ q p*q pq的对应数位的;

n n n​的高位:

1 0 3 ∗ x + 3 ∗ y ∗ p ∗ q 10^{3*x+3*y}*p*q 103x+3ypq 1 0 3 ∗ x + 2 ∗ y ∗ p ∗ q 10^{3*x+2*y}*p*q 103x+2ypq 1 0 2 ∗ x + 3 ∗ y ∗ p ∗ q 10^{2*x+3*y}*p*q 102x+3ypq有关

数位分别覆盖117-156;97-136;98-137

那么没有重叠的部分只有前19位的高位(实际上我认为还需要考虑加法之后的进位问题,也就是说可能只有18位的高位与 p ∗ q p*q pq相同,但是幸运的是在这道题关键位数没有进位,之后就可以少爆破两位)

n n n的低位:

1 0 x ∗ p ∗ q 10^{x}*p*q 10xpq 1 0 y ∗ p ∗ q 10^{y}*p*q 10ypq p ∗ q p*q pq有关

数位分别覆盖19-58;20-59;0-39

没有覆盖的部分只有后18位的低位

总结起来:

已知 p ∗ q p*q pq的高19位和低18位

p ∗ q p*q pq的总位数为 39 39 39

剩下两位需要爆破一下

由于 p ∗ q p*q pq不大,可以直接用sagemath的 f a c t o r ( ) factor() factor()分解,判断条件是

if len(factor(pq)) == 2 and factor(pq)[0][0].nbits() == 64:

得到 p p p q q q之后计算得到 P P , Q Q PP,QQ PP,QQ就进行正常的RSA求密

代码实现
# sage
nbit = 64
n = 177269125756508652546242326065138402971542751112423326033880862868822164234452280738170245589798474033047460920552550018968571267978283756742722231922451193
high = str(n)[:19]
low = str(n)[-18:]
for i in range(10):
    for j in range(10):
        pq = int(high + str(i) + str(j) + low)
        f = factor(pq)
        if len(f) == 2 and f[0][0].nbits() == 64: 
            p = f[0][0]
            q = f[1][0]
            print(p,q)

from Crypto.Util.number import *
import gmpy2
p,q = 9788542938580474429,18109858317913867117
c = 47718022601324543399078395957095083753201631332808949406927091589044837556469300807728484035581447960954603540348152501053100067139486887367207461593404096
e = 65537
PP = int(str(p) + str(p) + str(q) + str(q))
QQ = int(str(q) + str(q) + str(p) + str(p))
fai_n = (PP-1)*(QQ-1)
d = gmpy2.invert(e,fai_n)
m = pow(c,d,PP*QQ)
print(long_to_bytes(m))
参考文章(wp)

第四届美团网络安全高校挑战赛Write up (qq.com)

然后这道是类似的题目:

CTFtime.org / Crypto CTF 2021 / Hamul / Writeup

  • 6
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值