XCTF-L3HCTF2021 DeepDarkFantasy write up

XCTF-L3HCTF2021 DeepDarkFantasy write up

DeepDarkFantasy
XCTF-L3HCTF2021 DeepDarkFantasy write up_第1张图片
XCTF-L3HCTF2021 DeepDarkFantasy write up_第2张图片
题目描述和hint如下
XCTF-L3HCTF2021 DeepDarkFantasy write up_第3张图片
原始附件如下:
链接:https://pan.baidu.com/s/1Rs1A6viKUXuNvxExAvtvSQ
提取码:lqln

下载后得到一个pth文件,根据第一个提示,其被以简单异或的方式加密,遍历得到解密比特为0xde,此时可以发现PK头
解密后的文件如下:
链接:https://pan.baidu.com/s/1fTiS7DzQP_Rpv_fkB5CtbQ
提取码:7zel

由于找不到pytorch保存模型字典对象的方便的解析脚本,使用pycharm进行动态调试的方法对模型结构进行推测
XCTF-L3HCTF2021 DeepDarkFantasy write up_第4张图片
这个报错是由于保存路径时文件名为model导致的,但我默认为main,修改文件名(这个坑了我俩小时)
XCTF-L3HCTF2021 DeepDarkFantasy write up_第5张图片

提示需要一个MyAutoEncoder类,我们直接将myclass名字改为MyAutoEncoder

同样的,提示需要Encoder和Decoder类,一样给加上
XCTF-L3HCTF2021 DeepDarkFantasy write up_第6张图片
XCTF-L3HCTF2021 DeepDarkFantasy write up_第7张图片
当然我们也可以使用Netron这个软件来发现需要这两个类
XCTF-L3HCTF2021 DeepDarkFantasy write up_第8张图片
补上Decoder和Encoder后,代码如下:

import torch
from torch.nn import *

class Encoder(Module):
    def __init__(self):
        super().__init__()
        pass

class Decoder(Module):
    def __init__(self):
        super().__init__()
        pass

class MyAutoEncoder(Module):
    def __init__(self):
        super().__init__()
        pass

path='./decrypted.pth'
mymodel=MyAutoEncoder()
mymodel.load_state_dict(torch.load(path,map_location=torch.device('cpu')))

此时的报错信息变成了
XCTF-L3HCTF2021 DeepDarkFantasy write up_第9张图片
提示我们存在俩多出来的键,自然想到去看一看state_dict和model中的内容,这时我们利用pycharm的debug功能单步运行
XCTF-L3HCTF2021 DeepDarkFantasy write up_第10张图片
这样我们就利用pytorch自带的解析器得到了整个网络的结构和所有参数,然后我们使用控制台将这些信息dump出来看一看细节
XCTF-L3HCTF2021 DeepDarkFantasy write up_第11张图片
网络结构如下:
XCTF-L3HCTF2021 DeepDarkFantasy write up_第12张图片
我们根据其网络结构构建需要的Autoencoder,并从题目给的解密后的文件中读取参数

import torch
from torch.nn import *
from torchvision import transforms

