NSSCTF Round18 Crypto年画复现

年画

题目:

from secret import key
flag = b'NSSCTF{******FAKE*********}' 
enc_c = b''
for i in range(len(key)):
	enc_c += (key[i]^(flag[i%7])).to_bytes(1,'big')
print(f'enc_c={enc_c}')

from PIL import Image
class Depart:
    def get_pixel(self, image):
        #打开图片
        image = Image.open(image)
        imageRGB = image.convert('RGB')
        #获取图片宽
        width=image.size[0]
        #获取图片高
        height=image.size[1]	
        imageBin=''
        for i in range(width):	
            for j in range(height):
                #遍历对应宽高的
                r, g, b = imageRGB.getpixel((i, j))
                # 将红色通道的值按8位二进制格式添加到imageBin字符串中。
                imageBin+='{:08b}'.format(r)
                # 将绿色通道的值按8位二进制格式添加到imageBin字符串中。
                imageBin+='{:08b}'.format(g)
                # 将蓝色通道的值按8位二进制格式添加到imageBin字符串中。
                imageBin+='{:08b}'.format(b)
        return imageBin
    
    def departBin(self, bin_data):
        #初始化一个空列表以存储分割后的二进制数据块
        imageBinList=[]
        #获取输入的二进制数据的长度。
        data_length=len(bin_data)
        #使用循环来将二进制数据分割为64位的块,直到数据长度小于64
        while data_length>=64:		
            imageBinList.append(bin_data[:64])
            data_length-=64
            bin_data=bin_data[64:]
        # 填充字节大小为8。
        if data_length == 0:
            # 创建一个由8位填充字节组成的填充数据
            padding_byte_size=8
            padding_data=('{:08b}'.format(padding_byte_size))*padding_byte_size
            imageBinList.append(padding_data)

        #对于剩余数据长度小于64的情况
        else:
            #计算需要填充的长度
            padding_length = 64 - data_length
            # 计算填充字节的大小。
            padding_byte_size = (padding_length)//8
            padding_data = ('{:08b}'.format(padding_byte_size))*padding_byte_size
            imageBinList.append(bin_data + padding_data)	
        return imageBinList
        
    def make_cipher(self, imageBinList):
        f=open('cipher.txt','w')
        for i in imageBinList:
            f.write(i+'\n')
        f.close()

from Crypto.Cipher import AES
from Crypto.Util.Padding import *
from PIL import Image

class Convert:
    def encrypt_image(self, image_path, key):
        #打开图片地址
        image = Image.open(image_path)
        pixels = image.tobytes()
        #生成一个加密的key
        cipher = AES.new(key, AES.MODE_ECB)
        #对像素数据进行加密,使用AES密码对象加密并填充数据块。
        encrypted_pixels = cipher.encrypt(pad(pixels, AES.block_size))
        #将加密后的像素数据重新构造为图像对象。
        encrypted_image = Image.frombytes(image.mode, image.size, encrypted_pixels)
        return encrypted_image
    def run(self):
        image_path = "flag.png"
        encrypted_image = self.encrypt_image(image_path, key)
        encrypted_image.save("cipher.png")

import random
class Same:
    def generate_random_image(self, image_path):
        cipher_image = Image.open(image_path)
        width, height = cipher_image.size
        random_image = Image.new("RGB", (width, height))
        random_pixels = random_image.load()
        for x in range(width):  
            for y in range(height):  
                red = random.randint(0, 255)
                green = random.randint(0, 255)
                blue = random.randint(0, 255)
                random_pixels[x, y] = (red, green, blue)
        random_image.save("random_image.png")

def drawNewYearPicture():
    convert = Convert()
    convert.run()

    depart = Depart()
    image_dir = "cipher.png"
    #获得图片的对应每个像素的RGB值的二进制字符串imageBin
    bin_data = depart.get_pixel(image_dir)
    imageBinList = depart.departBin(bin_data)
    depart.make_cipher(imageBinList)

    getRandom = Same()
    getRandom.generate_random_image(image_dir)

if __name__ == '__main__':
	drawNewYearPicture()

# enc_c=b'&2#3-(\x1e9*6"&$\x02=&'

代码审计

1.通过aes对原始图片加密-不够64位的填充
2.读取加密后图片的rgb通道值-存储在txt文件中
3.用原始图片的宽高生成新的随机图片

解题思路

1.key通过给出的enc提示对位异或还原出来
2.读取随机图片的宽高,读取txt文件还原出rgb值,填充到新图片中
3.key解密新图片 getflag

exp


def restore_bin_data(file_path):
    with open(file_path, 'r') as f:
        bin_data = f.read().replace('\n', '')  # 将所有行连接起来,并移除可能的换行符
    return bin_data


def restore_image_from_bin(image_bin, width, height):
    image = Image.new('RGB', (width, height))
    pixels = []

    # 从二进制串中获取RGB值并按列放置到二维像素列表中
    index = 0
    for i in range(width):
        #空列
        column_pixels = []
        for j in range(height):
            #提取的红色通道的值
            r = int(image_bin[index:index + 8], 2)
            #提取的红色通道的值
            g = int(image_bin[index + 8:index + 16], 2)
            #提取红色通道的值
            b = int(image_bin[index + 16:index + 24], 2)
            #将提取到的RGB值作为元组(r, g, b)添加到列的像素列表column_pixels中
            column_pixels.append((r, g, b))
            index += 24
        pixels.append(column_pixels)

    # 将二维像素列表放入图像中(按列放置)
    for i in range(width):
        for j in range(height):
            image.putpixel((i, j), pixels[i][j])

    return image
#计算key
enc_c=b'&2#3-(\x1e9*6"&$\x02=&'
key=b''
flag = b'NSSCTF{******FAKE*********}'
for i in range(len(enc_c)):
	key += (enc_c[i]^(flag[i%7])).to_bytes(1,'big')
print(key)
from PIL import Image

#计算图片宽高
image_dir = "random_image.png"
im=Image.open(image_dir)
width =im.size[0]
height =im.size[1]

# 从 "cipher.txt" 文件中恢复二进制数据
bin_data_restored = restore_bin_data('cipher.txt')
# 从二进制数据中恢复图像
restored_image = restore_image_from_bin(bin_data_restored, width, height)
restored_image.save("1.png")
image_path="1.png"
#key解密图片
from Crypto.Util.Padding import *
from Crypto.Cipher import AES
image = Image.open(image_path)
pixels = image.tobytes()
cipher = AES.new(key, AES.MODE_ECB)
#aes解码
encrypted_pixels = cipher.decrypt(pad(pixels, AES.block_size))
encrypted_image = Image.frombytes(image.mode, image.size, encrypted_pixels)
encrypted_image.show()

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值