跳至主要內容

Scratch cat lost in the MAZE

Haynes原创...大约 5 分钟CTFMiscHKCert23

千層地獄?我選擇千層迷宮。

網頁: https://scratch.mit.edu/projects/919957145/open in new window

0x01 分析

同样是来到源码部分,看到每局都是从绿点走到红点,走过的路径是 seq ,在完成一张地图后通过计算上下左右的次数,计算出一个数放进 result
function-moved
使用 result 计算出 flag
function-flag

0x02 Exploit

根据以上信息,我们需要通过得到每一张地图的路径 seq
这里使用了 dfs 算法,通过递归的方式得到每一张地图的路径
然后计算出 result ,最后通过 result 计算出 flag
通过下载源码得到所有的地图

1. 裁切图片

附件中的图片并不是正方形,我们从过 Python 批量裁切多余部分

from PIL import Image
import os

def crop_images(input_folder, output_folder):
    # 确保输出文件夹存在
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 处理输入文件夹中的每张图像
    for filename in os.listdir(input_folder):
        input_path = os.path.join(input_folder, filename)
        output_path = os.path.join(output_folder, filename)

        # 打开图像
        img = Image.open(input_path)

        # 裁剪图像从右往左至720*720
        cropped_img = img.crop((0, 0, 720, 720))

        # 保存裁剪后的图像
        cropped_img.save(output_path)

if __name__ == "__main__":
    input_folder = "maps"  # 输入图像文件夹
    output_folder = "maps-cut"  # 输出图像文件夹

    crop_images(input_folder, output_folder)

2. 图片像素化

为了更方便的将图片转换为路径,我们需要把图片像素话后,把像素点转成数组

from PIL import Image
import os

def pixelate_image(image, pixel_size):
    # 获取图像的宽度和高度
    width, height = image.size

    # 计算图像中每个块的像素值
    for y in range(0, height, pixel_size):
        for x in range(0, width, pixel_size):
            box = (x, y, x + pixel_size, y + pixel_size)
            region = image.crop(box)
            average_color = tuple(int(sum(channel) / len(channel)) for channel in zip(*region.getdata()))

            # 将块中的所有像素设置为块的平均颜色
            image.paste(average_color, box)
    image = image.convert('RGB')

    return image

def process_images(input_folder, output_folder, pixel_size=16):
    # 确保输出文件夹存在
    if not os.path.exists(output_folder):
        os.makedirs(output_folder)

    # 处理输入文件夹中的每张图像
    for filename in os.listdir(input_folder):
        if filename.endswith(".png"):
            input_path = os.path.join(input_folder, filename)
            output_path = os.path.join(output_folder, os.path.splitext(filename)[0] + ".jpg")

            # 打开PNG图像
            img = Image.open(input_path)

            # 像素化图像
            pixelated_img = pixelate_image(img, pixel_size)

            # 保存为JPG格式
            pixelated_img.save(output_path, "JPEG")

if __name__ == "__main__":
    input_folder = "maps-cut"  # 输入图像文件夹
    output_folder = "maps-pix"  # 输出图像文件夹

    process_images(input_folder, output_folder)

3. 图片转文本

from PIL import Image
from collections import Counter

def convert_color_to_number(pixel):
    # 定义颜色映射
    color_mapping = {
        (11, 64, 240, 255): 1,  # 蓝色
        (255, 0, 0, 255): 2,  # 红色
        (0, 255, 0, 255): 3,  # 绿色
        (255, 255, 255, 255): 0  # 白色
    }

    return color_mapping.get(pixel, -1)  # 默认为-1表示未知颜色

def generate_txt_from_image(image_path, output_path):
    # 打开图像
    image = Image.open(image_path)

    # 获取图像的宽度和高度
    width, height = image.size

    # 创建一个空白的txt文件
    with open(output_path, 'w') as file:
        for y in range(height):
            for x in range(width):
                # 获取像素颜色
                pixel = image.getpixel((x, y))

                # 将颜色映射为对应的数字
                number = convert_color_to_number(pixel)

                # 写入txt文件
                file.write(f'{number} ')

            file.write('\n')

def extract_values(matrix):
    result = []
    for i in range(0, len(matrix),16):
        row_result = []
        for j in range(0, len(matrix[i]), 16):
            row_result.append(matrix[i][j])
        result.append(row_result)
    return result

def read_txt(filename):
    with open(filename, 'r') as file:
        lines = file.readlines()
    matrix = [list(map(int, line.split())) for line in lines]
    return matrix

def write_txt(filename, matrix):
    with open(filename, 'w') as file:
        for row in matrix:
            file.write(','.join(map(str, row)) + '\n')

def main(image_path):
    # image_path = 'maps-cut'  # 实际的图像路径
    output_path = 'test.txt'  # 输出每个像素值的txt文件路径
    generate_txt_from_image(image_path, output_path)

    output_filename = "test-2output.txt"  # 将上面txt文件中的每16个数字提取出来的输出文件名
    input_matrix = read_txt(output_path)
    output_matrix = extract_values(input_matrix)
    write_txt(output_filename, output_matrix)