class Encoder(Module):
    def __init__(self):
        super().__init__()
        self.conv = Sequential(
            Conv2d(1, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            ReLU(),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            Conv2d(16, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            ReLU(),
            Conv2d(8, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            MaxPool2d(kernel_size=2, stride=2, padding=1, dilation=1, ceil_mode=False),
            ReLU(),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            Flatten(start_dim=1, end_dim=-1)
        )
        self.fc=Linear(in_features=32, out_features=16, bias=True)

    def forward(self, x):
        x=self.conv(x)
        x=self.fc(x)
        return x

class Decoder(Module):
    def __init__(self):
        super().__init__()
        self.convt=Sequential(
            ConvTranspose2d(1, 256, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(256, 256, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(256, 512, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(512, 128, kernel_size=(4, 4), stride=(4, 4)),
            ReLU(),
            ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(4, 4)),
            ReLU(),
            ConvTranspose2d(64, 32, kernel_size=(2, 2), stride=(2, 2)),
            ReLU(),
            ConvTranspose2d(32, 1, kernel_size=(2, 2), stride=(2, 2)),
            Sigmoid()
        )

    def forward(self, x):
        x = self.convt(x)
        return x

class MyAutoEncoder(Module):
    def __init__(self):
        super().__init__()
        self.encoder=Encoder()
        self.decoder=Decoder()

    def forward(self, x):
        x=self.encoder.forward(x)
        x=self.decoder.forward(x)
        return x

path='./decrypted.pth'

mymodel=MyAutoEncoder()
mymodel.load_state_dict(torch.load(path,map_location=torch.device('cpu'))['state_dict'])

接下来让我们简单讲讲Autoencoder做了啥,参考知乎链接
https://zhuanlan.zhihu.com/p/123810703
简单来说,就是俩类似CNN的结构反着放,左边的是编码器,输入是图片输出是特征向量,用于提取能简洁地表示图片特征的向量。右边的是解码器,输入特征向量能生成图片。自动编码器可以进行降维,图像压缩,图像降噪,图像生成,特征提取等操作。本题中利用了其生成的图像和输入的图像相差无几的功能,通过控制中间的d的特征(如下图),还原出flag的不同部分
XCTF-L3HCTF2021 DeepDarkFantasy write up_第13张图片
这题的出题思路估计是这样的:出题人把一张含有flag的图片分成很多份,之后padding后送入Autoencoder进行训练,这样使得解码器具有在给定特定特征的情况下生成出flag残片的功能,因此我们可以控制d的值来控制输出。

尝试生成图片结果如下:

可以看出生成的图片明显分成有分界线,尝试后找到输入tensor形状为[1,1,1,1]时恰好能生成一块残片,编写代码遍历d

mymodel.eval()
num=-40.0
for i in range(7000):
    print(i,' ',i*0.01,' ',num)
    mylist=[[[[num+0.01*i]]]]
    print(mylist)
    data=torch.FloatTensor(mylist)
    #data = torch.randn(1, 1, 16, 16)
    pics = mymodel.forward(data)[0]
    # print(pics.shape)
    toPIL = transforms.ToPILImage()
    pic = toPIL(pics[0])
    pic.save('./pic/random'+str(i)+'.jpg')

得到所有的flag片段,多次尝试后拼接得到正确结果为

L3HCTF{blackbinarysencoder0xb1a876a}

XCTF-L3HCTF2021 DeepDarkFantasy write up_第14张图片
图片压缩包如下:
链接:https://pan.baidu.com/s/1B1jnNzvpi9P4_gChlh5ZXQ
提取码:bqfy

完整代码如下:

import torch
from torch.nn import *
from torchvision import transforms

class Encoder(Module):
    def __init__(self):
        super().__init__()
        self.conv = Sequential(
            Conv2d(1, 16, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            ReLU(),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            Conv2d(16, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            ReLU(),
            Conv2d(8, 8, kernel_size=(3, 3), stride=(2, 2), padding=(1, 1)),
            MaxPool2d(kernel_size=2, stride=2, padding=1, dilation=1, ceil_mode=False),
            ReLU(),
            MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False),
            Flatten(start_dim=1, end_dim=-1)
        )
        self.fc=Linear(in_features=32, out_features=16, bias=True)

    def forward(self, x):
        x=self.conv(x)
        x=self.fc(x)
        return x

class Decoder(Module):
    def __init__(self):
        super().__init__()
        self.convt=Sequential(
            ConvTranspose2d(1, 256, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(256, 256, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(256, 512, kernel_size=(1, 1), stride=(1, 1)),
            ReLU(),
            ConvTranspose2d(512, 128, kernel_size=(4, 4), stride=(4, 4)),
            ReLU(),
            ConvTranspose2d(128, 64, kernel_size=(4, 4), stride=(4, 4)),
            ReLU(),
            ConvTranspose2d(64, 32, kernel_size=(2, 2), stride=(2, 2)),
            ReLU(),
            ConvTranspose2d(32, 1, kernel_size=(2, 2), stride=(2, 2)),
            Sigmoid()
        )

    def forward(self, x):
        x = self.convt(x)
        return x

class MyAutoEncoder(Module):
    def __init__(self):
        super().__init__()
        self.encoder=Encoder()
        self.decoder=Decoder()

    def forward(self, x):
        #x=self.encoder.forward(x)
        x=self.decoder.forward(x)
        return x

path='./decrypted.pth'

mymodel=MyAutoEncoder()
mymodel.load_state_dict(torch.load(path,map_location=torch.device('cpu'))['state_dict'])
mymodel.eval()
num=-40.0
for i in range(7000):
    mylist=[[[[num+0.01*i]]]]
    data=torch.FloatTensor(mylist)
    pics = mymodel.forward(data)[0]
    toPIL = transforms.ToPILImage()
    pic = toPIL(pics[0])
    pic.save('./pic/random'+str(i)+'.jpg')

你可能感兴趣的:(网络安全)