pam-u2f: problematic PAM_IGNORE return values in pam_sm_authenticate() (CVE-2025-23013)
#local #PAM #CVETable of Contents
- 1) Introduction
- 2) Improper use of
PAM_IGNORE
Return Values - 3) Upstream Bugfix
- 4) Remaining Uses of
PAM_IGNORE
- 5) Possible Workaround
- 6) Timeline
- 7) References
1) Introduction
The pam-u2f module allows to use U2F (Universal 2nd Factor) devices like YubiKeys in the PAM authentication stack. The hardware tokens can be used as a second authentication factor, or to allow password-less login.
We have been checking all PAM modules in the openSUSE code base for bad return
values. During this effort we found that improper use of PAM_IGNORE
return
values in the pam-u2f module implementation could allow bypass of the second
factor or password-less login without inserting the proper device.
This report is based on pam-u2f release 1.3.0.
2) Improper use of PAM_IGNORE
Return Values
PAM modules basically consist of a set of hook functions that are invoked by
libpam based on the active PAM stack configuration. Each PAM module function
returns an int
containing one of the PAM_*
return
values defined in the libpam headers. These return
values are vital for the outcome of a PAM authentication procedure, since
libpam reports authentication success or failure depending on the return
values encountered while processing the modules configured in the auth
management group of the active PAM stack configuration.
The main business logic of the pam-u2f module is found in function
pam_sm_authenticate()
, which contains multiple code
paths that will result in a PAM_IGNORE
return value. The following is a list
of the possible situations that can cause this to happen:
- if an error occurs in
gethostname()
. - if various memory allocation errors occur in
strdup()
orcalloc()
. - if
resolve_authfile_path()
fails (which fails ifasprintf()
fails). - if
pam_modutil_drop_priv()
orpam_modutil_regain_priv()
fail.
Returning PAM_IGNORE
signifies to libpam that the pam-u2f module shall not
contribute to the return value that the application obtains. If no module
reports a decisive return value, then libpam will report an authentication
failure by default. However, if any other module in the auth
management group returns PAM_SUCCESS
, and no module marks an error
condition, the overall result of the authentication will be “success”.
How exactly this can happen is explored in the rest of this section.
In the pam-u2f documentation two main use cases for the PAM module are stated:
# as a second factor
auth required pam_u2f.so authfile=/etc/u2f_mappings cue
# for password-less authentication:
auth sufficient pam_u2f.so authfile=/etc/u2f_mappings cue pinverification=1
In the “second factor” scenario, a PAM_IGNORE
return from pam-u2f means that
login will be possible without actually providing a second factor. The first
factor authentication module (typically something like pam_unix
) will set a
PAM_SUCCESS
return value, which will become the overall authentication
result.
In the “password-less” authentication scenario, when pam-u2f is used
exclusively for authentication, a PAM_IGNORE
return could mean that login
will succeed without providing any authentication at all. The precondition for
this is that another module in the auth
management group returns
PAM_SUCCESS
. There exist utility modules that don’t actually authenticate
but perform helper functions or enforce policy. An example is the
pam_faillock
module, which can be added to the
auth
management group to record failed authentication attempts and lock the
account for a certain time if too many failed attempts occur. This module will
return PAM_SUCCESS
when running in “preauth” mode and if the maximum number
of failed attempts has not been reached yet. In such a case PAM_SUCCESS
would become the overall authentication result when pam-u2f returns
PAM_IGNORE
.
An attacker can attempt to provoke a situation that results in a PAM_IGNORE
return value in pam-u2f to achieve one of these outcomes. In particular,
provoking an out-of-memory situation comes to mind - for example if a local
attacker already has user level access and wants to escalate privileges via
sudo
or su
.
3) Upstream Bugfix
We suggested to upstream to change the problematic PAM_IGNORE
return values
to others that mark the authentication as failed, e.g. PAM_BUF_ERR
for
memory allocation errors or PAM_ABORT
for other critical errors. Furthermore
we suggested to harmonize the error handling in the affected function, because
different styles of return values have been used in
the retval
variable (PAM_*
constants mixed with literal integers returned
from sub-functions).
Upstream implemented a bugfix along these lines, which is available in commit a96ef17f74b8e4. This bugfix is available as part of release 1.3.1. Yubico also offer their own security advisory for this CVE.
4) Remaining Uses of PAM_IGNORE
PAM_IGNORE
should only be used in clearly defined circumstances, like when
necessary configuration for the PAM module is missing. Even then, this
behaviour ideally should require an explicit opt-in by administrators, by
passing configuration settings to the module’s PAM configuration line.
Two such cases remain in pam-u2f with the bugfix applied. These cases trigger if no auth file exists for the user to be authenticated and if the “nouserok” option has been passed to the PAM module.
5) Possible Workaround
If applying the bugfix is not possible right away, then a temporary workaround
for the issue can be applied via the PAM stack configuration by changing
the pam_u2f
line as follows:
auth [success=ok default=bad] pam_u2f.so [...]
This way even a PAM_IGNORE
return in pam_u2f.so
will be considered a bad
authentication result by libpam.
6) Timeline
2024-11-20 | We reported the issue to Yubico security, offering coordinated disclosure. |
2024-11-22 | Yubico security accepted coordinated disclosure and stated that they are working on a fix. |
2024-12-06 | Yubico security notified us that a bugfix release is planned in early January. |
2024-12-12 | Yubico security shared their suggested bugfix with us. We sent back minor suggestions for improvement. |
2025-01-08 | Yubico security informed us of the release date of 2025-01-14. |
2025-01-10 | Yubico security shared the CVE identifier and their formal security advisory with us. |
2025-01-14 | The upstream bugfix release 1.3.1 has been published as planned. |