我会详细讲一下这题的加密脚本的意思,还有解密脚本的意思,以便于想要学写脚本和密码的人更好的入门
可能讲的不好,大佬勿喷,如果途中有讲的错误地方,还望大家提出来
首先拿到的题目是一个类似RSA加密的题,题目代码如下
from Crypto.Util.number import *
from sympy import *
def givemeprime(x):
''' x < 502'''
p = getPrime(x)
print(p)
while (p).bit_length() <= 512:
p = nextprime(p*2**10)
return p
p = givemeprime(10)
q = givemeprime(10)
n = p*q
flag=b'?????'
m = bytes_to_long(flag)
e=2**32+1
c=pow(m,e,n)
print('n=',n)
print('c=',c)
'''
n= 9007989895621669259301762739598643626213892494330778168383286295463641223987867033273111296978959160408689408884183780314498828688143466136060628598819311509949865018608092450964012727526450914131409697944090166113416984201622940137239452703698919890772056684013237404520834408811118739546684092365102406400768733
c= 3155015611586304247269005826733691392085437186284673630268852999607965592611252562808748872502491405722341353019602057980123546192900359248245073985988035982837057433789538035295585235536446429172802713235552248615722281314286849930993306403034865999074888279573724168174433746677852218329931104122667029131804586
'''
开头的from...import和def这些都是什么我就不说了,如果连这些都不理解的,我还是建议先去学一下Python再来看这篇文章吧
首先看一下givemeprime函数,该函数会接受一个参数x,然后p=getPrime(x)
getPrime函数的作用是获取一个质素(一个大于1的自然数,除了1和它本身外,不能被其他自然数整除的数),也就是获取二进制长度为x的质素,这题中x=10,也就是获取二进制最大为10位以内的数,范围是大于0小于1023的质素
然后会将这个质素赋值给p,这个p是局部变量,和外面的p是互相不影响的,这里如果不理解,就去看一下局部变量和全局变量的区别吧。
接着往下分析,会进入一个while循环,判断p的二进制长度是否小于512,如果为真,则进入循环,然后p就会被nextprime(p*2**10)赋值,nextprime函数的作用是获取括号中的下一个质素,括号中的数也可以不是质素,就例如我们已知的10以内的质素为:1、2、3、5、7,然后nextprime(2)就会返回3,因为2的下一个质素为3,如果是nextprime(4)会输出5,括号中可以不是质素,该函数只返回比他大,且离他最近的下一个质素
接着分析,while的循环是干什么的现在也知道了,然后知道不满足while循环就会返回p的值,现在知道了这个函数作用,现在看一下整个代码中,在哪里调用了这个函数
在获取p和q的值的时候,调用了这个函数,传参是10,这也就是我刚才说的x为什么等于10的原因,然后接着往下,发现n=p*q,flag现在未知,m等于bytes_to_long(flag),也就是将flag转换为无符号整数然后传给m,与其对应的是long_to_bytes函数,也就是bytes_to_long的相反函数,这题得到m的值后,就需要利用long_to_bytes函数解出flag的具体内容
接着往下,e=2**32+1,python中两个星号(*)是次方的意思,这里也就是2的32次方加1,挺大的数,然后下面的c=pow(m,e,n)这个函数是计算RSA中的密文的,通过明文和公钥还有模数,具体怎么算的,需要去了解一下RSA的加密算法
然后经过上面的计算,就会输出n和c
已经分析完他的加密过程了,现在就可以思考怎么解密了,首先在RSA中要想解到密文,我们需要私钥d、密文c、模数n,目前已经知道了n、c,只需要求d即可,然后求d的具体方法还是需要去了解RSA的加密算法,这里在Python中可以直接利用mod_inverse(e, phi)函数即可,其中phi的算法是phi=(q-1)*(p-1),所以还是需要求出p和q的
求p和q的方法,我这里利用的是爆破思想,因为他题目中给的getPrime的参数为10,比较小,所以直接穷举p和q的值即可
先附上我的脚本,然后我逐一解释一下
from Crypto.Util.number import *
from sympy import *
from tqdm import *
import gmpy2
import libnum
c= 3155015611586304247269005826733691392085437186284673630268852999607965592611252562808748872502491405722341353019602057980123546192900359248245073985988035982837057433789538035295585235536446429172802713235552248615722281314286849930993306403034865999074888279573724168174433746677852218329931104122667029131804586
e=2**32+1
a = []
for i in range(100000):
a.append(getPrime(10))
un = sorted(list(set(a)))
for p in tqdm(un):
while (p).bit_length() <= 512:
p = nextprime(p * 2 ** 10)
for q in un:
while (q).bit_length() <= 512:
q = nextprime(q * 2 ** 10)
n = p * q
if n == 9007989895621669259301762739598643626213892494330778168383286295463641223987867033273111296978959160408689408884183780314498828688143466136060628598819311509949865018608092450964012727526450914131409697944090166113416984201622940137239452703698919890772056684013237404520834408811118739546684092365102406400768733:
phi = (q - 1) * (p - 1)
d = mod_inverse(e, phi)
m = gmpy2.powmod(c,d,n)
flag = long_to_bytes(m)
if b'ctf' in flag:
print(flag)
break
其实这个脚本就分为两大块,第一块是计算p和q的所有可能性
首先我定义一个空列表a,然后利用for循环,执行100000次getPrime(10),然后将每次的值放在a列表中,然后通过sorted、list、set函数处理a列表,其实也就是去重、从小到大排序一下列表un
set:创建一个无序且元素唯一的集合。
list:将一个可迭代对象转换为一个列表
sorted:返回一个新的排序列表,而不改变原列表
然后我们可以看一下输出是什么
输出了很多个数,然后我们查看一下一共有多少个数,需要利用len函数,在原先的print代码中加入len函数
可以看到输出是75,并且尝试了很多次输出,最终结果都是75,所以就可以猜测getPrime(10)的所有输出全部在这75个数中,所以我们就可以利用循环来遍历这75个数,从而实现爆破p和q的值
接着看另一段的代码
for循环嵌套,第一层遍历p的值,也就是让p等于un列表中的第一个数,然后依次往后取值,因为加密脚本中,givemeprime函数中,有个while循环,他作用是将getPrime(10)的p值进行转换,因为原本的p的二进制长度肯定小于512的,所以肯定会进入while循环,但是经过一次循环,p的二进制长度就会大于512,从而跳出while循环,将变换后的值赋值给p,同理q也是这么处理的,所以我在每层循环中的开头都加上了这个while循环,来改变p和q的值,第一层循环中,我加入了tqdm函数,这个函数的作用是在运行这个循环中加上进度条,可以让我们清晰的看出跑的结果到哪了,也就是下面这样
然后在q循环中,我加入了后面解RSA的运算,也就是在确定一个p和一个q值后,会将这两个数相乘,然后给到n,由于题目中是给n的输出结果了,所以我直接在这里加一个判断条件,判断n是否等于他给的n,如果等于,也就是找到了正确的p和q,从而进行下面的求phi和d,然后下面的求d、phi的方法上面也说了,这里就不赘述了
这里求m的方法也就是脚本中的利用gmpy2.powmod函数,该函数接收c、d、n即可,具体怎么求出的m,还是需要去看RSA加密算法,然后下面的long_to_bytes函数,就是上面说到的和bytes_to_long相对应的函数,就是将m转换为flag的原本值
然后下面的if判断,就是判断我们求得的flag中是否包含ctf这三个字符,如果包含,就输出结果
整体代码分析结果完毕,看一下输出结果
成功跑出flag,这并不是唯一的方法,我利用的是爆破方法,当让也有其他的方法,可以自行去了解一下