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 imageBindef departBin(self, bin_data):#初始化一个空列表以存储分割后的二进制数据块imageBinList=[]#获取输入的二进制数据的长度。data_length=len(bin_data)#使用循环来将二进制数据分割为64位的块,直到数据长度小于64while data_length>=64: imageBinList.append(bin_data[:64])data_length-=64bin_data=bin_data[64:]# 填充字节大小为8。if data_length == 0:# 创建一个由8位填充字节组成的填充数据padding_byte_size=8padding_data=('{:08b}'.format(padding_byte_size))*padding_byte_sizeimageBinList.append(padding_data)#对于剩余数据长度小于64的情况else:#计算需要填充的长度padding_length = 64 - data_length# 计算填充字节的大小。padding_byte_size = (padding_length)//8padding_data = ('{:08b}'.format(padding_byte_size))*padding_byte_sizeimageBinList.append(bin_data + padding_data) return imageBinListdef 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 Imageclass Convert:def encrypt_image(self, image_path, key):#打开图片地址image = Image.open(image_path)pixels = image.tobytes()#生成一个加密的keycipher = 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_imagedef 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.sizerandom_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值的二进制字符串imageBinbin_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_datadef restore_image_from_bin(image_bin, width, height):image = Image.new('RGB', (width, height))pixels = []# 从二进制串中获取RGB值并按列放置到二维像素列表中index = 0for 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 += 24pixels.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()