BUUCTF 每日打卡 2021-7-31

引言

打了一天i春秋云上巅峰赛,没爆零,还行

[AFCTF2018]Tiny LFSR

加密代码如下:

import sys
from binascii import unhexlify

if(len(sys.argv)<4):
	print("Usage: python Encrypt.py keyfile plaintext ciphername")
	exit(1)

def lfsr(R, mask):
	output = (R << 1) & 0xffffffffffffffff
	i=(R&mask)&0xffffffffffffffff
	lastbit=0
	while i!=0:
		lastbit^=(i&1)
		i=i>>1
	output^=lastbit
	return (output,lastbit)

R = 0
key = ""
with open(sys.argv[1],"r") as f:
	key = f.read()
	R = int(key,16)
	f.close
	
mask = 0b1101100000000000000000000000000000000000000000000000000000000000

a = ''.join([chr(int(b, 16)) for b in [key[i:i+2] for i in range(0, len(key), 2)]])

f=open(sys.argv[2],"r")
ff = open(sys.argv[3],"wb")
s = f.read()
f.close()
lent = len(s)

for i in range(0, len(a)):
	ff.write((ord(s[i])^ord(a[i])).to_bytes(1, byteorder='big'))

for i in range(len(a), lent):
    tmp=0
    for j in range(8):
        (R,out)=lfsr(R,mask)
        tmp=(tmp << 1)^out
    ff.write((tmp^ord(s[i])).to_bytes(1, byteorder='big'))
ff.close()

搜了一下LFSR是什么:
在这里插入图片描述
好像是一种伪随机数生成器(你说这个谁懂啊!)
但是在CTFwiki伪随机数生成器那块没有找到,就放弃了
看到这个lfsr()函数就头皮发麻,还有一堆argv[]不知道是干什么,看了几眼,找wp去了(看了wp之后发现也没那么难嘛[doge])
附件里面还有一个.bash_history.txt文件:

python Encrypt.py key.txt Plain.txt cipher.txt
python Encrypt.py key.txt flag.txt flag_encode.txt
rm flag.txt
rm key.txt

大概就是用加密程序和key.txt先加密了Encrypt.py文件,再加密了flag.txt,然后删除了flag.txt和key.txt
还给了Plain.txt,cipher.txt和flag_encode.txt,我们就可能要先用Plain.txt和cipher.txt解出key.txt,然后用flag_encode.txt和key.txt解出flag.txt

观察加密代码后半部分:

for i in range(0, len(a)):
	ff.write((ord(s[i])^ord(a[i])).to_bytes(1, byteorder='big'))

for i in range(len(a), lent):
    tmp=0
    for j in range(8):
        (R,out)=lfsr(R,mask)
        tmp=(tmp << 1)^out
    ff.write((tmp^ord(s[i])).to_bytes(1, byteorder='big'))

容易发现明文以len(a)为边界被分成两部分加密
前一部分是和a[i]作异或
这个a则是由a = ''.join([chr(int(b, 16)) for b in [key[i:i+2] for i in range(0, len(key), 2)]])得来的,也就是说a是由key得来的,因此前半部分是解出key的关键
但是我们不知道key的长度,先做个测试:

from Crypto.Util.number import *


def lfsr(R, mask):
    output = (R << 1) & 0xffffffffffffffff
    i = (R & mask) & 0xffffffffffffffff
    lastbit = 0
    while i != 0:
        lastbit ^= (i & 1)
        i = i >> 1
    output ^= lastbit
    return (output, lastbit)


R = 0
mask = 0b1101100000000000000000000000000000000000000000000000000000000000

with open('Plain.txt', 'rb') as f:
    s = f.read()
with open('cipher.txt', 'rb') as f:
    cipher = f.read()

t = hex(bytes_to_long(s) ^ bytes_to_long(cipher))[2:]
s = 'sdgfjkahblskdjxbvfskljdfbguisldfbvghkljsdfbghsjkldhbgjklsdbgvlkjsdgbkljb sdkljfhwelo;sdfghioeurthgbnjl k'
lent = len(s)
for i in range(1, len(t)):
    key = t[:i]
    R = int(key, 16)
    a = ''.join([chr(int(b, 16)) for b in [key[i:i + 2] for i in range(0, len(key), 2)]])
    c = b''
    for j in range(0, len(a)):
        c += (ord(s[j]) ^ ord(a[j])).to_bytes(1, byteorder='big')

    for j in range(len(a), lent):
        tmp = 0
        for k in range(8):
            (R, out) = lfsr(R, mask)
            tmp = (tmp << 1) ^ out
        c += (tmp ^ ord(s[j])).to_bytes(1, byteorder='big')

    print(key)
    print(c)
    print(cipher)
    print(c == cipher)
    if c == cipher:
        print(i)
        break

发现都是False
是不是key前面缺个“0”?
修改代码:t = '0' + hex(bytes_to_long(s) ^ bytes_to_long(cipher))[2:]
得到结果:
在这里插入图片描述
所以 key 为 0123456789abcdef
得到key之后前半段只要key和密文做异或即可得到明文,那后半段怎么办?
再观察代码可以发现,每次for i循环,都经过内部的for j循环生成一个tmp与明文作异或运算生成密文,而这个tmp与函数lfsr()有关,而lfsr()的参数一个参数R只与key有关,另一个参数mask是常量
那我们就可以不用管lfsr()函数,只要生成tmp与密文作异或运算就行了,也就是wp中所说的黑箱方法
解密代码如下(照搬wp):

key = '123456789abcdef'
R = int(key, 16)
mask = 0b1101100000000000000000000000000000000000000000000000000000000000


def lfsr(R, mask):
    output = (R << 1) & 0xffffffffffffffff
    i = (R & mask) & 0xffffffffffffffff
    lastbit = 0
    while i != 0:
        lastbit ^= (i & 1)
        i = i >> 1
    output ^= lastbit
    return (output, lastbit)


cipher = open('flag_encode.txt', 'rb').read()
a = ''.join([chr(int(b, 16)) for b in [key[i:i + 2] for i in range(0, len(key), 2)]])
ans = []
lent = len(cipher)

for i in range(0, len(a)):
    ans.append(chr(cipher[i] ^ ord(a[i])))

for i in range(len(a), lent):
    tmp = 0
    for j in range(8):
        (R, out) = lfsr(R, mask)
        tmp = (tmp << 1) ^ out
    ans.append(chr(tmp ^ cipher[i]))

flag = ''.join(ans)
print(flag)

结果为:
在这里插入图片描述
关于线性反馈移位寄存器 - LFSR,后来在CTFwiki中找到了相关结束
题面的代码大都是先定义一个函数lfsr()
这里附上wp中的注释帮助理解:

def lfsr(R, mask):
    # 左移1位:保留末尾 63 位,在最后添加一个0
    output = (R << 1) & 0xffffffffffffffff
    
    # i:保留 R 的前 0、1、3、4位
    i=(R&mask)&0xffffffffffffffff
    
    
    lastbit=0
    while i!=0:
        lastbit^=(i&1)
        i=i>>1
    # lastbit:统计 i 里面有多少个1, 奇数个则为1, 偶数个则为0

    # output: R 左移1位,再添加 lastbit
    output^=lastbit
    return (output,lastbit)

不同的地方就是这个0xffffffffffffffff里面“f”的个数,也就是初态的比特数
后面也会有一个生成tmp的过程:

for i in range(len(a), lent):
    tmp = 0
    for j in range(8):
        (R, out) = lfsr(R, mask)
        tmp = (tmp << 1) ^ out

方法和具体原理感兴趣的可以研究一下

结语

希望继续坚持

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值