图片
文件是一张图片,根据提示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
提取迷宫之后算法没搞出来