- vừa được xem lúc

Phân tích gadget CommonsCollections1 trong ysoserial

0 0 23

Người đăng: Real Alpha Man

Theo Viblo Asia

Để khai thác lỗ hổng Deserialization, ngoài việc phải kiểm soát được giá trị đầu vào để đưa vào hàm thực hiện deserialize, ta cũng cần phải có một Object mà khi nó được deserialize, nó sẽ gọi tới một loạt các Object khác, tới cuối cùng gọi tới hàm đại loại như Runtime.exec để thực thi lệnh do attacker điều khiển, người ta gọi đó là gadget chain.

Lúc mới biết tới lỗ hổng này, mình nghĩ rằng 1 Object chỉ cần được viết đại loại như sau:

import java.io.IOException;
public class CommandExecutor { public CommandExecutor(String command) { String command = "<command here>"; try { Process process = Runtime.getRuntime().exec(command); } } catch (IOException e) { e.printStackTrace(); } }
}

Sau đó, ta có 1 hàm Serialize(new CommandExecutor("whoami")); và gửi cái Object thu được lên server là sẽ thành công, và mình luôn đi theo lối suy nghĩ đó, không xem các gadget ở trong tool ysoserial hoạt động như thế nào, do vậy lúc đó không hề khai thác được bất kì một lỗ hổng nào liên quan đến Deserialization.

Đến một ngày có một người anh khai sáng cho mình về lỗ hổng này thì mình mới bắt đầu tìm hiểu xem cách các gadget được xây dựng nên, do vậy mình sẽ phân tích một gadget tiêu biểu trong ysoserial để làm tư liệu cho các bạn mới tìm hiểu về lỗi này.

1. Phân tích

Đây là chain của gadget này, lấy từ ysoserial: image.png

Để dễ dàng tìm hiểu, ta bắt đầu từ giữa của gadget, hàm LazyMap.get() trước. Hãy tưởng tượng rằng ta có thể gọi đến hàm get của lớp LazyMap với đầu vào là key image.png

Điều gì sẽ xảy ra tiếp theo? Object tên key là đầu vào của hàm get() sẽ được đưa vào hàm transform() của biến factory - là một Object Transformer image.png

Transformer này lại chỉ là một interface, tức là lúc trước khi chương trình được chạy, ta không thể biết được biến factory này thực sự là một instance của lớp nào image.png

Do có tới 21 class khác đã implement Transformer. Lúc này, khi chương trình thực tế chạy, factory sẽ là instance của class nào hoàn toàn do giá trị key quyết định.

Ở trong payload sử dụng ChainedTransformer, hàm transform() của nó trông như sau: image.png

Theo mô tả thì ChainedTransformer được sử dụng làm cầu nối để nối các Transformer khác lại với nhau image.png

Hàm transform() nhận đầu vào là Object, tương ứng với giá trị key phía trên, lại được đưa vào một hàm transform của một mảng instance khác của Transformer image.png

Gadget đã sử dụng 2 instance là ConstantTransformerInvokerTransformer.

Và đây là đoạn mã khai thác từ ysoserial: image.png

Đoạn mã này đã làm những gì? Đầu tiên nó tạo 1 mảng các Transformer.

Với new ConstantTransformer(Runtime.class), ta sẽ có ConstantTransformer.transformer() trả về Runtime.class image.png

Với mỗi InvokeTransformer tiếp theo, nó sẽ trả về một method tương ứng với class. image.png

Ví dụ: new InvokerTransformer("getMethod", new Class[] { String.class, Class[].class }, new Object[] { "getRuntime", new Class[0] }), Thì một list các class là String, Class[], args là getRuntime và method là getMethod, do vậy kết quả return sẽ là getMethod.invoke(Runtime, "getRuntime"), điều này bằng với Runtime.getMethod("getRuntime", null), đây là một kĩ thuật Reflection trong Java.

Tóm tắt lại:

1. ConstantTransformer.transform() --> Runtime
2. InvokerTransformer.transform() --> getMethod.invoke(Runtime, "getRuntime") == Runtime.getMethod("getRuntime",null)
3. InvokerTransformer.transform() --> invoke.invoke(Runtime.getMethod("getRuntime",null), null) == Runtime.getMethod("getRuntime",null).invoke(null, null)
4. InvokerTransformer.transform() --> exec.invoke(Runtime.getMethod("getRuntime",null).invoke(null, null), args) == Runtime.getMethod("getRuntime",null).invoke(null, null).exec(args)

Như vậy đến cuối cùng ta có thể chạy được lệnh tùy ý.

