CTFshow 单身杯 ex咖喱棒


我会详细讲一下这题的加密脚本的意思,还有解密脚本的意思,以便于想要学写脚本和密码的人更好的入门

可能讲的不好,大佬勿喷,如果途中有讲的错误地方,还望大家提出来


首先拿到的题目是一个类似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,这并不是唯一的方法,我利用的是爆破方法,当让也有其他的方法,可以自行去了解一下

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值