Credential Stuffing & Brute Force

Password SprayingOnline Brute Force

Credential Stuffing & Brute Force at a glance

What it is: Attackers use leaked credentials or automated guessing to take over accounts by trying many login attempts across accounts or many passwords against a single account.
Why it happens: Authentication vulnerabilities arise when login, token, or password-based flows lack proper safeguards, and attackers exploit reset or OAuth mechanisms to discover or compromise weakly protected accounts.
How to fix: Use layered defenses like rate limits, MFA, and risk-based controls; normalize responses to prevent enumeration; and monitor failed login patterns to block credential attacks.

Overview

Credential stuffing uses collections of username/password pairs, often from previous breaches, to test for account reuse. Password spraying tries a small set of common passwords across many accounts to avoid triggering account lockouts. Brute force targets single accounts with many guesses. Modern defenses combine per-account and per-IP throttling, device and behavior signals, and step-up MFA to reduce success rates.

sequenceDiagram participant Bot as Attack Bot participant App as App Server Bot->>App: POST /login {email: a@x.com, password: p1} App-->>Bot: 401 invalid Bot->>App: POST /login {email: b@x.com, password: p1} App-->>Bot: 200 token note over Bot: Success, repeats across many accounts using large lists
A potential flow for a Credential Stuffing & Brute Force exploit

Where it occurs

Authentication vulnerabilities typically appear in login, API token, and password handling endpoints, as well as in password reset and OAuth flows susceptible to account enumeration or user deception.

Impact

Successful credential stuffing results in account takeover, data exfiltration, fraud, and lateral attacks. Even failed attempts can increase support load, generate alerts, and indicate targeted campaigns against your customers.

Prevention

Prevent this vulnerability with layered defenses including adaptive authentication, rate limits, lockouts, short-lived tokens, credential breach detection, and normalized responses to block enumeration and large-scale credential abuse.

Examples

Switch tabs to view language/framework variants.

Express, login endpoint allows unlimited attempts (no rate limit)

Public login accepts username/password with no throttling or lockout, enabling credential stuffing attacks.

Vulnerable
JavaScript • Express — Bad
const express = require('express');
const app = express();
app.use(express.json());
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await db.findUserByEmail(email);
  if (!user) return res.status(401).send('invalid');
  const ok = await verifyPassword(user, password);
  if (!ok) return res.status(401).send('invalid');
  res.json({ token: issueToken(user) });
});
  • Line 6: No throttling or lockout, unlimited attempts permitted

Without rate limits automated attackers can test millions of credential pairs quickly.

Secure
JavaScript • Express — Good
const rateLimit = require('express-rate-limit');
app.use('/login', rateLimit({ windowMs: 15*60*1000, max: 10 }));
app.post('/login', async (req, res) => {
  // same as before but with rate limiting and additional device challenge
});
  • Line 1: Apply rate limiting to login routes to slow automated attacks

Rate limits slow attackers and make credential lists less effective; combine with IP reputation and device telemetry.

Engineer Checklist

  • Apply per-IP and per-account rate limits with adaptive policies

  • Implement progressive delays and temporary lockouts for repeated failures

  • Require step-up MFA for high-risk sign-ins and new devices

  • Normalize error messages and timing to prevent oracles

  • Block or warn on known breached credentials and enforce strong password policies

  • Monitor aggregated failure patterns and block credential lists across the fleet

End-to-End Example

An attacker runs a credential stuffing campaign against /login using 5M leaked credentials. The app has no per-account lockouts and only basic IP throttling that attackers bypass with a botnet.

Vulnerable
JAVASCRIPT
app.post('/login', async (req, res) => {
  const { email, password } = req.body;
  const user = await User.findOne({ email });
  
  if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
    return res.status(401).send('invalid credentials');
  }
  
  // Bug: no rate limiting or account lockout
  const token = generateToken(user.id);
  res.json({ token });
});
Secure
JAVASCRIPT
const rateLimit = require('express-rate-limit');
const accountLockout = new Map(); // track failed attempts by email

const loginLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5, // 5 requests per IP
  message: 'Too many login attempts'
});

app.post('/login', loginLimiter, async (req, res) => {
  const { email, password } = req.body;
  
  // Check account lockout
  const failedAttempts = accountLockout.get(email) || 0;
  if (failedAttempts >= 5) {
    return res.status(429).send('Account temporarily locked');
  }
  
  const user = await User.findOne({ email });
  if (!user || !(await bcrypt.compare(password, user.passwordHash))) {
    accountLockout.set(email, failedAttempts + 1);
    return res.status(401).send('invalid credentials');
  }
  
  // Success - clear lockout and issue token
  accountLockout.delete(email);
  const token = generateToken(user.id);
  res.json({ token });
});

Discovery

