选拔考试复现

发布于:2025-09-08 ⋅ 阅读:(20) ⋅ 点赞:(0)

图片

文件是一张图片,根据提示521,直接在RGB里面调整参数521提取图像位数据

可以得到一个压缩包的二进制

这边直接保存成压缩包,解压就可以得到一个图片

之后就没有上面提示了,可以先看属性

得到一个关键字符串,考试就卡在这里,后面要用steghide去提取隐藏数据

就可以得到flag

安卓

用jadx打开,根据提示在layout文件夹里面打开main函数就有了

漫长的旅途(reverse)

可以先用die查一下文件

发现是有upx壳,直接脱发现失败,用010打开去看会发现有魔改壳

考试的时候改了前面的U没改后面的X,脑子不知道在想什么,类似这样去改,一共有三处

改完去脱壳就可以了,要注意这边upx的版本要在3.91往上,脱完之后用ida打开,之际看string可以看到一个迷宫,直接追踪进去

可以看到这边顺序也帮我们排好了,接着利用这个可以直接追踪到主函数,就可以看到类似上下左右的四个方向代表的字母

把迷宫提取出来可以,那么接下来就是判断方向了,其中有两个变量v12和v13在进行自增和自减,自减对应的就是向上和向左,那么v12和v13就是分别对应水平还是垂直的方向,怎么判断呢,下面有代码

这边重新赋值v9和v10对应后面有一个while判断,其中v9对应的是59对应迷宫中,水平方向有60个字符,垂直有30,所以v12就是代表水平,v13代表垂直。

接下来就是脚本解密

from collections import deque

directions = [
    (0, -1, 'C'),  # 向左 (v12减1)
    (0, 1, 'Z'),  # 向右 (v12加1)
    (-1, 0, 'Y'),  # 向上 (v13减1)
    (1, 0, 'X')  # 向下 (v13加1)
]

file_path = '1.txt'


def read_grid_from_file(file_path):
    try:
        with open(file_path, 'r') as file:
            lines = file.readlines()
            grid = [list(line.strip()) for line in lines]
            return grid
    except FileNotFoundError:
        print(f"文件 {file_path} 未找到。")
        return None


def find_shortest_path(grid):
    rows = len(grid)
    cols = len(grid[0]) if rows > 0 else 0
    start = None
    end = None

    # 找到起点和终点的坐标
    for i in range(rows):
        for j in range(cols):
            if grid[i][j] == 'A':
                start = (i, j)
            elif grid[i][j] == 'B':
                end = (i, j)

    if not start or not end:
        print("未找到起点'A'或终点'B'")
        return None

    # 初始化队列和访问集合
    queue = deque([(start, "")])
    visited = set([start])

    while queue:
        (x, y), path = queue.popleft()

        # 如果到达终点,返回路径
        if (x, y) == end:
            return path

        # 尝试四个方向
        for dx, dy, dir_char in directions:
            new_x, new_y = x + dx, y + dy
            # 检查新坐标是否合法且未访问过,并且可以通过
            if (0 <= new_x < rows and
                    0 <= new_y < cols and
                    (new_x, new_y) not in visited and
                    grid[new_x][new_y] in ('0', 'B')):
                visited.add((new_x, new_y))
                queue.append(((new_x, new_y), path + dir_char))

    return None


grid = read_grid_from_file(file_path)
if grid:
    path = find_shortest_path(grid)
    if path:
        print("从 A 到 B 的最短路径是:", path)
    else:
        print("没有找到从 A 到 B 的路径。")

这个脚本其实之前是写过一篇博客去理解的,但是现在又忘了很多,所以代码写作能力还是很重要,要去学习一些py库的应用。

java

我尝试去运行这个,但是运行不起来,用jadx打开,可以直接找到main函数

这表调用了一些自定义的办法,去对指定的data.txt进行加密,生成加密之后的文件就是encrypted.enc,后面有一个key一个是定义的加密方法的aes的密钥。题目中给了加密之后的文件,那么我们可以猜测data.txt就是flag,我们现在要找到是key和他加密的方法,从代码中也可以看到,他调用的加密方法在左侧也都看得到,所以我们接着去看。

使用PBKDF2WithHmacSHA256算法从密码"PolarD&N CTF"得到加密的密钥也就是key,这边加密的盐值也给了,迭代次数是10000,输出128位的aes的密钥,然后还套了一层base64加密,所以最后输出的key的格式是base64加密的

package org.ctf.polar;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import javax.crypto.Cipher;
import javax.crypto.CipherOutputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.SecretKeySpec;

public class FileEncryptor {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";

