CTF-RSA已知高低位的攻击

CTF-RSA中知道p部分高位和低位的情况(babyRSA)

适用人群

知道是类似题目却又懒得理解原理,想要通过搬运解决问题的人。

搬运时出现问题,来看看模板(怎么样能不报错)的人。

原题

from Crypto.Util.number import *
from string import digits, ascii_letters, punctuation
from sympy import *
from random import *
from secret import flag

bytemap=digits+ascii_letters


while True:
    rand_str = ''.join(sample(bytemap,35))
    dots = b'A_S1mp13_P01y_pr0b13m' + bytes(rand_str.encode('utf-8')) + b'h0pe_u_3nj0y_th3_g4m3!'
    p = bytes_to_long(dots)
    q = bytes_to_long(dots[::-1])
    if isprime(p) and isprime(q):
        n = p * q
        e = 0x10001
        m = bytes_to_long(flag)
        c = pow(m, e, n)
        break
print(f'n = {n}')
print(f'c = {c}')

'''
n = 160508672993727358356100574316302368918534631512763166391965986459416715199014434847644161160721255203715386815079832158095605929815302757649142544200042372910754972968132210351705146824875858826811770334342006375827775154532627219377397067171480126883778382589913759906673058782641771905822391028460391246464115399357231839585151266124847230328797529208115402392357841549921
c = 44890366134356739026070529484575169308358584023921132030166935295829511775694646905781636144799301645824955711148476278048352767444326748892856410720229148435625075536672131990569624953503769758762262270319896220742551672641773475300480348064418068221385153263339673791806292988630895712258019379075857567539567061341157520204500506719761884664166941313528709164860727850793
'''

工具&语言

python:我用的是python3,别的不知道。

sage:

