0%

BUUCTF 每日打卡 2021-7-31

BUUCTF 每日打卡 2021-7-31

引言

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

[AFCTF2018]Tiny LFSR

加密代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
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文件:

1
2
3
4
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

观察加密代码后半部分:

1
2
3
4
5
6
7
8
9
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的长度,先做个测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
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):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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中的注释帮助理解:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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的过程:

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

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

结语

希望继续坚持

欢迎关注我的其它发布渠道

-------- 本文结束 感谢阅读 --------