The fix for the front door left the side window open — Unicode line breaks bypass morgan's earlier CR/LF sanitization
CVE-2026-15603 is a bypass of the earlier CVE-2026-5078 fix (morgan 1.11.0) that stripped ASCII CR/LF from log tokens. Researchers discovered that Unicode characters U+0085 (NEL), U+2028 (LINE SEPARATOR), and U+2029 (PARAGRAPH SEPARATOR) survive into log records and cause downstream line-oriented log processors to split a single access-log entry into multiple logical records. This affects all morgan versions below 1.12.0 and applies to every attacker-reachable token: :url, :user-agent, :referrer, :req[header], and :remote-user. Any of the built-in formats (combined, common, default, short) and custom formats using these tokens are vulnerable.
The vendor severity of MEDIUM / 5.3 is accurate and arguably generous. The CVSS vector (AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N) correctly captures that this is unauthenticated and network-reachable, but the impact is limited to integrity of log data only — there is no path to code execution, privilege escalation, data exfiltration, or denial of service. Log forging can mislead SIEM correlation, hide attacker tracks, or poison audit trails, but it requires the downstream consumer to be line-oriented (not JSON/structured). In structured logging pipelines (ECS, JSON), the Unicode characters are encoded within field values and the forge fails.
4 steps from start to impact.
Identify morgan-backed endpoint
- Target application uses morgan middleware for access logging
- Target endpoint is network-reachable
- Attacker cannot confirm morgan is in use from outside without source access or error leaks
Craft request with Unicode line separators
User-Agent header, the URL path, or the Authorization: Basic header (for :remote-user). No special tooling is needed; a single curl command with UTF-8 encoded bytes suffices.- Attacker can send arbitrary HTTP requests to the target
- WAFs that normalize or strip non-ASCII control characters in headers will block this
- Some reverse proxies (nginx, HAProxy) may reject or re-encode URLs containing raw U+2028/U+2029
\u0085, \u2028, \u2029 in request headers can flag thisMorgan writes unsanitized line to log stream
- Morgan version < 1.12.0
- Log format includes an attacker-controllable token
- Applications using structured/JSON log transports (winston JSON, pino, Bunyan) encode these characters inside field values — the forge has no effect
- Applications that pipe morgan output through a sanitizing transform are also immune
Downstream log consumer misparses records
grep-based alerting script, or a compliance audit tool — treats the injected Unicode newline as a record boundary. The attacker's forged content becomes a standalone log entry, potentially hiding the real request or injecting false entries that mislead investigation.- Log consumer splits records on Unicode line terminators, not just ASCII newlines
- Logs are not ingested as structured JSON
- Many modern SIEM pipelines (Splunk HEC, Elastic ECS, Datadog) ingest structured JSON and are immune
- Splunk's default line-breaking regex (
LINE_BREAKER) typically only splits on\r?\n, not Unicode separators — the forge may not even work against Splunk raw-text ingestion
The supporting signals.
| In-the-wild exploitation | No known exploitation. Not listed on CISA KEV. No reports of active campaigns. |
|---|---|
| Proof-of-concept | Trivial to reproduce with curl — e.g., curl -H 'User-Agent: legit\xe2\x80\xa8forged 200 OK' https://target/. No dedicated PoC repo published yet; the advisory by UlisesGascon provides sufficient detail. |
| EPSS score | Not yet scored (CVE published 2026-08-28, EPSS lags ~48 hours). Expected to be very low (<5th percentile) given integrity-only impact and no code execution. |
| KEV status | Not listed. No federal mandate to patch on an accelerated timeline. |
| CVSS vector | CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N — Network-reachable, no auth, no user interaction, but only Low integrity impact. No confidentiality or availability impact. |
| Affected versions | morgan < 1.12.0 (all versions including 1.11.x which fixed only ASCII CR/LF via CVE-2026-5078) |
| Fixed version | morgan 1.12.0 — now strips U+0085, U+2028, U+2029 in addition to CR/LF |
| Related CVE | CVE-2026-5078 — the ASCII CR/LF variant of the same class, fixed in 1.11.0. CVE-2026-15603 is the Unicode bypass of that fix. |
| Installed base | morgan has ~8–13 million weekly npm downloads. It is a dependency of thousands of Express-based applications. Exposure is broad but impact per-exploit is narrow. |
| Researcher | UlisesGascon — reported both CVE-2026-5078 and CVE-2026-15603 to the expressjs/morgan project via GitHub Security Advisories. |
noisgate verdict.
The single most decisive factor is that the blast radius is limited to log integrity — there is no path from this vulnerability to code execution, privilege escalation, or data breach regardless of the deployment role. Even on a high-value application tier, the worst outcome is polluted audit logs, not host or domain compromise.
Why this verdict
- Integrity-only impact ceiling: The CVSS vector confirms C:N/I:L/A:N. No matter how the chain is composed, the terminal impact is log forging — there is no escalation to RCE, privilege gain, or data exfiltration. This hard-caps severity.
- Role multiplier: morgan runs as an Express middleware in Node.js application servers. In low-value roles (dev, staging), impact is negligible. In typical roles (production web tier), forged logs can mislead SIEM correlation and hide attacker activity — a real but bounded concern. In high-value roles (e.g., Node.js microservices fronting an API gateway for an identity provider), the same log-forging ceiling applies — the attacker gains no additional code execution or identity-plane access. The blast radius is *host log stream* at most, never domain/fleet/supply-chain. The floor does not trigger because no role escalates beyond log integrity impact.
- Broad installed base, narrow per-exploit impact: ~8–13M weekly downloads means many apps are *reachable*, but the actual damage per exploitation is low. This is a volume × severity trade-off where severity anchors the score.
- Friction from structured logging: Organizations using JSON/structured log pipelines (pino, winston JSON mode, Elastic ECS) are effectively immune because Unicode characters are encoded within JSON string values and cannot forge new records. This materially reduces the exploitable population.
- Bypass of prior fix: This is a bypass of CVE-2026-5078's remediation (1.11.0), which means organizations that thought they patched the log-forging class are still vulnerable. This is notable but does not change the impact ceiling.
Why not higher?
The vulnerability cannot escalate beyond log integrity. There is no code execution, no privilege escalation, no data exfiltration, and no availability impact. Even if an attacker forges log entries on a mission-critical system, the host, domain, and fleet remain uncompromised. The CVSS Scope is Unchanged, meaning no cross-component impact. Without a path to a higher-consequence outcome, HIGH or CRITICAL is not warranted.
Why not lower?
The vulnerability is unauthenticated, requires no user interaction, and is trivially exploitable with a single HTTP request against one of the most widely-deployed npm packages. Log integrity is a real security property — forged logs can mask intrusions, poison compliance evidence, and mislead incident response. Downgrading to LOW would understate the operational risk in environments relying on morgan's access logs for security monitoring.
What to do — in priority order.
- Switch to structured/JSON log output — If your application pipes morgan output to a JSON transport (e.g.,
winstonJSON format,pino, or writes to Elastic ECS), Unicode line separators are encoded inside JSON string values and cannot forge new records. This neutralizes the vulnerability entirely. Implement within the 365-day noisgate remediation SLA window while planning the upgrade to 1.12.0. - Upgrade morgan to 1.12.0 — The definitive fix. Run
npm update morganor pin"morgan": "^1.12.0"inpackage.json. Test in staging first — the only behavioral change is that U+0085/U+2028/U+2029 are now escaped in log output. Deploy within the 365-day noisgate remediation window. - Add WAF rules to strip Unicode control characters from headers — Configure your WAF or reverse proxy to reject or sanitize request headers containing U+0085, U+2028, and U+2029. In nginx,
proxy_set_headerwith a Lua or njs filter can strip these. This is defense-in-depth — it does not fix morgan but prevents the payload from reaching it. - Validate SIEM line-breaking configuration — Audit your SIEM's line-breaking rules for access log sources. Ensure
LINE_BREAKER(Splunk) or equivalent only splits on\r?\n, not arbitrary Unicode separators. This limits the downstream impact even if forged bytes reach the log file.
- Rate limiting — the attack requires only a single HTTP request to inject a forged log entry. Rate limiting does not help.
- Input validation on request body — the vulnerable tokens come from HTTP *headers* and the *URL path*, not the request body. Body-focused validation is irrelevant.
- Upgrading to morgan 1.11.0 — this version only fixed ASCII CR/LF (CVE-2026-5078). Unicode line separators bypass that fix entirely. You need 1.12.0.
Crowdsourced verification payload.
Run this on each application host or in your CI pipeline to check the installed morgan version. Requires node and npm in PATH. No special privileges needed. Example: bash check_morgan_cve_2026_15603.sh /path/to/your/app
#!/usr/bin/env bash
# check_morgan_cve_2026_15603.sh
# Checks whether the installed morgan version is vulnerable to CVE-2026-15603
# (Log Forging via Unicode Line Separators)
# Usage: bash check_morgan_cve_2026_15603.sh [/path/to/node/app]
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN
set -euo pipefail
APP_DIR="${1:-.}"
if [ ! -d "$APP_DIR/node_modules/morgan" ]; then
echo "UNKNOWN — morgan is not installed in $APP_DIR/node_modules"
exit 2
fi
VERSION=$(node -e "try { console.log(require('$APP_DIR/node_modules/morgan/package.json').version) } catch(e) { console.log('error') }")
if [ "$VERSION" = "error" ]; then
echo "UNKNOWN — could not read morgan version from $APP_DIR"
exit 2
fi
echo "Installed morgan version: $VERSION"
# Compare version — vulnerable if < 1.12.0
MINOR=$(echo "$VERSION" | cut -d. -f2)
PATCH=$(echo "$VERSION" | cut -d. -f3)
MAJOR=$(echo "$VERSION" | cut -d. -f1)
if [ "$MAJOR" -gt 1 ] 2>/dev/null; then
echo "PATCHED — morgan $VERSION is >= 1.12.0"
exit 0
elif [ "$MAJOR" -eq 1 ] && [ "$MINOR" -ge 12 ] 2>/dev/null; then
echo "PATCHED — morgan $VERSION is >= 1.12.0"
exit 0
else
echo "VULNERABLE — morgan $VERSION is < 1.12.0 (CVE-2026-15603)"
exit 1
fiIf you remember one thing.
npm update morgan to 1.12.0 is a one-line change with minimal regression risk; slot it into your next scheduled dependency update cycle. If your Node.js apps already use structured JSON logging (pino, winston JSON, ECS), you are effectively immune and can deprioritize further. If you rely on morgan's raw text output feeding a line-oriented SIEM pipeline, prioritize the upgrade sooner — forged log entries can mask attacker activity during an active incident. Audit your SIEM line-breaking config as a parallel step. No emergency patching is warranted.Sources
- GitHub Advisory — GHSA-jxfw-x594-9x9m (CVE-2026-15603)
- GitHub Advisory — GHSA-4vj7-5mj6-jm8m (CVE-2026-5078, prior fix bypass)
- GitLab Advisory Database — CVE-2026-5078
- CWE-117: Improper Output Neutralization for Logs
- expressjs/morgan — GitHub repository
- Unicode Line Separator log injection research — lookout.net
- DailyCVE — CVE-2026-5078 analysis
What defenders are saying.
Crowdsourced verification outputs.
Results submitted by users who ran the verification payload against their environment.