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

参考博客:[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 True
    if x>=ord('A') and x<=ord('Z'):
        return True
    else:
        return False

def trans(x,y):
    if msg[x,y] != 0:
        return
    msg[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:ally
 proven 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 mathematically
 proven 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的使用并不熟悉,还需多花点时间在上面。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值