    public static void encryptFile(String inputFile, String outputFile, String key) throws NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException, IOException {
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), "AES");
        cipher.init(1, secretKeySpec);
        byte[] ivBytes = cipher.getIV();

        try (InputStream inputStream = new FileInputStream(inputFile)) {
            OutputStream outputStream = new FileOutputStream(outputFile);
            Throwable var9 = null;

            try {
                CipherOutputStream cipherOutputStream = new CipherOutputStream(outputStream, cipher);
                Throwable var11 = null;

                try {
                    outputStream.write(ivBytes);
                    byte[] buffer = new byte[4096];

                    int bytesRead;
                    while((bytesRead = inputStream.read(buffer)) >= 0) {
                        cipherOutputStream.write(buffer, 0, bytesRead);
                    }
                } catch (Throwable var56) {
                    var11 = var56;
                    throw var56;
                } finally {
                    if (cipherOutputStream != null) {
                        if (var11 != null) {
                            try {
                                cipherOutputStream.close();
                            } catch (Throwable var55) {
                                var11.addSuppressed(var55);
                            }
                        } else {
                            cipherOutputStream.close();
                        }
                    }

                }
            } catch (Throwable var58) {
                var9 = var58;
                throw var58;
            } finally {
                if (outputStream != null) {
                    if (var9 != null) {
                        try {
                            outputStream.close();
                        } catch (Throwable var54) {
                            var9.addSuppressed(var54);
                        }
                    } else {
                        outputStream.close();
                    }
                }

            }
        }

    }
}

这个就是主要的加密函数了,上面的那个函数是为了得到key值
可以看到主要用的加密方法是AES/CBC/PAS5Padding的加密算法
通过Cipher.getInstance(TRANSFORMATION)加载AES/CBC/PAS5Padding的加密算法,创建Cipher加密对象,Cipher是java中的一个类,提供假面和解密的功能,他更像一个加载器,就像他这边加载的是AES/CBC/PAS5Padding这个加密算法一样 

反编译中的if语句是反编译他加上的不是加密的代码,如果想更清晰一点可以用IDE打开

写解密脚本的话,首先是钥匙,可以直接用他给的函数得到,之后解AES的是要还要一个IV,这个就要用到cipher,利用这个去得到IV

package org.ctf.polar;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.PBEKeySpec;
import javax.crypto.spec.SecretKeySpec;

public class FileDecryptor {
    private static final String ALGORITHM = "AES";
    private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding";

    public static class KeyGenerator {
        public String getAesKeyB64String() throws InvalidKeySpecException, NoSuchAlgorithmException {
            byte[] salt = {1, 35, 69, 103, -119, -85, -51, -17};
            PBEKeySpec spec = new PBEKeySpec("PolarD&N CTF".toCharArray(), salt, 10000, 128);
            SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
            SecretKey secretKey = secretKeyFactory.generateSecret(spec);
            return Base64.getEncoder().encodeToString(secretKey.getEncoded());
        }
    }

  
    public static void decryptFile(String inputFile, String outputFile, String key)
            throws NoSuchPaddingException, NoSuchAlgorithmException,
            InvalidKeyException, IOException, InvalidAlgorithmParameterException {

        try (FileInputStream fileInputStream = new FileInputStream(inputFile)) {
            // 读取IV (AES CBC模式需要16字节的IV)
            byte[] ivBytes = new byte[16];


          
            Cipher cipher = Cipher.getInstance(TRANSFORMATION);
            SecretKeySpec secretKeySpec = new SecretKeySpec(key.getBytes(), ALGORITHM);
            IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
            cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, ivSpec);

            try (CipherInputStream cipherInputStream = new CipherInputStream(fileInputStream, cipher);
                 FileOutputStream fileOutputStream = new FileOutputStream(outputFile)) {

                byte[] buffer = new byte[4096];
                int bytesRead;
                while ((bytesRead = cipherInputStream.read(buffer)) != -1) {
                    fileOutputStream.write(buffer, 0, bytesRead);
                }
            }
        }
    }

    public static void main(String[] args) {
        try {
           
            KeyGenerator keyGenerator = new KeyGenerator();
            String key = keyGenerator.getAesKeyB64String();

            decryptFile("encrypted.enc", "data.txt", key);
            
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

File

解压压缩包得到一个没有后缀的文件,用记事本看一下

看到文件头是一个程序,直接改一下后缀格式,运行一下

给了提示可以去搜一下ace是一种压缩包的格式,直接提取

有密码,既然是压缩包用archpr爆破一下,可以得到答案

下载对应的解压ace压缩包的软件可以看到密码提示进而去爆破

EzPyeditor

可以直接用pycharm打开文件比较简洁,这边给了给路由/check,我们尝试访问看看

提交的方式不允许,后面强调是URL,猜测是GET传参,我们没有抓包改成POST传参

可以看到状态码200,主要是后面的绿色的字,类似一些查询还是执行的结果,返回去看源码,上面都是一些层叠样式的应用,我觉得这段才是关键的代码

    function checkCode() {
      var source = editor.getValue();
      fetch('/check', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ source: source })
      })
        .then(response => response.json())
        .then(data => {
          editor.operation(function () {
            editor.eachLine(function (line) {
              editor.removeLineClass(line, 'background', 'highlight-line');
            });
          });


          if (data.status === false && data.error) {
            var errorLine = parseInt(data.error.split('line ').pop().split(',')[0]) - 1;
            console.log("Error at line:", errorLine);
            editor.addLineClass(errorLine, 'background', 'highlight-line');
          }
        });
    }

这边定义了用POST请求check,发送的格式是json,这边重点还是在ast,去了解会知道这边有一个source的参数是必须要的,还有他的上传格式,那么我们试着传参看看source变量,记得要用json的格式

可以看到是有一些区别的,还是ast的语法,接着我们引入filename,简单来说就是通过报错带出内容,然后题目文件是给了flag的一个文件的,从里面我们可以看到报错在第六行

puls3.0

提取迷宫之后算法没搞出来


网站公告

今日签到

点亮在社区的每一天
去签到