Full Report
While playing around with the GreatFET One, the author found that Xorg would crash with format string payloads. After an advisory from Xorg that related to "input devices" the author decided to investigate to see if these bugs were the same thing. A format string is commonly used in many programming languages. In C, it has the potential for horrible memory corruption bugs though. When a printf family function is called, it takes in a string with format specifiers (%x, for instance) and a variable amount of arguments depending on the format specifier. If an attacker can control the string, then they can trigger the format specifier functionality on unintended data. In the case of Xorg, libinput is performing logging. While doing this logging, it is creating a format string dynamically by prepending a string with sprintf to be used later in another printf-like call. Because of this string concatenation for a format string with user controllable values, this created a fairly bad format string bug. This bug is explained at here. Since the root causing had already been done, the author decided to write up a proof of concept for it; the goal was to leak the stack canary. Immediately, the author ran into two problems. First, there is a length limit of an individual field is 126 characters. Secondly, because the data was being prepended and there was a %s in the end of the string, almost everything caused a crash. Finally, FORTIFY_SOURCE=2 was set on the binary, disallowing the usage of the %N for writes. To solve the %s crash problem, direct parameter access in the format string could be used. Since this doesn't increase the pointer being used for the stack, this was a good solution to the problem. Additionally, direct parameter access cannot skip any values because of FORTIFY_SOURCE=2 being set. Because of this and the length constraint, the furthest byte that can be accessed is 27. Now, the format string is not too bad looking It ends up being %1$p%2$p...%27$p, which is parameterized access for all 27 bytes. Depending on the location of the field in the payload, this would leak the stack canary in the last few bytes. Damn, that's super awesome! The author got this to work on Xubuntu 20.04.4. They tried this on Deban 11 systems but couldn't get it to work. Most distros have all binary protections, such as canaries, RELRO and things in place. They tried get code execution using this but the FORTIFY checks were just too much to overcome this. Overall, I love the post from a practical side. First, this bug should have been caught by the compiler but wasn't because some flags were turned off. Second, the exploit is practical and shows the complexity with real world exploits and exploit mitigations. Great job!
Analysis Summary
# Vulnerability: libinput User-Controlled Format String
## CVE Details
- **CVE ID**: CVE-2022-1215
- **CVSS Score**: 7.8 (High) - per NVD
- **CWE**: CWE-134: Use of Externally-Controlled Format String
## Affected Systems
- **Products**: `libinput` library (used by X.org and various Wayland compositors), X.org Server.
- **Versions**: Versions prior to 1.19.4 and 1.20.1.
- **Configurations**: Systems where `libinput` handles input device logging, specifically when a new device is connected and its metadata (name, serial, etc.) is logged.
## Vulnerability Description
The flaw exists in `evdev_log_msg` within `src/evdev.c`. When a new device is connected, `libinput` generates a log message by prepending device-specific strings (like the manufacturer or product name) to a buffer using `sprintf`. This modified buffer is then passed as the *format* argument to another printf-family function (`log_msg_va`).
Because the device name is user-controllable (via hardware descriptors) and acts as the format string, an attacker can insert format specifiers (e.g., `%p`, `%x`, `%s`). This allows for unauthorized memory reads (information disclosure) and potentially memory corruption.
## Exploitation
- **Status**: PoC available. A functional PoC was demonstrated to leak the stack canary on Xubuntu 20.04.4.
- **Complexity**: Medium to High. While the format string trigger is simple, modern mitigations like `FORTIFY_SOURCE=2` prevent the use of `%n` (needed for writes) and restrict positional parameter skipping.
- **Attack Vector**: Physical / Adjacent. An attacker must connect a malicious USB device (e.g., via GreatFET or Facedancer) with specially crafted device descriptors.
## Impact
- **Confidentiality**: High. Ability to leak sensitive stack data, including the stack canary and memory pointers, bypassing ASLR.
- **Integrity**: Low. `FORTIFY_SOURCE` protections currently make code execution (RCE) difficult, though theoretically possible if mitigations are bypassed.
- **Availability**: High. Crafting invalid pointers (e.g., using `%s` on non-addresses) causes immediate crashes of the X session or display server.
## Remediation
### Patches
- Update `libinput` to version **1.19.4** or **1.20.1**.
- The fix involves using a static format string (`"%s"`) to log the dynamic device information rather than passing the dynamic string as the format argument itself.
### Workarounds
- Restrict physical access to USB ports.
- Disable automatic unauthorized device polling if supported by the environment, though this is impractical for most desktop users.
## Detection
- **Indicators of Compromise**: Unexpected Xorg/Display Server crashes immediately upon plugging in a USB device. Log files containing unusual hexadecimal output or `%` symbols in device names.
- **Detection methods and tools**: Monitor system logs (`/var/log/Xorg.0.log` or `journalctl`) for malformed device strings. Vulnerability scanners can identify outdated versions of `libinput`.
## References
- **Vendor Advisory**: [https://gitlab.freedesktop.org/libinput/libinput/-/issues/752]
- **Researcher Analysis**: [https://www.assured.se/posts/accidental-intrusion]
- **Exploit Write-up**: [https://blog.coffinsec.com/nday/2022/08/04/CVE-2022-1215-libinput-fmt-canary-leak.html]