OpenCATS installer writes attacker PHP into config, enabling unauthenticated RCE
TL;DR - OpenCATS deployments with an active installer (INSTALL_BLOCK absent) are exposed to unauthenticated RCE. An attacker hits the installer databaseConnectivity AJAX route, injects PHP into config.php via a define() breakout, and that code runs on every subsequent page load. CVSS 9.2. Fixed in commit 3002a29.
What happened
OpenCATS is a PHP applicant tracking system. During installation it accepts database connection settings through an installer AJAX route and writes them directly into config.php.
CVE-2026-27760 (CWE-94) is a code injection in that flow. The databaseConnectivity handler passes raw $_REQUEST values to CATSUtility::changeConfigSetting(), which writes them into a define(...) call in config.php without safe escaping. An attacker breaks out of the quoted string - for example by injecting '); ... // - and appends arbitrary PHP. Because config.php is globally included on every request, that payload executes persistently with no further interaction required.
The gate is installer state. The vulnerable route is only reachable when INSTALL_BLOCK is absent, meaning the installation wizard was never completed. This is the classic forgotten-installer failure mode, except here the installer writes code directly into a file the application always loads. The persistence makes it worse than a typical SSRF or file-read: one successful request leaves a backdoor that survives restarts.
Commit 3002a29 (PR #706) addresses both the root cause and the persistence vector. It restricts ajax.php behaviour when INSTALL_BLOCK is missing, and it changes how installer values are written to config.php, using safer serialisation (for example var_export(...)) instead of raw string interpolation.
Who is impacted
- OpenCATS versions
<= 0.9.7.4, or any build prior to commit3002a29. - Deployments where
INSTALL_BLOCKis absent and the installation wizard was never completed. - Any internet-reachable instance where an attacker can reach
/ajax.php?f=install:ui&a=databaseConnectivity.
| Item | Detail |
|---|---|
| Affected component | Installer AJAX databaseConnectivity flow writing to config.php |
| Affected versions | <= 0.9.7.4 / prior to commit 3002a29 |
| Fixed in | commit 3002a29 (PR #706) |
| Severity | CVSS v4.0 9.2 (Critical) |
What to do now
- Update to a build that includes commit
3002a29(PR #706) as soon as possible. - Check installer exposure before patching. Probe
GET /ajax.php?f=install:ui&a=databaseConnectivity: a response containingsetActiveStepmeans the installer is active and you are vulnerable;installLockedmeans it is closed. - Audit
config.phpfor injected PHP - inspect everydefine(...)line for unexpected content or breakout patterns. Do this even if you patch, because a prior compromise may have already written a payload. - If you suspect past exploitation:
- Rotate database credentials and any other secrets reachable from the OpenCATS host or process.
- Treat
config.phpas a persistence point and restore it from a known-good copy after patching.
- While patching is in progress, block network access to the installer AJAX route at your edge - at minimum prevent untrusted networks from reaching it.
Related
Research
- Server-Side Request Forgery (SSRF)Explains how untrusted URLs can make servers reach internal or cloud metadata endpoints. Covers validation,…
- Command injection: how it works and how to prevent itCommand injection happens when untrusted input reaches a shell or process spawn. Avoid the shell, pass…
- Template InjectionExplains how untrusted input can be executed by template engines on server or client. Covers safe templating…
Content is AI-assisted and reviewed by our team, but issues may be missed and best practices evolve rapidly, send corrections to [email protected]. Always consult official documentation and validate key implementation decisions before making design or security choices.
