当前位置: 代码迷 >> 综合 >> BUUCTF 打卡 21/9/1 Many-Time-Pad
  详细解决方案

BUUCTF 打卡 21/9/1 Many-Time-Pad

热度:105   发布时间:2023-11-22 18:36:36.0

参考博客:[Pion1eer_blog](Many-Time-Pad 攻击 (ruanx.net))

因为最近遇到一道一次一密的题目,故把所学的记下。

Many-Time-Pad攻击:

本文讨论的情况是利用简单异或实现的加密,且是一个密钥进行多轮加密。

假设加密的明文分别为M1 , M2 , … , Mi ,对应密文C1 , C2 , … , C3 , 密钥为K。

加密过程为 Ci = Mi ^ K。

根据异或的性质(相同为0,不同为1),我们可以将任意两个密文进行异或,将会得到不含有密钥的式子:

Ci ^ C j = (Mi ^ K) ^ (Mj ^ K) = Mi ^ Mj

也就是说,将两个密文进行异或,就会得到相对应的明文进行异或。

先将第一个密文与其他密文进行异或。

c = [eval('0x'+ x.strip()) for x in open('Problem.txt', 'r').readlines()]
print(c)
m1 = c[0]
for i in range(1,len(c)):temp = hex(m1 ^ c[i])[2:]for i in range(0,len(temp),2):pd = chr(eval('0x'+temp[i:i+2]))if pd.isalpha():#判断是否为字母print(pd,end = '')else:print('.',end= '')print()

得到如下结果(只将字母显示出来):

....S....N.U.....A..M.N..
...Ro..I...I....SE....P.I
.E..H...IN..H...........T
..A.H.R.....E....P......E
...RT...E...M....M....A.L
d...V..I..DNEt........K.D
.......I....K..I.ST...TiS
.....f...N.I........M.O..
.........N.I...I.S.I..I..
....K.......o...QA...A...

有的一列有很多字母,有的一列没有字母。不觉得奇怪吗?当然,现在应该是看不出来的。

ASCII码:

- 空格 :'0x20' 						二进制: 0010 0000
- 大写字母A~Z : '0x41'~'0x5A'        二进制: 010? ????
- 小写字母a~z : '0x61'~'0x7A'        二进制: 011? ????

这下可以发现当大写字母与空格进行异或时,会转换成小写字母;小写字母与空格进行异或,会转换成大写字母。如果x与y做异或得到的时字母,则极有可能x,y之中一个是空格,一个是被大小写化的明文。再看C1和其他密文进行以获得结果,如果有一列出现大量的字母,那么C1中对应的位置极有可能是空格。因为另一个与之进行异或的密文的对应位置只是进行了字母大小,所以只需要再跟空格进行异或就可以得到明文,即

Ci[t] ^ Cj[t] ^ Ci[t] = Mi[t] ^ Mj[t] ^Mi[t] = Mj[t]

于是我们可以根据某个字符串的某一位是空格,就可以推出这一列的明文。

对于每一行密文,与剩余密文进行异或,根据每一列出现的字母的个数,作为该密文中对应位置为空格的评分。对评分进行从大到小排序,依次进行上述解密,如果出现冲突,舍弃最小评分的。

历经千辛万苦终于得到代码了(原来小丑是我?):

import Crypto.Util.strxor as xo
import numpy as np
from Crypto.Util.number import *def ischr(x):#因为从字节串中去出的是对应的ascii码值,所以进行数字比较if x>=ord('a') and x<=ord('z'):return Trueif x>=ord('A') and x<=ord('Z'):return Trueelse:return Falsedef trans(x,y):if msg[x,y] != 0:returnmsg[x,y] = ord(' ')for index in range(len(c)):if index != x:msg[index,y] = xo.strxor(c[x], c[index])[y] ^ ord(' ')c = [long_to_bytes(eval('0x'+x.strip())) for x in open('Problem.txt', 'r').readlines()]number = []
for i in range(len(c)):xor_c = [xo.strxor(c[i],c[j]) for j in range(len(c)) if j != i]#strxor:两个字节串进行异或for pos in range(len(c[i])):num = len([ci for ci in xor_c if ischr(ci[pos])])number.append([num,i,pos])#[个数,行,列]number = sorted(number,reverse = True)msg = np.zeros([len(c), len(c[0])], dtype=int)#创建一个全为零的数组,用于填充明文for i in number:trans(i[1],i[2])flag = ''
for i in msg:for j in i:flag += chr(j)flag += '\n'
print(flag)

下载Problem.txt有点问题,所以用了Pion1eer博客中的数据,结果如下:

Dear Friend, T%is tim< I u
nderstood my m$stake 8nd u
sed One time p,d encr ptio
n scheme, I he,rd tha- it 
is the only en.ryptio7 met
hod that is ma9hemati:allyproven to be #ot cra:ked 
ever if the ke4 is ke)t se
cure, Let Me k#ow if  ou a
gree with me t" use t1is e
ncryption sche e alwa s...

这肯定是要修改的,应该可以手动修改出某行得到正确的。

这里就写代码来修改吧。???

T%is 应该是 This , tim< 应该是 time

所以有如下代码:

def know(s,x,y):msg[x,y] = ord(s)for index in range(len(c)):if index != x:msg[index,y] = xo.strxor(c[x], c[index])[y] ^ ord(s)know('h',0,14)
know('e',0,21)

更正后的结果:

Dear Friend, This time I u
nderstood my mistake and u
sed One time pad encryptio
n scheme, I heard that it 
is the only encryption met
hod that is mathematicallyproven to be not cracked 
ever if the key is kept se
cure, Let Me know if you a
gree with me to use this e
ncryption scheme always...

如此一来,我们就知道了明文和密文,而密文 = 明文 ^密钥,所以密钥 = 明文 ^密文。

我们只要随便取一行就可以求出密钥了。

ming = 'Dear Friend, This time I '.encode()#删去最后一个,不然不等长
mi = b'%\x03\x02\x06F==911U_\x7f\x1d\x06\x1d@R\x11\x1a\x19TN.]'
#print(len(ming),len(mi))
flag = xo.strxor(ming,mi)
print(flag)

得到flag : b’afctf{OPT_1s_Int3rest1ng}’

总结:
1.利用异或的性质来解题,密文之间的异或就等同于明文之间的异或,可以添加相同项从而剩下一项来解决问题。
2.对python的使用并不熟悉,还需多花点时间在上面。

  相关解决方案