X41 D-Sec GmbH Security Advisory: X41-2018-003

Multiple Vulnerabilities in pam_pkcs11

Overview

Summary and Impact

It is possible to replay an authentication by using a specially prepared smartcard or token in case pam-pkcs11 is compiled with NSS support. Furthermore two minor implementation issues have been identified.

X41 did not perform a full test or audit of the software.

Product Description

This Linux-PAM login module allows a X.509 certificate based user
login. The certificate and its dedicated private key are thereby
accessed by means of an appropriate PKCS #11 module. For the
verification of the users’ certificates, locally stored CA
certificates as well as either online or locally accessible CRLs are
used.

Authentication Replay

Summary and Impact

A replay attack is possible due to a logic bug in file pam_pkcs11.c. In function pam_sm_authenticate() a nonce is generated and signed with the card to verify that the card holds the matching secret key, if a valid certifiate is found. This is done using the function get_random_value(), which in turn calls PK11_GenerateRandom(), which queries the smartcard for random data.

This allows for a replay attack with a malicious smartcard. If a user plugins in his card into a compromised computer, the nonce and answer can be recorded by an attacker. The attacker then modifies a smartcard or a smartcard emulator to replay with the exact same nonce and signed data, which allows the attacker to login to another computer without having further access to the smartcard.

Workarounds

Switch to pam_p11.

Buffer Overflow

Summary and Impact

In file openssh_mapper.c a stack based buffer overflow is possible if a user has a home directory with a length of more than 512 bytes. This allows to overwrite the passwd structure and possibly the return address in openssh_mapper_match_user();

openssh_mapper.c
static int openssh_mapper_match_user(X509 *x509, const char *user, void *context) {
        struct passwd *pw;
        char filename[512];
        if (!x509) return -1;
        if (!user) return -1;
        pw = getpwnam(user);
        if (!pw || is_empty_str(pw->pw_dir) ) {
            DBG1("User '%s' has no home directory",user);
            return -1;
        }
        sprintf(filename,"%s/.ssh/authorized_keys",pw->pw_dir);
        return openssh_mapper_match_keys(x509,filename);
}

Workarounds

Switch to pam_p11.

Memory not cleaned properly before free()

Summary and Impact

In several places memory is set to zero using memset() and passed on to free() afterwards. This is a pattern which modern compilers optimize away, which renders the call to memset() useless. This causes sensitive data such as passwords to remain in the memory, which defeats the original intention of the code.

   memset(password, 0, strlen(password));
   free(password); 

Workarounds

Switch to pam_p11.

Timeline