Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=963
The MAX86902 sensor has a driver that exposes several interfaces through which the device may be configured. In addition to exposing a character device, it also exposes several entries under sysfs.
Some of these entries are writable, allowing different values to be configured. Three such files are exposed under the paths:
/sys/devices/virtual/sensors/hrm_sensor/eol_test_result
/sys/devices/virtual/sensors/hrm_sensor/lib_ver
/sys/devices/virtual/sensors/uv_sensor/uv_lib_ver
The sysfs write handlers for these files all share approximately the same logic. Below is one such handler, for the "uv_lib_ver" sysfs entry:
1. static ssize_t max86900_uv_lib_ver_store(struct device *dev,
2. struct device_attribute *attr, const char *buf, size_t size)
3. {
4. struct max86900_device_data *data = dev_get_drvdata(dev);
5. unsigned int buf_len;
6. buf_len = (unsigned int)strlen(buf) + 1;
7. if (buf_len > MAX_LIB_VER)
8. buf_len = MAX_LIB_VER;
9.
10. if (data->uv_lib_ver != NULL)
11. kfree(data->uv_lib_ver);
12.
13. data->uv_lib_ver = kzalloc(sizeof(char) * buf_len, GFP_KERNEL);
14. if (data->uv_lib_ver == NULL) {
15. pr_err("%s - couldn't allocate memory\n", __func__);
16. return -ENOMEM;
17. }
18. strncpy(data->uv_lib_ver, buf, buf_len);
19. pr_info("%s - uv_lib_ver = %s\n", __func__, data->uv_lib_ver);
20. return size;
21. }
Since the code above does not use any mechanism to prevent concurrent access, it contains race conditions which allow corruption of kernel memory.
For example, one such race condition could occur when two attempts to call "write" are executed at the same time, where the underlying buffers have different lengths. More concretely, denote the two accessing tasks "task1" and "task2", correspondingly. Consider the following sequence of events:
-"task1" attempts to write to the entry, and provides a buffer of length 20.
-"task1" manages to execute lines 1-17 (inclusive)
-"task2" now attempts to write to the entry, and provides a buffer of length 2.
-"task2" manages to execute lines 1-13 (inclusive)
-"task1" now executes line 18, resulting in an overflow when writing to data->uv_lib_ver (since its actual length is now 2)
This issue can be addressed by adequate locking when accessing the sysfs entries.
I've statically and dynamically verified this issue on an SM-G935F device. The open-source kernel package I analysed was "SM-G935F_MM_Opensource", the device's build is "XXS1APG3".
The sysfs entries mentioned above have UID "system" and GID "radio". The SELinux context for these entries is: "u:object_r:sysfs_sensor_writable:s0".
According to the default SELinux rules as present on the SM-G935F (version XXS1APG3), the following contexts may access these files:
allow radio sysfs_sensor_writable : file { ioctl read write getattr lock append open } ;
allow factory_adsp sysfs_sensor_writable : file { ioctl read write getattr lock append open } ;
allow sensorhubservice sysfs_sensor_writable : file { write append open } ;
allow sysfs_sensor_writable sysfs_sensor_writable : filesystem associate ;
allow system_app sysfs_sensor_writable : file { ioctl read write getattr lock append open } ;
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/40993.zipData
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation