CISCN-imageencrypt

有意思的一题

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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
import random
from flag import flag,image,r,key1,key2
import md5

assert(flag[:5]=='CISCN')
assert(len(str(r))==3) # 1.2
data = ''.join(map(chr,image))
assert(flag[6:-1] == md5.new(data).hexdigest())
assert(key1<256)
assert(key2<256)

x0 = round(random.random(),6)

def generate(x):
return round(r*x*(3-x),6) # r < 1


def encrypt(pixel,key1,key2,x0,m,n):
num = m*n/8
seqs = []
x = x0
bins = ''
tmp = []
for i in range(num):
x = generate(x)
tmp.append(x)
seqs.append(int(x*22000))
for x in seqs:
bin_x = bin(x)[2:]
if len(bin_x) < 16:
bin_x = '0'*(16-len(bin_x))+bin_x
bins += bin_x
assert(len(pixel) == m*n)
cipher = [ 0 for i in range(m) for j in range(n)]
for i in range(m):
for j in range(n):
index = n*i+j
ch = int(bins[2*index:2*index+2],2)
pix = pixel[index]
if ch == 0:
pix = (pix^key1)&0xff
if ch == 1:
pix = (~pix^key1)&0xff
if ch == 2:
pix = (pix^key2)&0xff
if ch == 3:
pix = (~pix^key2)&0xff
cipher[index] = pix
return cipher

flagimage = image
testimage = []
for i in range(16*16):
testimage.append(random.randint(0,255))
print testimage
print encrypt(testimage,key1,key2,x0,16,16)
print encrypt(flagimage,key1,key2,x0,24,16)
"""
[205, 237, 6, 158, 24, 119, 213, 32, 74, 151, 142, 186, 57, 28, 113, 62, 165, 20, 190, 37, 159, 137, 196, 44, 97, 37, 7, 222, 220, 95, 4, 66, 0, 28, 199, 142, 95, 105, 119, 232, 250, 215, 60, 162, 91, 211, 63, 30, 91, 108, 217, 206, 80, 193, 230, 42, 221, 71, 136, 115, 22, 176, 91, 57, 61, 3, 87, 73, 250, 121, 51, 72, 83, 120, 77, 199, 236, 190, 249, 116, 45, 6, 134, 110, 149, 94, 214, 232, 153, 213, 119, 98, 81, 203, 240, 114, 240, 29, 122, 188, 156, 53, 128, 185, 40, 147, 245, 204, 47, 101, 80, 229, 41, 150, 28, 195, 25, 235, 119, 6, 192, 8, 73, 255, 159, 172, 77, 94, 254, 104, 236, 219, 141, 91, 195, 162, 97, 56, 252, 173, 163, 43, 167, 214, 50, 73, 115, 190, 254, 53, 61, 77, 138, 192, 15, 4, 190, 27, 37, 108, 101, 135, 90, 215, 106, 243, 112, 111, 106, 89, 143, 150, 185, 142, 192, 176, 48, 138, 164, 185, 61, 77, 72, 0, 17, 203, 210, 71, 186, 49, 162, 250, 218, 219, 195, 63, 248, 220, 155, 180, 219, 132, 219, 94, 144, 247, 211, 95, 70, 227, 222, 31, 69, 24, 13, 216, 185, 108, 137, 57, 186, 211, 55, 27, 158, 241, 223, 21, 134, 106, 152, 127, 187, 245, 246, 131, 176, 177, 228, 100, 112, 11, 84, 61, 193, 42, 41, 69, 229, 145, 254, 138, 3, 153, 123, 31]
[131, 92, 72, 47, 177, 57, 131, 118, 4, 38, 192, 19, 119, 82, 63, 143, 235, 165, 15, 140, 209, 223, 117, 133, 47, 148, 81, 144, 138, 246, 173, 235, 177, 181, 110, 39, 9, 192, 57, 166, 180, 153, 141, 19, 234, 157, 142, 80, 234, 197, 151, 152, 249, 143, 176, 155, 147, 17, 57, 194, 191, 254, 13, 144, 140, 85, 25, 248, 172, 208, 154, 249, 5, 201, 27, 137, 69, 23, 175, 34, 156, 72, 208, 32, 195, 16, 127, 65, 207, 131, 57, 203, 7, 98, 89, 36, 65, 75, 211, 21, 45, 132, 214, 239, 102, 58, 68, 130, 97, 204, 225, 76, 152, 216, 74, 149, 79, 165, 198, 72, 150, 94, 7, 177, 46, 226, 252, 247, 79, 62, 69, 106, 60, 21, 106, 236, 47, 145, 170, 28, 18, 101, 14, 152, 131, 7, 37, 15, 168, 99, 115, 27, 220, 150, 89, 82, 232, 170, 107, 221, 212, 46, 235, 129, 36, 66, 217, 222, 36, 15, 217, 192, 247, 192, 113, 230, 129, 196, 13, 247, 148, 228, 225, 86, 71, 133, 132, 238, 236, 127, 11, 83, 107, 141, 114, 150, 182, 146, 213, 250, 141, 53, 114, 16, 198, 70, 133, 17, 247, 173, 136, 73, 236, 78, 188, 150, 239, 58, 199, 136, 11, 122, 134, 77, 47, 167, 137, 188, 55, 195, 41, 49, 245, 92, 160, 213, 254, 0, 85, 205, 193, 69, 2, 140, 143, 155, 127, 236, 179, 199, 168, 35, 85, 40, 45, 174]
[198, 143, 247, 3, 152, 139, 131, 84, 181, 180, 252, 177, 192, 25, 217, 179, 136, 107, 190, 62, 4, 6, 90, 53, 105, 238, 117, 44, 5, 116, 132, 195, 214, 171, 113, 209, 18, 31, 194, 174, 228, 212, 196, 14, 27, 41, 211, 56, 139, 135, 225, 214, 89, 122, 178, 212, 185, 231, 204, 150, 204, 212, 160, 142, 213, 173, 186, 166, 65, 238, 5, 32, 45, 31, 25, 189, 148, 38, 78, 79, 33, 56, 227, 48, 103, 163, 31, 189, 37, 124, 106, 249, 86, 188, 86, 233, 41, 250, 89, 7, 212, 234, 111, 104, 245, 102, 227, 96, 160, 67, 181, 13, 26, 192, 214, 210, 188, 84, 216, 215, 243, 72, 233, 2, 122, 166, 107, 251, 70, 128, 94, 190, 185, 210, 34, 85, 77, 29, 182, 77, 115, 208, 228, 252, 73, 198, 151, 70, 10, 97, 138, 235, 21, 117, 239, 102, 129, 2, 253, 80, 53, 61, 184, 220, 41, 82, 37, 140, 23, 143, 179, 53, 153, 113, 213, 211, 111, 197, 248, 65, 60, 69, 1, 81, 48, 254, 251, 89, 195, 8, 93, 190, 66, 174, 97, 175, 210, 191, 66, 112, 123, 128, 33, 230, 237, 104, 16, 192, 239, 173, 44, 10, 120, 231, 114, 151, 140, 63, 103, 44, 243, 222, 242, 73, 51, 46, 98, 137, 163, 152, 147, 95, 223, 3, 15, 112, 85, 215, 133, 131, 240, 239, 224, 195, 140, 124, 70, 156, 221, 241, 37, 245, 1, 99, 9, 157, 99, 150, 47, 118, 225, 16, 13, 141, 135, 99, 18, 119, 63, 160, 6, 247, 27, 68, 45, 199, 86, 193, 252, 21, 135, 32, 42, 103, 114, 241, 49, 249, 182, 52, 18, 155, 157, 61, 4, 246, 158, 52, 118, 242, 195, 54, 139, 232, 100, 31, 11, 233, 58, 100, 101, 137, 83, 145, 209, 7, 241, 96, 57, 148, 207, 29, 237, 124, 177, 166, 161, 20, 116, 122, 61, 71, 46, 82, 18, 157, 253, 130, 112, 66, 94, 57, 221, 243, 222, 192, 147, 5, 130, 201, 174, 26, 160, 16, 188, 103, 187, 11, 238, 182, 144, 4, 137, 33, 84, 100, 7, 239, 219, 83, 112, 189, 166, 58, 93, 141, 30, 198, 220, 196, 118, 172, 5, 45]
"""

