slove
核心是使用明文字符串自身的元素生成一个“伪随机字符串”与自身异或生成密文。
伪随机字符串的生成规则由i + space < len(plainText) - 1判断
ord(plainText[i + space]),ord(plainText[space])的space的移动由明文对应字符的ascii字符奇偶性决定。
明文长度28字节。
爆破2^28,应至少存在一个bit序列等于当前密文的每字节最后1bit组成的序列。
知道这个bit序列后就知道了space的移动情况,进而可以获得“伪随机字符串”是明文的哪些项的置换。
根据flag的固定格式afctf可以提前获得某些明文部分。经测试至少知道前2个字符即可解密
def encrypt(plainText):
space = 10
cipherText = ""
try:
for i in range(len(plainText)):
if i + space < len(plainText) - 1:
cipherText += chr(ord(plainText[i]) ^ ord(plainText[i + space]))
else:
cipherText += chr(ord(plainText[i]) ^ ord(plainText[space]))
if ord(plainText[i]) % 2 == 0:
space += 1
else:
space -= 1
except:
return '1'
return cipherText
def brute():
for x in range(2**28):
print(x)
last_bit=bin(x)[2:].zfill(28)
assert len(last_bit)==28
tostr=''
for each in last_bit:
tostr+=chr(int(each,2))
tmp=encrypt(tostr)
tmp2=''
for each in tmp:
tmp2+=bin(ord(each))[-1]
if tmp2=='1010000101101011111111101000':
print('found',last_bit)
cipherText = "15120d1a0a0810010a031d3e31000d1d170d173b0d173b0c07060206"
tmp=''
for each in bytes.fromhex(cipherText):
tmp+=bin(each)[-1]
print(tmp) #1010000101101011111111101000
brute()
# found 1010011010010101111111101001
part2
# 10,9,10,9,10,11,10,9,10,9,10,11,10,11,10,11,10, 9, 8, 7, 6, 5, 4, 3, 4, 3, 4, 5 sapce
# 0 ,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 index
# 10,10,12,12,14,16,16,16,18,18,20,22,22,24,24,26,26,26,26,26,26,26,26,26,4,3,4,5, real index
# b'\x15\x12\r\x1a\n\x08\x10\x01\n\x03\x1d>1\x00\r\x1d\x17\r\x17;\r\x17;\x0c\x07\x06\x02\x06'
cipherText = "15120d1a0a0810010a031d3e31000d1d170d173b0d173b0c07060206"
print(bytes.fromhex(cipherText))
cipher=bytes.fromhex(cipherText)
space=[10,9,10,9,10,11,10,9,10,9,10,11,10,11,10,11,10, 9, 8, 7, 6, 5, 4, 3, 4, 3, 4, 5 ]
pseudo_array=[10,10,12,12,14,16,16,16,18,18,20,22,22,24,24,26,26,26,26,26,26,26,26,26,4,3,4,5,]
origin=[0 ,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]
for a,b in zip(pseudo_array,origin):
print('%2d'%a,end=',')
print()
for a,b in zip(pseudo_array,origin):
print('%2d'%b,end=',')
w=['ww']*28
know='afctf'
w[:len(know)]=know
# print(w,len(w))
while 'ww' in w:
for p,o in zip(pseudo_array,origin):
if w[o] !='ww':
w[p]=chr(ord(w[o])^cipher[o])
if w[p] !='ww':
w[o]=chr(ord(w[p])^cipher[o])
print(''.join(w))
# afctf{cryptanalysis_is_hard}