CTF竞赛密码学 之 LFSR_ctf 密码学的题
wptr33 2025-09-29 13:38 38 浏览
作者:CNinja
目录
概述:
解决LFSR问题
Part(1) 2018 强网杯 Streamgame1
第一种方法
第二种方法
第三种方法
Part(1) 2018 强网杯 Streamgame2
Part(3) [CISCN2018]oldstreamgame
Part(4) [De1CTF2019]Babylfsr考点:B-M 算法
前言:
CTF竞赛密码学好久没更新了,有兴趣的小伙伴可以点下方链接学习一下,当然欢迎和我一起探讨哦。
CTF密码学之RSA攻击算法
不学点《近世代数》怎么学好现代密码学
CTF现代密码之RSA之数论
概述:
线性反馈移位寄存器(LFSR)归属于移位寄存器(FSR),除此之外还有非线性移位寄存器(NFSR)。移位寄存器是流密码产生密钥流的一个主要组成部分。
上一个n级反馈移位寄存器由n个二元存储器与一个反馈函数组成,如下图所示。
移位寄存器的三要素:
- 初始状态:由用户确定
- 反馈函数:是n元布尔函数,即函数的自变量和因变量只取0和1这两个可能值
- 输出序列
如果反馈函数是线性的,那么我们称其为 LFSR,如下图所示:
LFSR的输出序列{ }满足:
- .....
- (i = 1,2,3,...)
举例:
下面是一个5级的线性反馈移位寄存器,其初始状态为
反馈函数为:,(i = 1,2,...) 可以得到输出序列为:
1001101001000010101110110001111 100110…
周期为31。
对于 n 级线性反馈移位寄存器,最长周期为(排除全零)。达到最长周期的序列一般称为 m 序列
解决LFSR问题
Part(1) 2018 强网杯 Streamgame1
考点:已知反馈函数,输出序列,求逆推出初始状态
题目:
from flag import flag
assert flag.startswith("flag{")
assert flag.endswith("}")
# 作用:判断字符串是否以指定字符 开头或结尾
assert len(flag)==25
def lfsr(R,mask):
output = (R << 1) & 0xffffff #将R向左移动1位,bin(0xffffff)='0b111111111111111111111111'
i=(R&mask)&0xffffff #按位与运算符&:参与运算的两个值,如果两个相应位都为1,则该位的结果为1,否则为0
lastbit=0
while i!=0:
lastbit^=(i&1) #按位异或运算,得到输出序列
i=i>>1
output^=lastbit #将输出值写入 output的后面
return (output,lastbit)
R=int(flag[5:-1],2) #flag为二进制数据
mask = 0b1010011000100011100
f=open("key","ab") #以二进制追加模式打开
for i in range(12):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
f.write(chr(tmp)) #将lfsr输出的序列每8个二进制为一组,转化为字符,共12组
f.close()
考点:
def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1 # R和mask进行异或操作,得到输出序列值
output^=lastbit #将输出值设置为output的最后一位
return (output,lastbit)
题目已知条件为 flag长度为19bits,mask长度也为19bits.
由LFSR的输出序列{ }满足的条件:
- (i = 1,2,3,...)
可知,输出值的结果与c的值相关,即题目中的mask。只有当c的值为1时,的值才可能为1
题目中mask中只有第(3,4,5,9,13,14,17,19)位为1,其余都是0(mask这里右边才是第一位,从右往左增大)
现在我们的目的就是为了求出前19位seed的值,而我们已知了seed后面输出序列的值(题目中给的附件key.txt)。那么我们逆推就能得到seed的值了。lfsr(R,mask)函数执行的是19bits的值。那么我们获取到输出序列前19bits值,即:
key = 0101010100111000111
现在需要计算的值,假设我们将 R = ,进行lfsr(R,mask)运算,那么我们将得到输出值为 key[-1]=1。
因为mask中只有第(3,4,5,9,13,14,17,19)位为1,所以线性反馈函数只取这几位对应的a值
1=^(R[-3])^(R[-4])^(R[-5])^(R[-9])^(R[-13])^(R[-14])^(R[-17])
得1=^0,得到=1
同理:R = 的输出值为 key[-2]=1,求得=1
第一种方法
#python3
from Crypto.Util.number import*
f = open('key.txt','rb').read()
r = bytes_to_long(f)
bin_out = bin(r)[2:].zfill(12*8)
R = bin_out[:19] #获取输出序列中与掩码msk长度相同的值
print(R)
mask = '1010011000100011100' #顺序 c_n,c_n-1,。。。,c_1
key = '0101010100111000111'
R = ''
for i in range(19):
output = 'x'+key[:18]
out = int(key[-1])^int(output[-3])^int(output[-4])^int(output[-5])^int(output[-9])^int(output[-13])^int(output[-14])^int(output[-17])
R += str(out)
key = str(out)+key[:18]
print('flag{'+R[::-1]+'}')
第二种方法
seed值只可能是0和1构成,所以猜就行了。
from Crypto.Util.number import*
import os,sys
os.chdir(sys.path[0])
f = open('key.txt','rb').read()
c = bytes_to_long(f)
bin_out = bin(c)[2:].zfill(12*8) #将key文本内容转换为 2 进制数,每个字节占 8 位
R = bin_out[0:19] #取输出序列的前19位
mask = 0b1010011000100011100
def lfsr(R,mask):
output = (R << 1) & 0xffffffff
i=(R&mask)&0xffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
#根据生成规则,初始状态最后一位拼接输出序列
#我们可以猜测seed的第19位(0或1),如果seed19+R[:18]输出值等于R[:19],那么就可以确定seed值了
def decry():
cur = bin_out[0:19] #前19位 2 进制数
res = ''
for i in range(19):
if lfsr(int('0'+cur[0:18],2),mask)[0] == int(cur,2):
res += '0'
cur = '0'+cur[0:18]
else:
res += '1'
cur = '1' + cur[0:18]
return int(res[::-1],2)
r = decry()
print(bin(r))
第三种方法
import os,sys
os.chdir(sys.path[0])
from Crypto.Util.number import *
key = '0101010100111000111'
mask = 0b1010011000100011100
R = ""
index = 0
key = key[18] + key[:19]
while index < 19:
tmp = 0
for i in range(19):
if mask >> i & 1:
tmp ^= int(key[18 - i])
R += str(tmp)
index += 1
key = key[18] + str(tmp) + key[1:18]
print (R[::-1])
Part(1) 2018 强网杯 Streamgame2
考点:已知反馈函数,输出序列,求逆推出初始状态
题目
from flag import flag
assert flag.startswith("flag{")
assert flag.endswith("}")
assert len(flag)==27
def lfsr(R,mask):
output = (R << 1) & 0xffffff
i=(R&mask)&0xffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
R=int(flag[5:-1],2)
mask=0x100002
f=open("key","ab")
for i in range(12):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
f.write(chr(tmp))
f.close()
解法与 2018 强网杯 Streamgame1不能说是毫不相干,简直是一m0一样
from Crypto.Util.number import*
bin_out = open('key.txt','rb').read()
key = bin(bytes_to_long(bin_out))[2:]
# print(key[0:21])
# print(bin(int('0x100002',16)))
key = '101100101110100100001'
mask= '100000000000000000010'
R = ''
for i in range(21):
output = '?' + key[:20]
ans = int(key[-1]) ^ int(output[-2])
R += str(ans)
key = str(ans) + key[:20]
print(R[::-1])
Part(3) [CISCN2018]oldstreamgame
考点:和前面的题目一样都是给出输出序列和反馈函数,求seed(初始状态)
题目:
flag = "flag{xxxxxxxxxxxxxxxx}"
assert flag.startswith("flag{")
assert flag.endswith("}")
assert len(flag)==14
def lfsr(R,mask):
output = (R << 1) & 0xffffffff
i=(R&mask)&0xffffffff
lastbit=0
while i!=0:
lastbit^=(i&1)
i=i>>1
output^=lastbit
return (output,lastbit)
R=int(flag[5:-1],16)
mask = 0b10100100000010000000100010010100
f=open("key","w")
for i in range(100):
tmp=0
for j in range(8):
(R,out)=lfsr(R,mask)
tmp=(tmp << 1)^out
f.write(chr(tmp))
f.close()
exp
#python3
import os,sys
os.chdir(sys.path[0])
from Crypto.Util.number import*
f = open('key.txt','rb').read()
key = bytes_to_long(f)
bin_out = bin(key)[2:].zfill(100*8)
# print(bin_out[:32]) #前32位就是key
key = '00100000111111011110111011111000'
mask = '10100100000010000000100010010100'
R = ''
for i in range(32):
output = 'x' + key[:31]
ans = int(key[-1]) ^ int(output[-3]) ^ int(output[-5]) ^ int(output[-8]) ^ int(output[-12]) ^ int(output[-20]) ^ int(output[-27]) ^ int(output[-30])
R += str(ans)
key = str(ans) + key[:31]
R = str(hex(int(R[::-1],2))[2:])
flag = "flag{" + R + "}"
print(flag)
Part(4) [De1CTF2019]Babylfsr
考点:B-M 算法
题目给了度为256的lfsr,和输出长度为504的输出序列,并提示了FLAG的特征。
在CTFWiki(
https://wiki.x10sec.org/crypto/streamcipher/fsr/lfsr-zh/)中有介绍道 B-M 算法:如果我们知道了长度为 2n 的输出序列,那么就可以通过构造矩阵来求出 mask,时间复杂度: 次比特操作,空间复杂度: 比特。
题目:
import hashlib
from secret import KEY,FLAG,MASK
assert(FLAG=="de1ctf{"+hashlib.sha256(hex(KEY)[2:].rstrip('L')).hexdigest()+"}")
assert(FLAG[7:11]=='1224')
LENGTH = 256
assert(KEY.bit_length()==LENGTH)
assert(MASK.bit_length()==LENGTH)
def pad(m):
pad_length = 8 - len(m)
return pad_length*'0'+m
class lfsr():
def __init__(self, init, mask, length):
self.init = init
self.mask = mask
self.lengthmask = 2**(length+1)-1
def next(self):
nextdata = (self.init << 1) & self.lengthmask
i = self.init & self.mask & self.lengthmask
output = 0
while i != 0:
output ^= (i & 1)
i = i >> 1
nextdata ^= output
self.init = nextdata
return output
if __name__=="__main__":
l = lfsr(KEY,MASK,LENGTH)
r = ''
for i in range(63):
b = 0
for j in range(8):
b = (b<<1)+l.next()
r += pad(bin(b)[2:])
with open('output','w') as f:
f.write(r)
这题中输出序列只给出了504个值,根据 B-M 算法,我们需要确定512个值 (即长度为2n的序列,n为lfsr的度,这里是256) 才能求出 mask ,所以我们可以爆破序列后面缺失的 8 位,可以得到 256 种 mask 可能值,用这 256 个 mask 恢复出 256 个key 值,再用限制条件筛选出 flag.
#sage
import hashlib
key = '001010010111101000001101101111010000001111011001101111011000100001100011111000010001100101110110011000001100111010111110000000111011000110111110001110111000010100110010011111100011010111101101101001110000010111011110010110010011101101010010100101011111011001111010000000001011000011000100000101111010001100000011010011010111001010010101101000110011001110111010000011010101111011110100011110011010000001100100101000010110100100100011001000101010001100000010000100111001110110101000000101011100000001100010'
#将二进制数据填充为8位
def pad(x):
pad_length = 8 - len(x)
return '0'*pad_length+x
# 获取 256个 key 可能值
def get_key(mask,key):
R = ""
index = 0
key = key[255] + key[:256]
while index < 256:
tmp = 0
for i in range(256):
if mask >> i & 1:
# tmp ^= int(key[255 - i])
tmp = (tmp+int(key[255-i]))%2
R = str(tmp) + R
index += 1
key = key[255] + str(tmp) + key[1:255]
return int(R,2)
# 将二进制流转化为十进制
def get_int(x):
m=''
for i in range(256):
m += str(x[i])
return (int(m,2))
# 获取到256个 mask 可能值,再调用 get_key()函数,获取到key值,将结果导入到 sm 中
sm = []
for pad_bit in range(2**8): #爆破rr中缺失的8位
r = key+pad(bin(pad_bit)[2:])
index = 0
a = []
for i in range(len(r)):
a.append(int(r[i])) #将 r 转换成列表a = [0,0,1,...,]格式
res = []
for i in range(256):
for j in range(256):
if a[i+j]==1:
res.append(1)
else:
res.append(0)
sn = []
for i in range(256):
if a[256+i]==1:
sn.append(1)
else:
sn.append(0)
MS = MatrixSpace(GF(2),256,256) #构造 256 * 256 的矩阵空间
MSS = MatrixSpace(GF(2),1,256) #构造 1 * 256 的矩阵空间
A = MS(res)
s = MSS(sn) #将 res 和 sn 的值导入矩阵空间中
try:
inv = A.inverse() # 求A 的逆矩阵
except ZeroDivisionError as e:
continue
mask = s*inv #构造矩阵求mask,B-M 算法
# print(mask[0]) #得到 256 个 mask 值(),type元组
# print(get_int(mask[0]))
# print(key_list)
# print(key[:256])
# print(hex(solve(get_int(mask[0]),key[:256])))
# break
sm.append(hex(get_key(get_int(mask[0]),key[:256])))
# 通过限制条件确定 最终 的flag值
for i in range(len(sm)):
FLAG = hashlib.sha256(sm[i][2:].encode()).hexdigest()
if FLAG[:4]=='1224':
print('flag{'+FLAG+'}')
output:
flag{1224473d5e349dbf2946353444d727d8fa91da3275ed3ac0dedeb7e6a9ad8619}
上面是我关于LFSR学习的一点总结,希望对大家有所帮助,后面会介绍关于LFSR更多的知识点.
复制下面链接,进入实操靶场!CTFCrypto练习之替换密码
https://www.hetianlab.com/expc.do?ec=ECID172.19.104.182015011915454100001&pk_campaign=toutiao-wemedia#stu
实验主要介绍了CTFCrypto练习之替换密码,这个实验可以帮助你对CTF竞赛中的密码学题型的理解与吸收。
相关推荐
- oracle数据导入导出_oracle数据导入导出工具
-
关于oracle的数据导入导出,这个功能的使用场景,一般是换服务环境,把原先的oracle数据导入到另外一台oracle数据库,或者导出备份使用。只不过oracle的导入导出命令不好记忆,稍稍有点复杂...
- 继续学习Python中的while true/break语句
-
上次讲到if语句的用法,大家在微信公众号问了小编很多问题,那么小编在这几种解决一下,1.else和elif是子模块,不能单独使用2.一个if语句中可以包括很多个elif语句,但结尾只能有一个else解...
- python continue和break的区别_python中break语句和continue语句的区别
-
python中循环语句经常会使用continue和break,那么这2者的区别是?continue是跳出本次循环,进行下一次循环;break是跳出整个循环;例如:...
- 简单学Python——关键字6——break和continue
-
Python退出循环,有break语句和continue语句两种实现方式。break语句和continue语句的区别:break语句作用是终止循环。continue语句作用是跳出本轮循环,继续下一次循...
- 2-1,0基础学Python之 break退出循环、 continue继续循环 多重循
-
用for循环或者while循环时,如果要在循环体内直接退出循环,可以使用break语句。比如计算1至100的整数和,我们用while来实现:sum=0x=1whileTrue...
- Python 中 break 和 continue 傻傻分不清
-
大家好啊,我是大田。今天分享一下break和continue在代码中的执行效果是什么,进一步区分出二者的区别。一、continue例1:当小明3岁时不打印年龄,其余年龄正常循环打印。可以看...
- python中的流程控制语句:continue、break 和 return使用方法
-
Python中,continue、break和return是控制流程的关键语句,用于在循环或函数中提前退出或跳过某些操作。它们的用途和区别如下:1.continue(跳过当前循环的剩余部分,进...
- L017:continue和break - 教程文案
-
continue和break在Python中,continue和break是用于控制循环(如for和while)执行流程的关键字,它们的作用如下:1.continue:跳过当前迭代,...
- 作为前端开发者,你都经历过怎样的面试?
-
已经裸辞1个月了,最近开始投简历找工作,遇到各种各样的面试,今天分享一下。其实在职的时候也做过面试官,面试官时,感觉自己问的问题很难区分候选人的能力,最好的办法就是看看候选人的github上的代码仓库...
- 面试被问 const 是否不可变?这样回答才显功底
-
作为前端开发者,我在学习ES6特性时,总被const的"善变"搞得一头雾水——为什么用const声明的数组还能push元素?为什么基本类型赋值就会报错?直到翻遍MDN文档、对着内存图反...
- 2023金九银十必看前端面试题!2w字精品!
-
导文2023金九银十必看前端面试题!金九银十黄金期来了想要跳槽的小伙伴快来看啊CSS1.请解释CSS的盒模型是什么,并描述其组成部分。答案:CSS的盒模型是用于布局和定位元素的概念。它由内容区域...
- 前端面试总结_前端面试题整理
-
记得当时大二的时候,看到实验室的学长学姐忙于各种春招,有些收获了大厂offer,有些还在苦苦面试,其实那时候的心里还蛮忐忑的,不知道自己大三的时候会是什么样的一个水平,所以从19年的寒假放完,大二下学...
- 由浅入深,66条JavaScript面试知识点(七)
-
作者:JakeZhang转发链接:https://juejin.im/post/5ef8377f6fb9a07e693a6061目录由浅入深,66条JavaScript面试知识点(一)由浅入深,66...
- 2024前端面试真题之—VUE篇_前端面试题vue2020及答案
-
添加图片注释,不超过140字(可选)1.vue的生命周期有哪些及每个生命周期做了什么?beforeCreate是newVue()之后触发的第一个钩子,在当前阶段data、methods、com...
- 今年最常见的前端面试题,你会做几道?
-
在面试或招聘前端开发人员时,期望、现实和需求之间总是存在着巨大差距。面试其实是一个交流想法的地方,挑战人们的思考方式,并客观地分析给定的问题。可以通过面试了解人们如何做出决策,了解一个人对技术和解决问...
- 一周热门
- 最近发表
- 标签列表
-
- git pull (33)
- git fetch (35)
- mysql insert (35)
- mysql distinct (37)
- concat_ws (36)
- java continue (36)
- jenkins官网 (37)
- mysql 子查询 (37)
- python元组 (33)
- mybatis 分页 (35)
- vba split (37)
- redis watch (34)
- python list sort (37)
- nvarchar2 (34)
- mysql not null (36)
- hmset (35)
- python telnet (35)
- python readlines() 方法 (36)
- munmap (35)
- docker network create (35)
- redis 集合 (37)
- python sftp (37)
- setpriority (34)
- c语言 switch (34)
- git commit (34)
