bitsctf-2017-beginners-luck

k e y w o r d s : keywords: keywords: bitsctf-2017-beginners-luck文件头格式异或

P r o b l e m Problem Problem

#!/usr/bin/env python

def supa_encryption(s1, s2):
    res = [chr(0)]*24
    for i in range(len(res)):
        q = ord(s1[i])
        d = ord(s2[i])
        k = q ^ d
        res[i] = chr(k)
    res = ''.join(res)
    return res

def add_pad(msg):
    L = 24 - len(msg)%24
    msg += chr(L)*L
    return msg

with open('fullhd.png','rb') as f:
    data = f.read()

data = add_pad(data)


with open('key.txt') as f:
    key = f.read()
    
enc_data = ''
for i in range(0, len(data), 24):
    enc = supa_encryption(data[i:i+24], key)
    enc_data += enc

with open('BITSCTFfullhd.png', 'wb') as f:
    f.write(enc_data)

附件有BITSCTFfullhd.png

A n a l y s i s Analysis Analysis

主要加密函数supa_encryption(s1,s2),其作用就是将长度均为 24 24 24s1s2相异或

在实际加密过程中是将原图像fullhd.png的十六进制数据分成以 24 24 24字节为一组的明文块与key进行异或运算,当然key是未知的;我们这里很显然就是要想办法求key

当然图像原数据长度很可能不是 24 24 24的倍数(这样就导致最后一组的数据并不是 24 24 24,在某些封装的异或函数中是不能执行的),加密脚本中提供了一个填充函数add_pad,其实现的作用就是将不足 24 24 24位的明文块使用chr(24 - (len(message) % 24))这样同一个字符填充至 24 24 24

所以填充的字符ASCII码一定在 0 0 0 24 24 24之间,更重要的是很可能重复多次

由于异或的性质,密文我们已知,key未知并且是需要求解的,那么我们可以试着寻找明文

由于明文是图像png原数据的十六进制,所以可以找文件头格式(这是每个文件类型特有的一段十六进制数据)

PNG (png),    文件头:89 50 4E 47              文件尾:AE 42 60 82

实际上不止是这么点长度(我们当然是希望明文的长度越长越好了,直接达到 24 24 24位是最好,可以直接异或出key,但是本题没有),自己创建一个png图像,观察到前 18 18 18位十六进制固定

89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00

因此可以推算出key的前 18 18 18

f = open("BITSCTFfullhd.png","rb")
ori_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00'
enc_data = f.read()
f.close()
# print(xor(ori_data,enc_data[:18]))
# b'rkh%QP4g0&3g46@4*%'
part_key = xor(ori_data,enc_data[:18])

剩余 6 6 6key未知,起初想的是直接爆破明文,查看爆破结果是否为可见字符(因为密钥前部分是可见字符,所以猜测之后的部分也是可见字符);但是需要时间显然太长了,并且不好判断哪些可见字符结果是真实结果

又想到了填充的字符相同

那么由于最后一组明文块很大可能进行了填充,所以文件尾在密文中的实际的位置不定,但是最后一组密文块(一定是 24 24 24位长的)可以先用已知的部分key进行解密得到填充使用的字节

last_enc_data = "\x36\xC5\x2A\x45\xD3\x43\x27\x74\x23\x35\x20\x74\x27\x25\x53\x27\x39\x36\x75\x3B\x46\x5D\x30\x4F"
print(xor(last_enc_data[:len(part_key)],part_key))
# b'D\xaeB`\x82\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13\x13'

显然填充的字符应该是\x13,所以使用该字节与剩余的密文部分进行异或运算得到剩余的key

temp = "\x13" * 6
print(xor(last_enc_data[len(part_key):],temp))
key = part_key + xor(last_enc_data[len(part_key):],temp)

key与整个密文进行异或导出为png文件即可

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

from pwn import *

f = open("C:\\Users\\Menglin\\Desktop\\beginners-luck-40\\BITSCTFfullhd.png","rb")
ori_data = b'\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00'
enc_data = f.read()
f.close()
# print(xor(ori_data,enc_data[:18]))
part_key = xor(ori_data,enc_data[:18])
last_enc_data = "\x36\xC5\x2A\x45\xD3\x43\x27\x74\x23\x35\x20\x74\x27\x25\x53\x27\x39\x36\x75\x3B\x46\x5D\x30\x4F"
# print(xor(last_enc_data[:len(part_key)],part_key))
temp = "\x13" * 6
# print(xor(last_enc_data[len(part_key):],temp))
key = part_key + xor(last_enc_data[len(part_key):],temp)
ori_data = xor(enc_data,key*(len(enc_data)//len(key)))
f = open("C:\\Users\\Menglin\\Desktop\\output.png","wb")
f.write(ori_data)
Logo

CSDN联合极客时间,共同打造面向开发者的精品内容学习社区,助力成长!

更多推荐