文件上传题
image.png
正常上传.png后回显文件路径

测试上传.php文件
image.png

检查源代码 发现有类型验证的逻辑

        function uploadFile() {
            const fileInput = document.getElementById('fileInput');
            const file = fileInput.files[0];
            
            if (!file) {
                alert('请选择一个文件进行上传!');
                return;
            }
            
            const allowedExtensions = ['png'];
            const fileExtension = file.name.split('.').pop().toLowerCase();
            if (!allowedExtensions.includes(fileExtension)) {
                alert('只允许上传后缀名为png的文件!');
                return;
            }
            
            const formData = new FormData();
            formData.append('file', file);

            fetch('upload.php', {
                method: 'POST',
                body: formData
            })
            .then(response => response.json())
            .then(result => {
                if (result.success) {
                    const uploadResult = document.getElementById('uploadResult');
                    const para = document.createElement('p');
                    para.textContent = ('地址:');
                    const link = document.createElement('a');
                    link.textContent = result.file_path;
                    link.href = result.file_path;
                    link.target = '_blank';
                    para.append(link);
                    uploadResult.appendChild(para);

                    alert('文件上传成功!');
                } else {
                    alert('文件上传失败:' + result.message);
                }
            })
            .catch(error => {
                console.error('文件上传失败:', error);
            });
        }

前端类型验证,使用白名单过滤,用bp绕过
将后缀改为.php
image.png
上传失败,说明后台对后缀进行了验证
将content-type改为application/octet-stream
image.png
上传失败,说明后台对content-type也做了验证

思路1 绕过后缀
将常见的php1 php2 phtml 文件类型都试了一遍,都不行 说明是白名单验证
image.png

思路2 .htaccess覆盖
上传.htaccess文件
image.png
上传失败,换思路

将文件扩展名改为 png.abc(灵感来源于apache解析漏洞)
image.png
显示上传成功!!!
这就说明了,虽然他是白名单验证,但是对于文件名ant.png.abc,他会将.png识别为文件扩展名
将文件扩展名改为.png.php
image.png
上传成功!!!
所以后台获取文件扩展名的逻辑是 从左到后获取第一个扩展名,而apache解析文件的逻辑是从右到左获取第一个可用的扩展名,所以对于ant.png.php,对于后台解析到png,通过白名单,apache解析到php
使用蚁剑连接,连接成功
image.png
在根目录下获取flag

分析一下后台的源代码

<?php
$targetDir = 'uploads/';
$allowedExtensions = ['png'];


if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['file'])) {
    $file = $_FILES['file'];
    $tmp_path = $_FILES['file']['tmp_name'];

    if ($file['type'] !== 'image/png') { 就在这里过滤content-type
        die(json_encode(['success' => false, 'message' => '文件类型不符合要求']));
    }

    if (filesize($tmp_path) > 512 * 1024) {
        die(json_encode(['success' => false, 'message' => '文件太大']));
    }

    $fileName = $file['name'];
    $fileNameParts = explode('.', $fileName); ant.png.php 被转换成数组 ['ant','png','php']

    if (count($fileNameParts) >= 2) { 要修复这个漏洞只要把这里改成 ==
        $secondSegment = $fileNameParts[1]; 取第二个元素为文件扩展名 这里是png
        if ($secondSegment !== 'png') {  白名单过滤
            die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
        }
    } else {
        die(json_encode(['success' => false, 'message' => '文件后缀不符合要求']));
    }

    $uploadFilePath = dirname(__FILE__) . '/' . $targetDir . basename($file['name']);

    if (move_uploaded_file($tmp_path, $uploadFilePath)) {
        die(json_encode(['success' => true, 'file_path' => $uploadFilePath]));
    } else {
        die(json_encode(['success' => false, 'message' => '文件上传失败']));
    }
}
else{
    highlight_file(__FILE__);
}
?>


小木
1 声望0 粉丝

引用和评论

0 条评论