Automated Threat Modeling

Control Web Panel CVE-2026-73697: File Write and OS Command Injection in phpini_editor

CVE-2026-73697 is a dual arbitrary file write and OS command injection flaw in Control Web Panel's phpini_editor user module. Here is the patch and detection guidance drawn from VulnCheck's research.

CVE Analysis

11 min read

Gaurav Sarraf
Gaurav Sarraf

2026-11-03

Control Web Panel CVE-2026-73697: File Write and OS Command Injection in phpini_editor
Experimental AI-Generated Content

This CVE analysis is an experimental publication that is completely AI-generated. The content may contain errors or inaccuracies and is subject to change as more information becomes available. We are continuously refining our process.

If you have feedback, questions, or notice any errors, please reach out to us.

[email protected]

Introduction

A single user panel module in Control Web Panel shipped two independent code execution primitives at once: one that writes attacker controlled files anywhere the account can reach, and one that drops client input straight into a shell command. For shared hosting operators running CWP, that combination meant any account with access to the phpini_editor module, and on older builds any unauthenticated visitor, could turn a configuration editor into a foothold.

Control Web Panel (CWP) is a free Linux hosting control panel developed by LINANTO LLC of Tbilisi, Georgia, with development starting in 2013. Hosting providers and system administrators use it to manage servers, email, domains, DNS, files and resource monitoring through a web interface, with an optional CWPpro tier. Because CWP installs sit directly on hosting infrastructure and have historically often run with elevated privileges, flaws in its web facing modules have outsized consequences.

Technical Information

The affected component is the CWP user panel's phpini_editor module (modules/phpini_editor.php) in builds before webpanel_version 1.16. VulnCheck acted as the CNA for this CVE, and the credited researcher is Valentin Lobstein (Chocapikk), who decoded each ionCube protected build and verified the patched logic directly rather than trusting the release notes.

The module exposes two separate dangerous actions. Correcting only one leaves the other mechanism in place.

Sink 1: arbitrary file write via acc=update (CWE-22)

The acc=update action takes both the destination path and the file content directly from the POST body with no path confinement:

// phpini_editor, acc=update: destination and content both from the POST body, no session $filename = $_POST['file']; $cont .= str_replace('__', '.', $title[$i]) . '=' . $value[$i] . "\r\n"; // $_POST['title'] / $_POST['value'] file_put_contents($filename, $cont);

The destination comes straight from the client with no limit on where it can point, so content can be written to arbitrary paths. The content itself is assembled from the title and value POST parameters as <title>=<value> pairs, which is why an attacker splitting a webshell across the two fields uses a trailing // to comment out the = the module inserts.

Sink 2: OS command injection via acc=addlabelphpiniadv (CWE-78)

The acc=addlabelphpiniadv action passes a client supplied value into a shell command unquoted:

// phpini_editor, acc=addlabelphpiniadv $hfilename = trim($_POST['hfilename']); shell_exec('rm -f ' . $hfilename); // raw, unquoted

Because $hfilename is concatenated into the command with no quoting or escaping, shell metacharacters chain additional commands onto the rm -f invocation.

Attack flow

The demonstrated initial command execution runs as the hosting account, not root. In VulnCheck's laboratory chain, the steps combine this CVE with separate surrounding defects:

  1. On builds before 1.12, the user panel sends a login redirect but keeps processing. This missing termination (tracked as CVE-2026-73696) means the dispatcher still reaches the module without authentication, and VulnCheck calls the redirect cosmetic.
  2. The attacker uses acc=update to write a PHP file to a world writable location such as /tmp, splitting the payload across the title and value fields.
  3. The attacker includes and executes that file through module name traversal (CVE-2026-73695), for example module=../../../../../../../tmp/evilmod&c=id.
  4. Alternatively, the attacker reaches code execution directly through the acc=addlabelphpiniadv shell injection, which does not need the file inclusion step.
  5. In the full chain, the foothold relays a command to the root owned loopback user API on 127.0.0.1:2302 (for example /v1/restoredatabase/) to escalate to root.

The uid=0(root) outcome in the complete demonstration is therefore attributable to the chain, not to CVE-2026-73697 alone. The standalone CVE describes execution in the user panel context.

CVSS scoring

The updated CNA record assigns the following vectors:

  • CVSS v3.1: CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H (8.8, High)
  • CVSS v4.0: CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N (8.7, High)

An earlier NVD API snapshot reported 9.8 under v3.1 and 9.3 under v4.0 with a PR:N vector, while the updated CNA record specifies PR:L. The practical implication is to evaluate both the installed module version and the surrounding authentication defects, since low privilege scoring does not rule out unauthenticated exposure on older builds.

Patch Information

The fix shipped in Control Web Panel webpanel 1.16, the build dated September 29, 2026. CWP is closed source and encoded with ionCube, so there is no public commit or diff; the fix arrived as an ordinary panel update. The vendor changelog has no 1.16 entry, jumping from 1.12 to 1.26, and none of the release notes mention this CVE or credit the reporter. VulnCheck's Valentin Lobstein confirmed the fix by decoding the ionCube protected builds and checking the patched logic directly.

According to VulnCheck's analysis of the decoded 1.16 build:

  1. Path confinement for every write. Every file write in phpini_editor now goes through a new helper, get_user_safe_path(). It runs the requested path through realpath() and accepts it only if it resolves inside the calling account's home directory (/home/<user>). Because realpath() resolves ../ sequences and symlinks to a real absolute path, a crafted path cannot escape the home directory and then pass a simple string prefix check. Editing your own PHP.ini override files still works, and writing elsewhere no longer does.
  2. Shell sink removed entirely. The rm -f shell_exec() call in acc=addlabelphpiniadv is gone, not wrapped in escapeshellarg(). Removing the call eliminates the injection point completely.

The same 1.16 build also hardened the module dispatcher for CVE-2026-73695 using basename(), an allow list of [a-zA-Z0-9_-] characters, and a realpath() check that the file stays inside the modules directory, removing the traversal include used to run a file written through phpini_editor.

The fix arrived in stages across a ten CVE chain:

Version milestoneSecurity significance
1.12 (September 8, 2026)Login redirect now calls exit() and the dispatcher checks for a session before loading a module (CVE-2026-73696), stopping unauthenticated access. The module itself was unchanged, so authenticated accounts could still use both sinks.
1.16 (September 29 build)phpini_editor file write and shell sinks fixed; module dispatcher hardened (CVE-2026-73695).
1.30VulnCheck decoded this release and found the 1.16 fixes still in place with no regressions.

Other CVEs in the same chain, in the root owned loopback user API, remained open until 1.27 and 1.29. VulnCheck's overall recommendation is to run 1.29 or later. The vendor's newest listed release is 1.30.1. Alongside upgrading, VulnCheck recommends keeping the user panel (port 2083) and admin panel (ports 2031/2087) off the public internet where possible and behind an allowlist.

Detection Methods

No ready made Sigma, YARA, Snort/Suricata or Nuclei rules were found for CVE-2026-73697, and there is no threat actor IoC feed for it. Detection currently has to be built from VulnCheck's technical write up. The filenames below are proof of concept artifacts, not campaign IoCs, so treat them as examples of a pattern.

1. Exposure check. Identify which servers expose the user panel on port 2083 and record each build number. Builds before 1.12 are reachable with no login because the dispatcher runs the module before checking authentication; score these as unauthenticated exposure. Builds 1.12 to 1.15 require a session, but any authenticated hosting account could still reach the file write and shell exec bugs.

2. Web access logs. Both bugs go through the per account dispatcher, so the URL looks like /<account>/index.php?module=phpini_editor&acc=... with one of two action values:

  • acc=update: the arbitrary file write, with a destination path (file) and content (title/value) in the POST body.
  • acc=addlabelphpiniadv: the command injection, with the hfilename field passed into rm -f.

Search nginx/CWP access logs on port 2083 for POSTs matching module=phpini_editor with either action, focusing on requests with no session cookie and source IPs that have never logged in to that account. Do not trust the status code: on vulnerable builds an unauthenticated request receives a 302 redirect to /login/?intended=<base64 of the original query>, but the module has already run. Decoding intended= values is a second way to spot phpini_editor activity in unauthenticated traffic (this second check is VulnCheck's documented behavior applied as inference).

3. Request body inspection. If a WAF or proxy can see POST bodies:

  • acc=update, file field pointing outside the account home directory (for example /tmp/...) is a strong anomaly, since 1.16 restricts writes to /home/<user>.
  • acc=update, title field containing a PHP open tag indicates a webshell split across the two fields.
  • acc=addlabelphpiniadv, hfilename field containing shell metacharacters such as ;. VulnCheck's example payload is x ; id > /tmp/proof ; chmod 666 /tmp/proof ; echo.

4. Related requests in the same attack. Account enumeration shows up as a burst of GET /<name>/ from one source with mixed 302 (exists) and 404 (missing) responses. Execution shows up as a module= value containing ../, especially pointing into /tmp, following an acc=update POST from the same IP. Privilege escalation relays a command to the root owned user API on 127.0.0.1:2302, for example /v1/restoredatabase/.

5. Host and file integrity artifacts. Watch /tmp, /var/tmp and /dev/shm for new .php files owned by a hosting user. VulnCheck's examples include /tmp/evilmod.php (manual PoC), a hidden randomly named /tmp/.<random>.php (its go-exploit logged /tmp/.yDKSWndF.php), and /tmp/proof holding id output. Files written through this bug look like <?php ... ;//= ?>, using a trailing // to comment out the = the module inserts. For process lineage, look for the per account PHP-FPM worker under /usr/local/cwpsrv spawning a shell that runs rm -f chained with other commands, or id and curl calls to 127.0.0.1:2302 from a hosting user process. EDR or auditd execve logging with parent/child data is the right tool.

6. Network and escalation signals. Watch for hosting user processes connecting to the loopback user API on 2302, especially /v1/restoredatabase/ POSTs whose file value contains ;, and new outbound connections from PHP-FPM or shell processes (VulnCheck's exploit ends with a reverse shell).

Hunt backwards. Patching does not remove webshells already written. On any host that ran a build before 1.16, search historical access logs for these patterns and sweep temp directories for PHP files before marking the host clean.

Affected Systems and Versions

  • Control Web Panel (CWP) webpanel_version before 1.16 is affected.
  • Builds before 1.12 expose phpini_editor without authentication through the dispatcher defects (CVE-2026-73695/73696).
  • Builds 1.12 through 1.15 require an authenticated hosting account but still contain both the file write and shell exec sinks.
  • The CVE specific fix is in 1.16 (September 29, 2026 build). Related chain CVEs in the root owned loopback user API remained open until 1.27 and 1.29; VulnCheck recommends 1.29 or later. The newest listed vendor release is 1.30.1.

Vendor Security History

CWP has prior exploited vulnerabilities. CVE-2022-44877 in CWP 7 saw a public proof of concept on 3 January 2023, followed by Shadowserver's report of exploitation in the wild on 6 January 2023 and GreyNoise observations of exploiting IPs; Rapid7 identifies affected versions as those before 0.9.8.1147. Belgium's Centre for Cyber Security warned on 13 January 2023 of active exploitation and reverse shells, noting installations frequently ran with root privileges. VulnCheck separately describes CVE-2025-48703, another earlier CWP flaw, as mass exploited within days. None of these establishes exploitation of CVE-2026-73697, but they show a concrete history of CWP compromise. There is also evidence of continuing hardening: the 1.30 changelog describes validation of user panel internal API inputs, removal of request values from shell execution paths, and account file operations performed with the account's own permissions.

References

Detect & fix
what others miss

Works with
  • GitHub
  • GitLab
  • Bitbucket
  • Azure DevOps Services
  • Gerrit
  • CVS
  • Jira
  • Linear
  • Slack
  • Security Compass
Explore integrations
Security magnifying glass visualization