> ## 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.

# 手把手玩 OAuth Labs（一）
- URL: https://taiwanding.com/shou-ba-shou-wan-oauth-labs-yi/
- Published: 2026-07-04T15:19:53.000Z
- Updated: 2026-07-15T02:50:32.000Z
- Author: Kevin Chen
- Tags: cloud, #OAuth Labs, machine

> 這是一篇 OAuth 漏洞的實戰入門紀錄，我用 cyllective 的 [oauth-labs](https://github.com/cyllective/oauth-labs?ref=taiwanding.com) 當靶場，從環境搭建（含 WSL2 踩到的坑）一路寫到 Lab 01 的完整分析，適合對 OAuth 攻擊有興趣、但還沒實際打過的人。

## 為什麼選 oauth-labs

OAuth 是現在幾乎每個「用 Google / GitHub 登入」背後的協定，實作錯了就是帳號接管等級的洞，但 OAuth 的攻擊面對新手不太友善——流程繞、名詞多，光看文件很難有感。

cyllective 的 oauth-labs 把常見的 OAuth 錯誤實作拆成六個獨立的 lab，每個只藏一個漏洞，而且**可以讀原始碼（白箱）**，對想建立 OAuth 攻擊直覺的人來說，這是很理想的起點。

六個 lab 的主題分別是：

- **Lab 00**：純 playground，沒有漏洞，用來熟悉流程
- **Lab 01**：不穩定的身分識別值（unstable claims）← 本篇
- **Lab 02**：完全不驗 `redirect_uri` 的 open redirect
- **Lab 03**：只驗 domain 的 open redirect
- **Lab 04**：JWT 簽章根本沒驗
- **Lab 05**：JWT `jku` claim 處理不當

## 環境搭建：Docker 很簡單，WSL2 才是關卡

靶場本身很好起，但我在 WSL2（Windows 上的 Kali）踩了一連串網路的坑，這段值得記錄，因為任何「綁在特定內部 IP 的 Docker 靶場」都會遇到。

### 起靶場

前置只有兩步：改 hosts、跑 make。先把 README 給的那一整串網域寫進 `/etc/hosts`（它用 `172.16.16.0/24` 這個網段，注意別跟你現有網路撞到），然後：

```bash
git clone https://github.com/cyllective/oauth-labs
cd oauth-labs
make config && make docker && make labs

```

`docker compose ps` 看到一排 `server-0x` / `client-0x` 都 `Up` 就成功了。

### 坑一：瀏覽器在 Windows，hosts 卻加在 WSL

第一個 `ERR_NAME_NOT_RESOLVED` 就卡住了，原因很經典：我 `sudo tee -a /etc/hosts` 加的是 **WSL（Linux）的 hosts**，只有 Linux 裡的程式查得到；但我用的是 **Windows 的瀏覽器**，它查的是 Windows 自己的 hosts，那裡根本沒有 `oauth.labs`。

**兩個系統各有各的 hosts**，這是 WSL2 開發最常見的認知落差之一。

### 坑二：服務綁死在 Docker 內部 IP，Windows 連不進去

就算 Windows 的 hosts 也加了，還是連不到。回頭看 `docker compose ps`，caddy（反向代理）的綁定是：

```
172.16.16.1:80->80/tcp, 172.16.16.1:443->443/tcp

```

它**只綁在 `172.16.16.1`**——這是 Docker 網路的 gateway，存在於 WSL 內部，Windows 路由不過去，

檢查 compose 檔證實了這點：

```yaml
caddy:
  ports:
    - "172.16.16.1:80:80"
    - "172.16.16.1:443:443"

```

治本的做法是讓它綁到全介面。把那個 IP 前綴拿掉：

```bash
sed -i 's|"172.16.16.1:80:80"|"0.0.0.0:80:80"|; s|"172.16.16.1:443:443"|"0.0.0.0:443:443"|' docker-compose.yaml
make labsdown && make labs

```

重啟後 caddy 變成 `0.0.0.0:443->443`，就綁到 WSL 的所有介面了，接著在 Windows 的 hosts 把所有 `*.oauth.labs` 指到 **WSL 的 eth0 IP**（用 `ip addr show eth0` 查，通常是 `172.2x.x.x` 那個），Windows 的瀏覽器與 Burp 就能連進來。

> 提醒：WSL 的 eth0 IP 重開機後可能改變，連不上時重查、更新 Windows hosts 即可。

## Lab 01：找出「client 拿什麼認你」

環境好了，進 Lab 01，攻擊目標寫在關卡說明：**取得 admin 使用者的資源存取權**，漏洞主題是「unstable claims」。

打漏洞之前，我習慣先掛上 Burp、走一遍**正常流程**——不知道正常長怎樣，就不知道哪裡不正常，

### 先看 authorization request

在 server-01 註冊帳號、從 client-01 發起登入，攔到的 authorization request（URL decode 後）是這樣：

```
/oauth/authorize?
  client_id=<uuid>
  code_challenge=<...>
  code_challenge_method=S256        ← 有 PKCE
  redirect_uri=https://client-01.oauth.labs/callback
  response_type=code                ← authorization code flow
  scope=read:profile
  state=<...>

```

這是標準的 authorization code flow 加上 PKCE，而且 `redirect_uri` 是寫死的。這告訴我：**這關的漏洞不在 `redirect_uri` 或 code 攔截**，要往別的方向找。

### 關鍵在「client 認人的那一頁」

真正的線索出現在登入成功後、client-01 的 profile 頁，它的回應長這樣（節錄）：

```html
<h1>Thanks for signing in!</h1>
<p>To change your profile details, please visit https://server-01.oauth.labs</p>
<label>Firstname</label> <input value="" readonly>
<label>Lastname</label>  <input value="" readonly>
<label>Email</label>     <input value="" readonly>
<p>... hrm something seems to be missing.</p>

```

這一頁給了兩個明示：

1. **Firstname / Lastname / Email 三個欄位全空**，還自嘲「something seems to be missing」——代表 client 想顯示這些資料，但拿不到（因為我註冊時沒填），**client 依賴 server 傳來的這些欄位。**
2. **它直接叫你去 server-01 改資料**——代表你的身分資料（claim）是在 server-01 那邊、由你自己填的、而且可以改。

### 推理：哪個欄位被拿來當「身分」？

到 server-01 的 profile 編輯頁，可以改的欄位正是 **firstname、lastname、email** 三個。

這三個裡，哪個最可能被 client 當成「你是誰」的唯一識別？

- `firstname` / `lastname` 比較像「顯示用的名字」，一般不會拿來認人。
- **`email` 則常被系統當成唯一身分**——很多網站就是用 email 認人的。

再加上這關的名字是「**unstable claims（不穩定的識別值）**」，而 email 剛好是「使用者能自己改」的欄位——**題目名稱本身就在暗示：client 蠢在拿了一個『可變』的東西當『身分』。**

這個判斷不是憑空猜的，是把四條觀察串起來推出來的：client 依賴 server 的欄位 → 欄位由使用者自填可改 → 三個欄位裡 email 最像身分識別 → 關卡名稱印證。**這一連串「觀察 → 推理」就是漏洞分析本身。**

### 驗證機制，再打目標

我沒有直接跳去填 admin 的 email，而是先做一個對照實驗：**把 email 填一個測試值，存檔，重新登入 client-01，看它的 profile 有沒有變化**，結果 client-01 的 profile 從「全空」變成顯示了我填的 email——**證實了 client-01 確實用 email 認人。**

先證明機制、再打目標，這個順序很重要：如果直接填 admin email 然後失敗，你會分不清是「email 根本不是身分識別」還是「admin email 被某種檢查擋掉」。

確認機制後，剩下的就是把自己的 email 改成 admin 的、重新登入，這關的 server 沒有對 email 做唯一性檢查，所以能直接改成 admin 的 email；client 用 email 認人，於是重新登入後就被誤認成 admin。

## 這個漏洞的本質

一句話總結：

> **client 拿了 OAuth 身分證明裡「使用者能自己修改的 email」當唯一身分，而不是用不可變的 `sub`；server 又沒鎖 email 唯一性**，**兩者相加，任何人都能冒充任何人。**

### 為什麼是 OAuth 漏洞

OAuth 流程結束時，authorization server 會回給 client 一組使用者資訊（claims），裡面通常有：

```
sub:   永久不變的內部 ID（伺服器指派，使用者控制不了）
email: 使用者能自己改
name:  使用者能自己改

```

OAuth／OIDC 標準明確規定：client 要用 `**sub**` 來辨識使用者，正因為它穩定、唯一、不可控，這關的 client 偷懶用了 `email`——一個「不穩定的 claim」——身分驗證的地基就塌了，這不是靶場才有的紙上談兵，真實世界一堆「用 email 認人」的整合都栽在這裡。

### 它其實是你熟悉的老朋友

如果你做過 web 滲透，這個漏洞的骨架你早就認識：

> **系統用「使用者可以控制的值」來做「安全決策」。**

- cookie 裡的 `role=user`，你改成 `role=admin`
- URL 的 `?user_id=123`，你改成 `?user_id=1` ← 這就是 **IDOR**
- 這關的 email，你改成 admin 的 ← 換了 OAuth 的外衣

所以「改 email 變 admin」不是一條通用公式，通用的是那個問法：**「這個決定我身分／權限的值，是不是我能控制的？能的話，改成別人的會怎樣？」** 這跟 IDOR／BOLA 是同一顆腦子。

## 小結

Lab 01 沒有花俏的技巧，但它把一個很重要的觀念講得很清楚：**OAuth 的安全，很大一部分取決於 client『選擇相信身分證明裡的哪個欄位』​**，選錯欄位，再完整的 PKCE、再嚴謹的流程都保護不了你。

*本文為個人學習紀錄，所有操作皆在本機自建的靶場環境進行，文中不含 flag 與具體識別值，鼓勵讀者自行部署 oauth-labs 動手實作。*