❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayZero Day Initiative - Blog

Exploiting the Sonos One Speaker Three Different Ways: A Pwn2Own Toronto Highlight

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 IOV_OFFSET:

This will also be used at:

The 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 [1], and later uses this value as the size of an outgoing HTTP header buffer at [2]. The team from STAR Labs used this bug by sending a crafted request that provides overly long parameters to the snprintf() function at [1]. In this way, the return value from snprintf() (request_len) exceeds the size of the request buffer, which is 0x1000 bytes. At [2], 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 request_body buffer.

From address location 0x1C889C:

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:

And 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 message_id as 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 MID), the 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 message_id of MID, the client will decode the data and find the corresponding PDU via that MID. This time the PDU’s callback 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 (smb2dir structure). Β Β Β Β Β Β Β Β -- 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 smb2dir):

This function will then insert a pdu (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:

If wait_for_reply fails, meaning smb2_cmd_query_directory_async did not receive any valid response for its pdu, smb2_closedir is invoked to free thesmbdir object. Then smb2_disconnect_share function is called. However, during this process, a dangling pointer to smbdir is left in the request queue.

In smb2_disconnect_share, it calls wait_for_reply->smb2_service->smb2_service_fd->smb2_read_from_socket->smb2_read_data. In 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.

  1. Send a command to the target device to add a new SMB share.
  2. Using a modified smb2QueryDirectory() function in the exploit’s impacket SMB server, return a malformed response to the client’s smb2_cmd_query_directory request, producing a dangling pointer.
  3. When the client calls smb2_disconnect_share function, it sends a disconnect tree request to server. Using a modified smb2TreeDisconnect function in the impacket SMB server, the exploit returns a Query Directory response with the message_id from step 2. Additionally, the exploit appends data to this response to reclaim the smb2dir object in the client.
  4. The exploit gains control of $PC via the overwritten smb2dir->cb pointer 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.

From address: 0x406604

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.

As the #x19 and #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.

CVE-2023-20869/20870: Exploiting VMware Workstation at Pwn2Own Vancouver

This post covers an exploit chain demonstrated by Nguyα»…n HoΓ ng ThαΊ‘ch (@hi_im_d4rkn3ss) of STAR Labs SG Pte. Ltd. during the Pwn2Own Vancouver event in 2023. During the contest, he used an uninitialized variable bug and a stack-based buffer overflow in VMware to escalate from a guest OS to execute code on the underlying hypervisor. His successful demonstration earned him $80,000 and 8 points towards Master of Pwn. All Pwn2Own entries are accompanied by a full whitepaper describing the vulnerabilities being used and how they were exploited. The following blog is an excerpt from that whitepaper detailing CVE-2023-20869 and CVE-2023-20870 with minimal modifications.


Prior to being patched by VMware, a pair of vulnerabilities existed within the implementation of the virtual Bluetooth USB device inside VMware Workstation. During the event, the VMware version used was 17.0.1 build-21139696. An attacker could leverage these two bugs together to execute arbitrary code in the context of the hypervisor. To exploit this vulnerability, the attacker must have the ability to execute high-privileged code on the guest OS. Bluetooth functionality is also required, but this is enabled by default. These bugs were patched in late April with VMSA-2023-0008.

CVE-2023-20870 – The Uninitialized Variable Info Leak

In VMware Workstation, in the USB Controller setting, there is the β€œShare Bluetooth devices with the virtual machine” option. This is enabled by default. It allows guest OSes to use Bluetooth devices. This functionality is handled by the Vbluetooth component, which is implemented in the vmware-vmx.exe binary. The VBluetooth device information can be read by lsusb command (in Linux) as follows:

Each time a guest OS sends a USB Request Block (URB) request to the VBluetooth device, the function VUsbBluetooth_OpNewUrb() is invoked to allocate memory to read or write data. The following code snippet is the sub_140740EB0 function in vmware-vmx.exe:

This function returns a VUsbURB object. Data is stored in this object in the urb_data buffer. This buffer is allocated by the RBuf_New() function and assigned to urb_data at [1]. Note that the RBuf_New() function calls the malloc function to allocate memory, so the memory is uninitialized. Then, the URB data is handled by VUsbBluetooth_OpSubmitUrb() function. This code snippet is from the sub_140740F50 function in vmware-vmx.exe:

total_urb_len is the length of data the guest OS wants to read from or write to the URB. This value is controllable by the attacker. At [2], total_urb_len is assigned to urb->urb_actualsize without a check. Then, based on the endpoint type and URB packet, the corresponding function is invoked. Afterwards, urb->urb_actualsize is set again in the handler function, but only if the packet is valid. We can see in the VUsbBluetooth_OpSubmitUrb() function that if the urb_data->bRequest is an invalid opcode (checked at [3]), urb->urb_actualsize will not be set, and it will remain set to an attacker-controllable value.

Finally, the UHCI_UrbResponse() function is invoked to send data back to the guest. The following snippet is in the sub_1401F7C50 function in vmware-vmx.exe, corresponding to assembly code from address 0x1401F7CBF:

A maximum of urb->urb_actualsize bytes of data from the urb->urb_data buffer will be returned to the guest. Since an attacker could control the value of urb->urb_actualsize and the urb->urb_data buffer is uninitialized, the guest OS could read uninitialized data from the heap.

CVE-2023-20869 – The Stack-based Overflow

The VBluetooth device also implements an Service Discovery Protocol (SDP) feature. When a guest OS wants to send an SDP packet to a specific Bluetooth peer, it must initialize a L2CAP connection to this peer. This is done by sending an L2CAP_CMD_CONN_REQ packet to the L2CAP_SIGNALLING_CID channel with the Protocol/Service Multiplexer (PSM) field set to 0x1. The result is a newly created SDP socket. This socket is used when processing subsequent SDP operations.

The SDP protocol data unit (PDU) format is well explained here. When the host OS processes an SDP PDU from the guest, it invokes SDPData_ReadElement() to parse the PDU. Here’s a look at the SDPData_ReadElement() function from the sub_14083C1D0 function in vmware-vmx.exe:

The switch-case [1] is used to parse the data element size descriptor to determine the size of the raw data. Then this size is passed to SDPData_ReadRawInt() to parse the unsigned int at [2].

Here’s another code snippet. This one is from the sub_14083C570 function in vmware-vmx.exe. Since the PDU is submitted by the guest OS, an attacker can control the size argument, which leads to a possible stack buffer overflow at [3]/[4].

These bugs were combined at Pwn2Own Vancouver to pop calc on the target system. The exploit itself started in the guest OS while the calculator spawned on the host OS.


Thanks again to Nguyα»…n HoΓ ng ThαΊ‘ch for providing this write-up and for his participation in Pwn2Own Vancouver. He has participated in multiple Pwn2Own contests, and we certainly hope to see more submissions from him in the future. Until then, follow the team on Twitter, Mastodon, LinkedIn, or Instagram for the latest in exploit techniques and security patches.

❌
❌