The Gaia OS Dynamic CLI ( sk144112 ) maps expert-mode diagnostic commands into the restricted clish shell. The mapping is served by dynamic_server , a daemon that runs as root and owns the local control socket /tmp/dynamic_socket . On affected builds the socket is created world writable and the daemon neither authenticates nor authorizes the process that connects to it, so any authenticated local account — a service account, a portal worker, an interactive shell user — writes a command string into the socket and the daemon executes it as root. The attack is local by design and does not need any credential beyond the ability to run a process on the appliance, which is why it is consistently used as the second stage of the published campaigns that begin with a web portal compromise.
This repository contains a containerised laboratory that reproduces the escalation on the affected behaviour, contrasts it with the patched behaviour, tests the published mitigation and the operator-side hardening, and reports the result of a 0-day sweep over the patched line. Every claim is backed by a capture in evidence/ .
What this repository delivers
The machine readable result of every run of the differential is in evidence/results/ , and evidence/differential-table.txt is generated from them.
1. Scope and fidelity of this laboratory
Gaia OS is proprietary, licence-gated and not redistributable, the Dynamic CLI daemon ships as an opaque binary inside the cprx / cphaprep packages, and the upstream fix is a binary hotfix with no public source commit. Nothing of the vendor product is redistributed here. What is reproducible in this laboratory is:
the vendor's own statement of the defect — CWE-732 and CWE-862, the Dynamic CLI daemon, the world-writable /tmp/dynamic_socket , the absence of caller authentication, the local privilege escalation to root, CVSS 7.8, and the affected/fixed version matrix — taken from advisory sk1000362 and the CVE record;
the attack surface of the component — a root daemon, an AF_UNIX control socket in the shared /tmp namespace, the length-framed request format # # , the command string executed with the credentials of the daemon, and the request log at /var/log/dynamicCli.log ;
the security-relevant behaviour of the service — a socket whose mode and peer validation decide whether an unprivileged local process can reach the command execution path;
the mitigation surface — the dbset process:dynamic_server off configuration switch and its effect on the published socket.
Everything the laboratory asserts the vendor product is sourced from the advisory and the CVE record; everything it asserts the model is labelled as such. The model runs in the laboratory, and the vulnerable and the patched sides differ in the authorization behaviour of the control socket alone — same base image, same configuration, same service, same entrypoint, the variant selected by an environment variable.
The lab image also carries the two partial fixes separately, so the two halves of the defect can be told apart:
The daemon creates its control socket and then decides who may use it. The affected behaviour creates the socket world writable and never asks the kernel who the caller is. lab/src/dynamic_server.c :
0777 on a socket grants connect() to every account on the appliance. The daemon then reads a request, and the request is a command string:
and the command runs with the credentials of the daemon, which are root:
The request handler of the affected build contains no authorization decision at all. The patched build makes the decision before a single byte of the request is interpreted, and it takes it from the kernel rather than from the request:
SO_PEERCRED is supplied by the kernel for every connection; a client cannot forge it, and it is evaluated before the request is parsed.
2.2 The request format
The Dynamic CLI client sends a length-framed request; lab/src/dynframe.c parses it:
The frame header is a convenience for the client, never a control. The authorization decision does not depend on it, which is why the unframed legacy form reaches the command execution path on the affected build just as the framed form does ( evidence/probe-vulnerable-run.txt ).
2.3 The escalation, observed
On the affected build the PoC runs as uid 1000 and the command it sends produces a file owned by uid 0 in the shared /tmp namespace ( evidence/exploit-vulnerable-run.txt ):
The proof is read from the attacker side: the marker is owned by uid 0 although the process that caused it ran as uid 1000.
3. Reproduction environment
The shared volume is what makes the topology of the real appliance: on Gaia, the daemon and every local account live in the same /tmp , so a socket file published there is reachable from an unprivileged process. In the laboratory the volume is mounted at /tmp in the appliance container and in the PoC runner container, so the runner reaches the very same socket inode as a local attacker would. The PoC is additionally executed inside the appliance container as gaia ( podman exec --user 1000:1000 ), which removes every doubt the topology.
The supervisor keeps the daemon running according to the local configuration, records the exit status of a daemon that dies, and releases the socket when the daemon stops.
The image digests of the binaries and of the images are recorded in evidence/versions.txt .
4. Running the environment
Every target is bounded and isolated: one Podman network per laboratory, one volume for the shared /tmp , --cpus 2 --memory 4g on every container.
The laboratory can also be driven directly:
poc/poc.py is a standard-library-only Python program. It models the unprivileged local attacker and refuses to run as root unless --allow-root is given.
Every run prints a machine readable summary line ( RESULT_JSON: {...} ) and can write the same object to a file with --json PATH . The laboratory collects them in evidence/results/ , and evidence/differential-table.txt is generated from them.
5.1 Reproduction paths
The differential the laboratory asserts is the pair of runs against the same PoC:
the affected build reports VULNERABLE — uid 1000 (gaia) reached command execution with uid 0 and produces a root owned marker file;
the patched build reports safe — access control refuses the connection and produces no marker.
evidence/differential-table.txt lists all 23 runs of the differential with their exit code, their verdict and what was observed; evidence/results/ holds the machine readable result of each of them.
6.1 The published mitigation
sk1000362 publishes the interim mitigation for the case that the hotfix cannot be installed yet:
Measured on the affected build ( evidence/mitigation-dbsetserver-off-run.txt ): the daemon stops, the socket is withdrawn, and the PoC reports mitigated — no control socket is published . Reverting the configuration restores the escalation ( evidence/mitigation-dbsetserver-reverted-run.txt ). The mitigation is effective and reversible, at the price of the Dynamic CLI commands of sk144112 failing while it is in force.
6.2 Operator-side hardening of the socket mode
Restricting the socket mode by hand blocks the escalation as well, but the daemon recreates the socket at startup and the hardening is silently lost on the restart of the daemon ( evidence/mitigation-socketmode-run.txt ):
The published dbset mitigation is the only one of the two that survives a restart, because it is the configuration the daemon itself obeys.
7. Environment preconditions
The flaw fires when all of the following hold:
the Dynamic CLI daemon dynamic_server runs — it is enabled by default in the appliance configuration, process:dynamic_server is on ;
the control socket is published at /tmp/dynamic_socket in a /tmp that is world writable, which is the case of the appliance /tmp ( 1777 );
the attacker holds a local execution context on the appliance — an authenticated account, a service account, or a compromised web portal worker. Any process that can create a file in /tmp can reach the socket;
the daemon runs with root privileges, which is its normal configuration.
Nothing of the chain requires a Gaia credential, a clish session or expert mode. The published campaigns combine CVE-2026-123039 with a remote entry point, but the escalation itself is local and unauthenticated on the appliance.
8. Additional findings (0-day sweep on the patched line)
The sweep runs against the patched build and asks one question: does any path remain open that lets an unprivileged local process reach command execution? The full transcript is evidence/0day-sweep.txt ; the same sweep against the affected build is evidence/0day-sweep-vulnerable.txt .
8.1 What the sweep does not cover
The sweep is a property of the laboratory, not of the vendor binary. SO_PEERCRED cannot be forged by a client, and the sticky /tmp blocks the removal of the socket entry, so neither of the two bypasses of the patched line has a route in the model; a vendor implementation that resolves its socket path differently, or that accepts the command string from a second channel, would have to be examined separately on the appliance itself.
This repository is published at — public, branch main , one clean commit, MIT licensed. The laboratory is reproduced from a fresh clone with make prepare && make differential . The state of the publication — the visibility, the description, the topics, the published tree and the published commit — is captured in evidence/publication.txt .
CVE-2026-123039 at NVD —
Check Point advisory sk1000362 — the Dynamic CLI local privilege escalation
Check Point sk144112 — the Dynamic / Extended CLI commands
Check Point Quantum Security Gateway and Security Management documentation
CVE-2026-109618 / CVE-2026-109619 — the remote entry points the published campaigns combine with this escalation
The full story
This article is one source in a clustered incident — the cluster page carries the summary, timeline and every other outlet covering it.
