---
# System prepended metadata

title: MiniCTF2025(Solved 8/8 Web Challenge)

---

# MiniCTF2025(Solved 8/8 Web Challenge)

## Super Cookie(Easy)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/ryqloSSjlg.png)

- Chúng ta tiến hành Đăng ký và đăng nhập thử
![image](https://hackmd.io/_uploads/B1FIirHoxx.png)

- Sau khi đăng nhập thành công, có thể thấy rằng phiên đăng nhập sẽ được cài đặt phần Cookie thông qua header ==Set-Cookie==. 
- Các ==request== sau đó đều sẽ được đính thêm header ==Cookie== cùng các thông tin ==user== và ==role==
![image](https://hackmd.io/_uploads/rysVnSrogl.png)

- Từ chúng ta thử sửa đổi các giá trị của tham số ==user== và ==role==
![image](https://hackmd.io/_uploads/BJAn3SHsee.png)

- Kết quả chúng ta được điều hướng đến trang ==/flag.html== và có được flag
:::success
:100: Giải Challenge thành công
:::

## The Queen's Secret(Easy)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/BJ7cTBHsel.png)

- Cuốn chuột xuống phía dưới một chút ta để ý dòng gợi ý nho nhỏ
![image](https://hackmd.io/_uploads/HySaprSoxl.png)
- Để ý đoạn chữ =="quyền quản trị"==, có vẻ như tương tự như challenge trước, ta cần làm như nào đó để server nhận diện rằng phiên làm việc của mình có ==role== như người quản trị
- Sử dụng ==Inspect== của Browser và đi đến phần ==Apllication==
![image](https://hackmd.io/_uploads/ry4mRSSogl.png)
- Ta sửa đổi giá trị của ==queen== thành ==true== và tải lại trang
![image](https://hackmd.io/_uploads/BJwrCSHjel.png)

:::success
:100: Giải Challenge thành công
:::

## Choose the way(Medium)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/ByupCBHsgl.png)

- Chức năng của challenge cho phép ta ==đăng tải== một số định dạng tệp, và cũng như cho phép ==tải xuống== tệp mình đã đăng tải
- ==Request== để tải tệp có dạng `GET /download?name=<tên tệp>`. Với kiểu challenge như vậy ta đoán ngay là ==Path Traversal== và tiến hành chứng minh
![image](https://hackmd.io/_uploads/S1ZOkIrigx.png)
- Để ý dòng thông tin ở gần cuối ==dMOsbSBnw6wgxJHhuqV5LCBjw6FpIG7DoHkgw6AgIm1pbmlDVEZ7VGgzX0g0cHB5XyIgbuG6v3UgbXXhu5FuIHRow6ptIHRow6wgeGVtIHRow7RuZyB0aW4gY3B1IHRo4butIHhlbSAoZ+G7o2kgw70gL3Byb2MxKQ\=\===, có định dạng rất giống ==Base64==. Ta Decode thử
- Kết quả ta thu được chuỗi: ==tìm gì đấy, cái này à "miniCTF{Th3_H4ppy_" nếu muốn thêm thì xem thông tin cpu thử xem (gợi ý /proc1)==
- Dựa vào gợi ý ta truy cập đến tệp: ==/proc1/cpuinfo==
![image](https://hackmd.io/_uploads/ryKt-IHjee.png)

- Ta thu được chuỗi ==cDR0aF8wZl8\===, Decode ta được chuỗi ==p4th_0f_==. Nhưng ==flag== ta thu được vẫn chưa hoàn thiện.
- Sau khi kiểm tra một số tệp khác như ==/proc/cpuinfo==, ==/proc/self/environ== cũng không chứa thông tin liên quan đến ==flag==. Để có thêm thông tin rất có thể các bạn cần nghĩ cách để tìm và đọc được tệp mã nguồn. Vấn đề hiện tại là mình chưa biết được tệp mã nguồn nằm ở đâu và tên như thế nào. 
- Có một kĩ thuật có thể sử dụng. Tệp ==/proc== là nơi chứa các thông tin về tiến trình đang chạy, Challenge này cũng được chạy bởi một tiến trình, đó là tiến trình có số ==01==, chúng ta có thể đọc thông tin thông qua tệp ==/proc/1/cmdline==, tệp tin sẽ chứa câu lệnh đã được chạy để khởi động challenge này lên và sẽ có tiến trình đảm nhận cho công việc này, sau khi đọc thông tin trong tệp ta biết được lệnh để chạy khởi động ==Server== cho challenge là `python app.py` -> ta biết được tên tệp ==(1)==. 
![image](https://hackmd.io/_uploads/HknbG48oee.png)


- Mỗi một tiến trình như vậy sẽ có một đường dẫn đặc biệt tên là ==cwd(current work directory)==, đường dẫn này bản chất là 1 ==symbolic link==, và nó sẽ trỏ đến thư mục làm việc hiện tại của tiến trình có ==PID== tương ứng ==(2)==.
- Từ ==(1)== và ==(2)== ta đã có đủ thông tin cần thiết để truy cập tệp mã nguồn. Đường dẫn ta cần truy cập sẽ là ==/proc/1/cwd/app.py==
![image](https://hackmd.io/_uploads/SJE_M4Uoex.png)

- Phần còn lại của ==flag== nằm ở chuỗi đã bị ==Base64Encode==:
![image](https://hackmd.io/_uploads/HJdy7EIolx.png)

- Decode ra ta được chuỗi: ==# Lấy timestamp từ tên file d1sc0v3ry}==. Ta thu được phần còn lại của flag
:::success
:100: Giải Challenge thành công
:::

## Flapy bird(Medium)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/S10amN8oxe.png)

- Chúng ta có thể xem qua tệp ==javascript== để xem cách thức Challenge này hoạt động như thế nào thông qua ==Inspect== của Browser
![image](https://hackmd.io/_uploads/ryaNEVIjel.png)

- Challenge này về cơ bản là yêu cầu ta chơi game dạng như game ==flapy bird== và khi đạt đủ ==9,999,999,999== điểm sẽ gọi đến hàm `returnof()` để lấy và trả về ==flag==.
- Tuy nhiên ta lại không thể truy cập đến đường dẫn chứa ==flag== nếu như ta tự truy cập
![image](https://hackmd.io/_uploads/HylAEVUjgx.png)

- Lúc này có thể tư duy logic theo 2 hướng:
    1. Nếu như đoạn javascript `fetch('get_flag.php')` trong hàm `returnof()` có trả về ==flag==, vậy thì điều gì khiến cho ta bị ==Access denied==. Từ đó có thể logic rằng có một cơ chế xác thực nào đó khiến cho ==request== do mình tự gửi đi không trả về ==flag==
    2. Có thể chúng ta khai thác việc chơi game để đạt được ==9,999,999,999== mà không phải chơi theo cách thông thường. thì lúc đó hàm `returnof()` sẽ trả về ==flag== cho chúng ta.
- Sau khi xem xét qua mình thấy chưa có phương pháp nào để khai thác theo hướng ==2.==, vậy nên ta sẽ thử suy nghĩ theo hướng ==1.==. Và ta sẽ dùng header ==Referer== để khai thác challenge này. Thông tin về header này các có thể tham khảo thêm [tại đây](https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Referer)
- Payload: ==Referer: \<IP Host của Challenge\>==
![image](https://hackmd.io/_uploads/r14FIEIiee.png)

:::success
:100: Giải Challenge thành công
:::

## 4lic3(Hard)

- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/rkPOd48igl.png)

- Cuốn chuột xuống một đoạn ta thu được thêm đoạn thông tin:
![image](https://hackmd.io/_uploads/SyGq0E8jxe.png)

1. Xem xét tổng quan
- Về cơ bản ý tưởng của challenge là dựa vào các thông tin có sẵn, ta tạo ra một mã ==session token== hợp lệ và ==submit== để lấy được flag

- Dựa vào phần gợi ý và phần ==format==, có thể ==token== được tạo ra bằng cách ==encrypt== đoạn ==format== sử dụng thuật toán ==aes-ecb-128==, thông tin về thuật toán xin tham khảo thêm [tại đây](https://www.geeksforgeeks.org/computer-networks/electronic-code-book-ecb-in-cryptography/)

2. Phân tích
- Ta có ==Sooty(admin) token== và ==Sweep(user) token== có chung ==session expried==
![image](https://hackmd.io/_uploads/ryTexHUsxe.png)

- Ta có ==Sweep(user) token== và ==Soo(user)== có chung ==user==:
![image](https://hackmd.io/_uploads/r16NgrLolg.png)

- Khi đối xứng các đoạn chuỗi giống nhau của các ==token== xuống phần ==format==, ta thấy vị trí giống nhau có vẻ tương đồng với các giá trị của ==format== từ =="user":""== đến =="ex":""== đến =="level":""== và =="bio":""== ==(1)==
- Hơn nữa độ dài của các token là ==bội số của 32== và challenge cũng có ==hint== rằng chương trình sử dụng mã hóa ==128bit== ==(2)==
- Từ ==(1)== và ==(2)== có thể suy ra 2 điều:
    1. ==Token== được tạo ra bằng cách mã hóa lần lượt từng giá trị trong ==format== bằng thuật toán ==aes-ecb-128== rồi ghép lại thành token thay vì mã hóa toàn bộ chuỗi ==format==
    2. Vì độ dài mã hóa là ==128 bit==, nên đoạn token sẽ được chia ra thành các khối ==32 ký tự==, do sau khi encrypt độ dài chuỗi là ==16 bytes(128bit)== và chuyển đổi về định dạng ==hex== là ==32 ký tự==, ==1 byte = 2 ký tự hex==
- Từ đây để ghép thành đoạn mã token hợp lệ ta cần có:
    - Phần ==session valid== từ ==Soo==
    - Phần ==level admin== từ ==Sooty==
- Tuy nhiên ta cũng cần chọn đúng ==user== và ==bio== của user tương ứng
- Payload cuối cùng có dạng: ==32 ký tự(block) user== của ==Sweep== + ==session valid block== từ ==Soo== + ==level admin block== từ ==Sooty== + ==bio block còn lại== của ==Sweep== 
- Payload: ==3061837c4f9debaf19d4539bfa0074c1174d4b2659239bbc50646e14a70becef837d1e6b16bfae07b776feb7afe57630caeb574f10f349ed839fbfd223903368873580b2e3e494ace1e9e8035f0e7e07==
![image](https://hackmd.io/_uploads/HypO7B8ixl.png)

:::success
:100: Giải Challenge thành công
:::

## MemoryS(Hard)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/HJKA4rUslg.png)

- Ta Click vào ==Truy cập page== và ngay lập tức được điều hướng đến ==/index.php?page=page.php== 
![image](https://hackmd.io/_uploads/r1jGrSIjeg.png)

- Ngay lập tức ta nghi ngờ loại lỗ hổng là ==Path Traversal== và tiến hành chứng minh
![image](https://hackmd.io/_uploads/r1YrSBIoxl.png)

- Tuy nhiên ta chưa thể xác định được đường dẫn đến tệp ==flag== cũng như tên tệp.

- Ta thử cố ý gây lỗi nhằm thu thập thêm thông tin về ==Server==:
![image](https://hackmd.io/_uploads/ryHqrSIoxe.png)

- Có vẻ như giá trị được truyền trực tiếp vào hàm `include()`. Qua đây ta có thể tận dụng một kĩ thuật để truyền ==PHP code== vào hàm `include()` và khiến hàm thực thi trực tiếp. Thông tin về phương pháp xin tham khảo thêm [tại đây](https://book.hacktricks.wiki/en/pentesting-web/file-inclusion/lfi2rce-via-php-filters.html)

- Về cơ bản kĩ thuật này sẽ thực hiện một loạt các biến đổi ==Encoding== để chèn ==PHP Code== vào hhàm`include()`, cách chèn thông thường sẽ không được do `include()` nhận đầu vào là một ==stream==
- Vì phép biến đổi này rất dài cho nên ta cần công cụ để hỗ trợ tạo ==Payload==, Thông tin về công cụ xin vui lòng tham khảo thêm [tại đây](https://github.com/synacktiv/php_filter_chain_generator)

- Sử dụng công cụ để tạo Payload thực thi đoạn mã `<?php system('ls /');?>`
![image](https://hackmd.io/_uploads/BJkvuBUoeg.png)

- Ta truy cập đến tệp ==flag==
![image](https://hackmd.io/_uploads/r1MKOr8slx.png)

:::success
:100: Giải Challenge thành công
:::

## Super Memory(hell)
- Phần ==Dashboard== của Challenge
![image](https://hackmd.io/_uploads/ByVp_SIjlg.png)

- Phần chức năng, cách khai thác giống với challenge trước, chỉ khác ở một điểm challenge này đã ==disable== đi một số hàm quan trọng
- Ta có thể kiểm tra thông qua ==Payload== để thực thi đoạn mã `<?php phpinfo();?>`
![image](https://hackmd.io/_uploads/BkuuYBLsee.png)

- Dòng ==disable_functions== cho ta thấy các hàm bị vô hiệu hóa
- Tuy nhiên ta có thể thay đổi payload sang thực thi đoạn mã `<?php print_r(scandir('/'));?>`
![image](https://hackmd.io/_uploads/ByAo9SIsxg.png)


- Truy cập tệp và ta thu được ==flag==
![image](https://hackmd.io/_uploads/H1m6cHIige.png)

:::success
:100: Giải Challenge thành công
:::

## Best Friend Memory Revenge(hell)
- Phần ==Dashboard== của Challenge:
![image](https://hackmd.io/_uploads/S1qziBLsxx.png)

- Sau khi xem xét thì có vẻ như chưa có phương pháp tiếp cận để ta khai khác. Ta có thể thử sử dụng công cụ để tìm thêm các ==surface== khai thác khác.
- Ở đây ta thử tìm thêm các tham số ẩn bằng công cụ ==arjun==, thông tin về công cụ xin tham khảo thêm [tại đây](https://github.com/s0md3v/Arjun)
- Sau khi dùng công cụ ta thu được tham số ==page==:
![image](https://hackmd.io/_uploads/BykDnrUjle.png)

- Thử truyền giá trị vào tham số
![image](https://hackmd.io/_uploads/rkYq2rUigg.png)

- Từ đây bài toán lại trở về như 2 challenge trước. Nhưng khác biệt ở chỗ challenge này đã chặn chuỗi ==convert== có trong ==Payload== khai thác
![image](https://hackmd.io/_uploads/ryMgTSLigg.png)

- Để ==bypass== ta dùng kĩ thuật là ==double encoding==, và chuỗi ==convert== sẽ thành ==%2563onvert==
- Ta tạo ==Payload== với công cụ để chạy đoạn mã `<?php print_r(scandir('/'));?>` và thay thế chuỗi ==convert== thành ==%2563onvert==
![image](https://hackmd.io/_uploads/HJRMASIiel.png)

- Ta truy cập tệp chứa ==flag==
![image](https://hackmd.io/_uploads/By8Y0BUoee.png)

:::success
:100: Giải Challenge thành công
:::






 










![image](https://hackmd.io/_uploads/H1WZ_w4PWe.png)

![image](https://hackmd.io/_uploads/SyeK7ODVPbg.png)
