攻防世界一波Crypto的学习
0x01前言
前两天有一个学长喊我给他做几道XCTF平台上的密码题,刚好之前一直很少接触,这次也当作是初学密码了
0x02题目
babyECC2(GFSJ1119)
#椭圆曲线加密ECC
题目描述:Tover说不想出题了,我把他上年的防AK题改了一下(逃随机数生成器就是逊啦,xor yyds!
华南师范大学的一个新生赛题
先看看附件的代码,并分析了一下代码的意思
1 | #sage |
out文件就是sage文件代码中的五个输出
是基于椭圆曲线加密(ECC)的简单加密算法,加密的内容就是flag
加密过程很简单,就是将flag的第i个字符与kp的两个分量进行异或,但是这里可以注意到
并且这是华师2021年的新生赛,flag格式当时写的是HSCTF{}或者hsctf{},所以kp还是可以爆出来的
这道题还是很简单的,把kp爆破出来然后解密就行了
1 | c = 'ac73a774a25bd512d543dc468542c9428141800dd041d043c918d112850dd515d6128214d1138211d71599' |
flag就是HSCTF{121c8fab-bead-4a4c-852a-1522f453f135}
RSA(GFSJ0820)
#RSA加密算法
RSA算法来了
先看看附件的源码加上解释
1 | #!/usr/bin/env python3 |
RSA加密算法的话可以先看看文章https://www.cnblogs.com/xiaxveliang/p/12395993.html,还是很常见的
这种算法题一般调教ai就能出来,但是得慢慢调了
解密步骤
从
pubkey.pem
文件中读取公钥,提取模数n
和公钥指数e
。1
2
3
4
5
6
7
8
9
10
11
12
13
14from Crypto.PublicKey import RSA
with open('pubkey.pem','rb') as f:
pubkey = RSA.importKey(f.read())
#print(pubkey)
#提取模数n和公钥指数e
n = pubkey.n
e = pubkey.e
# print(n)
#n = 62078208638445817213739226854534031566665495569130972218813975279479576033261
# print(e)
#e = 9850747023606211927因为p和q只有128位,所以可以分解模数
n
获取p和q,可以用大整数分解网站https://factordb.com/
所以拿到
1 | q=184333227921154992916659782580114145999 |
计算私钥
- 计算欧拉函数
phi = (p - 1) * (q - 1)
。 - 计算私钥指数
d = gmpy2.invert(e, phi)
。
- 计算欧拉函数
解密密文
- 使用私钥和 PKCS#1 v1.5 填充方案解密
flag.enc
文件中的内容。
- 使用私钥和 PKCS#1 v1.5 填充方案解密
最终的exp
1 | import gmpy2 |
前面写的时候一直报错,PKCS#1 v1.5 要求密文的长度必须与 RSA 模数 n
的字节长度一致,一开始的模数n是解不出来的,那个仅仅是公钥里面的n,和加密函数里面的n是不一样的,所以需要爆破爆出来
线性反馈移位寄存器(GFSJ1082)
#线性反馈移位寄存器伪随机数生成器
分析一下附件
1 | from secret import secret#从secret中导入secret |
一个基于 线性反馈移位寄存器 (LFSR) 的伪随机数生成器https://blog.csdn.net/weixin_37414365/article/details/139606928
先分析一下前面两个转化函数
1 | def string2bits(s):#将一个二进制字符串 s 转换为一个整数列表。 |
不清楚的话可以直接本地测试实操一下
最重要的还是最后一个**线性反馈移位寄存器 (LFSR)**的实现函数
1 | def lfsr(state, mask): |
输入两个长度为128数组 state, mask
,输出 output
值为$out=\sum{state_i \times mask_i} \mod 2$
所以这里的话会遍历每个状态和反馈,
output = output ^ (state[i] & mask[i])
- 计算
state[i]
和mask[i]
的按位与(&
),然后与当前的output
进行按位异或(^
)。这一步实现了 LFSR 的反馈逻辑。
然后我们需要注意一个点,在二进制运算中,异或运算(^
)和模 2 的加法(mod 2
)是等价的,位与运算(&)和模2的乘法是等价的。
本地测试一下
那么我们就可以算进行解密了
initState是初始化数组,也是LFSR的初始状态,mask
是secret转二进制之后的内容
然后最主要的操作就是进行256轮LFSR计算,注意到每轮 state
数组取值会向右移动一个位置,同时 initState
长度会增长1,即数组尾部追加了 output
值
这里有点小复杂,看看官方wp怎么说的
关键关系式
原始关系式:
$$
out=∑state i×mask i mod2
$$- 表示状态向量(
state
)和掩码向量(mask
)的点积,再取模 2。
- 表示状态向量(
矩阵乘法形式:
$$
out=State×Maskmod2
$$- 这里,
State
是一个 1×1281×128 的行向量,Mask
是一个 128×1128×1 的列向量。 - 矩阵乘法的结果是一个标量(即一个数),再取模 2。
- 这里,
1 | # sage |
其实这里的话也是根据一个矩阵的乘法和加法来实现二进制运算,从而进行解密
这里需要SageMath环境,可以手动安装一下,我在Ubuntu安装的
1 | sudo apt update |
安装好之后直接把代码传sage里运行就行,当热也可以编辑成sage文件然后run一下
之前提示
1 | flag{md5(secret).hexdigest()} |
所以我们需要再进行解码
1 | secret = 01000101011000010111001101111001010011010110000101110100011010000101011101101001011101000110100001010011011000010110011101100101 |
然后解码一下吧
1 | import hashlib |