写完了jndi注入,直接来看一下非常火的fastjson反序列化漏洞,基本也是基于jndi注入的。
首先来看下正常的fastjson的用法,其实就是一个json解析器,类似js的JSON.parse。在https://repo1.maven.org/maven2/com/alibaba/fastjson/1.2.24/下载需要的jar包。
先随便定义一个类Student,为了清楚观察get和set函数的调用加一些print。
1 | public class Student { |
然后写个测试类
1 | import com.alibaba.fastjson.JSON; |
可以看到当指定了@type时,才能反序列化出指定类型的对象,并且这个过程会调用类的set方法。序列化时toJSONString时要加上SerializerFeature.WriteClassName参数才会指定@type。
实际上fastjson就是省去了我们自己写readObject的代码,直接实现了反序列化。那么问题就处在,如果反序列化时对对象类型设置的太过宽松,就可能引入任意类。如果该类的getter、setter、构造函数中有可被利用的代码就会导致代码执行。当然如果指定类的上述函数中有可被利用的代码也一样。
1 | import com.alibaba.fastjson.JSON; |
服务可以用java -jar JNDI-Injection-Exploit-1.0-SNAPSHOT-all.jar -C calc.exe -A 127.0.0.1这个工具开,比较方便
分析下原理,根据JSON.parse
1 | public static Object parse(String text, int features) { |
调用了DefaultJSONParser.parse,继续跟进是一个switch,跳到了case12
1 | case 12: |
再跟进JSONObject的parseObject,这里就是遍历解析输入字符串,获取类名,得到类名后走到TypeUtils.loadClass引入了该类,然后调用了FastjsonASMDeserializer.deserialze()
1 | if (object.size() <= 0) { |
到deserialze函数就没办法动态调试了,最后看静态代码是调用了JdbcRowSetImpl的setDataSourceName和setAutoCommit这两个set函数,前者赋值了rmi/ladp服务地址,后者调用了InitialContext.lookup()引起了JNDI注入。
所以其实前面的代码都不重要,只是正常的反序列化过程。关键就是JSON.parse指定@type并赋值时会调用指定类的特定set方法,正好JdbcRowSetImpl的setAutoCommit方法存在JNDI注入。反序列化也只是个引子。