GHSA-HMW2-7CC7-3QXX

Vulnerability from github – Published: 2026-06-15 17:26 – Updated: 2026-06-15 17:26
VLAI
Summary
form-data: CRLF injection in form-data via unescaped multipart field names and filenames
Details

Summary

form-data builds multipart/form-data request bodies. Through v4.0.5, the field name passed to FormData#append and the filename option are concatenated directly into the Content-Disposition header with no escaping of CR (\r), LF (\n), or ". An application that uses untrusted input as a field name or filename therefore lets an attacker terminate the header line and either inject additional headers or smuggle whole additional multipart parts into the request the application forwards to a backend.

This is CWE-93 (CRLF injection). It is a divergence from how browsers and the WHATWG HTML spec serialize form-data (they escape these characters), so the fix is to match that behavior. Severity is conditional: it depends on the consuming application passing attacker-controlled data as a field name or filename. Applications that only use fixed/trusted field names are not affected.

Details

In lib/form_data.js, _multiPartHeader builds the part header as:

'Content-Disposition': ['form-data', 'name="' + field + '"'].concat(contentDisposition || [])

and _getContentDisposition builds filename="' + filename + '"'. Neither escapes control characters, so a \r\n in field/filename ends the header line. The same applies to ", which can break out of the quoted parameter.

Proof of concept

const FormData = require('form-data');
const form = new FormData();
form.append('email"\r\nX-Injected: true\r\nfake="', 'user@example.com');
console.log(form.getBuffer().toString());

Before the fix this emits an injected X-Injected: true header line. A field name that also includes --<boundary> sequences can introduce additional parts (e.g. an extra name="is_admin" field), which a downstream parser accepts as legitimate.

Impact

For an application that uses untrusted field names/filenames:

  • Field injection / override (integrity). Inject or override fields the backend trusts (e.g. is_admin, role) — the primary demonstrated impact.
  • Header injection into the generated multipart part.

Claims of guaranteed privilege escalation, authentication bypass, high confidentiality impact, and availability impact are application-dependent downstream consequences, not properties of form-data itself, and are not demonstrated by the PoC.

Severity

The demonstrated, library-attributable impact is integrity (field/header injection); there is no demonstrated confidentiality disclosure or availability impact in form-data itself, and exploitation requires the consuming app to feed untrusted data into field names/filenames. A Moderate (≈5.3, I:L) rating is also defensible given that precondition.

Patch

Fixed in 4.0.6, 3.0.5, and 2.5.6. Users on older 0.x/1.x/2.x releases should upgrade to 2.5.6 or later.

The fix escapes \r, \n, and " as %0D, %0A, and %22 in field names and filenames, matching the WHATWG HTML multipart/form-data encoding algorithm that browsers implement. This neutralizes the injection while leaving ordinary field names (including name[0], dotted, and unicode names) unchanged.

Workaround

Until upgrading, validate or reject field names/filenames that contain control characters before calling append:

if (/[\r\n]/.test(field)) { throw new Error('invalid field name'); }

Credit

Reported by yueyueL.

Show details on source website

