CTF中的LFSR考点(一)

CTF中的LFSR考点(一)

前提概要:
这是我在理解了作者:道路结冰的博客深入分析CTF中的LFSR类题目(一)下写的一次回顾和分析,只是在其中加上自己的见识和理解来加深印象。

博客地址:https://www.anquanke.com/post/id/181811#h2-0

.
.
前言:

LFSR(线性反馈移位寄存器)已经成为如今CTF中密码学方向题目的一个常见考点了,在今年上半年的一些国内赛和国际赛上,也出现了非常多的这类题目,但是其中绝大多数题目目前都没有writeups(或者writeups并没有做cryptanalysis,而是通过爆破的方法解决,这种思路只适用于部分类似去年强网杯出现的几道非常基础的LFSR类题目有效,对于绝大多数国际赛上的题目不仅是没有任何效果的,也是没有任何意义的,只有真正掌握了LFSR的密码学原理,才有可能在国际赛上解决一道高分值的LFSR类题目),网上针对这类考点的详细分析也不多,因此接下来我将通过几篇文章,对这类知识点进行一个详细的分析。
.
.
.

LFSR简介:

LFSR是属于FSR(反馈移位寄存器)的一种,除了LFSR之外,还包括NFSR(非线性反馈移位寄存器)。

附加:
反馈移位就是可以通过前面已经存在的寄存器中的值反馈出后面的的寄存器的值,通过不断移位对应不同的前寄存器值一直反馈出后面连续的后寄存器值。
在这里插入图片描述

FSR是流密码产生密钥流的一个重要组成部分,在GF(2)上的一个n级FSR通常由n二元存储器和一个反馈函数组成,如下图所示:
在这里插入图片描述

.

如果这里的反馈函数是线性的,我们则将其称为LFSR,此时该反馈函数可以表示为:
在这里插入图片描述

其中cn=0或1,⊕表示异或(模二加),也就是说反馈函数必然为a1……⊕……an形式中的一部分。

我们接下来通过一个例子来更直观的明确LFSR的概念,假设给定一个5级的LFSR,其初始状态(即a1到a5这5个二元存储器的值)为:
在这里插入图片描述

其反馈函数为:
在这里插入图片描述

整个过程可以表示为下图所示的形式:
在这里插入图片描述

接下来我们来计算该LFSR的输出序列,输出序列的前5位即为我们的初始状态10011,第6位的计算过程如下:
在这里插入图片描述

第7位的计算过程如下:
在这里插入图片描述

由此类推,可以得到前31位的计算结果如下:
1001101001000010101110110001111

对于一个n级的LFSR来讲,其最大周期为2^n-1 ,因此对于我们上面的5级LFSR来讲,其最大周期为 2^5-1=31,再后面的输出序列即为前31位的循环

注意
这里的就是能通过反馈函数生成完整连续序列最少寄存器数,因为a6=a1⊕a4,所以a5必须包含在内,所以是5级寄存器。

通过上面的例子我们可以看到,对于一个LFSR来讲,我们目前主要关心三个部分:初始状态反馈函数输出序列

那么对于CTF中考察LFSR的题目来讲也是如此,大多数情况下,我们在CTF中的考察方式都可以概括为:给出反馈函数输出序列,要求我们反推出初始状态,初始状态即为我们需要提交的flag,另外大多数情况下,初始状态的长度我们也是已知的。

显然,这个反推并不是一个容易的过程,尤其当反馈函数十分复杂的时候,接下来我们就通过一些比赛当中出现过的具体的CTF题目,来看一下在比赛当中我们应该如何解决这类问题,由于不同题目之间难度差异会很大,所以我们先从最简单的题目开始,我将尽可能的用最通俗的语言和脚本来进行演示,在后面会逐渐提升题目的难度,同时补充相应的代数知识。

.
.

CTF例题演示

2018 CISCN 线上赛 oldstreamgame
题目给出的脚本如下:

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()

.
.
分析一下我们的已知条件(1):

已知初始状态的长度为4个十六进制数,即32位,初始状态的值即我们要去求的flag,所以初步判断这里的是32,那么最大周期就是2^32-1
已知反馈函数lfsr,只不过这里的反馈函数是代码的形式,我们需要提取出它的数学表达式。 已知输出序列。
在这里插入图片描述

.
.
那么我们的任务很明确,就是通过分析lfsr函数,整理成数学表达式的形式求解即可,接下来我们一行一行的来分析这个函数:
在这里插入图片描述

.
.
通过上面的分析,我们可以看出在这道题的情境下,lfsr函数本质上就是一个输入R输出lastbit的函数,虽然我们现在已经清楚了R是如何经过一系列运算得到lastbit的,但是我们前面的反馈函数都是数学表达式的形式,我们能否将上述过程整理成一个表达式的形式呢?这就需要我们再进一步进行分析:

mask只有第3、5、8、12、20、27、30、32这几位为1,其余位均为0。
mask与R做按位与运算得到i,当且仅当R的第3、5、8、12、20、27、30、32这几位中也出现1时,i中才可能出现1,否则i中将全为0。
lastbit是由i的最低位向i的最高位依次做异或运算得到的,在这个过程中,所有为0的位我们可以忽略不计(因为0异或任何数等于任何数本身,不影响最后运算结果),因此lastbit的值仅取决于i中有多少个1:当i中有奇数个1时,lastbit等于1;当i中有偶数个1时,lastbit等于0。
当R的第3、5、8、12、20、27、30、32这几位依次异或结果为1时,即R中有奇数个1,因此将导致i中有奇数个1;当R的第3、5、8、12、20、27、30、32这几位依次异或结果为0时,即R中有偶数个1,因此将导致i中有偶数个1。
因此我们可以建立出联系:lastbit等于R的第3、5、8、12、20、27、30、32这几位依次异或的结果。

