iT邦幫忙

2025 iThome 鐵人賽

DAY 8
0
Security

規組駭了了::你無定落勾去的鑠奅 Web Hacking 步數系列 第 8

0x08 / CTF・PHP・LFI + PHAR 閣有新招

  • 分享至 

  • xImage
  •  

講到 PHP 的 include,逐家加減攏會想著 LFI (Local File Inclusion) 這个老朋友。本來想講,這个古早味的漏縫欲耍 LFI to RCE 到 Solving "includer's revenge" from hxp ctf 2021 without controlling any files 彼改應該已經變無步矣,毋過咱「規世界上好的程式語言」PHP 原仔是會使變新齣頭予咱!這篇文章欲來紹介 DeadsecCTF2025 的一題「baby-web」,雖然無用 PHP filter chain 烏白隨咱生資料彼改遐爾仔欻,毋過總是算一个好耍的特性。

題目

這題的 code 嘛算簡單,一隻 upload.php 處理檔案上傳,閣一隻 index.php 去共 include,

<?php
// index.php
$include_url = basename($_GET['url']);
$SANDBOX = ...;
// ... 一寡 sandbox 檢查/建立的 code ...
if (!file_exists($SANDBOX . '/' . $include_url)) die("Nope :<");
if (!preg_match("/\.(zip|bz2|gz|xz|7z)/i", $include_url)) die("Nope :<");
@include($SANDBOX . '/' . $include_url);
?>
<?php
// upload.php
$allowed_extensions = ['zip', 'bz2', 'gz', 'xz', '7z'];
$allowed_mime_types = [
    'application/zip', 'application/x-bzip2', 'application/gzip',
    'application/x-gzip', 'application/x-xz', 'application/x-7z-compressed',
];
function filter($tempfile) {
    $data = file_get_contents($tempfile);
    if (stripos($data, "__HALT_COMPILER();") !== false ||
        stripos($data, "PK") !== false ||
        stripos($data, "<?") !== false ||
        stripos(strtolower($data), "<?php") !== false) {
        return true;
    }
    return false;
}

// ... (後壁的上傳理路)

簡單講這个挑戰有 3 个重點:

  1. 檔案上傳限制:咱干焦會當傳 zip, bz2, gz, xz, 7z 這幾種壓縮檔去裡。
  2. 內容過濾:伊會檢查講咱傳的檔案內底袂使有 __HALT_COMPILER();PK<? 抑是 <?php 這款字,若有就失敗。
  3. 檔案包含index.php 會用 basename($_GET['url']) 來提著檔名,確保你袂使利用 ../ 來跳目錄,嘛袂使直接用 phar:// 這款的 wrapper。最後,伊會共你傳去裡的檔案 include 入來。

include 一个壓縮檔是會使創啥?內容都無 <? 矣閣,是欲按怎走 PHP code?這就是這題上趣味的所在。

深入 PHP 的底層

欲了解這个問題咱著愛共 PHP 的ia̋n-jín蓋掀開,看伊底層的 C source code 是按怎運作的。問題的關鍵,就佇 include 這个動作頂頭。

若 PHP 欲用 include 去共一个檔案印入來的時,伊會去叫一个叫做 compile_filename 的函式。這个函式會沓沓仔一步一步行,上尾仔才閣去叫著 phar_compile_file 來處理咱的 phar 檔案。

關鍵的 code 佇 phar_compile_file 內底遮:

if (strstr(ZSTR_VAL(file_handle->filename), ".phar") && !strstr(ZSTR_VAL(file_handle->filename), "://")) {
    // ...
}

遮的 code 檢查傳入來的檔名敢有包含 .phar 這个字串。若有,伊就煞會共這个檔案當做是 phar archive 來處理,就算伊無用 phar:// wrapper 來引入。

閣較趣味的代誌發生佇後面的 phar_open_from_fp 這个函式。 這个函式的流程大概是按呢:

  1. 檢查檔案敢是 Gzip 壓縮格式(檢查 magic bytes \x1f\x8b\x08)。若是,就共伊自動解壓縮,然後重新開始處理解壓縮了後的檔案。
  2. 檢查檔案敢是 Bzip2 壓縮格式(檢查 magic bytes BZh)。若是,仝款共伊自動解壓縮
  3. 檢查敢是 Zip 或者是 Tar 格式。
  4. 若是攏毋是,才會開始揣 __HALT_COMPILER(); 這个 phar 檔案的標記。
  5. 若閣無,就照起工擲一个錯誤轉來 say goodbye

結論就是:若你 include 一个檔名/路徑有 .phar 的檔案(親像 my_file.phar.gz),PHP 的底層會自動判斷伊是 Gzip 壓縮檔,共伊解壓縮了後,才閣當做 phar 檔案來執行!

這个發現直接就共頭前的所有限制破解矣。咱會當:

  1. 先產生一个內底有惡意 code 的 exploit.phar 檔案。
  2. 才閣共這个 exploit.phar 壓縮做 exploit.phar.gz
  3. 壓縮了後的 .gz 檔案,內容早就無 <? 抑是 __HALT_COMPILER(); 這寡關鍵字,所以會當順利通過 upload.php 的檢查。
  4. 最後,咱只要 include('exploit.phar.gz'),PHP 就會佇底層恬恬仔共伊解壓縮,才閣執行內底的惡意 code,造成 RCE!

include + phar 一寡閣較趣味的耍法

咱會記得講咱檔案名共焦需要有「.phar」這五字,include 就tsua̋nn會共彼个檔案當做 phar 處理、引入來。所以毋管講是:

  1. ./foo.phar.gzip
  2. ./foo.pharzzzzz
  3. ./foo.pharmeow/bar/baz.png

攏會當予 PHP 共當做 PHAR 處理!


咱愛來加讀寡 PHP source,予 PHP 閣再偉大 :D
明仔載咱就轉來繼續講一寡 conferences 研究的物件囉


上一篇
0x07 / CTF・Race Condition・Golang 的 = 佮 := 有啥爭差恁敢知
系列文
規組駭了了::你無定落勾去的鑠奅 Web Hacking 步數8
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言