As part of the background research on the U2F HID handshake information leak, I discovered through manual code analysis that the Yubico libu2f-host host-side C library contained an out of bounds write vulnerability which could be triggered by a malicious U2F client device.
Since the libu2f-host library is the basis for PAM over U2F login functionality that can be added to Unix-like operating systems such as Linux, BSD and macOS, this issue had the potential to affect a number of specially protected systems. MITRE assigned CVE-2018-20340 for this issue.
Note: part two describes CVE-2019-9578, a closely related second vulnerability.
As described in depth in the U2F HID issue article, the U2F HID communication with an U2F client device is always initiated by the host side. The U2F client - usually a physical token connected via USB - then responds with with a specific reply message to the initial request and the main U2F communication including the cryptographic challenges can be done over the arranged channel id.
The vulnerability is present due to faulty handling of the first reply message in the libu2f-host library.
The U2F handshake is initiated via the
U2FHID_INIT message in the
The actual communication happens in the
u2fh_sendrecv function which is built to generically handle the send- and receive logic. Note that
u2fh_sendrecv conveniently collects and returns the relevant bytes of the reply message(s) to the calling function. The generic nature of this utility function is part of the general problem, as I will show later.
In the beginning of
init_device, the response buffer
resp with the size 1024 bytes is allocated and
resplen is set to value
1024, as shown in the previous code snippet.
resplen is then handed to
u2fh_sendrecv where it is used under the
recvlen name as an upper limit on the number of U2F “payload” bytes that may be accepted during the receive stage:
resplen is also used as a return parameter to specify how many U2F message payload bytes were actually written to the
u2fh_sendrecv is a generic send/receive function, it is not designed to limit the type and length of the received message.
For the relevant U2F client response, the U2F specification defines a particular response message with a fixed number of 17 bytes of U2F payload. This data is normally contained in a single HID response packet.
The FIDO U2F standard mandates compatibility with larger responses:
An U2FHID host shall accept a response size that is longer than the anticipated size to allow for future extensions of the protocol, yet maintaining backwards compatibility.
u2fh_sendrecv imposes no meaningful checks at all and happily assembles multi-packet messages (via so-called continuation packets) which allows attackers to provide up to 1024 bytes of U2F payload data instead of the regular 17 bytes while still returning
U2FH_OK and continuing in the program flow.
The actual vulnerability is triggered in
init_device once the
resp buffer of attacker-controlled size and with attacker-controlled contents is memcpy‘ed to the
initresp struct that is at most 20 bytes in size, which leads to the out of bounds write:
Up to 1004 bytes of attacker-controlled data are written to the stack.
Attack scenario and security implications
The attack applies to host systems which use of libu2f-host through
- an authentication module like pam_u2f.so (privileged execution)
- other userspace applications that depend on libu2f-host such as yubikey-manager
- the command-line tool bundled with libu2f-host
Once the library initiates a handshake with an U2F token, e.g. during an authentication attempt, the token can trigger the buffer overflow on the stack and potentially execute code with the relevant privileges or potentially skip the login authentication. Any malicious USB device enumerating as a recognized U2F token class can perform this attack.
Yubico has assessed this with a CVSSv3 score of 6.3 via AV:P/AC:H/PR:N/UI:R/S:U/C:H/I:H/A:H.
In my opinion, the CVSS Scope (S) metric should be Changed if the attack allows to move past authentication barriers and therefore past the vulnerable component, but I find their overall classification reasonable.
Relation to other attacks
This vulnerability shares a number of similarities with the CVE-2018-14779 buffer overflow in Yubico host-side code related to smart cards which was discovered mid-2018 by Eric Sesterhenn of X41 D-Sec. Eric’s talk “In Soviet Russia Smart Card Hacks You” at 35C3 describes both the general patterns which are relevant for this attack vector as well as the specific impact that buffer overflows in privileged authentication code can have.
I find it plausible that drivers and low-level code for local, “safe” systems such as smartcards have less thoroughly analyzed or hardened interfaces since the involved developers are mainly focused on other development aspects or different attack scenarios. This is clearly an interesting area for further research.
I was surprised to find that the libu2f-host build system did not enable
-fstack-protector-all or other protections such as
-D_FORTIFY_SOURCE=2 when compiled from source:
|Partial RELRO||No canary found||NX enabled||PIE enabled||No RPATH||No RUNPATH||136 Symbols||No||0||8||u2f-host|
Particularly the missing stack canary is a serious problem in the context of this vulnerability as it reliably detects the attack and limits the impact to denial of service.
During discussion with Yubico, it turned out that most of the hardening flags are usually provided by the build environment. This meant that most binary packages were in fact shipped with mitigations in place by major distributions like Debian, Ubuntu and others since their build environment includes the hardening flags:
|Full RELRO||Canary found||NX enabled||PIE enabled||No RPATH||No RUNPATH||No Symbols||Yes||2||4||u2f-host|
However, it is possible that the library is used productively in other places and systems without these mitigations, e.g. as a self-compiled binary or as a code dependency. In my opinion, these hardening flags should be included and active by default.
During research, I found that the host library always uses the identical “nonce” when initiating contact with the U2F client:
For the malicious client, this provides a simple and accurate way to detect that libu2f-host is used on the host side. Through this detection, a malicious U2F token can behave functionally correct when used against unaffected host-side U2F code (e.g. browsers) and only trigger the exploit once used with the vulnerable library.
I’ve built a simple “malicious” U2F token on the basis of the Trezor One. The device detects the characteristic nonce value of the library (see above) and “attacks” with one packet full of
AAAAA to demonstrate the issue.
For hardened libu2f-host binaries with stack canary, this leads to a crash, as expected:
I responsibly disclosed the vulnerability to Yubico in December 2018 and received quick and competent feedback during the holiday season.
They applied for CVE-2018-20340 and originally asked me for 90 days of disclosure embargo but managed to ship the release after about ~52 days.
A number of Linux distributions were given early warning to prepare security releases.
Overall, I am fairly satisfied with the disclosure process.
Relevant libu2f-host sources
|Yubico upstream||Github||1.1.7 via 1, 2||YSA-2019-01, changelog|
|Debian package||Debian||1.1.2-2+deb9u1||#921725, DSA-4389, bugtraq|
|Ubuntu package||Ubuntu||1.1.4-1ubuntu0.1, 1.1.6-1ubuntu0.1||#1814153, CVE tracker|
|SUSE / openSUSE||see references||CVE tracker, #1124781, SUSE-SU-2019:1340-1|
|Mac homebrew||Homebrew||see source||-|
|2018-12-17||First email to Yubico, PGP key requested|
|2018-12-19||Technical disclosure to Yubico|
|2018-12-20||Yubico confirms the issue|
|2018-12-21||Yubico requests a CVE from MITRE|
|2019-02-08 12:00 CET||Public disclosure|
|2019-02-11||Linux distributions such as Debian and Arch publish security advisories|
|2019-02-18||OpenSuse publishes security avisory|
|2019-04-09||Fedora releases a patch for Fedora30|
|2019-05-19||Fedora releases a patch for Fedora29|
|2019-05-24||SUSE publishes a security advisory|
|2019-06-05||Gentoo switches to a patched version|
Yubico provided a number of their hardware products as a bug bounty for this issue.