An information disclosure vulnerability exists in the Security Monitor SMSyscallWriteBlockToStageImage functionality of Microsoft Azure Sphere 21.01. A specially-crafted set of syscalls can lead to a disclosure of sensitive information. An attacker can use SMCs or ioctls 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 21.01
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-20 - Improper Input Validation
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 process of flashing Azure Sphere applications and firmware is a particularly complex procedure. First one must call
SMSyscallOpenImageForStaging in order to get a handle that references a firmware image being staged, and then one must actually write to this image via multiple calls to
SMSyscallWriteBlockToStageImage. Once the image has been fully written to the staging area,
SMSyscallInstallStagedImages are then called, to verify and transfer the image from the staging partition to the production partition.
It is also worth noting that most images require an
ImageManifest to be staged as well, but for what concerns this advisory, it’s enough to discuss
SMSyscallOpenImageForStaging slightly and
SMSyscallWriteBlockToStageImage in depth. So let’s start with the arguments for
syscall.number = SMSyscallOpenImageForStaging; syscall.flags = 0x444a; syscall.args = buffer_size; syscall.args = 0xf0cf0cf0; // doesn't matter, clobbered syscall.args = &handle; // output pointer for handle syscall.args = 0x8;
Of these arguments,
syscall.args is the size of the staging buffer that we wish to allocate, with the maximum total size for all images being
syscall.args is clobbered, and
syscall.args is the output
uint64_t for the resultant image handle, assuming there’s enough space and handles available. Once we’ve allocated a staging buffer and we have the handle, we can now write to this area via
syscall.number = SMSyscallWriteBlockToStageImage; syscall.flags = 0x454446; syscall.args = &handle; syscall.args = 0x8; syscall.args = dst_offset; syscall.args = src_offset; syscall.args = &src_buffer; syscall.args = src_block_size;
syscall.args, we pass in the same handle from the previous syscall, while with
syscall.args we pass in the handle size. The
syscall.args is the offset into the staging buffer that we wish to start writing, which cannot be greater than the
buffer_size input passed in when opening the staging image.
syscall.args is the offset into our source buffer, which we pass in at
syscall.args, along with the source block size at
syscall.args. To summarize and simplify the result of this syscall:
memcpy(stage_buffer + dst_offset, src_buffer + src_offset, src_block_size);. The end result is a set of images, now stored in one of two different places in flash that are not normally reachable except from these staging syscalls.
An interesting thing to note about the
src_offset parameter in particular: there’s only one instance of this argument ever being used.
803dc1c4 struct partition_table* r4_3 = staging_partitions->nested_parttable 803dc1d6 int64_t end_of_src_of_write 803dc1d6 end_of_src_of_write.d = srcptr_arg0x3 + srcptr_arg0x4 //  803dc1d8 r3_8:r2_7 = flash_dst_len 803dc1e0 r3_9:r2_8 = flash_dst_addr 803dc1e4 int32_t r0_12 = TableCommWrite(clobbered: r4_3, some_struct: r4_3->nested_parttable), dst_high: r2_8, dst_low: r3_9, write_len: r3_8:r2_7, src_of_write: end_of_src_of_write) // 
No other cross references to our argument occur except for the above at , whereby the source of the write in
arg + arg (i.e. the source buffer + the buffer offset).
This results in an attacker being able to start the source of the write from anywhere in Security-Monitor’s memory space. After this write has occurred, it’s possible to simply utilize
SMSyscallReadFlash to read back the buffer from flash, resulting in an information leak of Security-Monitor’s entire memory space.
2021-06-08 - Vendor Disclosure
2021-08-10 - Public Release
Discovered by Claudio Bozzato and Lilith >_> of Cisco Talos.