Back to Blog

Hardening Linux PAM Modules against Credential Stuffing

Hardening Linux PAM Modules against Credential Stuffing

In the modern threat landscape, the perimeter is no longer a well-defined boundary. As organizations migrate to hybrid and multi-cloud environments, the primary vector for unauthorized access has shifted from complex zero-day exploits to the much simpler: credential stuffing. Using massive databases of leaked username and password pairs, attackers systematically attempt to hijack legitimate accounts. While much of the industry focus remains on Web Application Firewalls (WAFs) and identity providers (IdPs), the Linux operating system itself remains a critical, often overlooked, battleground.

The Pluggable Authentication Modules (PAM) framework is the Linux kernel's gatekeeper. If you can harden the PAM stack, you can implement defensive layers-such as account lockouts, multi-factor authentication (MFA), and environmental constraints-directly at the point of authentication.

The PAM Architecture: Understanding the Stack

To defend against credential stuffing, one must first master the mechanics of the PAM stack. PAM is not a single security mechanism but a modular framework that allows system administrators to chain different authentication methods together.

The power of PAM lies in its "stacking" semantics. When a service (like `sshd` or `login`) requests authentication, it calls a specific PAM service file (e'g., `/etc/pam.d/sshd`). This file contains a series of modules categorized into four management groups:

  1. `auth`: Verifies the identity of the user (e.g., checking a password).
  2. `account`: Verifies if the user is allowed to authenticate (e.g., checking account expiration or time-of-day restrictions).
  3. `password`: Handles password updates and complexity enforcement.
  4. `session`: Manages the environment for the user's session (e.g., mounting home directories or logging).

The control flags-`required`, `requisite`, `sufficient`, and `optional`-determine how the stack reacts to a module's success or failure. For hardening, understanding the distinction between `required` (continues the stack even on failure, but returns failure at the end) and `requisite` (immediately terminates the stack on failure) is critical for preventing attackers from "probing" the stack to see which modules are active.

Strategy 1: Implementing Stateful Account Lockouts with `pam_faillock`

The most direct defense against credential stuffing is preventing the automated, high-frequency attempts characteristic of stuffing attacks. While `pam_tally2` was the legacy standard, modern Linux distributions (RHEL 8+, Ubuntu 20.04+) utilize `pam_faillock`.

`pam_faillock` tracks failed authentication attempts in a persistent state. By configuring a threshold, you can effectively neutralize automated stuffing by locking the account after $N$ failed attempts.

Implementation Example

In your `/etc/pam.d/sshd` (or the common-auth file), you should implement the following logic:

```text

Pre-auth: Check for existing failures and deny if threshold reached

auth required pam_faillock.so preauth silent deny=5 unlock_time=1800

Post-auth: Increment failure counter on failed attempt

auth [default=die] pam_faillock.so authfail deny=5 unlock_time=1800

Reset counter on successful login

auth sufficient pam_faillock.so authsucc deny=5 unlock_time=1800

```

Technical Breakdown:

  • `deny=5`: The threshold for failed attempts.
  • `unlock_time=1800`: The duration (in seconds) the account remains locked (30 minutes).
  • `authfail`: This module specifically handles the incrementing of the failure counter upon a bad password.
  • `authsucc`: This ensures that a successful login resets the failure tally, preventing legitimate users from being locked out due to a single typo following several previous failures.

Strategy 2: Multi-Factor Authentication (MFA) as a Hard Barrier

Credential stuffing relies on the assumption that the password is the sole factor of authentication. By injecting an MFA module into the `auth` stack, you render stolen credentials useless.

The most common implementation is `pam_google_authenticator`, which uses TOTP (Time-based One-Time Password).

Implementation Example

After installing the module, you must modify the `sshd` PAM configuration. A common mistake is only updating the PAM file without updating the `sshd_config` to allow the second factor.

In `/etc/pam.d/sshd`:

```text

auth required pam_google_authenticator.so

```

In `/etc/ssh/sshd_config`:

```text

KbdInteractiveAuthentication yes

UsePAM yes

```

By setting `auth required`, the authentication process cannot proceed unless the TOTP token is validated. Even if an attacker possesses the correct password from a leak, they cannot satisfy the `required` requirement of the TOTP module.

Strategy 3: Contextual Authentication with `pam_exec`

For high-security environments, you can implement "contextual" authentication. This involves using `pam_exec` to run a custom script that evaluates the metadata of the authentication attempt-such as the source IP, the time of day, or even the presence of a specific VPN certificate.

If your organization only operates within specific geographic regions, a `pam_exec` script can query a GeoIP database and return a non-zero exit code to abort the authentication if the IP originates from a high-risk region.

```text

Example: Execute a script to validate the connection source

auth required pam_exec.so /usr/local/bin/validate_connection_context.sh

```

The script `/usr/local/bin/validate_

Conclusion

As shown across "The PAM Architecture: Understanding the Stack", "Strategy 1: Implementing Stateful Account Lockouts with `pam_faillock`", "Strategy 2: Multi-Factor Authentication (MFA) as a Hard Barrier", a secure implementation for hardening linux pam modules against credential stuffing depends on execution discipline as much as design.

The practical hardening path is to enforce strict token/claim validation and replay resistance, certificate lifecycle governance with strict chain/revocation checks, and host hardening baselines with tamper-resistant telemetry. This combination reduces both exploitability and attacker dwell time by forcing failures across multiple independent control layers.

Operational confidence should be measured, not assumed: track mean time to detect and remediate configuration drift and certificate hygiene debt (expired/weak/mis-scoped credentials), then use those results to tune preventive policy, detection fidelity, and response runbooks on a fixed review cadence.

Related Articles

Explore related cybersecurity topics:

Recommended Next Steps

If this topic is relevant to your organisation, use one of these paths: