题目代码
1 | import sys |
主流程分析
1 | deskey="imnotkey" |
我们首先来看一下代码主流程看了什么:
1.设置了一个未知的deskey
2.然后用这个未知的deskey加密了code
3.然后用deskey+code作为key,调用blowfish密码,加密了flag
然后我们有
1.deskey的密钥编排后的子密钥
2.code加密后的密文correct
3.blowfish加密后的密文
所以思路还算清晰:
1.用deskey的子密钥反推deskey
2.用deskey的子密钥解密correct得到code
3.用得到的deskey和code作为密钥解密blowfish密文得到flag
然后我们容易知道DES的密钥编排过程为:
1.首先输入64bit密钥
2.将64bit密钥经过PC-1盒变成56bit
3.将56bit分为C0和D0,分别28bit
4.将C0,D0分别循环左移位,得到C1,D1
5.将C1,D1拼接,经过PC-2盒变成48bit子密钥key1
6.重复步骤4
7.生成16组子密钥
所以这里我有想法:
1.由子密钥key1经过逆PC-2盒推出C1,D1(得到48位已知和8位未知)
2.由C1,D1分别循环右移1位,得到C0,D0
3.由C0,D0经过逆PC-1盒得到deskey(已知48位,未知16位)
4.然后将deskey的16个未知量设置成a,b,c,d……
5.用带有未知参数的deskey生成16个子密钥
6.用16个带未知参数的子密钥和16个已知子密钥建立方程组
7.可以解出其中8个bit的未知量,剩余8个bit不重要,因为deskey实际加密只用了56位密钥
8.随机给剩下8bit赋值,作为一个deskey,解密correct
9.爆破剩余8bit的deskey变量,根据题目特性,应该会有一个可以是明文的字符串,即deskey
10.用deskey+code作为key解密blowfish密文,得到flag
由子密钥反推deskey
我们有子密钥1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18DES.Kn =[
[1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1],
[1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1],
[1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
[0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1],
[0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0],
[1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0],
[1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0],
[1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0],
[1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0],
[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1],
[0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1],
[1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1],
[0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1],
[0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1]
]
由子密钥key1开始逆推:
首先是逆PC-2盒:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15key1 = [1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1]
__pc2 = [
13, 16, 10, 23, 0, 4,
2, 27, 14, 5, 20, 9,
22, 18, 11, 3, 25, 7,
15, 6, 26, 19, 12, 1,
40, 51, 30, 36, 46, 54,
29, 39, 50, 44, 32, 47,
43, 48, 38, 55, 33, 52,
45, 41, 49, 35, 28, 31
]
C1D1 = ['*']*56
for i in range(0,len(key1)):
C1D1[__pc2[i]] = key1[i]
print C1D1
可以得到C1D1的值为:1
[1, 1, 0, 1, 0, 1, 1, 0, '*', 1, 1, 0, 0, 1, 1, 0, 0, '*', 1, 0, 1, '*', 0, 1, '*', 0, 1, 1, 1, 1, 1, 1, 1, 1, '*', 1, 1, '*', 1, 1, 1, 1, '*', 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, '*', 0, 1]
然后我们循环右移动1位逆推出C0D01
2C1:11010110*11001100*101*01*011
D1:111111*11*1111*1000000010*01
循环右移一位:1
2C0:111010110*11001100*101*01*01
D0:1111111*11*1111*1000000010*0
然后可以逆PC-1盒得到deskey1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19C0='111010110*11001100*101*01*01'
D0='1111111*11*1111*1000000010*0'
__pc1 = [56, 48, 40, 32, 24, 16, 8,
0, 57, 49, 41, 33, 25, 17,
9, 1, 58, 50, 42, 34, 26,
18, 10, 2, 59, 51, 43, 35,
62, 54, 46, 38, 30, 22, 14,
6, 61, 53, 45, 37, 29, 21,
13, 5, 60, 52, 44, 36, 28,
20, 12, 4, 27, 19, 11, 3
]
C0D0 = C0+D0
res = ['*']*64
deskey = ""
for i in range(0,len(__pc1)):
res[__pc1[i]] = C0D0[i]
for i in res:
deskey += i
print deskey
得到deskey1
11000***11**011*0010011*1001011*0111011*11*00*1*1*0*011*1001111*
然后我们给每个未知量替换为变量a,b,c……
得到1
11000abc11de011f0010011g1001011h0111011i11j00k1L1m0n011o1001111p
然后我们用这个带未知量的deskey生成16个子密钥: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
35def zuoyiwei(str,num):
my = str[num:len(str)]
my = my+str[0:num]
return my
def key_change_1(str):
key1_list = [57,49,41,33,25,17,9,1,58,50,42,34,26,18,10,2,59,51,43,35,27,19,11,3,60,52,44,36,63,55,47,39,31,23,15,7,62,54,46,38,30,22,14,6,61,53,45,37,29,21,13,5,28,20,12,4]
res = ""
for i in key1_list:
res+=str[i-1]
return res
def key_change_2(str):
key2_list = [14,17,11,24,1,5,3,28,15,6,21,10,23,19,12,4,26,8,16,7,27,20,13,2,41,52,31,37,47,55,30,40,51,45,33,48,44,49,39,56,34,53,46,42,50,36,29,32]
res = ""
for i in key2_list:
res+=str[i-1]
return res
def key_gen(str):
key_list = []
key_change_res = key_change_1(str)
key_c = key_change_res[0:28]
key_d = key_change_res[28:]
for i in range(1,17):
if (i==1) or (i==2) or (i==9) or (i==16):
key_c = zuoyiwei(key_c,1)
key_d = zuoyiwei(key_d,1)
else:
key_c = zuoyiwei(key_c,2)
key_d = zuoyiwei(key_d,2)
key_yiwei = key_c+key_d
key_res = key_change_2(key_yiwei)
key_list.append(key_res)
return key_list
deskey = "11000abc11de011f0010011g1001011h0111011i11j00k1L1m0n011o1001111p"
print key_gen(deskey)
得到结果1
2
3
4
5
6
7
8['101110011111010100011001111100110010101110010111', '1j0n111101d110001m001110101k011110100011be0a0111',
'00111010jm100d11111110001011011ae01001111100011b', '1d0111000101110m00101nj1011111b010k00e1111000111',
'11j00011d01010110101110m01k1e1101110010b11001a11', '0000110m1111111010n001d1011011101e110101a10010k1',
'n1d1001100111101m11j10101b101k10111101010110101a', '111m1j00111001n011100001e11011a0110111110k10b010',
'10n111011011m00j0d1101100k00111e11011b01011110a0', '101001100dm011101110101j1100ba01110111010111k100',
'1111101j01110m1d001n0100110010011b0k11101a111e00', '1m001n0010011111j101100d11011001a1011110e0k11101',
'010j01ndm111001001111111b00110110101ka101011110e', '1010n11111011101d10000m01001a0e1011110b1101k0101',
'01md1010011010111110nj11101b001k0a10101e10110101', '00101111100mdj10n111010110110e110110a0k011010b11']
和题目中的Kn比对:1
2
3
4
5
6
7
8['101110011111010100011001111100110010101110010111', '110011110111100010001110101001111010001111000111',
'001110101010011111111000101101101010011111000111', '110111000101110000101011011111101000011111000111',
'111000111010101101011100010111101110010111001011', '000011001111111010000111011011101111010101001001',
'011100110011110101111010111010101111010101101010', '111011001110010011100001111011001101111100101010',
'100111011011000101110110000011111101110101111000', '101001100100111011101011110010011101110101110100',
'111110110111001100100100110010011100111010111100', '100010001001111111011001110110010101111010011101',
'010101010111001001111111100110110101001010111101', '101001111101110111000000100100110111101110100101',
'010110100110101111100111101100100010101110110101', '001011111000111001110101101101110110000011010111']
我们容易得到8个变量的值,然后得到带有8个未知数的deskey1
"1100001"+c+"1111011"+f+"0010011"+g+"1001011"+h+"0111011"+i+"1110001"+l+"1000011"+o+"1001111"+p
然后题目的第一个坑人的地方来了
这个deskey大家会发现怎么都不对,我们阅读题目中给的程序
发现他对deskey的处理:1
key = self.__permutate(des.__pc1, self.__String_to_BitList(self.getKey()))
我们跟进这个__String_to_BitList()1
2
3
4
5
6
7
8
9
10
11
12
13
14def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
for i in range(0,8):
result[(pos<<3)+i]=(ch>>i)&1
pos+=1
return result
可以发现这个根本不是原版的pydes库的函数,我们来看看原版函数:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19def __String_to_BitList(self, data):
"""Turn the string data, into a list of bits (1, 0)'s"""
if _pythonMajorVersion < 3:
# Turn the strings into integers. Python 3 uses a bytes
# class, which already has this behaviour.
data = [ord(c) for c in data]
l = len(data) * 8
result = [0] * l
pos = 0
for ch in data:
i = 7
while i >= 0:
if ch & (1 << i) != 0:
result[pos] = 1
else:
result[pos] = 0
pos += 1
i -= 1
return result
容易发现,我们题目中的处理deskey的函数
1.会先把deskey转换成64bit的二进制
2.然后将64bit的2进制,8个一组进行分组
3.再对每一组倒叙输出
4.然后再把8组拼接回来
什么意思呢?
比如1
abcdefjhABCDEFJH
他处理后会变成1
hjfedcbaHJFEDCBA
所以我们的deskey要进行处理:1
2
3
4
5deskey_old = '1100001"+c+"1111011"+f+"0010011"+g+"1001011"+h+"0111011"+i+"1110001"+L+"1000011"+o+"1001111"+p'.replace('"+','').replace('+"','')
deskey_new = ""
for i in range(0,len(deskey_old),8):
deskey_new += deskey_old[i:i+8][::-1]
print deskey_new
得到1
c1000011f1101111g1100100h1101001i1101110L1000111o1100001p1111001
然后我们就可以爆破8bit寻找可读明文字符串了1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16def bintostr(str):
res = ""
for i in range(0,len(str),8):
res += chr(int(str[i:i+8],2))
return res
for c in "01":
for f in "01":
for g in "01":
for h in "01":
for i in "01":
for L in "01":
for o in "01":
for p in "01":
str = c+"1000011"+f+"1101111"+g+"1100100"+h+"1101001"+i+"1101110"+L+"1000111"+o+"1100001"+p+"1111001"
str = bintostr(str)
print str
运行程序容易发现,只有一个可见字符串:1
CodinGay
所以这一定使我们的deskey
解密correct
既然题目改变了des库的函数,所以为了解密的保险起见,我们调用题目中的代码进行解密
但是题中删去了des的解密函数
所以我们添加:1
2
3
4
5
6def decrypt(self, data, pad=None, padmode=None):
data = self._guardAgainstUnicode(data)
if pad is not None:
pad = self._guardAgainstUnicode(pad)
data = self.crypt(data, des.DECRYPT)
return self._unpadData(data, pad, padmode)
但是发现解密还不成功
于是我diff了一下题中的代码和pyDes库里的代码,发现这个函数需要改动
增加一个else1
2
3
4
5
6
7
8
9
10
11
12
13def __des_crypt(self, block, crypt_type):
"""Crypt the block of data through DES bit-manipulation"""
block = self.__permutate(des.__ip, block)
self.L = block[:32]
self.R = block[32:]
if crypt_type == des.ENCRYPT:
iteration = 0
iteration_adjustment = 1
else:
iteration = 15
iteration_adjustment = -1
但是依旧解密不成功,我又接着看
发现crypt_type要依托来区分1
2
3# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x00
而作者把这个都改成了0x00
所以我们恢复1
2
3# Type of crypting being done
ENCRYPT = 0x00
DECRYPT = 0x01
终于可以愉快的解密了
得到code:1
LafetoborasdileiShelvapelocrana0
解密blowfish
这里就很简单了,直接给出代码1
2
3
4
5
6
7from Crypto.Cipher import Blowfish
import base64
deskey = "CodinGay"
code = "LafetoborasdileiShelvapelocrana0"
key= deskey+code
cipher = Blowfish.new(key, Blowfish.MODE_ECB)
print cipher.decrypt(base64.b64decode("fxd+VFDXF6lksUAwcB1CMco6fnKqrQcO5nxS/hv3FtN7ngETu95BkjDn/ar+KD+RbmTHximw03g="))
运行即可得到flag
后记
总体来说这题还是不错的,考察点主要还是在密钥编排和DES流程分析,以及一些代码审计的能力
但是作者有点阴……改了一些东西我还是有点不爽= =毕竟一开始deskey很容易就拿到了,一直以为是原封的des,搞了一天不知道什么问题,后来审计了一下代码才发现问题……so坑!!