{
  "affected": [
    {
      "package": {
        "ecosystem": "npm",
        "name": "form-data"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.5.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "form-data"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "3.0.0"
            },
            {
              "fixed": "3.0.5"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    },
    {
      "package": {
        "ecosystem": "npm",
        "name": "form-data"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "4.0.0"
            },
            {
              "fixed": "4.0.6"
            }
          ],
          "type": "ECOSYSTEM"
        }
      ]
    }
  ],
  "aliases": [
    "CVE-2026-12143"
  ],
  "database_specific": {
    "cwe_ids": [
      "CWE-93"
    ],
    "github_reviewed": true,
    "github_reviewed_at": "2026-06-15T17:26:26Z",
    "nvd_published_at": "2026-06-12T19:16:26Z",
    "severity": "HIGH"
  },
  "details": "## Summary\n\n`form-data` builds `multipart/form-data` request bodies. Through v4.0.5, the `field` name passed to `FormData#append` and the `filename` option are concatenated directly into the `Content-Disposition` header with no escaping of CR (`\\r`), LF (`\\n`), or `\"`. An application that uses **untrusted input as a field name or filename** therefore lets an attacker terminate the header line and either inject additional headers or smuggle whole additional multipart parts into the request the application forwards to a backend.\n\nThis is CWE-93 (CRLF injection). It is a divergence from how browsers and the WHATWG HTML spec serialize form-data (they escape these characters), so the fix is to match that behavior. Severity is **conditional**: it depends on the consuming application passing attacker-controlled data as a field name or filename. Applications that only use fixed/trusted field names are not affected.\n\n## Details\n\nIn `lib/form_data.js`, `_multiPartHeader` builds the part header as:\n\n```javascript\n\u0027Content-Disposition\u0027: [\u0027form-data\u0027, \u0027name=\"\u0027 + field + \u0027\"\u0027].concat(contentDisposition || [])\n```\n\nand `_getContentDisposition` builds `filename=\"\u0027 + filename + \u0027\"\u0027`. Neither escapes control characters, so a `\\r\\n` in `field`/`filename` ends the header line. The same applies to `\"`, which can break out of the quoted parameter.\n\n### Proof of concept\n\n```javascript\nconst FormData = require(\u0027form-data\u0027);\nconst form = new FormData();\nform.append(\u0027email\"\\r\\nX-Injected: true\\r\\nfake=\"\u0027, \u0027user@example.com\u0027);\nconsole.log(form.getBuffer().toString());\n```\n\nBefore the fix this emits an injected `X-Injected: true` header line. A field name that also includes `--\u003cboundary\u003e` sequences can introduce additional parts (e.g. an extra `name=\"is_admin\"` field), which a downstream parser accepts as legitimate.\n\n## Impact\n\nFor an application that uses untrusted field names/filenames:\n\n- **Field injection / override (integrity).** Inject or override fields the backend trusts (e.g. `is_admin`, `role`) \u2014 the primary demonstrated impact.\n- **Header injection** into the generated multipart part.\n\nClaims of guaranteed privilege escalation, authentication bypass, high confidentiality impact, and availability impact are application-dependent downstream consequences, not properties of `form-data` itself, and are not demonstrated by the PoC.\n\n### Severity\n\nThe demonstrated, library-attributable impact is integrity (field/header injection); there is no demonstrated confidentiality disclosure or availability impact in `form-data` itself, and exploitation requires the consuming app to feed untrusted data into field names/filenames. A Moderate (\u22485.3, `I:L`) rating is also defensible given that precondition.\n\n## Patch\n\nFixed in **4.0.6**, **3.0.5**, and **2.5.6**. Users on older 0.x/1.x/2.x releases should upgrade to 2.5.6 or later.\n\nThe fix escapes `\\r`, `\\n`, and `\"` as `%0D`, `%0A`, and `%22` in field names and filenames, matching the WHATWG HTML `multipart/form-data` encoding algorithm that browsers implement. This neutralizes the injection while leaving ordinary field names (including `name[0]`, dotted, and unicode names) unchanged.\n\n## Workaround\n\nUntil upgrading, validate or reject field names/filenames that contain control characters before calling `append`:\n\n```javascript\nif (/[\\r\\n]/.test(field)) { throw new Error(\u0027invalid field name\u0027); }\n```\n\n## Credit\n\nReported by [yueyueL](https://github.com/yueyueL).",
  "id": "GHSA-hmw2-7cc7-3qxx",
  "modified": "2026-06-15T17:26:26Z",
  "published": "2026-06-15T17:26:26Z",
  "references": [
    {
      "type": "WEB",
      "url": "https://github.com/form-data/form-data/security/advisories/GHSA-hmw2-7cc7-3qxx"
    },
    {
      "type": "ADVISORY",
      "url": "https://nvd.nist.gov/vuln/detail/CVE-2026-12143"
    },
    {
      "type": "WEB",
      "url": "https://github.com/form-data/form-data/commit/64190db548c0179e37206858e39f27cf513e9435"
    },
    {
      "type": "WEB",
      "url": "https://github.com/form-data/form-data/commit/be3f3cf553978bac15a5182f1f3c3d2d38ccf229"
    },
    {
      "type": "WEB",
      "url": "https://github.com/form-data/form-data/commit/c7133499c2ee1b80c678e411244f4442bf902045"
    },
    {
      "type": "PACKAGE",
      "url": "https://github.com/form-data/form-data"
    },
    {
      "type": "WEB",
      "url": "https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#multipart-form-data"
    },
    {
      "type": "WEB",
      "url": "https://www.npmjs.com/package/form-data"
    }
  ],
  "schema_version": "1.4.0",
  "severity": [
    {
      "score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    },
    {
      "score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
      "type": "CVSS_V4"
    }
  ],
  "summary": "form-data: CRLF injection in form-data via unescaped multipart field names and filenames"
}


Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…