{%hackmd @themes/dracula %} # Serialize và Deserialize trong Java Java là một ngôn ngữ lập trình hướng đối tượng, chính vì thế khi có nhu cầu trao đổi dữ liệu dưới dạng các object thì các format thông thường như JSON hay XML sẽ không thể nào làm tốt, do đó ta cần sử dụng serialized data để thực hiện việc trao đổi dữ liệu của object dưới dạng bytes Ví dụ code serialize và deserialize ```java // User.java import java.io.Serializable; public class User implements Serializable { private String name; private int age; public User() { } public User(String name, int age) { this.name = name; this.age = age; } ... } ``` ```java // main.java import java.io.*; public class Main { public static void main(String[] args) { User a = new User("endy", 20); // Serialize data try { FileOutputStream fileOut = new FileOutputStream("data.ser"); ObjectOutputStream objectOut = new ObjectOutputStream(fileOut); objectOut.writeObject(a); objectOut.close(); fileOut.close(); System.out.println("Object serialized and saved to data.ser"); } catch (Exception e) { e.printStackTrace(); } // Deserialize data try { FileInputStream fileIn = new FileInputStream("data.ser"); ObjectInputStream objectIn = new ObjectInputStream(fileIn); User deserializedObject = (User) objectIn.readObject(); objectIn.close(); fileIn.close(); // Print the deserialized object System.out.println("Deserialized Object:"); System.out.println("Name: " + deserializedObject.getName()); System.out.println("Age: " + deserializedObject.getAge()); } catch (IOException | ClassNotFoundException e) { e.printStackTrace(); } } } ``` Đoạn code trên sẽ khởi tạo object của class `User` sau đó dùng method `writeObject` để serialize và ghi vào file `data.ser` Tiếp theo sử dụng method `readObject` để deserialize và in ra console Sau khi đoạn code trên thực thi ta sẽ nhận được kết quả: ![](https://hackmd.io/_uploads/Hk8dcoKdn.png) Ta có thể thấy format serialized data trong Java khác hoàn toàn so với serialized data trong PHP thông qua file `data.ser` ![](https://hackmd.io/_uploads/ByQcdsYd3.png) ```php <!-- Serialized data trong PHP --> O:4:"User":2:{s:10:"\0User\0name";N;s:9:"\0User\0age";N;} ``` Serialized data của Java sẽ có cấu trúc như sau ![](https://hackmd.io/_uploads/rk8YznKdn.png) > Note: Kiểu dữ liệu của thuộc tính age là `I` (interger), còn giá trị của thuộc tính age là 20 tương đương với `0x14` Ta có thể dùng `jdeserialize-1.2.jar` để hiển thị serialized data một cách dễ nhìn hơn ![](https://hackmd.io/_uploads/ByyzAjtO2.png) Và cũng tương tự như lỗ hổng Unsafe Deserialize trong các ngôn ngữ khác thì Java Deserialized Vulnerability sẽ xảy ra khi untrusted data rơi vào quá trình serialize và deserialize, attacker có thể kiếm soát Object được deserialized từ đó dẫn đến nhiều hậu quả khác nhau, tùy theo logic code của chương trình # Reflection trong Java Reflection là một khái niệm quan trọng trong khai thác lỗ hổng Java deser, tưởng tượng ta tìm được một chain có thể dẫn đến RCE, tuy nhiên muốn làm được ta phải thay đổi được giá trị của một property private của một class trong chain đó. Để làm được vậy ta sẽ dùng đến `Reflection` đây là một API cho phép thay đổi giá trị của các thuộc tính trong quá trình Runtime, nó thường được dùng để set giá trị cho các private properties của class Ví dụ: Ta có class User như sau ```java public class User { public String name; private boolean isAdmin = false; public User(String name) { this.name = name; } ... } ``` Để thay đổi giá trị của `isAdmin` mà không cần dùng đến setter ta sẽ dùng Reflection API ```java import java.lang.reflect.Field; public class Main { public static void main(String[] args) throws IllegalAccessException { User a = new User("endy"); Field f = null; try { f = a.getClass().getDeclaredField("isAdmin"); f.setAccessible(true); f.set(a, true); } catch (NoSuchFieldException e) { throw new RuntimeException(e); } System.out.println(a.isAdmin()); } } ``` Kết quả: ![](https://hackmd.io/_uploads/rJQ6LSxKn.png) Ngoài ra Reflection còn được sử dụng trong trường hợp ta muốn gọi method của một class nhưng không muốn tạo instant của class đó, ví dụ có một private class `XY` và `ab` là public method của class `XY`. Ta muốn sử dụng `ab` nhưng không muốn tạo instant của `XY` nên sẽ dùng Reflection Syntax: ```java <Object>.class.getMethod("<method name>").invoke(agrs); ``` # Gadgetchain trong Java Ở trên là những gì cơ bản nhất về serialized/deserialized và Reflection trong java, phần quan trọng nhất là làm sao từ một methods deserialize như `readObject` mà dẫn đến RCE, writefile hay bất kỳ thứ gì mà ta muốn. Ta sẽ sử dụng gadgetchain để làm được điều đó, có thể ví dụ các object, method như là các đoạn ống nước, ta phải nối chúng lại làm sao để nước có thể đi từ source đến sink, khiến untrusted data rơi vào method mà ta muốn thực thi. Đoạn đường ống nước nối từ source đến sink chính là gadgetchain Nhắc đến Java Deser mà không nhắc đến `ysoserial` thì rất thiếu sót, `ysoserial` là một tool cung cấp cho ta một số gadgetchain phổ biến trong thư viện của Java ![](https://hackmd.io/_uploads/Byzvjv5uh.png) Để sử dụng thì ta chỉ cần gọi tên gadgetchain nó sẽ tự động gen payload cho ta ![](https://hackmd.io/_uploads/Bykdnvq_h.png) ![](https://hackmd.io/_uploads/BJn_2Dqu2.png) Tiếp theo mình sẽ phân tích lại một gadgetchain đơn giản là `URLDNS`. Cách phân tích và các bước mình tham khảo theo [blog này](https://sec.vnpt.vn/2020/02/co-gi-ben-trong-cac-gadgetchain/) và [blog này](https://tsublogs.wordpress.com/2023/02/17/javasecurity101-4-java-deserialization-ysoserial-2/). Các bạn có thể xem bài viết góc để dễ hiểu hơn, ở đây mình chỉ phân tích lại để phục vụ nhu cầu của mình thôi ## URLDNS ### Gen payload Gadgetchain này sẽ gửi DNS query đến URL mà ta truyền vào, thông thường gadgetchain này được sử dụng để test xem target có thể deserialize object được hay không. Đây cũng là gadgetchains đơn giản và dễ phân tích nhất trong `ysoserial` Đầu tiên ta sẽ thử gen payload để sử dụng ![](https://hackmd.io/_uploads/rkW_Wucu2.png) Khi chạy chương trình thì tại request repo của mình nhận được dns query ![](https://hackmd.io/_uploads/Hyymz_5_3.png) Vậy thì gadgetchain này bao gồm những gì? Tại [src](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java) gen payload của `ysoserial` ta biết được chain này bao gồm: ![](https://hackmd.io/_uploads/HkcYf_qOn.png) Ta cũng biết được cách `ysoserial` tạo payload ![](https://hackmd.io/_uploads/rypoMuqdh.png) Đầu tiên object của class `URL` được tạo với `url` là input của ta, `handler` là object của class `URLStreamHandler` Sau đó class `u` được put vào hashmap Và cuối cùng là dùng `Reflecton` để thay đổi giá trị của property private `hashCode` ### Cụ thể quá trình deser Sau khi biết được đường đi từ source đến sink, bây giờ tiến hành debug hoii Vì gadgetchain này lợi dụng class có sẵn trong Java nên ta không cần phải khai báo thêm thư viện gì cả Sơ đồ gadgetchain là như sau: ``` HashMap.readObject() ->HashMap.putVal() ->HashMap.hash() ->URL.hashCode() ``` Gadgetchain này sẽ được trigger khi ta gọi `readObject` của clas `Hashmap`, do đó để debug ta sẽ bắt đầu từ `Hashmap` Tại InteliJ nhấn `Ctrl+N` và tìm `Hashmap` để xem src của class `HashMap` ![](https://hackmd.io/_uploads/SykBSucd2.png) Tại đây nhấn `Ctrl+F12` để tìm methods `readObject` ![](https://hackmd.io/_uploads/B1Yvruc_h.png) > Note: Java cho phép dev có thể ghi đè lên các methods `readObject` và `writeObject`, và thông thường dev cũng thích làm thế. Do đó ở một số class methods `readObject` hoặc `writeObject` sẽ khác so với mặc định của Java Tại method `readObject` của `Hashmap`, nó sẽ loop qua từng key của hashmap và thực hiện `putVal(hash(key))` ![](https://hackmd.io/_uploads/rkHEUd5dh.png) Đặt breakpoint tại đây để xem giá trị của `key` ![](https://hackmd.io/_uploads/ByWywOqO3.png) Ta thấy `key` chính là object `URL` mà ta truyền vào Tiếp tục nhấn `F7` và chọn method `hash` để nhảy vào method này ![](https://hackmd.io/_uploads/rk1QPOcO2.png) Method này gọi đến `key.hashCode()` trong trường hợp này key chính là object `URL`, nghĩa là nó đang gọi đến `URL.hashCode()` Tiếp tục nhảy vào method `hashCode` ![](https://hackmd.io/_uploads/SJevDd9_3.png) Method này sẽ kiểm tra giá trị của property `hashCode` có khác `-1` hay không, nếu khác thì tiếp tục gọi đến `handler.hasCode()` còn nếu không thì gadgetchain của ta bị gãy từ đây. Tuy nhiên vấn đề nằm ở `hashCode` được defined mặc định là `-1` ![](https://hackmd.io/_uploads/SkkQ_uqO3.png) Vì đây là property private nên ta không thể thay đổi giá trị của nó lúc khai báo, nhìn lại code gen payload của `ysoserial` ta thấy nó sử dụng `Reflection` để thay đổi giá trị của `hashCode` trong quá trình runtime ![](https://hackmd.io/_uploads/rkEPO_5dn.png) `Reflection` trên là một class của `ysoserial.payloads.util` nên để tái hiện lại mình sẽ dùng cách mặc địch của Java ```java URL u = new URL(null, "http://ydg1nw3a.requestrepo.com", handler); Field field = URL.class.getDeclaredField("hashCode"); field.setAccessible(true); field.set(u, 1337); ``` Ta có thể test bằng cách dùng `Evalute Expression` của InteliJ ![](https://hackmd.io/_uploads/HyWeq_q_3.png) Khi comment dòng `set` ta thấy giá trị của `hashCode` là -1, khi gỡ comment dòng đó thì: ![](https://hackmd.io/_uploads/Sy7WqOcdn.png) Giá trị của `hashCode` đã là `1337`. Vậy thông qua `reflection` ta có thể thay đổi giá trị của `hashCode` Gadget của `ysorial` đến đây là hết, tuy nhiên ta vẫn chưa thấy dòng nào thực hiện DNS query, do đó mình sẽ tiếp tục debug Sau khi bypass được `hashCode = -1`, tiếp tục code gọi đến `handler.hashCode` ![](https://hackmd.io/_uploads/By0Fq_5dh.png) `handler` này là class của object `URLStreamHandler` ![](https://hackmd.io/_uploads/HyN3cdqd3.png) `F7` để nhảy vào `handler.hashCode` thì ta thấy nó gọi đến `getHostAddress` với u là class `URL` ta truyền vào ![](https://hackmd.io/_uploads/SJ2CidqO3.png) method này sẽ gọi đến `InetAddress.getByName()` để resolve DNS đến host của ta ![](https://hackmd.io/_uploads/rk0OhOqOh.png) Vậy thì gadgetchain đầy đủ sẽ là ``` HashMap.readObject() ->HashMap.putVal() ->HashMap.hash() ->URL.hashCode() ->URLStreamHandler.getHostAddress() ->InetAddress.getByName() ``` Đây là gadgetchain đơn giản nhất trong các gadgetchain `ysoserial`, nó giúp mình có cái nhìn tổng quát về gadgetchain trong Java cũng như là học được cách debug trong InteliJ. Phần tiếp theo mình sẽ phân tích thêm một gadgetchain nữa trong bộ `ysoserial` # Refer https://www.infoworld.com/article/2072752/the-java-serialization-algorithm-revealed.html https://learn.snyk.io/lessons/insecure-deserialization/java/ https://sec.vnpt.vn/2020/02/co-gi-ben-trong-cac-gadgetchain/ https://tsublogs.wordpress.com/2023/02/16/javasecurity101-2-java-deserialization-overview/ https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/URLDNS.java