代码审计Day15 - $_SERVER[PHP_SELF]导致的防御失效问题
字数 1200 2025-08-18 11:37:42
PHP代码审计:$_SERVER['PHP_SELF']导致的防御失效问题
1. 漏洞概述
本教学文档将详细分析由$_SERVER['PHP_SELF']引发的安全漏洞,该漏洞可导致任意网址跳转和防护系统绕过问题。
2. 漏洞示例代码分析
2.1 示例代码
// 示例漏洞代码
if(isset($_GET['redirect'])) {
$redirect = new Redirect();
$redirect->startRedirect($_GET['params']);
}
class Redirect {
public function startRedirect($params) {
$parts = explode('/', $_SERVER['PHP_SELF']);
$baseFile = array_pop($parts);
$url = $baseFile . '?' . http_build_query($params);
$this->setHeaders($url);
}
private function setHeaders($url) {
$url = urldecode($url);
header("Location: " . $url);
}
}
2.2 漏洞原理
-
$_SERVER['PHP_SELF']可控性问题:PHP_SELF表示当前执行脚本的路径- 当URL包含PATH_INFO时(如
http://example.com/index.php/admin),PHP_SELF会包含用户可控部分(/admin)
-
双编码绕过:
- 浏览器自动对URL进行一次编码
- 服务器接收时会解码一次
urldecode()函数会再次解码,导致双编码可绕过某些过滤
3. 漏洞利用方式
3.1 任意URL跳转
构造Payload:
http://victim.com/index.php/http:%252f%252fattacker.com?redirect=test¶ms=test123
解析过程:
- 初始URL:
http:%252f%252fattacker.com - 第一次解码(服务器):
http%2f%2fattacker.com - 第二次解码(urldecode):
http://attacker.com - 最终导致跳转到攻击者控制的网站
3.2 防护系统绕过(以360webscan为例)
- 旧版本360webscan使用
$_SERVER['PHP_SELF']进行白名单检测 - 白名单中包含
admin目录 - 构造URL:
http://victim.com/index.php/admin?test=<script>alert(1)</script> - 系统检测到
admin在白名单中,绕过XSS防护
4. 实际案例分析
4.1 360webscan防护绕过
漏洞代码片段:
// 旧版360webscan中的白名单检测
$url_path = $_SERVER['PHP_SELF'];
foreach($webscan_white_directory as $value) {
if(strpos($url_path, $value) !== false) {
$webscan_white = true;
break;
}
}
问题:
- 直接使用未过滤的
PHP_SELF - 攻击者可构造包含白名单目录的URL绕过检测
5. 修复建议
-
使用
$_SERVER['SCRIPT_NAME']替代:SCRIPT_NAME只包含脚本路径,不包含PATH_INFO- 更安全可靠
-
输入验证:
$scriptPath = basename($_SERVER['SCRIPT_NAME']); $url = $scriptPath . '?' . http_build_query($params); -
限制跳转目标:
$allowedDomains = ['example.com', 'trusted.com']; if(!in_array(parse_url($url, PHP_URL_HOST), $allowedDomains)) { die('Invalid redirect target'); }
6. CTF题目分析
6.1 题目代码
// index.php
include "./config.php";
include "./flag.php";
error_reporting(0);
$black_list = "/admin|guest|limit|by|substr|mid|like|or|char|union|select|greatest|%00|\'|";
$black_list .= "in|<|>|-|chal|and|if|database|where|concat|insert|having|sleep/i";
if(preg_match($black_list, $_GET['user'])) exit(":P");
if(preg_match($black_list, $_GET['pwd'])) exit(":P");
$query="select user from users where user='$_GET[user]' and pwd='$_GET[pwd]'";
echo "<h1>query : <strong><b>{$query}</b></strong><br></h1>";
$result = $conn->query($query);
if($result->num_rows > 0){
$row = $result->fetch_assoc();
if($row['user']) echo "<h2>Welcome {$row['user']}</h2>";
}
$result = $conn->query("select pwd from users where user='admin'");
if($result->num_rows > 0){
$row = $result->fetch_assoc();
$admin_pass = $row['pwd'];
}
if(($admin_pass)&&($admin_pass === $_GET['pwd'])){
echo $flag;
}
highlight_file(__FILE__);
6.2 解题思路
- 目标:获取flag需要满足
$admin_pass === $_GET['pwd'] - 限制:
- 黑名单过滤了大量SQL关键字
- 需要绕过过滤获取管理员密码
- 可能的解法:
- 使用大小写变异绕过关键字检测
- 利用注释符
/**/分割关键字 - 使用
||替代or - 利用
=进行盲注
7. 总结
$_SERVER['PHP_SELF']存在安全风险,应谨慎使用- 在需要脚本路径时,优先使用
$_SERVER['SCRIPT_NAME'] - 所有用户输入都应视为不可信的,必须进行严格验证
- 防护系统的实现需要考虑各种绕过方式
- 黑名单过滤往往不够安全,应考虑白名单机制
8. 扩展学习
- PHP官方文档中关于
$_SERVER变量的说明 - OWASP关于不安全的URL重定向的指南
- 各种WAF绕过技术的研究
- SQL注入的高级绕过技术