CVE-2022-35821
An out-of-bounds read vulnerability exists in the /proc/fdt mmap operation functionality of Microsoft Azure Sphere 22.02. A specially-crafted mmap invocation can lead to a kernel memory leak. An attacker can issue an mmap call to trigger this vulnerability.
The versions below were either tested or verified to be vulnerable by Talos or confirmed to be vulnerable by the vendor.
Microsoft Azure Sphere 22.02
Azure Sphere - https://azure.microsoft.com/en-us/services/azure-sphere/
4.4 - CVSS:3.0/AV:L/AC:L/PR:H/UI:N/S:U/C:H/I:N/A:N
CWE-119 - Improper Restriction of Operations within the Bounds of a Memory Buffer
Microsoft’s Azure Sphere is a platform for the development of internet-of-things applications. It features a custom SoC that consists of a set of cores that run both high-level and real-time applications, enforces security and manages encryption (among other functions). The high-level applications execute on a custom Linux-based OS, with several modifications to make it smaller and more secure, specifically for IoT applications.
The Azure Sphere Linux Kernel exposes a /proc/fdt
file to allow Flattened Device Tree (FDT) access from userland. The file driver backing /proc/fdt
implements the following functions:
static const struct proc_ops fops_fdt = { // [1]
.proc_read = read_file_fdt,
.proc_open = simple_open,
.proc_lseek = default_llseek,
.proc_mmap = simple_mmap, // [2]
};
While the simple_open
function is implemented by the mainline Linux kernel, the simple_mmap
function at [2] is Azure Sphere specific, which will be important later on. Continuing on, the /proc/fdt
driver is implemented as such by of_fdt_raw_procfs_init
:
static int __init of_fdt_raw_procfs_init(void)
{
struct proc_dir_entry *proc_entry;
if (!initial_boot_params)
return 0;
if (of_fdt_crc32 != crc32_be(~0, initial_boot_params,
fdt_totalsize(initial_boot_params))) {
pr_warn("not creating '/proc/fdt': CRC check failed\n");
return 0;
}
proc_entry = proc_create("fdt", 0, NULL, &fops_fdt);
if (proc_entry != NULL)
proc_set_size(proc_entry, fdt_totalsize(initial_boot_params)); // [3]
return 0;
}
At [3] we see that proc_set_size
is called to set the size of this proc entry to fdt_totalsize(initial_boot_params)
:
void proc_set_size(struct proc_dir_entry *de, loff_t size)
{
de->size = size;
}
While we can see that several operations are defined at [1] in our initial struct proc_ops fops_fdt
, for this advisory we’re interested specifically in the mmap
operation [2], which is implemented by the simple_mmap
function.
static int simple_mmap(struct file *filp, struct vm_area_struct *vma)
{
unsigned long pfn;
size_t size = vma->vm_end - vma->vm_start; // [4]
phys_addr_t offset = (phys_addr_t) vma->vm_pgoff << PAGE_SHIFT;
if (initial_boot_params == NULL)
return -ENOENT;
pfn = virt_to_phys(initial_boot_params) >> PAGE_SHIFT;
// offset overflow check
if (offset >> PAGE_SHIFT != vma->vm_pgoff)
return -EINVAL;
// ensure the mmap is read-only
if ((vma->vm_page_prot & PAGE_READONLY) != PAGE_READONLY)
return -EPERM;
if (remap_pfn_range(vma, vma->vm_start,
pfn, size, // [5]
vma->vm_page_prot)) {
return -EAGAIN;
}
return 0;
}
When mmap
is called on a /proc/fdt
file-descriptor, the function above will be executed. At [4] the size of the page is calculated, based on the bounds computed from the size as specified by the mmap
userland invocation. In this function, there isn’t any check on size
against the fdt size (the one saved via proc_set_size
at [3]). At [5] the mapping is performed, using the unchecked size
parameter.
This lack of size checking allows a userland process to open /proc/fdt
and mmap
an arbitrarily large memory, which will read out-of-bounds of the fdt memory mapping, leaking kernel memory. If the mapping is made large enough, it is possible to dump the whole kernel memory.
In order to open /proc/fdt
, one needs root privileges, however, since /proc/fdt
is only readable by the root user. Commonly, being root in a system already implies ability to load modules, thus having the ability to execute kernel code. In Azure Sphere’s case, it’s not possible to insert modules at runtime, and there are a set of additional capabilities (i.e. AZURE_SPHERE_CAPABILITIES
) that are added to processes, to isolate userland processes and prevent them access to kernel functionality. For these reasons, there is a defacto security boundary that must still be traversed by attackers even if they achieve root privileges, which makes this a legitimate vulnerability in Azure’s context. It is worth noting also that in order to trigger this vulnerability, the root user does not need any special capability (neither Azure capabilities, nor Linux capabilities).
2022-04-04 - Vendor Disclosure
2022-08-09 - Vendor Patch Release
2022-08-17 - Public Release
Discovered by Claudio Bozzato and Lilith >_> of Cisco Talos.