Skip to main content

Overview

This vulnerability occurs when an application allows attackers to modify internal object properties or attributes that they should not be able to control. This is a common flaw in dynamically-typed languages or frameworks that map user input (like JSON or form data) directly onto objects.
  • In JavaScript (Node.js): This is known as Prototype Pollution. An attacker sends a crafted payload (e.g., {"__proto__": {"isAdmin": true}}) that modifies the Object.prototype. Since most objects inherit from this prototype, the attacker pollutes all objects in the application, potentially adding properties like isAdmin or overwriting critical functions, leading to security bypasses or RCE.
  • In Server-Side Frameworks (Rails, Laravel, ASP.NET): This is known as Mass Assignment. An attacker submits data for fields that aren’t in the form (e.g., {"name": "Attacker", "is_admin": true}). If the application maps all submitted data to the model (User.update(params)), the attacker can “assign” themselves the is_admin role.

Business Impact

Exploiting this flaw can lead to:
  • Privilege Escalation: The most common impact. Attackers grant themselves administrative rights (isAdmin = true) or modify their user ID.
  • Data Tampering: Overwriting critical object properties (like an item’s price) before processing.
  • Security Bypass: Disabling security controls or flags (isVerified = true).
  • Remote Code Execution (RCE): In prototype pollution, if a polluted property is later used to execute code (e.g., as part of a child_process command or template render).

Reference Details

CWE ID: CWE-915 OWASP Top 10 (2021): A08:2021 - Software and Data Integrity Failures Severity: High to Critical

Framework-Specific Analysis and Remediation

Defenses vary by language and framework but center on validating attribute names and controlling data binding. Never trust that user input only contains the fields you expect.
  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Risk occurs when using setattr() with user-controlled property names or unsafely using __dict__.update() with user data.

Vulnerable Scenario 1: Unsafe setattr()

A view updates a user profile by dynamically setting attributes from POST data.
# views.py
@login_required
def update_profile(request):
    user = request.user
    for key, value in request.POST.items():
        # DANGEROUS: Attacker can set ANY attribute.
        # POST data: {"__class__": ...} (might change object type)
        # POST data: {"is_staff": True} (Privilege Escalation)
        # POST data: {"is_superuser": True}
        setattr(user, key, value)
    user.save()
    return redirect('profile')

Vulnerable Scenario 2: Unsafe __dict__.update()

# utils/data_loader.py
def update_object_from_dict(obj, data_dict):
    # DANGEROUS: User-controlled dictionary 'data_dict'
    # is merged directly into the object's attributes.
    # Payload: {"is_admin": True}
    obj.__dict__.update(data_dict)
    obj.save() # Example

Mitigation and Best Practices

Use Django Forms or DRF Serializers. These tools act as a secure intermediary. They explicitly define which fields are allowed to be updated from user input, creating a secure allow-list. Avoid setattr and __dict__.update with user-controlled keys/data.

Secure Code Example

# forms.py (Django - Secure)
from django import forms

class ProfileForm(forms.ModelForm):
    class Meta:
        model = User
        # SECURE: Only 'first_name' and 'last_name' can be updated via this form.
        # Attacker submitting 'is_staff' will be ignored by form.is_valid().
        fields = ['first_name', 'last_name']

# views.py (Secure)
@login_required
def update_profile_secure(request):
    user = request.user
    if request.method == 'POST':
        # SECURE: Form validates and binds *only* the allowed fields.
        form = ProfileForm(request.POST, instance=user)
        if form.is_valid():
            form.save()
            return redirect('profile')
    else:
        form = ProfileForm(instance=user)
    return render(request, 'profile_form.html', {'form': form})

Testing Strategy

Identify endpoints that update objects (user profiles, settings). Submit requests containing additional, malicious properties (e.g., is_staff=True, is_superuser=True, __class__=...). Check if the server state reflects these unauthorized changes. Review code for setattr(obj, key, ...) or obj.__dict__.update(...) where key or the update dictionary are user-controlled.