During Pwn2Own Toronto 2022, three different teams successfully exploited the Sonos One Speaker. In total, $105,000 was awarded to the three teams, with the team of Toan Pham and Tri Dang from Qrious Secure winning $60,000 since their entry was first on the schedule. Part of Pwn2Own competitions involves a random drawing for order. Not only does the team selected first get the full award if they are successful, but the subsequent entries are also more likely to have bug collisions. That means if three teams show up with the same exploit, only the first one randomly selected will get full credit. That’s not exactly what happened during the event, but some bug collisions did occur. In fact, there were four unique bugs used by the three different teams. Let’s take a look at the vulnerabilities used during the contest and see which team used which bug.
CVE-2023-27354 – The Unique libsmb2 Info Leak
With three different teams targeting the Sonos speaker, it’s obviously a huge advantage to go first. The team from Qrious Secure was randomly selected to go first, and they successfully exploited the speaker using a two-bug chain. This first bug used was this info leak, and they ended up being the only team to use this particular bug during the contest.
On the speaker, there exists a daemon named
anacapad that handles all Sonos-specific functions, including accessing music services, LED control, and audio playback. The vulnerability exists in the way
anacapad handles SMBv2 replies from a server, specifically in the
smb2_process_query_directory_fixed() function that processes query directory reply data. It does this by allocating an
smb2_query_directory_reply struct and storing the result in the Protocol Data Unit (PDU)
pdu->payload field. The function then extracts the output buffer offset and length from the query directory reply from the server:
The speaker then checks that the output buffer does not overlap with the query directory reply header. The offset here will later be used to calculate the
This will also be used at:
output_buffer here will later be used to decode file information from that directory PDU response from the server in the
smb2_decode_fileidfulldirectoryinformation field. However, it never checks if the offset is within the
len of a PDU packet. This can be leveraged to perform an information leak by acting as a SMB server and sending a malformed PDU query directory response with a large offset. The client will decode the file information from an out-of-bounds (OOB) memory region and send that information back to the malicious SMB server as part of a filename. By manipulating the offset, the SMB server can determine libc and heap addresses on the client, which are useful for the next step of the exploit chain.
CVE-2023-27353 – The Other Infoleak
The libsmb2 bug wasn’t the only infoleak we saw during the contest. Two of the teams used CVE-2023-27533/ZDI-23-448 to kick off their exploit. This vulnerability resides in the
/msprox endpoint, which serves as a proxy for Sonos Speaker array communication. It forwards the user-supplied SOAP request to other registered speakers. When a user sends a request to this endpoint, the request is handled by function
sub_15DFA0(), which in turn calls
sub_1C86C0(). The following code snippet is from
sub_1C86C0 in the
anacapad binary, corresponding to assembly code from address 0x1C876C:
The code fails to check the return value of
snprintf() at , and later uses this value as the size of an outgoing HTTP header buffer at . The team from STAR Labs used this bug by sending a crafted request that provides overly long parameters to the
snprintf() function at . In this way, the return value from
request_len) exceeds the size of the
request buffer, which is 0x1000 bytes. At , it calls
ana_server_send_request() to send the contents of the
request buffer, using
request_len as the length. STAR Labs used this out-of-bounds read (OOBR) vulnerability to leak the address of the .text segment.
The DEVCORE Team took a slightly different route to achieve the same effect. Their exploit relied on acting like a rogue Sonos Speaker adjacent on the network to the target. This allowed them to reach the following code, which contains an out-of-bounds read vulnerability analogous to the one discussed above. The result is leakage of stack data right after the
From address location
While they took different approaches, both teams arrived at the same vulnerable code and leaked the data needed to continue their exploit.
CVE-2023-27352 – Remote Code Execution Through libsmb2
Now that we have our info leaks established, let’s take a look at how two teams leveraged that information for code execution. Both the Qrious Secure and STAR Labs teams leveraged a use-after-free (UAF) bug in the libsmb2 library. Again, since Qrious Securewas randomly chosen to go first, they won the full $60,000 while the bug collision resulted in STAR Labs earning $22,500.
Sonos provides SMB functionality by incorporating the open-source libsmb2 library with a few modifications. It runs within the
anacapad daemon and can be reached by unauthenticated users to play music via the smb2 shared directory.
smb2_closedir is implemented as below in libsmb2:
smb2_lazy_readdir is implemented as follows:
The main function handling data returned from an SMB server is as follows:
The control flow proceeds as follows:
(1) smb2_lazy_readdir -> smb2_fetchfiles_async -> smb2_cmd_query_directory_async -> create pdu (PDU) with internal message_id (MID) -> add to wait_queue (3)
When the client receives data from a server (4), it will decode the header (5) and find the PDU via its message_id (6). The interesting thing is that
smb2_fetchfiles_async() function adds the PDU to
wait_queue, which then holds a callback to
fetchfiles_cb() at (7). This callback keeps the
dir structure within its private data (8). Before it finishes, it invokes
dir->cb callback at (9).
Back to (6), in a normal scenario, the SMB server will return the data with the valid
MID, and the callback will be triggered before
dir is freed in
close_dir at (2). However, if the server sends an invalid
message_id (different from
PDU will not be found and will still be alive in
wait_queue. Should this occur, the PDU will keep holding on to the
dir struct pointer. When (2) finishes, the
dir pointer will be freed, and the PDU will be left holding a dangling pointer.
The next time the client tries to read data from the server, if we reply with a previously valid
MID, the client will decode the data and find the corresponding PDU via that
MID. This time the
fetchfiles_cb will be called, and at (4) it will access the dangling pointer. By reallocating the freed
dir structure before
fetchfiles_cb is called, we can control the value
cb and thereby gain control of $PC by pointing
cb to maliciously crafted data.
Combined with the memory address leak from CVE-2023-27354, this vulnerability can be used to achieve remote code execution.
The STAR Labs team took a different approach to hit the same vulnerability. As stated above, the speaker allows us to play media files remotely using SMB using libsmb2. One of the added functions to this library is
smb2_lazy_readdir(). Here is how they triggered the bug.
smb2_opendir() is called, which will return an
smbdir is later passed into
smb2_lazy_readdir() together with a custom callback function.
smb2_cmd_query_directory_async() receives a callback function with the following prototype and any user-defined structure (in this case
This function will then insert a
smb2_pdu) into a request queue waiting to be handled. After a reply is received, the callback will be invoked with the received data. The Sonos device passes the
smbdir structure as
cb_data, then populates it inside the callback. This code snippet is in
sub_109C0() function of the libsmb2.so.1 binary, corresponding to assembly code from address 0x10A04:
wait_for_reply fails, meaning
smb2_cmd_query_directory_async did not receive any valid response for its
smb2_closedir is invoked to free the
smbdir object. Then
smb2_disconnect_share function is called. However, during this process, a dangling pointer to
smbdir is left in the request queue.
smb2_disconnect_share, it calls
smb2_read_data, it uses
smb2_find_pdu to retrieve the
pdu based on the
message_id of the response packet, which is controllable by the attacker. If our response specifies the
message_id of the Query Directory pdu,
smb2_find_pdu will return the
smb2_cmd_query_directory_async’s pdu. Finally, when
pdu->cb is invoked from
z_query_directory_cb_109C0, the dangling pointer leads to $PC control.
Reclaiming the freed
smb2dir object is accomplished by appending extra data onto the response packet from server. The client will allocate a buffer to store this extra padding data.
The exploit uses a modified
impacket to implement a malicious smb server.
The exploit proceeds as follows.
smb2QueryDirectory()function in the exploit’s
impacketSMB server, return a malformed response to the client’s
smb2_cmd_query_directoryrequest, producing a dangling pointer.
smb2_disconnect_sharefunction, it sends a disconnect tree request to server. Using a modified
smb2TreeDisconnectfunction in the
impacketSMB server, the exploit returns a Query Directory response with the
message_idfrom step 2. Additionally, the exploit appends data to this response to reclaim the
smb2dirobject in the client.
smb2dir->cbpointer and uses ROP to get shell.
CVE-2023-27355 – Remote Code Execution via the MPEG-TS Parser
This remote code execution bug was used only by the DEVCORE team. Due to the random draw, this was the third attempt on the Sonos, which left them at a disadvantage as they were more likely to run into a bug collision. They did collide regarding their info leak, which we have already discussed above. However, the remote code execution portion of their exploit chain was unique and earned its own CVE.
While parsing a
.ts audio file, the Sonos speaker does not check the length of the Adaptation field, which leads to a stack buffer overflow. The bug results from the ability to specify an arbitrary value to the
afelen field. The speaker will then read the specified number of bytes into the payload buffer, smashing the stack.
Since Sonos enables exploit mitigations, the attacker needs to leak some information first. During the contest, the DEVCORE entry first leaked the stack canary, stack address and program base address using the previously described CVE-2023-27353. Once they obtained those values, exploitation is straightforward. They used the MPEG-TS parser vulnerability to overwrite the return address and jump to the exec() wrapper.
#x22 registers are also controllable by the exploit, the attacker can set these registers to a controllable stack buffer and execute arbitrary commands.
The end result was a successful demonstration during the contest, but due to the collision of CVE-2023-27353, the DEVCORE team didn’t win the full amount. Still, they earned $22,500 for being the third team to exploit the Sonos speaker during the event.
Wrapping Things Up
It’s always interesting to see different teams reach similar conclusions when targeting a piece of software. It’s equally as interesting when they take completely different paths but still end up with the same result. In this example, we had three different teams use a various combination of three different bugs to get code execution on a Sonos speaker. In the end, we awarded these teams a total of $105,000 for their efforts. Situations like this also highlight how bug collisions can encourage more thorough and innovative research to avoid duplicate entries in the future. All three of these teams had participated in Pwn2Own before, and we certainly hope they return for future events.
Now that many of the bugs disclosed during Pwn2Own Toronto are patched, we’ll continue to disclose some of those details on this blog. Until then, follow the team on Twitter, Mastodon, LinkedIn, or Instagram for the latest in exploit techniques and security patches.