CVE-2026-23698: Verified Repro With Script Download
CVE-2026-23698: Vtiger CRM through 8.4.0 allows authenticated admin users to achieve remote code execution by uploading a crafted module ZIP that places PHP files in the web-accessible modules/ directory.
CVE-2026-23698 is verified against Vtiger CRM · other. Affected versions: through 8.4.0. Vulnerability class: RCE. This high reproduction includes runnable sandbox proof, artifacts, and a plain-text agent view under REPRO-2026-00273.
What Is CVE-2026-23698?
CVE-2026-23698 is a high-severity (CVSS 8.6) authenticated remote code execution vulnerability in Vtiger CRM through 8.4.0. An admin user can upload a crafted module ZIP through the Module Manager import feature to place PHP files in the web-accessible modules/ directory. Pruva reproduced it (reproduction REPRO-2026-00273).
CVE-2026-23698 Severity & CVSS Score
CVE-2026-23698 is rated high severity, with a CVSS base score of 8.6 out of 10.
High — serious impact or readily exploitable. Prioritize remediation.
Affected Vtiger CRM Versions
Vtiger CRM · other versions through 8.4.0 are affected.
How to Reproduce CVE-2026-23698
pruva-verify REPRO-2026-00273 curl -O https://pruva.dev/api/v1/reproductions/REPRO-2026-00273/artifacts/bundle/repro/reproduction_steps.sh && chmod +x reproduction_steps.sh && ./reproduction_steps.sh Proof of Reproduction for CVE-2026-23698
- reached the target end-to-end
- full exploit chain demonstrated
- on the real production code path
- high confidence
- the upstream fix blocks the same trigger
crafted module ZIP containing a valid manifest.xml and a PHP shell file
- Module Manager Import Module endpoint (index.php?module=ModuleManager&parent=Settings&view=ModuleImport&mode=importUserModuleStep1)
- install
- /modules/<module>/shell.php
Vtiger CRM 8.4.0 Module Manager Import also allows RCE via language-pack and layout-pack ZIPs, sharing the same web-root extraction root cause; the layout path has a bypassable PHP content filter.
How the agent worked
Root Cause and Exploit Chain for CVE-2026-23698
Vtiger CRM 8.4.0 (and earlier 8.x versions) allows an authenticated administrator to upload a crafted module ZIP archive through the Module Manager → Import Module endpoint. The upload handler accepts any ZIP that contains a valid manifest.xml, and the installer extracts the entire archive under the web-root modules/ directory. Because there is no file-type/content validation, an attacker can include an executable PHP file in the ZIP. Once extracted, the PHP file is reachable directly via the web server (e.g. /modules/<moduleName>/shell.php) and executes outside of Vtiger’s routing/authentication layer, giving the attacker a persistent web shell and arbitrary remote code execution.
- Product/component: Vtiger CRM (open-source PHP/MySQL CRM)
- Affected versions: through 8.4.0 (tested on 8.4.0; the Module Manager import code path exists across the 8.x line)
- Risk level: High
- Consequences: A low-privilege administrator with access to the Module Manager can install arbitrary PHP into the web root, bypassing the application’s authentication/authorization boundary and achieving persistent, unauthenticated remote code execution as the web-server user (
www-data).
Impact Parity
- Disclosed/claimed maximum impact: Authenticated remote code execution via a crafted module ZIP (code execution).
- Reproduced impact from this run: The reproduction script logged into the real Vtiger 8.4.0 application, uploaded a malicious ZIP through the Module Manager endpoint, completed the import, and then accessed
http://localhost/modules/<module>/shell.php?cmd=whoami. The response contained the markerPRUVA_SHELL_OKand the outputwww-data, demonstrating that attacker-controlled PHP was executed. - Parity:
full— the reproduced impact matches the claimed RCE impact. - Not demonstrated: None.
Root Cause
The vulnerability is in the Module Manager import workflow:
- Upload (
modules/Settings/ModuleManager/views/ModuleImport.php::importUserModuleStep2) saves the uploaded ZIP totest/vtlib/and only validates thatmanifest.xmlexists and declares a valid module name and Vtiger version compatibility. It does not inspect the file list or file extensions inside the ZIP. - Installation (
modules/Settings/ModuleManager/actions/Basic.php::importUserModuleStep3) callsVtiger_Package::import()on the saved ZIP, which extracts all contents under the web root, includingmodules/<ModuleName>/andlanguages/en_us/. - Direct execution: Apache resolves
/modules/<ModuleName>/shell.phpdirectly, invoking the PHP interpreter before Vtiger’sindex.phprouting or authentication checks. Any PHP file placed in the ZIP is therefore executable by anyone who knows its URL.
The architectural gap is that Vtiger treats modules/ as both an application-code directory and an import target, with no allowlist of file types or relocation of uploaded module contents outside the web root. The ticket/advisory notes that the only validation is the manifest XML, satisfying the file-type check for CWE-434.
Fix commit: The advisory lists patched versions as unknown, and no specific fix commit was supplied. The remediation should add a content-type/allowlist check before extraction (e.g., only .php, .tpl, .js, .css, .xml, .png, .jpg, etc., and reject PHP files that are not legitimate module class files, or move extracted modules outside the web root and route them through Vtiger’s authenticated front controller).
Reproduction Steps
The full, automated reproduction is in bundle/repro/reproduction_steps.sh. At a high level it:
- Installs PHP 8.5, MariaDB, Apache, and Python tools in the sandbox.
- Downloads Vtiger CRM 8.4.0 from SourceForge and deploys it to
/var/www/html. - Creates a fresh MariaDB database and user, then runs the Vtiger web installer automatically.
- Logs in as the admin user and completes the first-time System/User setup.
- Builds a malicious ZIP with a valid
manifest.xml, a boilerplate language file, andmodules/<module>/shell.php. - Navigates to Module Manager → Import Module, uploads the ZIP, and completes the import.
- Accesses the extracted PHP file directly via HTTP with
?cmd=whoamiand captures the output.
Expected evidence of reproduction: The webshell responds with the marker PRUVA_SHELL_OK and the output of the injected command (e.g., www-data).
Evidence
bundle/logs/reproduction_steps.log— full setup, install, and exploit trace.bundle/repro/proof.log— captured webshell output for the last run.bundle/repro/runtime_manifest.json— runtime evidence manifest (services, proof artifacts, endpoint).bundle/artifacts/malmod_<module>/malmod.zip— the crafted module ZIP used in the successful run.
Key excerpt from bundle/repro/proof.log (second run):
PRUVA_SHELL_OK
www-data
www-data
Key excerpt from bundle/logs/reproduction_steps.log showing the install response and trigger:
[+] Upload accepted: file=usermodule_1783465710.zip name=ModSiUWLcoD type=module
[*] Installing the module
[+] Install response: {"success":true,"result":{"success":true,"importModuleName":"ModSiUWLcoD"}}
[*] Triggering webshell: http://localhost/modules/ModSiUWLcoD/shell.php?cmd=whoami
[+] Webshell output: 200 PRUVA_SHELL_OK
www-data
www-data
Environment captured during the run:
- Vtiger CRM 8.4.0 (tarball from SourceForge)
- Apache 2.4.66 + PHP 8.5 (mod_php)
- MariaDB 11.8.6
- Ubuntu 26.04 (resolute)
Recommendations / Next Steps
- Immediate fix: Before calling
Vtiger_Package::import(), enumerate the ZIP contents and reject the archive if it contains any file outside a strict allowlist of module file types (e.g., legitimate PHP class/ action/ view files, templates, language files, assets). In particular, reject arbitrary PHP files that do not belong to the module’s expected class hierarchy. - Architectural fix: Do not extract user-supplied module archives directly into the web-root
modules/directory. Instead, stage them outside the document root and only copy validated files into the application tree, or serve module assets through a non-executable endpoint. - Upgrade guidance: Until an official patch is released, restrict admin access and monitor the
test/vtlib/andmodules/directories for unexpected PHP files. - Testing: Add a regression test that uploads a ZIP containing a
manifest.xmlplus an arbitrary PHP file and asserts that the import is rejected and the PHP file is never created undermodules/.
Additional Notes
- Idempotency: The reproduction script was run twice consecutively in the same environment and succeeded both times. Each run uses a fresh database and a unique module name to avoid collisions with previously imported modules, so it is safe to run repeatedly.
- Limitations: The reproduction requires the Vtiger 8.4.0 installer to create the database schema, which is a synchronous, long-running operation; the script waits up to 10 minutes for the installer to complete.
- No sanitizer: The primary proof was produced against the real product without ASAN/UBSAN; the success oracle is the actual webshell response.
Variant Analysis & Alternative Triggers for CVE-2026-23698
Vtiger CRM 8.4.0's authenticated admin Module Manager → Import feature is not only exploitable by uploading a malicious module ZIP (the original CVE-2026-23696 proof of concept), but also by uploading a language pack ZIP or a layout/theme ZIP through the same endpoint. Both alternate paths extract attacker-controlled PHP files into the web root without adequate validation. The language import path performs no content validation at all; the layout import path attempts a regex-based PHP detection that is bypassed by the PHP short-echo tag <?=. In both cases, the extracted PHP file is reachable directly via HTTP and executes as the web-server user, producing the same persistent, unauthenticated RCE outcome as the original module-import vulnerability. No vendor patch is available, so a true bypass of a fixed version could not be demonstrated; the confirmed findings are alternate triggers on the vulnerable 8.4.0 release.
Fix Coverage / Assumptions
The original advisory does not reference a vendor fix. Public disclosure (Jiva Security, 2026-07-06) confirms that Vtiger did not respond to the 90-day disclosure window and that no patch was released. A narrow fix that only inspected the module import path (Vtiger_PackageImport) would leave two structurally similar sinks exposed:
Vtiger_LanguageImport::import_Language()— no file content or extension validation.Vtiger_LayoutImport::import_Layout()— partial PHP content validation that is bypassable.
The current code assumes that the import type alone (module, language, layout) is enough to determine which extraction logic to run, but it does not enforce a uniform safe-extraction policy across all three types.
Variant / Alternate Trigger
Two distinct, confirmed alternate triggers were found through the same authenticated admin endpoint.
Variant 1 — Language Pack Import
- Entry point:
index.php?module=ModuleManager&parent=Settings&view=ModuleImport&mode=importUserModuleStep1(HTTP POST toimportUserModuleStep2, thenBasic&mode=importUserModuleStep3). - Dispatch:
modules/Settings/ModuleManager/actions/Basic.php::importUserModuleStep3createsnew Vtiger_Language()whenmodule_import_type == 'language'. - Extraction sink:
vtlib/Vtiger/LanguageImport.php::import_Language(). - Trigger: Upload a ZIP containing a valid
manifest.xmlwith<type>language</type>and a<prefix>value, plus a PHP file undermodules/shell.php. The sink rewrites the path tolanguages/<prefix>/shell.php. - Execution: Access
http://<host>/languages/<prefix>/shell.php?cmd=<command>.
Variant 2 — Layout Import PHP-Filter Bypass
Entry point: Same Module Manager Import endpoint as above.
Dispatch:
modules/Settings/ModuleManager/actions/Basic.php::importUserModuleStep3createsnew Vtiger_Layout()whenmodule_import_type == 'layout'.Extraction sink:
vtlib/Vtiger/LayoutImport.php::import_Layout().Trigger: Upload a ZIP containing a valid
manifest.xmlwith<type>layout</type>and<parent>v7</parent>, plus a PHP file underlayouts/<name>/skins/<name>/shell.phpthat uses the short-echo tag<?= ... ?>. The regex/(<\?php?(.*?))/iinimport_Layout()does not detect<?=, so the file is extracted tolayouts/<name>/skins/<name>/shell.php.Execution: Access
http://<host>/layouts/<name>/skins/<name>/shell.php?cmd=<command>.Product/component: Vtiger CRM 8.4.0 (open-source PHP/MySQL CRM)
Affected versions: through 8.4.0 (tested on the 8.4.0 SourceForge release tarball)
Risk level: High
Consequences: Any authenticated administrator who can access the Module Manager Import feature can plant a persistent, unauthenticated web shell in
languages/orlayouts/in addition tomodules/, achieving arbitrary remote code execution as the web-server user (www-data).
Impact Parity
- Disclosed/claimed maximum impact: Authenticated remote code execution via a crafted module ZIP (code execution).
- Reproduced impact from this variant run: The variant script logged into Vtiger 8.4.0 as admin, uploaded a language pack ZIP and a layout pack ZIP through the same Module Manager Import endpoint, and confirmed that both produced a working webshell returning the marker
PRUVA_LANG_SHELL_OKandPRUVA_LAYOUT_SHELL_OKrespectively, plus the output of the injected command (www-data). - Parity:
full— the reproduced impact matches the claimed RCE impact. - Not demonstrated: A true bypass of a released vendor patch, because no patch is available.
Root Cause
The root cause is the same as the original vulnerability: Vtiger extracts attacker-supplied ZIP archives directly into web-accessible directories without a uniform, robust allowlist of safe file types and contents. The Module Manager Import dispatcher treats the three package types (module, language, layout) as separate code paths, but none of them:
- Stages the archive outside the web root before validation.
- Maintains a per-type allowlist of expected file extensions and relative paths.
- Rejects PHP files that are not legitimate, known source files for the package type.
The language import path has no validation at all. The layout import path has an incomplete regex that can be bypassed by the PHP short-echo tag. The module import path has no validation at all. The architectural trust boundary is identical: an authenticated admin can turn any of these imports into a persistent web shell.
Reproduction Steps
The full, automated reproduction is in bundle/vuln_variant/reproduction_steps.sh. At a high level it:
- Logs in to the already-running Vtiger 8.4.0 instance as the admin user.
- Builds a malicious language pack ZIP with
<type>language</type>, a<prefix>, andmodules/shell.phpcontaining a PHP shell. - Uploads the ZIP through the Module Manager Import endpoint and completes the import.
- Verifies the shell by accessing
http://localhost/languages/<prefix>/shell.php?cmd=whoami. - Builds a malicious layout pack ZIP with
<type>layout</type>,<parent>v7</parent>, andlayouts/<name>/skins/<name>/shell.phpusing the<?=short-echo tag. - Uploads the ZIP through the same endpoint and completes the import.
- Verifies the shell by accessing
http://localhost/layouts/<name>/skins/<name>/shell.php?cmd=whoami. - Records the results to
bundle/vuln_variant/proof.logand exits with code 1 because no vendor patch is available to test a true bypass.
Expected evidence of reproduction: Both webshells respond with their respective marker (PRUVA_LANG_SHELL_OK or PRUVA_LAYOUT_SHELL_OK) and the output of the injected command (e.g., www-data).
Evidence
bundle/logs/vuln_variant_reproduction_steps.log— full trace of the two variant tests.bundle/vuln_variant/proof.log— structured result file (LANGUAGE_VARIANT=True,LAYOUT_VARIANT=True).bundle/vuln_variant/reproduction_steps.sh— the idempotent reproduction script.- Key excerpts from
bundle/logs/vuln_variant_reproduction_steps.log:
[+] Upload accepted: file=usermodule_1783466053.zip name=LangycQzbm type=language
[+] Install response: {"success":true,"result":{"success":true,"importModuleName":"LangycQzbm"}}
[*] Triggering language webshell: http://localhost/languages/langycqzbm/shell.php?cmd=whoami
[+] Webshell output: 200 PRUVA_LANG_SHELL_OK
www-data
www-data
[+] Upload accepted: file=usermodule_1783466053.zip name=LaynpRQix type=layout
[+] Install response: {"success":true,"result":{"success":true,"importModuleName":"LaynpRQix"}}
[*] Triggering layout webshell: http://localhost/layouts/LaynpRQix/skins/LaynpRQix/shell.php?cmd=whoami
[+] Webshell output: 200 PRUVA_LAYOUT_SHELL_OK
www-data
www-data
Environment captured during the run:
- Vtiger CRM 8.4.0 (SourceForge tarball, md5
360f394f5f7ffabc89eaca05b18523f9) - Apache 2.4.66 + PHP 8.5 (mod_php)
- MariaDB 11.8.6
- Ubuntu 26.04 (resolute)
Recommendations / Next Steps
- Immediate fix: Add a uniform, pre-extraction validation layer in the Module Manager import dispatcher (
modules/Settings/ModuleManager/actions/Basic.php) before callingVtiger_Package::import(),Vtiger_Language::import(), orVtiger_Layout::import(). The validator should enforce a per-type allowlist of expected file extensions and relative paths, and reject any PHP file that is not a legitimate module/language/layout source file. - Fix layout content check: Replace the bypassable regex
/(<\?php?(.*?))/iinvtlib/Vtiger/LayoutImport.phpwith a robust check (e.g., tokenization viatoken_get_all()) or, better, reject PHP files entirely in layout packs. - Fix MIME validation:
Vtiger_Functions::verifyClaimedMIME()should compare file MIME types against a MIME type allowlist, not against the$upload_badextextension list. - Architectural fix: Stage all user-uploaded module/language/layout archives outside the document root. Copy only validated files into the application tree, and serve assets through an authenticated PHP handler that sets
Content-Disposition: attachmentfor non-executable files. - Regression tests: Add tests that attempt to import each package type with an embedded PHP shell and assert rejection and that the PHP file is never created under
modules/,languages/, orlayouts/.
Additional Notes
- Idempotency: The reproduction script was run twice consecutively against the same Vtiger 8.4.0 installation. Both runs succeeded, using unique module/language/layout names each time to avoid collisions.
- Fixed version test: No vendor patch is available. The script therefore cannot demonstrate a true bypass of a fixed version and exits with code 1 even though the alternate triggers are confirmed on the vulnerable version.
- No repro state mutation: The variant stage reused the existing 8.4.0 deployment created by the repro stage; it did not check out a different source ref or alter the original repro artifacts.
- Third-party research alignment: The Jiva Security disclosure for CVE-2026-23698 explicitly flagged the language and layout import paths as structurally similar alternate routes, but did not publish a full PoC for them. This run provides that validation.
CVE-2026-23698 Reproduction Transcript
The agent's step-by-step process — every tool call, every handoff, the moment the exploit fired.
Full session Replay every step — scrub the timeline or play it back.
Artifacts and Evidence for CVE-2026-23698
Scripts, logs, diffs, and output captured during the reproduction.
How to Fix CVE-2026-23698
FAQ: CVE-2026-23698
How does the CVE-2026-23698 exploit work?
Which versions of Vtiger CRM are affected by CVE-2026-23698?
How severe is CVE-2026-23698?
How can I reproduce CVE-2026-23698?
References for CVE-2026-23698
Authoritative sources for CVE-2026-23698 — official vulnerability databases and the upstream advisory. Pruva's reproduction verifies the issue firsthand; these are the primary records to corroborate it.