In 2020, Project Zero’s Jann Horn gave a piece of security research a title that has since hardened into a maxim: Mitigations are attack surface, too. The defensive code does not sit outside the attack surface. It is part of it.
The OS command injection CVE-2026-85656 (CVSS v4: 8.5 - CWE-78) is a sharper case than that. The vulnerable code is not a hardening mechanism. It is a standalone remediation tool, a privileged daemon Amazon shipped to clean up after Log4Shell on hosts that could not be upgraded in time or the JVM could not be restarted.
Disclosure timeline:

- The fix landed after operators had stopped applying updates. The package was built on the last day of Amazon Linux 2 support; the advisory appeared eight days later.
- Vulnerability scanners had nothing to match for fifty-eight days. ALAS2-2026-3784 was published with no CVE assigned. CVE-2026-85656 did not exist until September 4.
- Most operators do not know the package is installed. The platform JDK packages declare it as a transitive dependency, and its scriptlets enable and start the service automatically.
Photon addresses this at runtime, with no patch, no restart of the daemon, and no scheduled downtime. No CVE-specific rule was required; the invariants that block this attack were already in force before the vulnerability was disclosed.
The hotpatch and the bug
When Log4Shell (CVE-2021-44228) broke the internet in December 2021, Amazon shipped a hotpatch for Amazon Linux: log4j-cve-2021-44228-hotpatch. It runs as a privileged (root) daemon, scans the host for running Java processes, and live-patches them so the Log4Shell gadget can no longer fire.
The daemon is a roughly 800-line bash script and the fix is one changed line:
- if grep -e "[^a-zA-Z0-9/_. -]" <<<$JVM; then
+ if [[ "$JVM" =~ [^a-zA-Z0-9/_.\ -] ]] || [[ "$JVM" == *$'\n'* ]]; then
log "JVM binary \"$JVM\" for pid $pid contains special characters, skipping for safety."
continue
fi
The intent is defensive: reject any JVM path that contains a “special” character. $JVM comes from readlink /proc/<pid>/exe, in other words an attacker-chosen file path.
The root cause of the OS command injection vulnerability is that $JVM is spliced into a string used to invoke the bash interpreter as root. The grep validation filter does not reject newlines, for two reasons. First, because the string $JVM is unquoted, bash expands and word-splits $JVM before grep is executed. Word splitting uses the shell’s Internal Field Separator (IFS), whose default value is space, tab, and newline. So every newline in $JVM is treated as a field boundary, the fields are rejoined with single spaces, and a space is an allowed character. Second, grep could not have caught it in any case: it is line-oriented, and a newline is its record separator, never part of the string data.
Photon defense in depth
What the buggy script consumes is not a path it discovers on disk; it is readlink /proc/<pid>/exe of a running process. The path must nonetheless exist on disk for that link to resolve, and what makes the condition detectable is the control character itself, the newline.
Photon exposes a has_control_chars() predicate, and the rule that stops the exploit's first move is one line:
[[rules]]
name = "block mkdir of a directory whose name holds a control byte"
type = "dir_create"
severity = "high"
condition = 'payload.file_path.name.has_control_chars()'
action = "block"To reach the daemon, an attacker must first create a directory whose name carries the newline, then place a binary named java inside it and run it. mkdir -p builds that path one component at a time, and the first component holding a control byte is denied at the path_mkdir LSM hook. The staging aborts and the binary is never planted, so the daemon never encounters a java process whose /proc/<pid>/exe contains a newline. The chain is severed before a single component of the crafted path exists.
Blocking the mkdir stops the exploit at its first move, but it is the outer layer, not the guarantee. The guarantee rests on two rules, on the exec and rename hooks. A live process named java is dangerous to the daemon only when its /proc/<pid>/exe resolves to a multi-line string, and a process reaches that state in one of two ways:
- it was executed from a path that already carried the newline
- it was executed from a clean path, and the binary or one of its ancestor directories was later renamed into a crafted one.
Photon closes both doors with the same one-line primitive.
Execution — refuse to run a binary whose path holds a control byte. This holds even when the crafted tree already exists, shipped in a container image, restored from a backup, or staged while the policy was down. It is a runtime virtual patch for the bug.
[[rules]]
name = "block exec of a binary whose path holds a control byte"
type = "exec"
severity = "high"
condition = 'payload.file_path.abs.has_control_chars()'
action = "block"Rename — block any rename whose destination name holds a control byte.This closes the bypass where an attacker execs a clean path and then renames the binary, or one of its ancestor directories, into its crafted form to poison the live /proc/<pid>/exe.
[[rules]]
name = "block rename whose destination name holds a control byte"
type = "file_rename"
severity = "high"
condition = 'payload.destination.name.has_control_chars()'
action = "block"Photon in action
With no policy loaded, the attacker’s staging succeeds.
With the Photon rule loaded, the mkdir command is denied in the kernel:
mkdir: cannot create directory '/tmp/hp/A\ntouch ': Operation not permittedAnd the corresponding threat surfaces in the live monitor, with the malicious name captured intact:
ThreatEvent {
image: "/usr/bin/mkdir", pid: 196999, uid: 1000,
payload: DirCreate { file_path: { name: "B\ncp bash rootbash" } },
rule: "CVE-2026-85656, block mkdir ... control byte", action: Block }Conclusion
Exein Photon does not need a signature for this specific CVE, and it does not need the vulnerable package to be present. It enforces a simple system invariant, that a path component should never contain a control character, in the kernel across host and container.
A mitigation compiled into software is part of its attack surface. A privileged tool added alongside it to remediate is more of the same. Runtime enforcement applies to both, and does not depend on which of them turns out to be defective.
For kernel-level runtime enforcement on your own hardware, or a firmware analyzer scan contact the Exein team for a demo.
Author
The Exein Tech Team



