HTB LinkVortex
題目:
https://app.hackthebox.com/machines/LinkVortex
靶機的 IP 以下統一用
HTB_TARGET_IP代表 ( 每次 spawn 都不一樣,請自行代換 ) 。目標:
- 拿到靶機上的
user.txt和root.txt的內容。
- 拿到靶機上的
靶機已退役 ( 2024/12/07 release ,難度 Easy ) ,本文僅供教育與學習用途,請勿將文中技術用於未經授權的系統,文中也不會貼出 flag 內容。
nmap 掃描 HTB_TARGET_IP
sudo nmap -sSCV -Pn HTB_TARGET_IP |
-sS代表 SYN 掃描 ( 不進行完整的三次握手 ) 。-sC代表執行內建的預設腳本掃描。-sV代表服務版本探測。-Pn代表 no ping mode ( 適用於目標封鎖 ICMP 或防火牆過濾 ping 的情況 ) 。
大致結果:
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) |
這樣代表 HTB_TARGET_IP 應該要 redirect 到 linkvortex.htb 。
設定 HTB_TARGET_IP redirect 到 linkvortex.htb
到 /etc/hosts 底部加入 IP 和對應域名:
HTB_TARGET_IP linkvortex.htb |
這樣就可以直接從 browser 輸入 http://linkvortex.htb 查看網頁。
爆破 linkvortex.htb subdomain
用 ffuf 進行 subdomain 爆破:
# 使用的 wordlist : |
根據指令結果可以找到有 dev.linkvortex.htb 這個 subdomain ,也要把它加入 /etc/hosts :
HTB_TARGET_IP linkvortex.htb dev.linkvortex.htb |
這邊小總結一下:找 domain 跟 subdomain 的時候通常都是要先用 可以檢測到 的方法 ( 通常不會是 browser 直接輸入,不然這樣就不需要設定 IP-Host 對應表了 ) 先偵測出有甚麼名稱,之後再來設定 IP-Host 對應表。
用 browser 打開 dev.linkvortex.htb 有東西,但是沒看到甚麼有用的資訊,先把這個線索放著。
nmap 掃描 linkvortex.htb
既然前面有寫好 IP-Host 對應表,那這次就掃 linkvortex.htb :
sudo nmap -sSCV -Pn linkvortex.htb |
大致結果:
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) |
上面的結果有 robots.txt ( http://linkvortex.htb/robots.txt ) 並且可以看到它有 disallow 一些可疑的路徑。
robots.txt等等的路徑其實可以直接用通靈的方式猜它就是有這個路徑,或者使用dirsearch爆破得到。
嘗試 http://linkvortex.htb/ghost
在 browser 輸入 http://linkvortex.htb/ghost 會到 http://linkvortex.htb/ghost/#/signin ,是一個登入介面。 ( 當時手動戳 SQL injection 沒有甚麼結果 )
目前卡在沒有手段通過或繞過登入介面。
nmap 掃描 dev.linkvortex.htb
sudo nmap -sSCV -Pn dev.linkvortex.htb |
大致結果:
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0) |
可以看到它有 git leak ,並且還知道它應該是用 https://github.com/TryGhost/Ghost.git 這個專案。
對 http://dev.linkvortex.htb 的 git leak 攻擊
這邊有可能可以使用的專案,我這次是使用 GitHack :
https://github.com/lijiejie/GitHack ( 這題用這個 ) |
python3 GitHack.py http://dev.linkvortex.htb/.git |
觀察剛剛解出來的 git repository
可以用各種想得到的關鍵字去做整個專案的搜尋,這邊我使用 password :
grep -rn "password" dev.linkvortex.htb/ --include="*.js" | grep -v node_modules |
可以看到許多有趣的資訊:
// dev.linkvortex.htb/ghost/core/test/regression/api/admin/authentication.test.js |
會往 test/ 目錄看是因為測試相關檔案可能是 hardcoded credentials 的重災區 ( ? ) ,開發者很容易把測試時用的 password 直接留在 repository 裡面。
到這邊開始 "通靈" 要怎麼利用這些帳號密碼
專案裡面可以看到 password 之類的東西,但是帳號都是 test ,推測是測試用的檔案,但是 password 感覺 如果開發者懶得修改 ,而 沿用 test 時候的測試密碼 ,那就代表 可能有一組或者多組帳號適用 test 時期的密碼 ,先猜有 admin ,再來可以看到 email 那邊出現 example.com ,推測可能還要猜 domain name ( 畢竟 example.com 應該只是測試用 ) ,目前知道的 domain name 就是 linkvortex.htb ,那就先猜測帳號是:
admin@linkvortex.htb |
接下來拿所有密碼嘗試看看 http://linkvortex.htb/ghost/#/signin ,就發現這組帳號密碼會直接可以登入:
帳號: |
進去之後有一個類似 admin 的介面。
接下來可能可以看 admin 有甚麼事情可以做,沒有想法的話可能可以查看看有沒有 CVE 可以直接做到某些事情。
使用 CVE-2023-40028
這邊首先可以從先前的 nmap 結果知道剛剛登入的管理系統是 Ghost ,並且版本是 5.58 。
找一下資料並且對應一下版本後發現有這個任意讀檔漏洞:
https://github.com/0xDTC/Ghost-5.58-Arbitrary-File-Read-CVE-2023-40028 |
About:
- CVE-2023-40028 affects Ghost, an open source content management system, where versions prior to 5.59.1 allow authenticated users to upload files that are symlinks. This can be exploited to perform an arbitrary file read of any file on the host operating system.
受影響版本為 Ghost ≤ 5.59.0 ,修補版本是 5.59.1 , CWE 分類是 CWE-22 ( 路徑穿越 ) + CWE-59 ( 符號連結處理不當 ) 。
5.58 剛好是適用範圍。
它的先決條件是自己要是 authenticated users ,而我們先前已經達到。
exploit 的核心其實只有兩行:
# 做一個指向目標檔案的 symlink ,副檔名偽裝成 .png |
少了 -y , zip 會在自己的電腦上就把 symlink 跟到底、存成檔案內容,傳上去對靶機沒有意義。
Ghost 的修補其實不在 Ghost 本體:v5.59.0 → v5.59.1 沒有加任何 symlink 檢查,只是把 @tryghost/zip 從 1.1.34 升到 1.1.37 ,再補一個 regression test 。真正的檢查加在那個套件的 lib/extract.js ,新增了一個檢查函式:
function throwOnSymlinks(entry) { |
然後把它套用到解壓流程上:
if (opts.onEntry) { |
重點在 throwOnSymlinks 怎麼判定。 zip 的每個 entry 都帶有 externalFileAttributes 欄位, Unix 打包時會把檔案 mode 寫進它的高 16 bits ,所以 >> 16 先把 mode 取出來。接著 & IFMT ( 程式碼裡的 61440 ) 這個遮罩只留下最高 4 個 bit ,那 4 個 bit 就是檔案類型:
mode 1010000111111111 ← fake.png 的 mode ( 0o120777 ) |
被遮掉的後 12 個 bit 是權限 ( 777 ) ,判定類型時不看。一般檔案的前 4 bit 是 1000 、目錄是 0100 、 symlink 是 1010 ,所以只有 symlink 會命中。 IFMT 和 IFLNK 就是 inode(7) 裡定義的 S_IFMT / S_IFLNK 。
命中就丟 UnsupportedMediaTypeError ,對外是 HTTP 415 。
使用該工具任意讀檔:
chmod +x CVE-2023-40028 |
進入該工具的 shell 後輸入檔案路徑就可以讀檔。
"通靈" 要讀什麼檔...
目前已經可以任意讀檔,但是還是不確定要讀哪個才是關鍵。
這邊要注意讀檔範圍是在 Ghost 的 container 裡面,不是宿主機 ,所以 /root/root.txt 這種檔案讀不到,思路要改成在 container 裡面找可以橫向移動到宿主機的憑證。
我這邊是先從 dev.linkvortex.htb/Dockerfile.ghost 去找,因為它會直接告訴我們這個 container 的檔案都放在哪,從該檔案可以找到這些路徑:
/var/lib/ghost/config.production.json |
嘗試過後 /var/lib/ghost/config.production.json 給出了一些有趣的資訊:
"mail": { |
這邊找到的是 SMTP 服務的密碼,可以嘗試 SMTP 服務,但是同時也可以多多嘗試其他服務,因為可能會用相同的密碼!
發現 ssh 可以用上面找到的帳密登入
指令:
ssh bob@linkvortex.htb |
密碼:
fibber-talented-worth |
密碼重用成功,而且這次是真的登進 宿主機 了,不是 container 。登入後就可以直接看到 user.txt :
cat /home/bob/user.txt |
接下來要想辦法提權,應該是要讀取 /root/root.txt 。
檢查 bob 權限
這邊可以先看一下 bob 能做到甚麼事情:
sudo -l |
Matching Defaults entries for bob on linkvortex: |
可以知道 bob 可以用 sudo 執行 /usr/bin/bash /opt/ghost/clean_symlink.sh *.png 這個特定指令。
另外 env_keep+=CHECK_CONTENT 這行代表 CHECK_CONTENT 可以被我們帶進 sudo 裡面( sudo 預設的 env_reset 會把其他環境變數清掉),這是最後提權指令能成立的前提。
檢查 /opt/ghost/clean_symlink.sh 的內容 ( 這邊有加上我的註解 )
|
以上 script 告訴我們它主要是用來讀取 symlink 所連結到的內容,但是我們需要的 /root/root.txt 又會被它擋掉,可能要想辦法繞過它。
用多個 symlink 繞過檢查
觀察 /opt/ghost/clean_symlink.sh 可以知道它是使用 readlink 指令來解析 "最終位置" ,然後用它認為的 "最終位置" 來當作過濾 etc 、 root 的依據。
readlink 補充 ( 假設現在有一個 symlink 是 a --> b --> c ) :
readlink a會得到b,只會印出 該連結的直接目標 。readlink -f a會得到c,會遞迴解析到最後。
可以看到 script 裡面沒有使用 -f ,所以它會解到第一層而已,那就使用兩層 symlink 來繞過檢查即可。
Sticky Bit 與 Symlink 衝突的補充:
https://forum.hackthebox.com/t/official-linkvortex-discussion/333971/282?page=10
Sticky Bit 在 Linux 中主要是確保只有擁有者或
root能夠刪除或修改。當用戶嘗試從有 sticky bit 的目錄內建立一個 symlink 到
root擁有的檔案,並讓某個權限較高的 process ( 例如root) 去讀取該 symlink 時, Linux kernel 的某些 安全保護機制 會介入,導致rootprocess 無法解析這個 symlink 。
用多個 symlink 繞過 script 限制,並且用新創的 folder 來繞過有 sticky bit 的 /tmp 會觸發的 Linux kernel 安全保護機制,這樣就可以拿到 /root/root.txt 的內容了:
mkdir -p /tmp/new_folder |
BTW
grep -Eq '(etc|root)'是 子字串比對 ,不是比對路徑層級。所以中繼 symlink 的路徑裡面不能出現etc或root這兩個子字串,像fetch.png( f-etc-h ) 就會莫名其妙被擋掉,取名字的時候要小心。

