or
or
By clicking below, you agree to our terms of service.
New to HackMD? Sign up
Syntax | Example | Reference | |
---|---|---|---|
# Header | Header | 基本排版 | |
- Unordered List |
|
||
1. Ordered List |
|
||
- [ ] Todo List |
|
||
> Blockquote | Blockquote |
||
**Bold font** | Bold font | ||
*Italics font* | Italics font | ||
~~Strikethrough~~ | |||
19^th^ | 19th | ||
H~2~O | H2O | ||
++Inserted text++ | Inserted text | ||
==Marked text== | Marked text | ||
[link text](https:// "title") | Link | ||
 | Image | ||
`Code` | Code |
在筆記中貼入程式碼 | |
```javascript var i = 0; ``` |
|
||
:smile: | ![]() |
Emoji list | |
{%youtube youtube_id %} | Externals | ||
$L^aT_eX$ | LaTeX | ||
:::info This is a alert area. ::: |
This is a alert area. |
On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?
Please give us some advice and help us improve HackMD.
Do you want to remove this version name and description?
Syncing
xxxxxxxxxx
SVATTT-Quals-2020: ascis_rmi_v1
Trước khi đi vào writeup bài này thì mọi người cần biết về RMI trong Java, mình sẽ nói sơ qua về RMI, và nó có liên quan gì đến Java deser
RMI là gì
Khái niệm
RMI (Remote Method Invocation) là một API trong java cho phép có thể truy cập method của một object từ xa trên máy khác. Ví dụ mình có 1 server có method
hello
của interfaceUser
, và phía client có interfaceUser
nhưng lại không có methodhello
, client muốn gọi method này sẽ đứng từ xa và dùng RMI để truy cập đến methodhello
của serverRMI được sử dụng trong các hệ thống phân tán, hoặc gọi đến các method của bên thứ 3. Ví dụ một thư viện
XYZ
cung cấp methodimgRender
để xử lý ảnh, src của thư viện được host ở một nơi khác, ta có thể dùng RMI để truy cập đến methodimgRender
mà không cần tải cả thư viện về.Nói sơ qua về cấu trúc của RMI sẽ như sau:

RMI sẽ bao gồm:
Stub
,Stub
là đại diện của remote Object, và mọi hành động của client đến server đều đi quaStub
Skeleton
, tác dụng tương tựStub
chỉ là khác tên gọiCode minh họa
Interface có ở client và server:

Remote method trên server:

Hàm main của server:

Code tại client:

Kết quả

Chạy server:
Chạy client:


Vậy thì RMI liên quan gì đến Java deser
Như đã nói về công dụng của việc seri dữ liệu trong Java, thì để trao đổi các Object qua lại như trong RMI đương nhiên ta sẽ cần đến việc tuần tự hóa dữ liệu, tuy nhiên RMI handle chuyện đó như thế nào.
Các bạn có thể tham khảo blog này. Bài blog nói khá chi tiế về cách RMI xử lý dữ liệu cũng như là các dạng tấn công RMI, có nhiều đoạn mình vẫn chưa hiểu vẫn cần debug thêm, trong tương lai mình sẽ có bài phân tích rõ RMI và các cách exploit RMI.
Tóm lại thì ta cần biết, trong 3 thành phần của RMI, thành phần nào cũng sẽ đều thực hiện quá trình tuần tự hóa, và nếu ta có thể kiểm soát được object truyền vào, ta hoàn toàn có thể exploit được cả 3 thành phần.
Bài
ascis_rmi_v1
ta sẽ exploit Java deser ở thằng RMI Server nên trong quá trình write up mình cũng sẽ giải thích sơ quá cách RMI server deser dữ liệuWriteups ascis_rmi_v1
Analysis
Link src chall : https://drive.google.com/file/d/1Gfwhs0K-sNEQtNtHNK2gZz9IDdrpyOHF/view
Chall cho ta 2 file jar là
ascis_player.jar
(client) vàascis_service1.jar
(server)File

ascis_player.jar
bao gồm:Src của
ASCISPlayer.java
File

ascis_service1.jar
bao gồm:Src của
ASCISServer.java
Đây là một RMI server đơn giản, tuy nhiên thứ mình chú ý tới là nội dung của
Player.java
vàASCISInterImpl.java
ASCISInterImpl:
Player:
Ta chú ý 2 điều:
Player.java
thì nếuisAdmin
là true thì ta hoàn toàn có thể thực thi cmd thông qua biếnlogCommand
, tuy nhiên điều kiện để điều này xảy ra là methodtoString
phải được gọilogin
củaASCISInterImpl.java
nhận vào một objectTa có thể tùy ý set giá trị của
isAdmin
vàlogCommand
bằng Reflection API, còn vấn đề làm cách nào gọi đượctoString
thì ta sẽ dùngBadAttributeValueExpException.readObject()
Flow exploit sẽ như sau
Chain thì cực kỳ đơn giản, tuy nhiên ta thấy server không hề gọi đến
readObject
, vậy thì làm sao ta trigger được payload ?Debug vào src code của RMI để biết cách RMI xử lý data, ta có cái nhìn tổng quát như sau:
Khi client
lookup
, nó sẽ seri tên object và gửi cho RegistryHàm
lookup
tạiRegistryImp_Stub.class
Sau đó nó deser dữ liệu trả về từ
Registry
Kết quả từ
(Remote)var4.readObject()
được gán chovar22
và returnvar22
cũng chính là giá trị trả về củaregistry.lookup
Tiếp thục ta gọi remote method với tham số

name
Lúc này method
unmarshalValue
tạiUnicastRef.class
sẽ được gọiunmarshalValue
sẽ deservar1
, màvar1
cũng chính là tham số ta truyền vào remote method ở trên.Tóm lại nếu ta truyền vào một tham số có kdl là object, thì tại RMI Server sẽ thực hiện deser object đó, vậy bây giờ ta chỉ cần tạo gadgetchain và đưa gadgetchain lên server thông qua method
login
Exploit
Payload:
Khi chạy code,

BadAttributeValueExpException.readObject
được gọi, gadgetchain được thực thi, ta popup được calcSVATTT-Quals-2022: Waf-deser
Analysis
Src của chall: https://github.com/to016/CTFs/tree/main/SVATTT/2022/Qual/WAF-Deser/src
Khi giải nén src thì ta được như sau:

Nội dung của
nginx.conf
Ta thấy file cấu hình này có 3 chức năng chính như sau
H4sI
thì sẽ return 403http://web:8080
Mở một project bất kỳ trên IntelliJ và import file jar chall cung cấp ta được các class như sau:

Nhìn lướt qua 3 class thì có vẻ chỉ có
UserController
là cần đáng lưu tâmClass này cũng chính là Controller của web, nó nhận một chuỗi base64 thông qua path
/info/<base64>
và decode để hiển thị ra dòng chữHello <name>
, ngoài ra nếu URL có paramcompress=true
, thì sẽ thực hiện gzip decompress và sau đó deserialize.Như vậy là ta đã tìm được sink của ứng dụng, chỉ cần đưa serialize data ở format gzip và được base64 encode, thì chương trình sẽ thực hiện deserialize.
Mở lib của ứng dụng để tìm thư viện có thể lợi dụng để làm gadgetchain, ta thấy lib có
CommonsCollections4
Vậy thì bây giờ chỉ cần gen payload và bem thôi
Exploit
Mình sẽ include
ysoserial
và dùnggetObject
để gen payload cho tiệnKết quả:

Mà hình như có gì đó sai sai, đoạn nginx config ở đầu đã block
H4sI
, nhưngH4sI
lại là magic bytes của gzip khi encode base64. Bây giờ mình phải tìm cách để bypass.Để bypass cũng khá đơn giản, mình đã từng có bài viết về hành vì decode base64 của PHP link, cụ thể thì nếu trong chuỗi base64 có ký tự rác, khi decode PHP sẽ tự động loại bỏ các ký tự đó
Mình tự hỏi là trong Java thì hành vi có tương tự không ?
Và câu trả lời là có, mình test bằng đoạn code sau:
Kết quả:
Ta thấy dù có thêm
@@
vào giữa chuỗi base64 thì vẫn decode ra cùng 1 kết quả, vậy chỉ cần thêm@@
vào giữa chuỗiH4sI
là easy bypassTuy nhiên ta còn một của ải cuối cùng là hàm
Hàm này thì khá đơn giản. Ta chỉ cần thay các ký tự
/
trong payload thành-
và lợi dụngreplaceAll("_", "/")
để đưa payload về đúng dạng.Code gen payload cuối cùng để reverse shell
Payload sẽ là
Đừng quên thêm

@@
vào giữaH4sI
Kết quả:

Refer
https://www.javatpoint.com/RMI
https://su18.org/post/rmi-attack/
https://drive.google.com/file/d/1Gfwhs0K-sNEQtNtHNK2gZz9IDdrpyOHF/view
https://github.com/tsug0d/LearnJavaVulnerability
https://github.com/vinhjaxt/CTF-writeups/issues/2
https://nhienit.wordpress.com/2022/10/20/waf-deser-ascis-2022-quals/
https://nguyendt.hashnode.dev/ascis-2022-qual-writeup-web-challenges#heading-waf-deser