This vulnerability is discovered by testing login endpoints for missing or weak rate limiting, account lockout mechanisms, CAPTCHA challenges, and MFA requirements that would prevent automated credential testing at scale.

  1. 1. Test rate limiting presence

    http

    Action

    Submit multiple rapid login attempts from a single IP to check for rate limiting

    Request

    POST https://app.example.com/login
    Headers:
    Content-Type: application/json
    Body:
    {
      "email": "test@example.com",
      "password": "wrongpassword"
    }

    Response

    Status: 200
    Body:
    {
      "note": "After 20 rapid attempts in 10 seconds, all requests still return 401 with no 429 rate limit response, confirming no IP-based throttling"
    }

    Artifacts

    http_status response_time_log
  2. 2. Test account lockout mechanism

    http

    Action

    Submit 10 failed login attempts for a single account to check for account-level lockouts

    Request

    POST https://app.example.com/login
    Headers:
    Content-Type: application/json
    Body:
    {
      "email": "victim@example.com",
      "password": "wrongpass"
    }

    Response

    Status: 200
    Body:
    {
      "note": "All 10 attempts return 401 with identical response times (~200ms), no lockout or progressive delays observed"
    }

    Artifacts

    http_status timing_analysis
  3. 3. Verify credential acceptance

    http

    Action

    Test a few known leaked credentials from public breach databases to confirm successful authentication is possible

    Request

    POST https://app.example.com/login
    Headers:
    Content-Type: application/json
    Body:
    {
      "email": "known@leaked.com",
      "password": "Password123"
    }

    Response

    Status: 200
    Body:
    {
      "note": "Valid credentials return 200 with authentication token, confirming the vulnerability can lead to account takeover"
    }

    Artifacts

    http_response_body session_token
  4. 4. Assess distributed attack feasibility

    analysis

    Action

    Test from multiple IPs to verify no coordinated attack detection across distributed sources

    Request

    ANALYSIS N/A - Analysis step

    Response

    Status: 200
    Body:
    {
      "note": "Requests from 5 different IP addresses show no cross-IP correlation or blocking, confirming distributed credential stuffing is feasible"
    }

    Artifacts

    multi_ip_test_results

Exploit steps

An attacker exploits this by using automated tools to test large collections of username/password combinations from data breaches against login endpoints, taking advantage of password reuse to compromise accounts at scale, often distributing requests across multiple IPs to evade basic rate limiting.

  1. 1. Acquire credential database

    Obtain leaked credentials

    analysis

    Action

    Download 5M email:password combinations from public breach databases and underground forums

    Request

    ANALYSIS N/A - Analysis step

    Response

    Status: 200
    Body:
    {
      "note": "Successfully compiled credential list containing 5,248,392 unique email:password pairs from various historical breaches"
    }

    Artifacts

    credential_database breach_sources_list
  2. 2. Deploy distributed infrastructure

    Setup botnet proxy network

    analysis

    Action

    Configure attack infrastructure using residential proxy network with 10,000+ IP addresses across multiple countries to evade IP-based blocking

    Request

    ANALYSIS N/A - Analysis step

    Response

    Status: 200
    Body:
    {
      "note": "Successfully established distributed testing infrastructure with IP rotation every 50 requests"
    }

    Artifacts

    proxy_network_config ip_pool
  3. 3. Execute credential stuffing campaign

    Automated login testing

    http

    Action

    Run automated credential testing at 500 requests/second distributed across the proxy network

    Request

    POST https://app.example.com/login
    Headers:
    Content-Type: application/json
    Body:
    {
      "email": "<EMAIL>",
      "password": "<PASSWORD>"
    }

    Response

    Status: 200
    Body:
    {
      "note": "After 3 hours of testing 1.5M credentials, achieved 2,847 successful logins (0.19% hit rate), obtaining valid authentication tokens for compromised accounts"
    }

    Artifacts

    successful_logins_list auth_tokens attack_metrics
  4. 4. Exploit compromised accounts

    Monetize account access

    analysis

    Action

    Use compromised accounts to extract personal data, make fraudulent purchases, or resell account access

    Request

    ANALYSIS N/A - Analysis step

    Response

    Status: 200
    Body:
    {
      "note": "Compromised 43 premium accounts, 284 accounts with stored payment methods, and extracted $127K in unauthorized transactions before detection"
    }

    Artifacts

    compromised_account_inventory fraud_transaction_log

Specific Impact

The attacker compromises multiple customer accounts, exfiltrates private data and performs fraudulent actions. A number of high-value accounts without MFA are accessed, resulting in financial loss and reputational damage.

Operationally the incident requires forced password resets, MFA enforcement, review of API logs, and potential notification to affected users and regulators.

Fix

Add layered defenses: rate limits, device and risk checks, and step-up MFA. Monitor patterns that indicate credential lists and block or sinkhole offending IP clusters. Rotate tokens and force reauth for sessions created during the attack window.

Detect This Vulnerability in Your Code

Sourcery automatically identifies credential stuffing & brute force vulnerabilities and many other security issues in your codebase.

Scan Your Code for Free