Sploitus

Exploit for cve-2023-41564-research

kitploit · 2026-08-31

Exploit Code

MARKDOWN109 lines
## https://sploitus.com/exploit?id=KITPLOIT:TOOLS-GITHUB-SOTA70-CVE-2023-41564-RESEARCH
# Обзор уязвимости CVE-2023-41564

CVE-2023-415641 — это уязвимость типа Stored XSS в CMS Cockpit (<= v2.6.3).  
Если при загрузке файла загрузить HTML-файл с расширением .shtml, то в момент открытия загруженного файла выполнится JavaScript.  
  
1: https://nvd.nist.gov/vuln/detail/CVE-2023-41564

# Порядок проведения атаки

Сначала рассмотрим метод атаки на основе PoC2, опубликованного, предположительно, автором отчета об уязвимости.  
Сначала войдите на сайт под учетной записью, имеющей права на загрузку файлов.  
![cms login](https://assets.kitploit.com/production/public/readmes/32770/cad01abf8fa9ad2f96c269bcde00cde1e45282e3bf2f4ef3a440079b9bd51799.png)  
![cms assets](https://assets.kitploit.com/production/public/readmes/32770/1a521cf05dbe087b10a4f3413dda91054d639e28a187ed474bd3bed69fce10a4.png)  
Затем загрузите shtml-файл следующего содержания:  
```html

root@kitploit:~
    
    
    Скопируйте URL загруженного файла (Asset).<br />![cms link](https://assets.kitploit.com/production/public/readmes/32770/6a1d6857f6a6b20d1feb81a1f0237f189b016c4bcfb9700c41b3cbcf05093c43.png)<br />При переходе по скопированной ссылке выполнится скрипт, описанный в HTML выше.<br />![cms xss](https://assets.kitploit.com/production/public/readmes/32770/92fdb22909f8f16219b555fa9d1145818576b4c8232e5c7785d7ac5fcd408d36.png)<br /><br />2: https://github.com/LongHair00/Mitre_opensource_report/blob/main/CockpitCMS-StoredXSS.md
    
    # Конкретная причина возникновения
    
    Теперь я объясню результаты моего более глубокого исследования.<br />Обработка загрузки Assets начинается с POST-запроса к пути /assets/upload.<br />Обработка запроса на загрузку определена в строке 51 файла modules/Assets/bootstrap.php<sup>3</sup>.<br />Ниже приведен код.
    ```php
    // assets api
    $this->module('assets')->extend([
    
        // 省略
        'upload' => function(string|array $param = 'files', array $meta = [], bool $isUpload = true) {
    
            $files = [];
    
            if (is_string($param) && isset($this->app->request->files[$param])) {
                $files = $this->app->request->files[$param];
            } elseif (is_array($param) && isset($param['name'], $param['error'], $param['tmp_name'])) {
                $files = $param;
            }
    
            $finfo      = finfo_open(FILEINFO_MIME_TYPE);
            $uploaded  = [];
            $failed    = [];
            $_files     = [];
            $assets    = [];
    
            $allowed   = $this->app->retrieve('assets/allowed_uploads', '*');
            $allowed   = $allowed == '*' ? true : str_replace([' ', ','], ['', '|'], preg_quote(is_array($allowed) ? implode(',', $allowed) : $allowed));
            $max_size  = $this->app->retrieve('assets/max_upload_size', 0);
    
            $forbiddenExtension = ['bat', 'exe', 'sh', 'php', 'phar', 'phtml', 'phps', 'htm', 'html', 'xhtml', 'htaccess'];
            $forbiddenMime = [
                'application/x-httpd-php', 'application/x-php', 'text/x-php',
                'text/html', 'application/xhtml+xml'
            ];
    
            if (isset($files['name']) && is_array($files['name'])) {
    
                $cnt = count($files['name']);
    
                for ($i = 0; $i < $cnt; $i++) {
    
                    $_file  = $this->app->path('#tmp:').'/'.$files['name'][$i];
                    $_mime = $finfo->file($files['tmp_name'][$i]);
                    $_isAllowed = $allowed === true ? true : preg_match("/\.({$allowed})$/i", $_file);
                    $_sizeAllowed = $max_size ? filesize($files['tmp_name'][$i]) < $max_size : true;
    
                    $extension = strtolower(pathinfo(parse_url($_file, PHP_URL_PATH), PATHINFO_EXTENSION));
    
                    if (!$extension) {
                        $_isAllowed = false;
                    }
    
                    // prevent uploading php / html files
                    if ($_isAllowed && (
                        in_array($extension, $forbiddenExtension) ||
                        in_array(strtolower($_mime), $forbiddenMime)
                    )) {
                        $_isAllowed = false;
                    }
    
                    if (!$files['error'][$i] && $_isAllowed && $_sizeAllowed && ($isUpload ? move_uploaded_file($files['tmp_name'][$i], $_file) : rename($files['tmp_name'][$i], $_file))) {
    
                        $_files[]   = $_file;
                        $uploaded[] = $files['name'][$i];
    
                        if (\preg_match('/\.(svg|xml)$/i', $_file)) {
                            file_put_contents($_file, \SVGSanitizer::clean(\file_get_contents($_file)));
                        }
    
                    } else {
                        $failed[] = $files['name'][$i];
                    }
                }
            }
    
            if (count($_files)) {
    
                $assets = $this->add($_files, $meta, isset($meta['_id']));
    
                foreach ($_files as $file) {
                    unlink($file);
                }
            }
    
            return ['uploaded' => $uploaded, 'failed' => $failed, 'assets' => $assets];
        },
        // 省略
    ]);