Java Reflection

Một loạt các bài blog này có nhiệm vụ note và hệ thống lại kiến thức mình đã đọc + học từ những đàn anh, đàn em, ... Thật ra, phần lớn kiến thực được đạo lại + copy từ blog của anh tsug0d, Jang, ...


Về bản thân Java Reflection là một tính năng của ngôn ngữ Java, cho phép truy cập đối tượng bao gồm: tên class, field, method và có thể thao tác chỉnh sửa các field của đối tượng (kể cả private field) trong quá trình runtime.


Về hướng security thì Java Reflection thường được dùng để bypass filter hay dùng để get và set lại giá trị của các field của của đối tượng để theo đúng gadget chain trong bug Java Deserialization.


1. Bypass Filter

Ví dụ: Có bug Code injection, tuy nhiên string "Runtime", "exec" đã bị chặn, lúc này chúng ta có thể sử dụng Java Reflection để bypass

Đoạn code bypass sẽ như sau:


Kết quả:


Ở đây có sự xuất hiện của các Reflection API: getClass(), getMethod(), forName() và invoke()

Note: class java.lang.Class là một entry point của mọi thao tác của reflection API. Hiểu cách cục súc hơn là nó là lớp cha của mọi class trong Java.

Trong Java chúng ta có 2 cách để lấy java.lang.Class object:
  1. Sử dụng Object.getClass() method ➜ method này sẽ trả về runtime Class object type
  2. Sử dụng .class ➜ ClassName.class ➜ trả về Class object static
Dễ hiểu hơn thì chúng ta sử dụng tính năng Evaluate Expression của IntelliJ


Ở dòng 18, String.class là một object của class java.lang.String, sau đó sử dụng method Object.getClass() để lấy Object của class java.lang.Class ➜ Có được entrypoint


Và như đã nói, Java reflection rất linh động, ví dụ: chúng ta không nhất thiết cứng nhắc theo kiểu String.class.getClass(), mà có thể gọi bằng cách "".getClass().getClass()

Okay, sau khi có được entrypoint java.lang.Class() rồi thì chúng ta gọi forName(<tên_class>) để lấy class chúng ta cần:


Như bạn có thể thấy, vì forName() nhận vào là String nên chúng ta có thể tùy biến để bypass filter.

Khi đã có object của class rồi, chúng ta có thể lấy method của class đó bằng getMethod(), như hình bên dưới lấy method exec() nhận String làm argument

Vì sao phải có cái String.class phía sau? đơn giản là vì trong java có nhiều method bị trùng tên nhưng khác argument nên phải khai báo để máy biết cái nào mà lấy.

Kết quả là chúng ta lấy được method java.lang,Runtime.exec(java.lang.string) ➜ tiếp theo cần gọi nó lên ➜ sử dụng invoke()

Method.invoke(Object object, Object[] args) ➜ method invokes the underlying method represented by this Method object 

Nếu bạn thấy khó hiểu thì nó sẽ như thế này, về cơ bản thì payload của chúng ta là Runtime.getRuntime().exec("whoami");

Bây giờ chúng ta đã có exec, và exec lại được gọi từ Runtime.getRuntime() nên: exec.invoke(Runtime.getRuntime(), "whoami")

2. Chỉnh sửa field

Phần này lên quan đến bug Deserialization, trong gadget chain chúng ta cần phải sửa một số giá trị của các field để đúng ý đồ ➜ dùng Reflection 

Nhận xét