Trở lại phần phía trên, theo payload trong ysoserial, bắt đầu với AnnotationInvocationHandler.readObject(), hàm này khi được gọi sẽ gọi tới hàm Map(Proxy).entrySet() rồi lại gọi AnnotationInvocationHandler.invoke() image.png

Điều này thực sự khá là lạ vì trong hàm entrySet() không có cách nào có thể gọi tới hàm invoke đó cả.

Thủ thuật ở đây rất thú vị image.png

Tải trọng đã tạo một proxy, dưới đây là các hàm liên quan trong ysoserial

 public static <T> T createMemoitizedProxy ( final Map<String, Object> map, final Class<T> iface, final Class<?>... ifaces ) throws Exception { return createProxy(createMemoizedInvocationHandler(map), iface, ifaces); } public static InvocationHandler createMemoizedInvocationHandler ( final Map<String, Object> map ) throws Exception { return (InvocationHandler) Reflections.getFirstCtor(ANN_INV_HANDLER_CLASS).newInstance(Override.class, map); } public static <T> T createProxy ( final InvocationHandler ih, final Class<T> iface, final Class<?>... ifaces ) { final Class<?>[] allIfaces = (Class<?>[]) Array.newInstance(Class.class, ifaces.length + 1); allIfaces[ 0 ] = iface; if ( ifaces.length > 0 ) { System.arraycopy(ifaces, 0, allIfaces, 1, ifaces.length); } return iface.cast(Proxy.newProxyInstance(Gadgets.class.getClassLoader(), allIfaces, ih)); }

với image.png Kết quả cuối cùng sẽ tạo thành một variable như sau: Map proxyMap = (Map) Proxy.newProxyInstance(AValidClass.class.getClassLoader(), new Class[] { Map.class }, new AnnotationInvocationHandler(lazyMap));

Đi vào hàm Proxy.newProxyInstance(), ta có thể thấy image.png

Một dynamic proxy Object đã được khởi tạo từ LazyMap với một AnnotationInvocationHandler. Như vậy từ Map(Proxy).entrySet() đã trở thành AnnotationInvocationHandler.invoke(mapProxy, "entrySet"). Và hàm invoke() ở đây đã thực hiện gọi đến LazyMap.get() (với việc vượt qua một số switch/case phía trước) image.png

2. Các thông tin khác

Việc thực hiện Deserialization thường sẽ gây ra các Exception không mong muốn, nhưng thực tế các class đều đã được gọi nên cuối cùng thì command sẽ được thực thi trước khi Exception xảy ra.

Có thể chặn các gadget bằng cách blacklist 1 trong những class tham gia vào trong gadget. Điều này có thể bị bypass bằng cách attacker tìm con đường vòng, không thông qua class đó nhưng vẫn có thể gọi tới class phía sau.

Sử dụng whitelist có hạn chế là sẽ ảnh hưởng khá nhiều tới hệ thống và phải cẩn trọng trong việc sử dụng whitelist, tránh bị thiếu các class cần thiết.

Bình luận

Bài viết tương tự

- vừa được xem lúc

Tạo ra virus bằng tool (Part1)

Virus. Tác hại của nó để lại cũng nặng nề:. . Gây khó chịu cho chúng ta là tác hại đầu tiên.

0 0 48

- vừa được xem lúc

Facebook và google "hiểu" chúng ta như thế nào?

Tổng quan. Đã bao giờ bạn gặp những tình huống dưới đây và đặt câu hỏi thắc mắc tại sao chưa.

0 0 48

- vừa được xem lúc

Mã hoá dữ liệu trên Android với Jetpack Security

Jetpack Security (JetSec) là thư viện được xây dựng từ Tink - dự án mã nguồn mở, bảo mật đa nền tảng của Google. Jetpack Security được sử dụng cho việc mã hoá File và SharedPreferences.

0 0 66

- vừa được xem lúc

Tái hiện vụ bị đánh cắp 2 triệu DAI (~2 triệu USD) của Akropolis

Tổng quan. .

0 0 108

- vừa được xem lúc

Bảo mật internet: HTTPS và SSL/TLS như giải thích cho trẻ 5 tuổi

(Mình chém gió đấy, trẻ 5 tuổi còn đang tập đọc mà hiểu được cái này thì là thần đồng, là thiên tài, là mình cũng lạy). . . Xin chào các bạn.

0 0 90

- vừa được xem lúc

Phân biệt server xịn và server pha ke bằng SSL Pinning

Xin chào các bạn, trong bài viết này mình muốn chia sẻ về một kĩ thuật rất nên dùng khi cần tăng tính bảo mật của kết nối internet: SSL Pinning. Trong bài viết trước, mình đã giải thích khá kĩ về SSL,

0 0 584