[如果没有sage可以用网页: https://sagecell.sagemath.org/]

原理

coppersmith定理及应用(可以不看,我也不会)

有道类似的题目的wp

分析

flag构成

不考虑编码转换的话,m就是flag。

或者说:

flag=bytes.decode(long_to_bytes(m))

因此,这就是道求明文的题目。

密文和n都给了,但是pq没有直接给出来。

看到pq都有提示了,可以猜到应该是不能依靠大数分解得到pq的。不信的话可以自己尝试。

pq构成

bytemap=digits+ascii_letters
rand_str = ''.join(sample(bytemap,35))
dots = b'A_S1mp13_P01y_pr0b13m' + bytes(rand_str.encode('utf-8')) + b'h0pe_u_3nj0y_th3_g4m3!'
p = bytes_to_long(dots)
q = bytes_to_long(dots[::-1])

while True不管,不关我们的事。

我们知道p的部分高位和低位,但是缺少中间一段……字符?(应该是这么叫的吧?)

这段字符是由一个字符集合中随机选取35个得到的。

digits代表的是09的数字的字符串,ascii_letters代表的是字母(az和A~Z)

然后p的值就是这段字符串的字符转ascii码结果。

q的值更加简单粗暴,直接将这段字符串倒过来用,用法和p一样

p与dots的关系

我曾经看到一篇讲long与bytes转换关系的blog,分析的简单粗暴清晰明了。让我明白了可以把dots当做p的256进制表示。

这256进制对应的数字就是ascii表,当然这转换不需要我们理解,计算机代劳即可,bytes_to_long()就是专门干这个的。

对于p来说,dots的高位加上随机数加上低位连在一起,就是p的一种256进制表示。也就是说,p在256进制下,是一个(21+35+22)位的数字,那么在2进制下,就是一个8*(21+35+22)位的数字,高位低位和中间的未知部分依然泾渭分明,不影响我们的计算。

显然我们知道的位数已经过半了,不需要另外爆破,可以直接运用上面那个定理来做。

[byte_to_long函数] https://blog.csdn.net/weixin_41818069/article/details/106532665

个人思路(吐槽题目,可略过)

看到这里的时候是个人都知道pq的关联性很强。我的第一个想法是找出pq的线性关系,然后利用n=p*q解方程算出pq,如果有多个解的话可以用dots来验证。很遗憾,我查找了一大堆,全都是小学奥数,说的什么两者相减是9的倍数……

这谁不知道啊???

狼狈的去问学长,给了提示说是coppersmith定理相关,就上网查相似的题目了,代码就一行一行挪下来的。

然后不知道是因为只有我不会找,或者我不会用,还是什么别的原因,网上好多wp都把python脚本和sage合在了一起,让我怎么搞都无法运行,最后在学长的手把手指导之下打完了代码。

从结果来看,我用pq的线性关系解方程这个想法是没救了,但是现在来看这个题目感觉ta还是在勾引我解方程!

解题

正儿八经的解题思路,就是在sage下用定理算出p,在python下解决剩下的工作。

简单的让人无法反驳,所以这篇文章的目的是为了留下sage里面我没学会的代码。毕竟这一段我是模仿都模仿不来,只能直接复制的。

当然,首先得到pq高位还是必要的。

这里我踩了个坑,虽然字符串是从0开始计数的,但是对于21位的高位来说,选取字符串的时候依然要用dots[:21](因为是<而不是<=?)如果不放心的话可以直接把字符串放进括号里面,没那么好看,但简单省事。

from Crypto.Util.number import *
import gmpy2
from string import digits, ascii_letters, punctuation
from sympy import *
from random import *

dots = b'A_S1mp13_P01y_pr0b13m'  + b'h0pe_u_3nj0y_th3_g4m3!'
p_high=bytes_to_long(dots[:21])
print(dots[:21])
p_low=bytes_to_long(dots[-22:])
print(p_high)
print(p_low)
#p_high = 95541815817330350130425149533914010078810221917037
#p_low = 38981813338547046700101524093008643321092413022090017

然后进入sage部分:

mod1和mod2是我自己打的补丁,看起来思路没那么清晰,它们的作用是让这些数字在long形态下可以正确的拼接。

再下面就不是我能看懂的了,哪天知道原理了再说。

e = 0x10001
n = 160508672993727358356100574316302368918534631512763166391965986459416715199014434847644161160721255203715386815079832158095605929815302757649142544200042372910754972968132210351705146824875858826811770334342006375827775154532627219377397067171480126883778382589913759906673058782641771905822391028460391246464115399357231839585151266124847230328797529208115402392357841549921
c = 44890366134356739026070529484575169308358584023921132030166935295829511775694646905781636144799301645824955711148476278048352767444326748892856410720229148435625075536672131990569624953503769758762262270319896220742551672641773475300480348064418068221385153263339673791806292988630895712258019379075857567539567061341157520204500506719761884664166941313528709164860727850793

p_high = 95541815817330350130425149533914010078810221917037
p_low = 38981813338547046700101524093008643321092413022090017
mod1 = 256^22
#这个是低位,中间的值需要乘的数
mod2 = 256^57
#这个是高位部分需要乘的数
PR.<x> = PolynomialRing(Zmod(n))
f = p_high * mod2 + p_low + x * mod1
f=f.monic()
out_p = f.small_roots(2^280,beta=0.4,epsilon=0.03)

p = gcd(int(f(out_p[0])),n)
print(p)

算出p之后这道题就可以说是结束了。后面的python步骤平平无奇,没有亮点。

from Crypto.Util.number import *
import gmpy2
from string import digits, ascii_letters, punctuation
from sympy import *
from random import *
e = 0x10001
n = 160508672993727358356100574316302368918534631512763166391965986459416715199014434847644161160721255203715386815079832158095605929815302757649142544200042372910754972968132210351705146824875858826811770334342006375827775154532627219377397067171480126883778382589913759906673058782641771905822391028460391246464115399357231839585151266124847230328797529208115402392357841549921
c = 44890366134356739026070529484575169308358584023921132030166935295829511775694646905781636144799301645824955711148476278048352767444326748892856410720229148435625075536672131990569624953503769758762262270319896220742551672641773475300480348064418068221385153263339673791806292988630895712258019379075857567539567061341157520204500506719761884664166941313528709164860727850793
p=17777533830541882333815981119159197905758393675130560629403650383690014411161496047927341536475256420928974390183963581379233984191390025279242558157263772543458912958743931816360146711329
assert n % p == 0
#断言只是为了加个保险,其实并无必要。
q = n // p
fn = (p-1) * (q-1)
d = gmpy2.invert(e,fn)
m = pow(c,d,n)
print(bytes.decode(long_to_bytes(m)))
#flag{th15_P01yn0m141_15_n0t_d1ff1cu1t!!}

总结

目前看到的大多数题目wp都是知道部分高位,鲜有说高位低位各知道一部分的,除了上面那篇攻略。而这道题目则比上面那篇的题目简单一些。反正我初学碰壁,看到flag的时候也算扬眉吐气,遂来这里显摆一下自己的一点点所得。

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值