.
.
将其写成数学表示式的形式,即为我们要求的反馈函数,反馈函数的形式也说明了初始位数要32位
在这里插入图片描述
.
.
.
然后我们看一下明文是怎么来的:

key=20FDEEF8A4C9F4083F331DA8238AE5ED083DF0CB0E7A83355696345DF44D7C186C1F459BCE135F1DB6C76775D5DCBAB7A783E48A203C19CA25C22F60AE62B37DE8E40578E3A7787EB429730D95C9E1944288EB3E2E747D8216A4785507A137B413CD690C

最后八行代码,对flag,它做了一百次循环,每次循环都产生一个结果,并将这个结果写到key里面去,所以key里面总共有一百个ASCII字符,共两百个16进制数。
.
补充:
题目之所以会出现 100 个ASCII字符是因为 4 循环次加内嵌的 8 位一次共 32 位循环往后产生的 4 个flag生成的加密字符共 8 个 16 进制数后,继续用这 4 个加密后的 flag 字符继续新一轮加密,就是多层加密。所以我们取 32 位即可,结果 flag 也是 4 个ASCII字符拆分出的 8 个 16 进制数。
.
在这里插入图片描述

.
.
.
显然,lastbitR之间满足线性关系,那么接下来我们就可以开始求解了。
而且从这里我们可以知道,这种移位的CTF题目类型要反推32位的初始值要多少位输出序列呢,答案就是32位,因为这种移位的题目是32位为一个循环,这就和我们前面说的2^32-1的循环不太同了,因为这里是移位操作。

32位Key值:00100000111111011110111011111000

解题的关键是发现在明文只剩最后一位时,可以通过最后的结果来求出排在第32位的第一位,然后就可以反推回去了。
.
我们想象这样一个场景,当即将输出第32位lastbit时,此时R已经左移了31位,根据上面的数学表达式,我们有:
在这里插入图片描述
.
这样我们就可以求出R的第1位,同样的方法,我们可以求出R的第2位:
在这里插入图片描述
以此类推,R的全部32位我们都可以依次求出了

.
.
.
最终脚本,这里注意小端顺序反序取位:

key1="00100000111111011110111011111000"	#lastbit全部值,就是反馈函数生成的值,32位的key1
key2=key1
flag=[]
for i in range(32):
	output='?'+key1[:31]	#?0100000111111011110111011111000,因为后面有key1=str(lastbit)+key1[:31],key1不断填补,output不断取前31位,所以这里output每次把?定在第i位上,注意output和key1是独立的分开的。
	flag.append(str(int(key2[-1-i])^int(output[-3])^int(output[-5])^int(output[-8])^int(output[-12])^int(output[-20])^int(output[-27])^int(output[-30])))
#这里之所以取负数是因为我们截取是从左往右取,而在计算机中是小端顺序,应该从右往左取才对,这里的key2[-1-i]就是小端左到右的第i位,也就是前面分析的反馈函数生成值的第i位。flag值的原第i位,现在在第32位,可以通过⊕的可逆性来求,就是R32=lastbit ⊕ R3 ⊕ R5 ⊕ R8 ⊕ R12 ⊕ R20 ⊕ R27 ⊕ R30                    				
	key1=str(flag[i])+key1[:31]#不断填补key1,让key1向右推进,
print("flag{"+hex(int(''.join(flag[::-1]),2)).replace('0x','')+"}")#这里是经过一系列操作,首先把flag变成小端顺序的[::-1],然后就是转十六进制。

在这里插入图片描述

.
.
结果:
在这里插入图片描述

  • 10
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
CTF的Miscellaneous(简称Misc)是指一类涵盖了多个领域的题目,常见的包括密码学、网络协议、二进制分析等。 在CTF比赛,解决这类题目的方法主要有以下几种: 1. 阅读题目描述:首先要仔细阅读题目描述,通常会提供一些线索或提示,例如题目类型、题目背景或所需的技术知识。这些大致的信息能够帮助你确定解题方法的方向。 2. 分析题目附件或源代码:如果题目提供了附件或源代码,要仔细分析其的内容。有时可能需要进行逆向工程、二进制分析或查找隐藏信息。需要注意的是,不同题目类型可能需要使用不同的分析工具和技术。 3. 猜测和尝试:在整个解题过程,可能需要多次猜测和尝试。例如,对于密码学类题目,尝试使用不同类型的密码学算法进行解密;对于网络协议类题目,尝试使用Wireshark等工具进行数据包分析。 4. 查找前人的经验:CTF解题是一个积累经验的过程,很多题目类型都有经典解法,可以通过学习前人的经验来提高解题效率。可以参考CTF比赛的writeup、CTF讨论论坛或CTF相关的学习资源等。 5. 团队合作:在解题过程,可以与队友或其他选手进行合作,分享解题过程的思路和发现。这样可以锻炼团队合作的能力,也能够快速找到解题思路或解题方法。 综上所述,CTFMisc题目的解题方法主要包括阅读题目描述、分析附件或源代码、猜测和尝试、查找前人的经验以及团队合作等。通过不断学习和实践,提高解题的技巧和经验,才能更好地应对各种Misc题目的挑战。
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

沐一 · 林

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值