0x01 open_basedir
open_basedir是php.ini中的一個配置選項,可用於將用戶訪問文件的活動范圍限制在指定的區域。
假設open_basedir=/var/www/html/web1/:/tmp/,那么通過web1訪問服務器的用戶就無法獲取服務器上除了/var/www/html/web1/和/tmp/這兩個目錄以外的文件。
注意:用open_basedir指定的限制實際上是前綴,而不是目錄名。
為了演示下面的幾個示例,我這里環境的open_basedir設置為Web目錄和tmp目錄:
測試一下,我在/home目錄中新建一個1.txt文件,嘗試對其進行讀取,發現讀取失敗:
換了Web目錄及其子目錄和tmp目錄中的文件就能成功讀取,這就是open_basedir所起到的作用。
0x02 利用命令執行函數Bypass
但是open_basedir對命令執行函數沒有限制,我們可以使用system()函數試一下,在前面的代碼前加上system()代碼來進行對比:
|
確實能夠成功讀到目標文件,不受open_basedir的限制:
至於其他的命令執行函數可自行嘗試。
但是一般情況下,system()等命令執行函數可能會被disable_functions給禁用掉,因此運用到的場景可能並不多。
0x03 利用symlink()函數Bypass
符號鏈接
符號鏈接又叫軟鏈接,是一類特殊的文件,這個文件包含了另一個文件的路徑名(絕對路徑或者相對路徑)。路徑可以是任意文件或目錄,可以鏈接不同文件系統的文件。在對符號文件進行讀或寫操作的時候,系統會自動把該操作轉換為對源文件的操作,但刪除鏈接文件時,系統僅僅刪除鏈接文件,而不刪除源文件本身。
symlink()函數
(PHP 4, PHP 5, PHP 7)
symlink()函數創建一個從指定名稱連接的現存目標文件開始的符號連接。如果成功,該函數返回TRUE;如果失敗,則返回FALSE。
symlink ( string $target , string $link ) : bool |
參數 | 描述 |
---|---|
target | 必需。連接的目標。 |
link | 必需。連接的名稱。 |
當然一般情況下這個target是受限於open_basedir的。
Bypass
先給出payload,原理在后面說明,這里需要跨幾層目錄就需要創建幾層目錄:
|
訪問該PHP文件后,后台便生成了兩個目錄和一個名為exp的符號鏈接:
在Web中我們直接訪問exp即可讀取到目標文件:
原理就是:創建一個鏈接文件7ea,用相對路徑指向A/B/C/D,再創建一個鏈接文件exp指向7ea/../../../../etc/passwd。其實指向的就是A/B/C/D/../../../../etc/passwd,其實就是/etc/passwd。這時候刪除7ea,再創建一個7ea目錄,但exp還是指向7ea/../../../etc/passwd,所以就成功跨到/etc/passwd了。
重點在這四句:
symlink("A/B/C/D","7ea"); |
payload構造的注意點就是:要讀的文件需要往前跨多少路徑,就得創建多少層的子目錄,然后輸入多少個../來設置目標文件。
0x04 利用glob://偽協議Bypass
glob://偽協議
glob:// — 查找匹配的文件路徑模式。
glob://是php自5.3.0版本起開始生效的一個用來篩選目錄的偽協議,其用法示例如下:
|
Bypass
只是用glob://偽協議是無法直接繞過的,它需要結合其他函數組合利用,主要有以下兩種利用方式,局限性在於它們都只能列出根目錄下和open_basedir指定的目錄下的文件,不能列出除前面的目錄以外的目錄中的文件,且不能讀取文件內容。
方式1——DirectoryIterator+glob://
DirectoryIterator是php5中增加的一個類,為用戶提供一個簡單的查看目錄的接口。
DirectoryIterator與glob://結合將無視open_basedir,列舉出根目錄下的文件:
|
輸入glob:///*
即可列出根目錄下的文件,但是會發現只能列根目錄和open_basedir指定的目錄的文件:
方式2——opendir()+readdir()+glob://
opendir()函數為打開目錄句柄,readdir()函數為從目錄句柄中讀取條目。
這里結合兩個函數來列舉根目錄中的文件:
|
效果和方式1是一樣的,只能Bypass open_basedir來列舉根目錄中的文件,不能列舉出其他非根目錄和open_basedir指定的目錄中的文件。
0x05 利用chdir()與ini_set()組合Bypass
基本原理
這種利用方式跟open_basedir存在缺陷的處理邏輯有關,具體原理可參考:
Bypass
測試Demo,放置在Web根目錄下,在執行輸入參數的PHP代碼前后獲取open_basedir的值看是否改變了:
|
輸入以下payload:
mkdir('mi1k7ea');chdir('mi1k7ea');ini_set('open_basedir','..');chdir('..');chdir('..');chdir('..');chdir('..');ini_set('open_basedir','/');echo file_get_contents('/etc/passwd'); |
可以看到open_basedir被設置為’/‘了,整個失去了效果:
注意,如果php文件在Web根目錄,則需要構造一個相對可上跳的open_basedir:
mkdir('mi1k7ea'); |
如果php文件直接在Web目錄的子目錄的話,就可不用創建相對可上跳的open_basedir了。
0x06 利用bindtextdomain()函數Bypass
bindtextdomain()函數
(PHP 4, PHP 5, PHP 7)
bindtextdomain()函數用於綁定domain到某個目錄的函數。
函數定義如下:
bindtextdomain ( string $domain , string $directory ) : string |
Bypass
利用原理是基於報錯:bindtextdomain()函數的第二個參數\$directory是一個文件路徑,它會在\$directory存在的時候返回\$directory,不存在則返回false。
payload:
|
成功訪問到存在的文件是會返回當前文件的路徑的:
若訪問的文件不存在則返回false:
可以看到,和前面幾種方法相比,實在是相形見絀,只能應用於判斷目標文件是否存在,有利於后續和其他漏洞進行組合利用。
0x07 利用SplFileInfo::getRealPath()類方法Bypass
SplFileInfo類
(PHP 5 >= 5.1.2, PHP 7)
SplFileInfo類為單個文件的信息提供高級面向對象的接口。
SplFileInfo::getRealPath
(PHP 5 >= 5.2.2, PHP 7)
SplFileInfo::getRealPath類方法是用於獲取文件的絕對路徑。
Bypass
和bindtextdomain的原理一樣,是基於報錯的方式,返回結果都是一樣的,就不再多演示,這里直接給出payload:
|
0x08 利用realpath()函數Bypass
realpath()函數
(PHP 4, PHP 5, PHP 7)
realpath — 返回規范化的絕對路徑名。它可以去掉多余的../或./等跳轉字符,能將相對路徑轉換成絕對路徑。
函數定義如下:
realpath ( string $path ) : string |
Bypass
環境條件:Windows
基本原理是基於報錯返回內容的不用,設置自定義的錯誤處理函數,循環遍歷匹配到正則的報錯信息的字符來逐個拼接成存在的文件名,另外是需要結合利用Windows下的兩個特殊的通配符<和>,不然只能進行暴破。
payload:
|
可以看到,首字母不同的文件就被列出來了,首字母相同的文件中只列了第一個:
0x09 腳本合集
p牛的腳本
腳本原理就是利用symlink()函數來Bypass的原理。
|