4. 使用 DFS 计算路径

#include <bits/stdc++.h>
using namespace std;
namespace fs = std::filesystem;
typedef long long int ll;
const int MAXN = 1e5 + 5;
const int INF = 1e9;
int n, m, ans = INF;
string path = "";

char mp[50][50];   // 地图
bool vis[50][50];  // 是否遍历

int dir[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};  // 上下左右

char d[4] = {'d', 'u', 'r', 'l'};  // 上下左右

void DFS(int x, int y, int step) {
    if (step > ans) return;
    if (mp[x][y] == '2') {
        ans = min(ans, step);
        return;
    }
    for (int i = 0; i < 4; i++) {
        int nx = x + dir[i][0], ny = y + dir[i][1];
        if (nx >= 0 && nx < n && ny >= 0 && ny < m && mp[nx][ny] != '1' && !vis[nx][ny]) {
            vis[x][y] = true;
            path += d[i];
            //            cout<<path<<endl;
            DFS(nx, ny, step + 1);
            if (ans != INF)  // 如果找到终点,直接返回,不再继续搜索
                return;
            // 该条路无法到达终点则退回
            vis[x][y] = false;
            path.pop_back();
        }
    }
}

int get_seq(string filename) {
    path = "";
    // 读取文件iostream
    ifstream ifs(filename.c_str());
    n = 45, m = 45;
    if (!ifs.is_open()) {
        std::cerr << "Error opening file: " << filename << std::endl;
        return 1;  // 返回错误代码
    }

    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            ifs >> mp[i][j];
        }
    }
    int x = 0, y = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            vis[i][j] = false;
            if (mp[i][j] == '3')  // 标记起点
            {
                x = i;
                y = j;
                vis[i][j] = true;
            }
        }
    }
    ans = INF;
    DFS(x, y, 0);
    if (ans == INF) ans = -1;  // 无法到达终点
    //    cout << ans << endl;
    if (ans != -1) {
        cout << "Path: " << path << endl;
        // 新建filename_output文件
        string output_filename = filename + "_output";
        ofstream ofs(output_filename.c_str());
        ofs << path;
    }
    return 0;
}

void TraverseDirectory(const std::string& path) {
    for (const auto& entry : fs::directory_iterator(path)) {
        if (fs::is_regular_file(entry)) {
            // 处理文件
            std::cout << "File: " << entry.path() << std::endl;

            get_seq(entry.path().string());
        }
    }
}

int main() {
    string directoryPath = "maps-right";
    TraverseDirectory(directoryPath);
    return 0;
}

5. 计算 flag

import json
import math
import os

# JSON 格式的字符串
json_str = '填入 .sb3 中图片名称与地图层数关系的json文件'

'''
{ "costumes": [ { "name": "1", "bitmapResolution": 2, "dataFormat": "png", "assetId": "1a8e468eaf36ba24a200783e113c0d07", "md5ext": "1a8e468eaf36ba24a200783e113c0d07.png", "rotationCenterX": 480, "rotationCenterY": 360 },
.......
{ "name": "1400", "bitmapResolution": 2, "dataFormat": "png", "assetId": "930b75957d9f7056fcc44c56c425473c", "md5ext": "930b75957d9f7056fcc44c56c425473c.png", "rotationCenterX": 480, "rotationCenterY": 360 } ] }
'''

ascii = [
'''
填入ascii数组
'''
]


results = [-1]
final_flag = ""

def moved(seq):
    seq=" "+seq
    i,cl,cu,cr,cd=0,0,0,0,0
    for k in range(0,len(seq)-1):
        i=i+1
        if seq[i]=='l':
            cl+=i
        elif seq[i]=='u':
            cu+=i
        elif seq[i]=='r':
            cr+=i
        elif seq[i]=='d':
            cd+=i
    results.append(((cl%4)*64)+((cu%4)*16)+((cr%4)*4)+(cd%4))
    flag()


def flag():
    i=0
    flag=""
    for k in range(0,math.ceil((len(results)-1)/20)):
        j=0
        for l in range(0,20):
            i=i+1
            if(i>len(results)-1):
                break
            j=j+results[i]
        j= j % 256
        flag=flag+ascii[j+2]
    print('flag: ',flag,len(flag))
# 反序列化
decoded_data = json.loads(json_str)

def read_txt(filename):
    with open(filename, 'r') as file:
        lines = file.readlines()
    return lines[0]

# 按照name的顺序输出assetid
for i in decoded_data.get('costumes'):
    # 读取文件txt
    seq_file=i.get('assetId')+'_seq.txt'
    seq=read_txt(seq_file)
    print(seq)
    moved(seq)

print(final_flag)

0x03 Flag

hkcert23{ePiC_10st_iN_tH3_infInItE_m4ZE_j0UrnEy_tHr0uGh_3he_lAbYRiNth}
上次编辑于:
贡献者: HaynesChen
评论
  • 按正序
  • 按倒序
  • 按热度
Powered by Waline v3.1.3