Format string vulnerability in evdev device handling
A format string vulnerability exists in the
libinput library, allowing a local attacker to achieve arbitrary code execution in the context of
- Current git commit 395d12d6
- Latest release 1.20.0
evdev devices are logged using
format parameter is manipulated at src/evdev.h:785 to prepend (among other things) the device name.
The resulting string
buf is then passed as the
format parameter to
log_msg_va at src/evdev.h:796
In X.org (and probably other users of
libinput), this logging function eventually leads into the system's
If the device name contains
printf-style formatting placeholders such as
%d, these will be passed on to the new format string, and interpreted incorrectly. User-controlled format strings are a known security vulnerability, CWE-134, and can be used by an attacker to execute malicious code.
This vulnerability requires physical access or write access to
/dev/uinput, in order to introduce a new
evdev device to the system. The attacker gains execution under the same privileges as the process where
libinput is loaded. In the case of X.org, this may be
root. This vulnerability could, for example, be used to bypass screen lockers and login screens.
Steps to reproduce
OS: Ubuntu 20.04 LTS
- Create a hardware device whose device name is
%s%s%s%s%s%s%s. This bug was discovered by renaming a pair of bluetooth headphones supporting AVRCP.
- Start X.Org and pair or plug in the device.
- Observe a crash
The following is a backtrace from a system running Ubuntu 20.04 LTS, where a pair of bluetooth headphones were connected.
[Current thread is 1 (Thread 0x7fc09775c500 (LWP 283870))] (gdb) bt #0 __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:50 #1 0x00007fc097d51859 in __GI_abort () at abort.c:79 #2 0x0000558f266eb810 in OsAbort () at ../../../../os/utils.c:1351 #3 0x0000558f266f0c49 in AbortServer () at ../../../../os/log.c:872 #4 0x0000558f266f1aaa in FatalError (f=f@entry=0x558f26725310 "Caught signal %d (%s). Server aborting\n") at ../../../../os/log.c:1010 #5 0x0000558f266e8b59 in OsSigHandler (unused=<optimized out>, sip=0x7fff57a7daf0, signo=11) at ../../../../os/osinit.c:156 #6 OsSigHandler (signo=11, sip=0x7fff57a7daf0, unused=<optimized out>) at ../../../../os/osinit.c:110 #7 0x00007fc097f353c0 in <signal handler called> () at /lib/x86_64-linux-gnu/libpthread.so.0 #8 __strlen_avx2 () at ../sysdeps/x86_64/multiarch/strlen-avx2.S:65 #9 0x00007fc097da7d45 in __vfprintf_internal (s=s@entry=0x7fff57a7ec30, format=format@entry=0x7fff57a7f220 "event25 - %s%s%s%s%s%s%s%s (AVRCP): is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n", ap=ap@entry=0x7fff57a7f208, mode_flags=mode_flags@entry=2) at vfprintf-internal.c:1688 #10 0x00007fc097dbafca in __vsnprintf_internal (string=0x7fff57a7edc5 "event25 - Keyboard (AVRCP): is tagged by udev as:", maxlen=<optimized out>, maxlen@entry=1019, format=0x7fff57a7f220 "event25 - %s%s%s%s%s%s%s%s (AVRCP): is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n", format@entry=0x3fb <error: Cannot access memory at address 0x3fb>, args=args@entry=0x7fff57a7f208, mode_flags=mode_flags@entry=2) at vsnprintf.c:114 #11 0x00007fc097e5cfc2 in ___vsnprintf_chk (s=<optimized out>, maxlen=maxlen@entry=1019, flag=flag@entry=1, slen=slen@entry=18446744073709551615, format=format@entry=0x3fb <error: Cannot access memory at address 0x3fb>, ap=ap@entry=0x7fff57a7f208) at vsnprintf_chk.c:34 #12 0x0000558f266f06e7 in vsnprintf (__ap=0x7fff57a7f208, __fmt=0x3fb <error: Cannot access memory at address 0x3fb>, __n=1019, __s=<optimized out>) at /usr/include/x86_64-linux-gnu/bits/stdio2.h:80 #13 Xvscnprintf (args=0x7fff57a7f208, format=0x3fb <error: Cannot access memory at address 0x3fb>, n=1019, s=<optimized out>) at ../../../../os/xprintf.c:207 #14 Xvscnprintf (s=<optimized out>, n=1019, format=format@entry=0x7fff57a7f220 "event25 - %s%s%s%s%s%s%s%s (AVRCP): is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n", args=args@entry=0x7fff57a7f208) at ../../../../os/xprintf.c:202 #15 0x0000558f266f230b in LogVMessageVerb (type=<optimized out>, verb=3, format=0x7fff57a7f220 "event25 - %s%s%s%s%s%s%s%s (AVRCP): is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n", args=0x7fff57a7f208) at ../../../../os/log.c:720 #16 0x00007fc08884b084 in evdev_log_msg (device=device@entry=0x558f285ba530, priority=priority@entry=LIBINPUT_LOG_PRIORITY_INFO, format=format@entry=0x7fc088872d00 "is tagged by udev as:%s%s%s%s%s%s%s%s%s%s%s\n") at ../src/evdev.h:779 #17 0x00007fc08884e9a4 in evdev_configure_device (device=<optimized out>) at ../src/evdev.c:1774 #18 evdev_device_create (seat=seat@entry=0x558f27a8ef70, udev_device=udev_device@entry=0x558f281914b0) at ../src/evdev.c:2225 #19 0x00007fc088869ad6 in path_device_enable (input=0x558f27a8dc90, udev_device=0x558f281914b0, seat_logical_name_override=<optimized out>) at ../src/path-seat.c:191 #20 0x00007fc088869cb9 in path_create_device (libinput=0x558f27a8dc90, udev_device=0x558f281914b0, seat_name=0x0) at ../src/path-seat.c:268 #21 0x00007fc088869eab in libinput_path_add_device (libinput=0x558f27a8dc90, path=0x558f2816b210 "/dev/input/event25") at ../src/path-seat.c:399 ...
Non-literal format strings should be avoided in general, and sanitized when unavoidable.
If the prepending of device name is important, a different method is probably more suitable. For example, rendering the prefix separately from the log message and concatenating the two.
This section will be updated as we go along:
0001-evdev-strip-the-device-name-of-format-directives.patch: v1, replacing
0001-evdev-strip-the-device-name-of-format-directives.patch: v2, replacing
%%and splitting devname and prefix
- 0001-evdev-strip-the-device-name-of-format-directives.patch: v3, using zalloc and fixing some other issues with the previous patch
Embargo cancelled due to independent bug filed here: xorg/xserver#1280 (moved)
Title: Format string vulnerability in libinput Component: libinput, affecting all Wayland compositors and X.Org when using xf86-input-libinput Report URL: https://gitlab.freedesktop.org/libinput/libinput/-/issues/752 Reporter: Albin Eldstål-Ahrens and Lukas Lamster CVSS: 7.1 AV:L/AC:L/PR:H/UI:N/S:C/C:H/I:H/A:H/E:U/RL:O/RC:C Disclosure date: CANCELLED DUE TO INDEPENDENT PUBLIC BUG When a device is detected by libinput, libinput logs several messages through log handlers set up by the callers. These log handlers usually eventually result in a printf call. Logging happens with the privileges of the caller, in the case of Xorg this may be root. The device name ends up as part of the format string and a kernel device with printf-style format string placeholders in the device name can enable an attacker to run malicious code. An exploit is possible through any device where the attacker controls the device name, e.g. /dev/uinput or Bluetooth devices. All versions of libinput since 1.10 (released Feb 2018) are affected. Upstream patches are available as commit 2a8b8fde90d63d48ce09ddae44142674bbca1c28 libinput releases that include these patches are: - 1.20.1 - 1.19.4 - 1.18.2 Releases of versions 1.17.x and earlier are not planned at this stage. Many thanks to Albin Eldstål-Ahrens and Benjamin Svensson from Assured AB for their discovery and responsible reporting of this issue. This issue was independently discovered by Lukas Lamster. Many thanks for their discovery and responsible reporting.