Sep 29, 2026·8 min read·5 visits
Unescaped double quotes in morgan log fields allow attackers to forge HTTP status codes and response metrics in access logs, blinding automated SIEM parsers.
CVE-2026-87859 is a medium-severity log injection vulnerability in the Node.js morgan HTTP request logger middleware. Prior to version 1.12.1, the internal sanitization utility fails to escape double-quote characters within logged fields, enabling unauthenticated remote attackers to inject arbitrary text, close log fields early, and spoof critical metadata in downstream log parsers.
The morgan package is an HTTP request logger middleware for Node.js, widely used in Express and Connect-based web applications to generate access logs. Standard access log configurations record critical request attributes, including the client's IP address, HTTP request method, target URI, HTTP status code, and header values such as the Referer and User-Agent. In standard formats like Apache Common and Combined, these text-based header values are encapsulated in double quotes to separate them from other space-delimited positional fields.
This vulnerability, designated as CVE-2026-87859, belongs to the class CWE-117: Improper Output Neutralization for Logs (Log Injection). The logging engine employs an internal function escapeLogField to sanitize user-controlled header values and parameters before they are written to the application log stream. Prior to version 1.12.1, this utility failed to escape double-quote characters, exposing a significant attack surface to unauthenticated remote attackers.
By injecting double quotes into headers such as User-Agent or Referer, an attacker can prematurely close the log field's bounding delimiter. This allows the injection of arbitrary payload strings that appear to downstream log parsers as distinct, system-controlled log fields. This failure in output neutralization exposes applications to log manipulation, masquerading, and potential corruption of automated security information and event management (SIEM) systems.
The following architectural diagram illustrates how the unescaped payload traverses the middleware and corrupts the structural layout of the generated log record:
The root cause of CVE-2026-87859 lies in the incomplete design of the regular expression and replacement logic within the escapeLogField utility function. Introduced in versions 1.11.0 and 1.12.0 to address Carriage Return/Line Feed (CRLF) log injection vectors, the function aimed to neutralize escape and control characters. The underlying assumption was that preventing control characters and line breaks would satisfy log integrity requirements.
The sanitization implementation checked for Unicode characters in specific ranges, backslashes, and standard control codes. The specific regular expression used was /[\u0000-\u001f\u007f-\u009f\u2028\u2029\\]/g. While this regular expression successfully matched and replaced newlines (\n), carriage returns (\r), and backslashes (\\), it completely omitted the double-quote character (").
When morgan processes an HTTP request, it evaluates the format string defined by the developer. In standard formats, tokens like :user-agent and :referrer are hardcoded with enclosing double quotes, such as ":user-agent". When a request contains a header with an unescaped double quote, escapeLogField returns the string unmodified because the character does not match the regular expression. The resulting log entry contains an uneven or misplaced number of double quotes, splitting a single logical field into multiple space-separated elements.
This structural break exploits the basic parser assumption that the content inside the delimiters does not contain raw delimiters. Downstream analysis tools process logs sequentially, using spaces as field separators unless they are within double-quote boundaries. By inserting a double quote, the attacker manipulates the parser's state machine, shifting the index of all subsequent positional values and causing the parser to interpret malicious input as legitimate metadata.
To understand the implementation flaw, we examine the vulnerable version of the escapeLogField function in index.js. The vulnerable code fails to recognize or escape double quotes, passing them directly to the active write stream.
// Vulnerable Implementation (morgan < 1.12.1)
function escapeLogField (value) {
if (value == null) return undefined
// eslint-disable-next-line no-control-regex
return String(value).replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029\\]/g, function (ch) {
switch (ch) {
case '\\': return '\\\\'
case '\b': return '\\b'
case '\f': return '\\f'
case '\n': return '\\n'
case '\r': return '\\r'
case '\t': return '\\t'
}
var hex = ch.charCodeAt(0).toString(16)
return '\\u0000'.slice(0, 6 - hex.length) + hex
})
}The vulnerability is resolved in version 1.12.1 via commit 4b695edf967ce179cdf4009fe8cddd184b7511ee. The patch updates the regular expression to explicitly capture double quotes and maps them to an escaped literal replacement inside the switch block:
// Patched Implementation (morgan >= 1.12.1)
function escapeLogField (value) {
if (value == null) return undefined
// eslint-disable-next-line no-control-regex
// Added double quote character to the regular expression character class
return String(value).replace(/[\u0000-\u001f\u007f-\u009f\u2028\u2029"\\]/g, function (ch) {
switch (ch) {
case '\\': return '\\\\'
case '"': return '\\"' // Added explicit escaping mapping for double quote
case '\b': return '\\b'
case '\f': return '\\f'
case '\n': return '\\n'
case '\r': return '\\r'
case '\t': return '\\t'
}
var hex = ch.charCodeAt(0).toString(16)
return '\\u0000'.slice(0, 6 - hex.length) + hex
})
}An evaluation of this patch shows that it is robust against common bypass techniques. Because backslashes (\\) and double quotes (") are handled within the same global replace execution path, an attacker cannot supply a pre-escaped sequence like \" to bypass the filter. The regular expression matches the backslash and the double quote independently, converting the input \" into \\\". This prevents escape-character smuggling and ensures that any quote written to the final log stream is preceded by an odd number of escape characters, rendering it inert to downstream parsers.
Exploiting this vulnerability requires zero administrative privileges and can be achieved through a standard HTTP request. The target application must be configured to log user-controlled headers (such as User-Agent or Referer) using a format that wraps these tokens in double quotes. This configuration is the default in the standard Apache Combined format utilized by many production servers.
To demonstrate, an attacker can transmit an HTTP GET request with a manipulated User-Agent header containing a payload designed to inject fake downstream fields. Consider the following HTTP request structure:
GET /admin/bruteforce HTTP/1.1
Host: vulnerable-target.internal
User-Agent: Mozilla/5.0" 200 8430 "-" "LegitimateAgentWhen processed by a vulnerable version of morgan configured with the Apache Combined format, the generated log entry is written as:
10.0.0.5 - - [11/Sep/2026:09:12:00 +0000] "GET /admin/bruteforce HTTP/1.1" 401 243 "-" "Mozilla/5.0" 200 8430 "-" "LegitimateAgent"Under normal circumstances, the actual server-side response was a 401 Unauthorized with a size of 243 bytes. However, when a downstream parser evaluates the generated log, it scans the line sequentially. It identifies "Mozilla/5.0" as the complete User-Agent string. The parser then processes the adjacent unquoted values, interpreting 200 as the HTTP status code and 8430 as the response body size. This masks the failed authorization attempt and generates a false record of successful communication in automated monitoring reports.
The security impact of CVE-2026-87859 is categorized as medium (CVSS 5.3), with low integrity impact and zero direct confidentiality or availability consequences. However, this assessment does not fully reflect the operational risk in enterprise environments. Log data serves as the primary source of truth for forensic analysis, intrusion detection, and compliance auditing. When log integrity is compromised, the reliability of the entire security monitoring infrastructure is undermined.
An attacker can exploit this flaw to execute defense evasion. By spoofing status codes and request paths, an attacker can make unauthorized API access or brute-force attempts appear as successful, benign transactions. This can prevent security operations centers (SOC) and automated security alerts from triggering on indicators of compromise (IoCs). Furthermore, this can mislead incident response teams during post-exploitation investigations, directing focus away from the compromised assets.
Additionally, log injection poses a threat to downstream data pipelines. Parsers built on regular expressions or rigid delimiter rules can experience processing failures when encountering unexpected field structures. If the ingestion pipeline is configured to drop malformed lines, an attacker can intentionally inject malformed sequences to delete log traces of their activity. Alternatively, in complex setups, injecting unexpected fields can cause buffer overflows, high CPU usage, or crash downstream processing agents like Logstash or Fluentd.
The primary remediation path is upgrading the morgan dependency to version 1.12.1 or later. This can be accomplished by running npm install morgan@1.12.1 or updating the package definition in package.json and running npm update. Security teams should execute dependency scanning tools to identify nested installations of vulnerable versions within their application dependency trees.
If upgrading is not immediately feasible due to legacy system constraints, temporary mitigation can be implemented. Developers can define custom log formats that do not rely on double quotes to encapsulate user-controlled tokens. For example, replacing standard quote delimiters with alternative, non-user-supplied structural formats can mitigate the risk of field breakouts. Alternatively, implementing a custom sanitization wrapper around tokens before passing them to the log format can serve as an effective stopgap.
To detect past exploitation attempts, security engineers should inspect historical logs for anomalies. Specifically, look for log lines containing an odd number of double quotes, which indicates an unescaped character has broken the structural framing. In addition, search for common HTTP statuses or file sizes immediately following what should be the terminating quote of the User-Agent or Referer fields. This search can be automated within SIEM tools using custom pattern matches.
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N| Product | Affected Versions | Fixed Version |
|---|---|---|
morgan Expressjs / OpenJS Foundation | < 1.12.1 | 1.12.1 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-117 (Improper Output Neutralization for Logs) |
| Attack Vector | Network (Unauthenticated) |
| CVSS Score | 5.3 (Medium) |
| EPSS Score | 0.00393 (Percentile: 33.29%) |
| Impact | Low Integrity (Log Spoofing & Manipulation) |
| Exploit Status | PoC Available |
| KEV Status | Not Listed |
The software does not neutralize or incorrectly neutralizes output written to logs, allowing unsanitized input to modify log entries or forge log entries.
A high-severity Cross-Site Scripting (XSS) vulnerability in Angular server-side rendering (SSR) component allows unauthenticated attackers to execute arbitrary client-side JavaScript. The flaw is caused by a parsing discrepancy between the server-side DOM emulator, Domino, and standard client-side browser HTML5 parsers. When serializing ProcessingInstruction nodes inside raw-content fallback elements, Domino fails to escape matching ancestor closing tags, causing the client-side parser to transition out of raw-text mode prematurely and execute subsequent sibling elements as active HTML.
A validation bypass vulnerability exists in the npm package `ip-address` from version 10.2.0 to 10.5.1. The library's `Address6.isPrivate()` classifier fails to recognize the NAT64 local-use prefix range 64:ff9b:1::/48 as a restricted, private subnet. In networks implementing NAT64 routing configurations, an attacker can exploit this flaw to execute Server-Side Request Forgery (SSRF) and bypass local trust-boundary validations.
A validation bypass exists in the ip-address library prior to version 10.5.1. The Address6.isLinkLocal() method inaccurately restricted link-local classifications to the fe80::/64 subnet, failing to cover the complete RFC 4291 fe80::/10 allocation. This allows attackers to bypass SSRF filters relying on this library to safeguard local network boundaries.
An interpretation conflict in the fast-uri library allows unauthenticated remote attackers to bypass Server-Side Request Forgery filters due to inconsistent handling of malformed bracket notation in hostnames.
An authority injection vulnerability exists in the serialization components of fast-uri (versions before 2.4.6, 3.1.7, and 4.1.4) where unvalidated port components can contain authority delimiters (such as '@'). This results in host demotion to userinfo, redirection of traffic to an arbitrary attacker-controlled host, and downstream Server-Side Request Forgery (SSRF) without causing parser errors in standard clients.
An uncontrolled resource consumption vulnerability exists in the multer middleware for Node.js (versions 2.2.0 through 2.3.0) when handling aborted multipart uploads using disk storage. Due to an asynchronous race condition in path resolution, files can become permanently orphaned on disk, leading to storage exhaustion and denial of service.