Show Notes

110 - Rooting Ubuntu By Accident and Samsung Kernel Bugs

Here, we have a case of one function returning a pointer to a caller, but expecting to retain ownership over that pointer; the caller, thinking it has ownership, frees it; and by calling the same function multiple times the same pointer gets freed multiple times.

The user_get_fallback_value function exists essentially to take a key name, and return a default value which is used when a value for that key cannot be found in other sources (such as the user’s .pam_envrionment file). The default value is stored in a static pointer and only allocated once, expecting the caller to use the value and forget about it. Instead in user_change_language_authorized_cb it recieves the value and annotes it with the g_autofree macro which as the name implies will end up freeing the value, leaving the original function with a dangling pointer. Repeating this process multiple times will result in a double free situation.


The post also goes into some exploitation strategy and talks a bit about the general process of going from this dangling pointer and how to turn it into something useful, and its a great read for some of that process. The final attack ended up being a data-oriented attack targeting the CheckAuthData structure used by polkit.

Polkit is an authorization API, meant to be used by privileged processes offering services to unprivileged users. The basic process is that when a new service request comes in this CheckAuthData structure is generated for the request, and it contains a callback function pointer. The privileged service sends off a asynchronous request to polkit, and when it gets a response, if the response indicates the user is authorized to perform the action the callback in the structure will be executed.

The attack process would target this structure, first by triggering the invalid free and having it get picked up by a benign request to change a user’s email. Polkit will reply that the request is authorized, but before it does, the invalid free will be triggered again. This time with a more sensitive action, such as changing the root password. If this happens fast enough, and it manages to reuse the targeted memory block, when the authorized response comes back from the benign action, it will execute the callback for the more sensitive action.

This is one of those issues that is an obvious code-smell once you’re aware of it, inp_join_group will release a lock, so that is can call another function that requires the same lock, and then take the lock back after the function has executed creating a window where another function can obtain the lock and free the pointer from under it.

Integer overflows everywhere in this elf loader leading to overflowing kernel memory with controlled content.

The DSP device supports an ioctl for loading custom models for the device. These are elf files that the driver will parse, calculating how much meory the segments need, and then copy into memory. The __dsp_elf32_get_section_list_size is ultimately used to calculate a section’s size, which is a naive implementation, just summing the values read from the EFL without concern for overflowing.

The memory is allocated potentially much smaller value due to the overflow, and later another function does copy of each segment, copying in much more data than expected.

A similar issue exists when processing the data segment and offsets, where the offsets can overflow.

Additionally, these overflows can in-turn learn to other issues, such as underflowing a bounds-check by returning a size smaller than the minimum needed.

Basically, there are just a bunch of silly issues in this code, probably because it is not directly accessible from an unprivileged context. So the developers just treat everything as trusted input, leading to chaos.

Missing bounds-check leading to out-of-bounds write in Samsung Exynos S20 device’s DSP driver.

There are two ioctls of importance for this issues. DSP_IOC_BOOT which loads the DSP’s firmware images, shared libraries and such, and DSP_IOC_LOAD_GRAPH used load custom graph models (elf libraries) from user-space using a shared memory region.

When these libraries are loaded, the linker will resolve relocations based on the relocation headers in the elf file. However these relocations are not validated or checked, and so can point out of bounds.