Case Study: Exploitation RMI vulnerability with Codebase feature

LowK
CDLabs
Published in
4 min readMar 2, 2020

1. Codebase là gì?

Code base có thể được định nghĩa là source, là địa chỉ mà các class sẽ được load vào máy chủ virtual machine của java.
Có thể hiểu CLASSPATH chính là local codebase, ngoài CLASSPATH được gọi là remote codebase.
Client có thể yêu cầu server thực hiện trên 1 object mà không tồn tại trên server.

2. Cách thức hoạt động

  • Máy server của victim là 149.28.149.123, đang listening dịch vụ RMI port 1099
  • Cùng với đó, máy chủ khác đang chạy port web 8000 có chứa file Payload.class. Nhưng vì không có nhiều tài nguyên nên mình sử dụng luôn máy server. Nên có thể thấy trong hình có ip là 127.0.0.1
  • Máy client của attacker là 14.169.59.1
  • Bước 1: attacker chạy RMI-Client kết nối đến RMI-Server (gói tin số 4 — gói tắt là №4)
  • Bước 2: RMI-Server tiếp nhận bằng cách trả về gói tin ACK (№6)
  • Bước 3: RMI-Client gửi thêm một số thông tin cho RMI-Server biết (№8)
  • Bước 4: RMI-Client gửi chuỗi serialization cho RMI-Server. Có thể thấy trong hình bên dưới, thông tin codebase là http://localhost:8000
  • Bước 5: RMI-Server nhận được thông tin codebase rồi sau đó thực hiện request HTTP đến máy chủ web để lấy Class mà không tồn tại trong CLASSPATH. (№15, №18)
  • Bước 6: cuối cùng, sau khi lấy được đầy đủ thông tin để deserialize thì trả kết quả về cho RMI-Client (№23, №24)

3. Tận dụng khai thác lỗ hổng

Các bạn biết rằng quá trình deserialization gồm có 2 phase cần giải quyết khi muốn khai thác lỗi. Đó là Target phải deserialize inputCó gadgetchain.
Quá trình RMI chính là quá trình serialize và deserialize dữ liệu ở 2 endpoint server và client. Việc sử dụng codebase chính là giải quyết vấn đề không có gadget chains trong classpath trên server.
Mình sẽ setup bài labs theo bài CTF này: sources2017/pwn-lamermi
Bài CTF này từ 2017 rồi nên có rất nhiều writeup tuy nhiên mình thấy thích hơp để làm case study cho gadget chains về codebase RMI.

Setup labs:
Bài CTF có 3 phần: server và 2 client (thực chất là 2 solution)

Trước hết phải build code server và chạy sau đó mình sẽ phân tích client.

  • Build bằng gradle ./gradlew genPackage
  • File jar sau khi build nằm ở: build/package
  • Sửa nội dung file start.sh: -Djava.rmi.server.hostname=localhost là địa chỉ của máy server.
  • Sau đó chạy server bằng cách chạy tập tin start.sh là đủ.
  • Dùng nmap kiểm tra, kết quả là có 1 số thông tin trả về từ RMI server

Làm sao biết nên sử dụng codebase?

  • -Djava.rmi.server.useCodebaseOnly=false khi được đặt là false thì RMI chấp nhận lấy source từ server khác
  • grant { permission java.security.AllPermission; }; Cho phép All Permission
  • RMI-Server không có gadget chains để mình lợi dụng

Solution của Mortic

RMI-Client kết nối đến RMI-Server ở dòng 33
Sau khi kết nối thành công, Client sẽ gửi cho Server thông tin codebase để tải class Payload khai thác đọc file flag.

Chú ý là class Payload có readObject trong đó gọi function exploit. Khi RMI deserialize, magic method này sẽ được tự động gọi.

4. Điều kiện khai thác

  • Bài blog này mình phân tích trên jdk 1.8.111 vì lý do ở version jdk chưa checkAccess
  • Bắt đầu từ version 1.8.121 và một số update khác ở jdk cũ đã có checkAccess để chặn bind Object từ host khác (non-local)

Troubleshoot: java.rmi.AccessException: Registry.bind disallowed; origin /10.8.0.3 is non-local host

Code base bị chặn connect từ host không cùng chung VM
Đọc link Attacking Java RMI services after JEP 290 | MOGWAI LABS GmbH

--

--