# Root Cause Analysis: GHSA-jf2j-jhvf-rc56 / CVE-2026-23698

## Summary

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.

## Impact

- **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 marker `PRUVA_SHELL_OK` and the output `www-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:

1. **Upload** (`modules/Settings/ModuleManager/views/ModuleImport.php::importUserModuleStep2`) saves the uploaded ZIP to `test/vtlib/` and only validates that `manifest.xml` exists and declares a valid module name and Vtiger version compatibility. It does not inspect the file list or file extensions inside the ZIP.
2. **Installation** (`modules/Settings/ModuleManager/actions/Basic.php::importUserModuleStep3`) calls `Vtiger_Package::import()` on the saved ZIP, which extracts all contents under the web root, including `modules/<ModuleName>/` and `languages/en_us/`.
3. **Direct execution**: Apache resolves `/modules/<ModuleName>/shell.php` directly, invoking the PHP interpreter before Vtiger’s `index.php` routing 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:

1. Installs PHP 8.5, MariaDB, Apache, and Python tools in the sandbox.
2. Downloads Vtiger CRM 8.4.0 from SourceForge and deploys it to `/var/www/html`.
3. Creates a fresh MariaDB database and user, then runs the Vtiger web installer automatically.
4. Logs in as the admin user and completes the first-time System/User setup.
5. Builds a malicious ZIP with a valid `manifest.xml`, a boilerplate language file, and `modules/<module>/shell.php`.
6. Navigates to **Module Manager → Import Module**, uploads the ZIP, and completes the import.
7. Accesses the extracted PHP file directly via HTTP with `?cmd=whoami` and 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/` and `modules/` directories for unexpected PHP files.
- **Testing**: Add a regression test that uploads a ZIP containing a `manifest.xml` plus an arbitrary PHP file and asserts that the import is rejected and the PHP file is never created under `modules/`.

## 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.
