====================================================================

                               CERT-Renater

                    Note d'Information No. 2024/VULN406
_____________________________________________________________________

DATE                : 04/10/2024

HARDWARE PLATFORM(S): /

OPERATING SYSTEM(S): Systems running OATH Toolkit pam_oath, liboath
                               versions prior to 2.6.12.

=====================================================================
https://www.nongnu.org/oath-toolkit/CVE-2024-47191.html
_____________________________________________________________________


OATH Toolkit
One-time password components
OATH Toolkit pam_oath usersfile ${HOME} privilege escalation
(CVE-2024-47191)


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.



=========================================================
+ CERT-RENATER        |    tel : 01-53-94-20-44         +
+ 23/25 Rue Daviel    |    fax : 01-53-94-20-41         +
+ 75013 Paris         |   email:cert@support.renater.fr +
=========================================================
