刚好在ByteCTF遇到了这道magic_lfsr,也是因此才开始对LFSR进行学习
以下是赛后参考结合了W&M的WriteUp和队里师傅x1ao的wp进行了复现
题面:
1 | from Crypto.Cipher import AES |
先进行分析:
提供了mask1~mask4四个掩码
1 | def lfsr(R, mask): |
- 先把R转为二进制数并且填充到128位,mask同理
- 然后将R和mask按位与,然后对所有位数
求和
后取二进制形式的最低位
得到s - R左移一位,把s加到R的最右边
- 返回了R和s
1 | def ff(x0, x1, x2, x3): |
此处是对数据的一种加密的处理,返回一个一位二进制的布尔值
1 | def round(R, R1_mask, R2_mask, R3_mask, R4_mask): |
- 先是根据初始状态
R
和相应的掩码来初始化四个LFSR的状态。 - 然后是循环更新状态,在这一步中,进行了270次迭代,每次迭代:
- 更新每个LFSR的状态;
- 获取每个LFSR的最后一位输出
x1
,x2
,x3
,x4
; - 将这些输出位传递给
ff
函数,得到一个布尔值(即0或1),该布尔值被追加到out
的最低有效位(LSB)。
1 | cipher = AES.new(long_to_bytes(key), mode=AES.MODE_ECB) |
然后把key当作AES加密的密钥,在**cipher.encrypt(pad(flag, 16))**中对flag
进行AES加密
思路:
已知mask1~mask4,以及AES加密后的结果,所以我们的目的也就是从lfsr中恢复出key
但是难点是ff函数难以逆向,所以考虑爆破出ff的真值表重新定义ff函数
题解:
出于学习的目的,我这里将两种思路都思考了一下
x1ao(未出flag,但值得学习):
先对ff函数进行爆破得到真值表
1 | from Crypto.Util.number import * |
得到的ff真值表如图:
然后我们重构ff函数:
1 | #from z3 import * |
然后利用z3的SMT求解器添加约束条件后进行求解即可得到key
1 | solver = Solver() #创建一个SMT求解器实例solver |
完整代码:
1 | from z3 import * |
W&M ‘s WriteUp:
根据真值表猜测了一个可能的逻辑关系:(x0+x1+x3)%2
1 | from Crypto.Util.number import * |
发现仅有一种情况不符合:
因此选取所有的1值(总共127个)造矩阵,至于剩下的那个把所有的0值选取一遍爆破一下即可
这里先贴个wp,然后再进行分析:
1 | from Crypto.Cipher import AES |
首先是:
1 | m = matrix(GF(2),128,129) |
创建了几个矩阵,用掩码填充了矩阵M1
、M2
、M3
,同时设置了下三角矩阵的部分
1 | out = bin(out)[2:].zfill(270) |
把out转换为二进制字符串,并记录其中1
的位置索引
1 | n = 0 |
n
用于跟踪当前要填充的列索引
identity_matrix(GF(2),128)
创建了一个有限域为2的128维单位矩阵,
将单位矩阵与掩码矩阵M1
,M2
,M3
相乘,这样每个矩阵就变成了形如**[mask,1,0,…,0]**的形式
1 | for i in range(270): |
- 迭代:然后进行迭代270次,模拟的是270次的LFSR的迭代
- 更新矩阵
MM1
,MM2
,MM3
:每次迭代都将MM1
,MM2
,MM3
分别与掩码矩阵M1
,M2
,M3
相乘,模拟一次 LFSR 的迭代。 - 检查索引
i
是否在si
中:如果索引i
在si
(即out
字符串中为1
的位置),则将(MM1+MM2+MM3)[:,0]
填充矩阵m
的第n
列
1 | assert n == 127 |
由以上部分得到了n == 127
,
-
定义了一个输出向量
output
,他是以一个129×1的矩阵,前127个1和2个0,目的是为了构造线性方程组,是的方程组的形式为
$$
m\ ·\ key\ =\ output
$$ -
重新初始化了三个单位矩阵,并将它们与掩码矩阵相乘,以模拟初始状态
-
在每次迭代中更新矩阵
MM1
,MM2
,MM3
,模拟LFSR的迭代 -
检查索引
i
是否不在si
中,如果不在,则填充矩阵m
的倒数第二列 -
使用深拷贝创建新的矩阵副本,并再次迭代更新矩阵,知道找到一个不在
si
中的索引j
,然后填充矩阵m
的最后一列
1 | try: |
尝试求解线性方程组:
$$
m\ ·\ key\ =\ output
$$
找到解key
后,将其转换为字节,并使用AES解密enc