None
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, SMSyscallCommitImageStaging
and 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 SMSyscallOpenImageForStaging
:
syscall.number = SMSyscallOpenImageForStaging;
syscall.flags = 0x444a;
syscall.args[0] = buffer_size;
syscall.args[1] = 0xf0cf0cf0; // doesn't matter, clobbered
syscall.args[2] = &handle; // output pointer for handle
syscall.args[3] = 0x8;
Of these arguments, syscall.args[0]
is the size of the staging buffer that we wish to allocate, with the maximum total size for all images being ~0x6ec000
. syscall.args[1]
is clobbered, and syscall.args[2]
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 SMSyscallWriteBlockToStageImage
:
syscall.number = SMSyscallWriteBlockToStageImage;
syscall.flags = 0x454446;
syscall.args[0] = &handle;
syscall.args[1] = 0x8;
syscall.args[2] = dst_offset;
syscall.args[3] = src_offset;
syscall.args[4] = &src_buffer;
syscall.args[5] = src_block_size;
With syscall.args[0]
, we pass in the same handle from the previous syscall, while with syscall.args[1]
we pass in the handle size. The syscall.args[2]
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[3]
is the offset into our source buffer, which we pass in at syscall.args[4]
, along with the source block size at syscall.args[5]
. 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 // [1]
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) // [2]
No other cross references to our argument occur except for the above at [1], whereby the source of the write in TableCommWrite
is arg[3] + arg[4]
(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.