hctf-2018_Crypto_xor-game

k e y w o r d s : keywords: keywords: 分组逐个爆破密钥字符

D e s c r i p t i o n Description Description

from Crypto.Util.strxor import strxor
import base64
import random

def enc(data, key):
    key = (key * (len(data) / len(key) + 1))[:len(data)]
    return strxor(data, key)


poem = open('poem.txt', 'r').read()
flag = "hctf{xxxxxxxxxxx}"

with open('cipher.txt', 'w') as f:
    f.write(base64.b64encode(enc(poem, flag[5:-1])))
    f.close()

这是一首英文诗,但它是加密的。找到flag,并恢复它

A n a l y s i s Analysis Analysis

加密代码很短,实际上实现的就是正常的异或过程

有点类似于 维吉尼亚密码,只不过这里是异或加密而不是双表替换密码

还有一点是这里密钥长度实际上是未知的(刚开始以为是x的个数

根据提示,原文是一首英文诗,那么意味着明文空间是

import string
table = string.ascii_letters + "-,.?!:;\"'\n "

那么可以通过猜测在明文中的字符来与密文异或,这样就可以得到key,也就是flag

为了提高猜测的准确性,我们这里使用分组爆破

另外由于flag的长度未知,借用攻击维吉尼亚密码的获悉密钥长度的方法(kasiski test

我们先假设flag的长度为 10 10 10,那么在密文中以 10 10 10个字符为一组进行划分

将明文空间中的字符分别与第一组的第i位字符(实际上异或出来的结果也就是flag的第i位结果),第二组的第i位字符进行异或,如果两者异或的结果相同,那么很有可能这个结果就是flag的第i

def getkey(i,j):
    ls = []
    for a in table:
        for b in table:
            if ord(a) ^ enc[i] == ord(b) ^ enc[j]:
                ls.append(chr(ord(a) ^ enc[i]))
    return ls

这样做的原因是明文(这里以及之后提到的明文都是指英文诗)中的每一组字符与等长的flag(假设我们猜对了flag长度)进行异或得到的密文,而密文与明文中所有可能的字符(也就是明文空间)进行异或,并且不同分组的密文很可能是需要异或不同的明文空间里的字符,才能得到相同的结果(因为本来明文中的字符就很可能不同,这样就摆脱了kasiski试验的局限性,后者必须要有密文前后存在相同的密文段),这个结果很可能是flag

但不能确保这里得到的结果一定是flag,因为可能还是有巧合存在,所以我们把结果都统计下来,出现频率最高的字符即是flag

剩下的就只是爆破密钥长度了

S o l v i n g   c o d e Solving~code Solving code

import base64,string
from Crypto.Util.strxor import strxor
import string
from tqdm import tqdm
import collections

table = string.ascii_letters + "-,.?!\n " # 明文空间这里缩小了一点是为了程序运行稍微快一点
f = open("cipher.txt","r")
temp = f.read()
f.close()
# print(list(base64.b64decode(temp)))
enc = list(base64.b64decode(temp))
# print(enc)
def getkey(i,j):
    ls = []
    for a in table:
        for b in table:
            if ord(a) ^ enc[i] == ord(b) ^ enc[j]:
                ls.append(chr(ord(a) ^ enc[i]))
    return ls
for key_length in tqdm(range(1,30)):
    key = ""
    group_nums = len(enc) // key_length - 1 # 分组
    # print(group_nums)
    for i in range(key_length):
        ls = list(getkey(i,key_length + i))
        # print(ls)
        for j in range(1,group_nums):
            ls = ls + list(getkey(j * key_length + i,(j + 1) * key_length + i)) #相当于取并集
        key += "".join(collections.Counter(ls).most_common(1)[0][0])
    # if key_length == 21:
    print(key)
    # or_is_interesting!@#

# 检验得到的flag,再进行一次异或,如果得到的结果是英文诗,那么即是正确的flag
txt = list(base64.b64decode(temp))
def enc(data, key):
    key = (key * (len(data) // len(key) + 1))[:len(data)]
    return strxor(data, key)
flag = "xor_is_interesting!@#"
plaint = enc("".join(list(map(chr,txt))).encode(),flag.encode())
f = open("plaint.txt","a+")
f.write(bytes.decode(plaint))

R e f e r e n c e Reference Reference

2018 HCTF 总结 | gml’s blog (igml.top)

这篇文章使用的是取交集(至少我没有跑出来,取交集会导致最后没有满足的结果)所以需要先取并集,然后取出现频率最高的字符

O t h e r s Others Others

我这里的思路是爆破英文诗从而异或得到flag

其他的思路还可以先通过类似kasiski试验来得到flag的确切长度,然后爆破flag,通过判断结果是否为正常的英文单词(英文诗的一部分),然后通过搜索引擎搜索整个英文诗,来与密文异或得到key

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M3ng@L

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值