
HTTP Parameter Pollution (HPP) and mass assignment are two input-binding flaws that look minor on the surface and routinely lead to privilege escalation in practice. HPP exploits the fact that web stacks disagree on what to do when a parameter appears twice, while mass assignment exploits frameworks that automatically bind every field in a request to an object, including fields the developer never intended to expose.
This post explains how each works with concrete payloads, how to test for them with Burp Suite, how they map to the OWASP API Top 10 (API6:2023) and broken access control, and the allowlist approach that fixes both. If your app uses Rails, Spring, Laravel, or any framework with autobinding, these are bugs you should be probing for.
HTTP Parameter Pollution is sending the same parameter name multiple times in a single request and relying on the inconsistent way different components pick a value. Some frameworks take the first occurrence, some the last, some concatenate them, and a WAF in front may evaluate a different one than the application behind it, which is what makes HPP useful for filter evasion.
For example, ?role=user&role=admin might be seen as user by a validation layer and admin by the backend, or a WAF might inspect only the first value while the app uses the last. HPP shows up in query strings, POST bodies, and even within a single value via array syntax like role[]=user&role[]=admin. It is a standard check in any thorough web application pentesting checklist.
The danger of HPP comes from the fact that there is no standard, so each stack resolves duplicates differently and a request that passes one layer can mean something else at the next. Knowing the behavior of your target's stack tells you which value to weaponize.
# Request: color=red&color=blue
ASP.NET (classic) -> "red,blue" (concatenated)
PHP / Apache -> "blue" (last wins)
JSP / Tomcat -> "red" (first wins)
Node.js (express) -> ["red","blue"] (array)
Python (Flask) -> "red" (first via .get)A practical exploit: a payment WAF rule inspects the first amount parameter, but the backend reads the last. Send amount=100&amount=1 and you may pay 1 while the filter sees 100. Always confirm the actual resolution against your specific target rather than trusting the table.
Mass assignment, also called over-posting or autobinding, is when a framework automatically maps all incoming request fields onto an internal object, letting an attacker set properties that should be off-limits. If a user-update endpoint binds the whole JSON body to a User model, adding a field the form never showed can flip privileges.
PATCH /api/users/me HTTP/1.1
Content-Type: application/json
{
"displayName": "Alex",
"isAdmin": true, // never exposed in the UI
"role": "superadmin", // bound anyway by autobinder
"emailVerified": true,
"accountBalance": 999999
}Rails (update_attributes without strong parameters), Spring (@ModelAttribute binding the whole entity), and Laravel (unguarded $fillable) have all shipped this by default. Mass assignment is API6:2023 in the OWASP API Top 10, so it belongs in every API penetration testing scope.
Test mass assignment by discovering an object's full field set, then submitting privileged fields the endpoint did not advertise and checking whether they stick. The fastest way to learn the model's properties is to read a GET response for the same object, because the read API often returns fields the write API silently accepts.
Concretely: capture a legitimate update in Burp Suite, then add candidate fields (isAdmin, role, verified, balance, user_id, tenant_id) to the JSON body and resend. Re-fetch the object to confirm the change persisted. Try nested objects too ("owner":{"id":1}) and watch for IDOR-style cross-tenant assignment by setting an ID that belongs to someone else. This is exactly the kind of binding logic that a continuous, reasoning-driven approach like agentic pentesting is well suited to probe across hundreds of endpoints.
Fix both with explicit allowlists rather than trusting framework defaults. For mass assignment, bind only the fields you intend to accept: use Rails strong parameters (params.require(:user).permit(:name, :email)), Spring DTOs instead of binding entities directly, Laravel $fillable with a tight list, and dedicated request/response schemas in your API layer.
For HPP, normalize duplicate parameters at the edge (decide first-wins or reject-on-duplicate and apply it consistently across WAF and app), validate that each parameter appears once where it should, and make sure your WAF and application resolve parameters the same way. Both flaws come down to the same principle from the OWASP Top 10: never let the client decide which internal properties get written.