大概流程如下
x0作为初始值, 通过xi+1=txi(3xi)x_{i+1} = t*x_{i}*(3-x_{i})来产生序列, 其中t为系数
产生的序列作为密钥, 密钥用于加密列表, 但是密钥的每2bit组合决定使用哪个key进行加密
虽然代码中写的是pix = (~pix^key1)&0xff
但是(~pix^key1) == (pix^~key1), 那么可以理解为有两组key

  • key1 & ~key1
  • key2 & ~key2

用于测试的列表相异或即可得到两组key

  • 78 & 177
  • 86 & 169

现在我们需要知道这两组key如何对应key1, key2, ~key1, ~key2

一共存在8种情况
对于每种情况都进行测试, 还原出8种密钥, 再通过这8种密钥还原出8种序列
由于序列的产生满足xi+1=txi(3xi)x_{i+1} = t*x_{i}*(3-x_{i})的规律
那么xi+1xi(3xi)=t\frac{x_{i+1}}{x_{i}*(3-x_{i})} = t是成立的

那么可以通过使用每个序列来计算每次计算过程的系数t, 当t的值稳定在某一值附近时, 则可以确定t的值, 同时可以确定key1&key2以及正确的序列
经过计算得到

  • key1 = 169
  • key2 = 78
  • t = 1.2

那么还剩下一个x0
对于x0的计算, 我们完全可以爆破一个六位的小数, 使其迭代结果与正确的序列相匹配
(这里有一个小坑: 正确的序列使用乘以22000之后的值)
爆破结果为0.840264

那么通过这些条件, 再调用一次encrypt函数就可以获得flag

EOF