oath-toolkit-help
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[SECURITY ADVISORY] oath-toolkit: pam_oath usersfile ${HOME} privilege e


From: Simon Josefsson
Subject: [SECURITY ADVISORY] oath-toolkit: pam_oath usersfile ${HOME} privilege escalation
Date: Fri, 04 Oct 2024 12:38:31 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/27.1 (gnu/linux)

OATH Toolkit pam_oath usersfile ${HOME} privilege escalation
============================================================

Security Vulnerability
----------------------

OATH Toolkit provides two components liboath and pam_oath.  Pam_oath
is normally run as root and assumes that the "usersfile" path setting
value points to a root-controlled and protected file containing the
OATH secrets (cryptographic HMAC keys) for users.

The documentation suggests using a root-owned /etc/users.oath and to
do "chmod go-rw /etc/users.oath" on it.  The design assumes that file
permissions are set up to prevent malicious read or writes to the
credential file by unauthorized users.  Whether file permissions are
setup correctly or not is not checked by the code.

On every successful authentication, the file is rewritten to prevent
OTP replay attacks.  The rewriting logic works by acquiring a POSIX
file lock on a newly created *.lock file (in the same directory), and
then writing file content to a newly created *.new file (also in the
same directory).  File ownership of the new file is set to the same as
the original file.  The original usersfile is replaced atomically with
the newly created file.

With the introduction of the ${HOME} indirection variable in the
usersfile parameter the design assumptions no longer holds.  The
feature was added in version 2.6.7 released on 2021-05-01.

A typical setup when ${HOME} is used in a usersfile value such as
usersfile=${HOME}/.config/oath.secrets is to allow users to manage
their own credentials.  The file is owned by the user who is
responsible for adding the secret to it, and to set read/write
permissions on the file appropriately.

The security problem is easy to exploit.  To demonstrate the
vulnerability with a vulnerable version and configuration, create a
symbolic link $HOME/.config/oath.secrets.new that points to a
privileged file such as /etc/shadow.  After successful login as the
user, pam_oath/liboath has followed the symbolic link and rewrote the
target file with new updated OATH credentials and sets ownership of
that file to the user.  The user is now able to modify /etc/shadow.

We are not aware of any active exploits in the wild of this flaw.

Affected versions and configurations
------------------------------------

OATH Toolkit pam_oath and liboath version 2.6.7 to version 2.6.11 are
affected.  Version 2.6.12 prevents the attack.

The attack requires that the "usersfile" setting has a file or path
component that is in a vulnerable location.  The common setup with a
write/read-protected usersfile=/etc/users.oath setup is not
vulnerable.

While admin's may specify a "usersfile" in a world-writeable directory
like /tmp we regard that as a configuration error.  In most scenarios,
only "usersfile" with ${HOME} in them should be regarded as a
vulnerable configuration.

One vulnerable setting is usersfile=/home/joe/.config/system.oath
giving joe the ability to modify root-owned files, assuming a non-root
user joe with write access to anything below /home/joe.  This is
somewhat similar to having a system SSH configuration of HostKey
/home/joe/ssh_hostkey which we believe is unlikely and also consider
to be a configuration error.

Another example is via the ${USER} setting as in
usersfile=/home/${USER}/.config/oath.secrets giving any non-root user
the ability to control system files, assuming they have write access
to anything below /home/${USER}.  We have improved the documentation
regarding ${USER} to be for those settings where root-controlled
per-user files are desired.  The intended use of ${USER} strings are
for setups like usersfile=/var/oath/oath.${USER}.secrets where the
files are per-user but owned by root and have file permissions setup
to prevent read/write from the user.  The root-ownership and file
permission should prevent users from being able to reach their OATH
credentials when ${USER} is used.

The pam_oath fix only address configurations that uses ${HOME} and not
any other vulnerable configurations.  The liboath fix prevent direct
attacks via *.new and *.lock symlinks, but other scenarios are
possible and the input to oath_authenticate_usersfile() MUST be a
trusted filename -- suitable sanitization is application-dependent.

Details
-------

The vulnerable code in liboath is inside
oath_authenticate_usersfile(), quoting code from version 2.6.11 which
can be reviewed here:

https://gitlab.com/oath-toolkit/oath-toolkit/-/blob/oath-toolkit-2.6.11/liboath/usersfile.c?ref_type=tags#L324

The lock file is acquired:

  /* Open lockfile. */
  {
    int l;
    l = asprintf (&lockfile, "%s.lock", usersfile);
    if (lockfile == NULL || ((size_t) l) != strlen (usersfile) + 5)
      return OATH_PRINTF_ERROR;
    lockfh = fopen (lockfile, "w");
    if (!lockfh)
      {
        free (lockfile);
        return OATH_FILE_CREATE_ERROR;
      }
  }

Since fopen("w") is used any existing file with the predictable *.lock
filename will be opened, including if that file happens to be a
symbolic link that points elsewhere.  The code is run as root, so a
symbolic link pointing to confidential files under /etc are happily
opened.  Note that since "w" is used, the file will be automatically
truncated.  So this allows non-root to truncate root-owned files.

Reading on to the *.new file handling:

https://gitlab.com/oath-toolkit/oath-toolkit/-/blob/oath-toolkit-2.6.11/liboath/usersfile.c?ref_type=tags#L360

    outfh = fopen (newfilename, "w");
    if (!outfh)
      {
        free (newfilename);
        fclose (lockfh);
        free (lockfile);
        return OATH_FILE_CREATE_ERROR;
      }

Similarily, this will open a file for writing, and later code will
essentially copy data from the existing file into the new one.  This
new file is moved back into the original place.

The PAM module invokes oath_authenticate_usersfile() in pam_oath.c,
which can be shown here:

https://gitlab.com/oath-toolkit/oath-toolkit/-/blob/oath-toolkit-2.6.11/pam_oath/pam_oath.c?ref_type=tags#L452

The code is running as root so it may work on an untrustworthy
filename.  The design used to be that admin's specify a trusted
pathname here, but the ${HOME} use-case broke this design assumption
and the pathnames should then no longer be considered trusted.

Solution
--------

Version 2.6.12 contains the following liboath patch to use fopen(wx):

https://gitlab.com/oath-toolkit/oath-toolkit/-/commit/3235a52f6b87cd1c5da6508f421ac261f5e33a70

Some non-glibc and non-ISO C11 platforms needs the following patch to
enable gnulib's fopen(wx) workaround:

https://gitlab.com/oath-toolkit/oath-toolkit/-/commit/3271139989fde35ab0163b558fc29e80c3a280e5

Then pam_oath.c is modified to call seteuid()/setegid() as follows:

https://gitlab.com/jas/oath-toolkit/-/commit/95ef255e6a401949ce3f67609bf8aac2029db418

We recommend you to upgrade to version 2.6.12.

If that is unpractical we recommended you to apply the patches on top
of your earlier version.

Reproducer
----------

Included in the 2.6.12 release is a C program to test if a liboath is
vulnerable or not.  It can be built as follows on a system with
liboath properly installed:

git clone https://gitlab.com/oath-toolkit/oath-toolkit.git
cd oath-toolkit
git checkout oath-toolkit-2.6.12
cd liboath/tests
cc -o tst_fopen-wx tst_fopen-wx.c $(pkg-config --libs --cflags liboath)
rm -f cve.oath cve.oath.new cve.sshd-config cve.oath.lock
printf 
'HOTP/E/8\tsilver\t4711\t3132333435363738393031323334353637383930313233343536373839303132\n'
 > cve.oath
echo my-magic-cookie > cve.sshd-config
ln -s cve.sshd-config cve.oath.new
./tst_fopen-wx cve.oath silver 670691 4711

When invoked on a Trisquel 11 system with liboath0 and liboath-dev
version 2.6.7-3build1 installed, it will print the following:

Liboath fopen(wx) bug test for oath.h 2.6.7 liboath.so 2.6.7
FAIL: Liboath VULNERABLE to fopen(wx) bug.

To test a particular liboath use LD_PRELOAD as follows:

LD_PRELOAD=/usr/local/lib/x86_64-linux-gnu/liboath.so.0 ./tst_fopen-wx cve.oath 
silver 670691 4711

The liboath/tests/tst_fopen-wx.sh script can be used to setup and
invoke the C program testing two different vulnerable configurations.

Related work
------------

The logic for the usersfile handling was inspired by earlier versions
of mod-authn-otp and pam_google_authenticator although their modern
design appears to be somewhat different from pam_oath's current code.

https://github.com/archiecobbs/mod-authn-otp
https://github.com/google/google-authenticator-libpam/

While the liboath oath_authenticate_usersfile() vulnerability is not a
typical "time-of-check, time-of-use" race condition (since there is no
check happening in the code) it exhibits the same pattern in the code
since fopen(w) is used instead of fopen(wx).  Running GitLab semantic
analysis (SAST) on the vulnerable code flagged it as problematic:

https://gitlab.com/oath-toolkit/oath-toolkit/-/security/vulnerabilities/139535897
https://gitlab.com/oath-toolkit/oath-toolkit/-/security/vulnerabilities/139535890
https://wiki.sei.cmu.edu/confluence/display/c/FIO45-C.+Avoid+TOCTOU+race+conditions+while+accessing+files

We have enabled GitLab SAST and Coverity scanning of OATH Toolkit and
will review the findings.

SUSE's alternative patch and advisory can be found via:

https://security.opensuse.org/2024/10/04/oath-toolkit-vulnerability.html

It rely on Linux kernel specific features and uses fork() which was
determined to be contrary to the liboath design, which aims to be
portable to macOS and *BSD and beyond.  This alternative patch may be
used by some vendors, and it is assumed to fix the security problem.

History
-------

Fabian Vogt reported this issue in private e-mail on 2024-08-08.
Matthias Gerstner reported the issue as a GitLab confidential issue on
2024-08-20.  These reports came in during vacation time and were not
read by the maintainer.  Salvatore Bonaccorso reached out on
2024-09-29 via SMS, and the progress since then has been tracked in
the bug tracker:

https://gitlab.com/oath-toolkit/oath-toolkit/-/issues/43

Credits
-------

The problem was discovered by Fabian Vogt of SUSE.  An initial patch
against liboath was developed by Matthias Gerstner of SUSE Security
Team.  An alternative and portable patch to liboath and pam_oath were
developed by Simon Josefsson.

/Simon

Attachment: signature.asc
Description: PGP signature


reply via email to

[Prev in Thread] Current Thread [Next in Thread]