Recently, Trend Micro’s ZDI has published multiple advisories concerning Deep Sea Electronics DSE855, a communications device that allows monitoring of a single DSE controller with USB connectivity over a wired network connection. This blog post looks at these reports in further detail. All the findings were made against DSE855 software version 1.1.0 (the latest available at the moment of writing) and hardware version 4.00. None of these bugs were patched within the standard ZDI 120-day timeframe and were published as 0-day advisories in June.
The DSE855
The DSE855 device presents itself as an unassuming black plastic box with several connection options on the front. The device is supposed to be mounted on a DIN rail, oriented vertically.
On the front, we have an RJ45 port, a small hole for an LED, a USB type A connector, and a 5-pin connector for powering the device.
Inside, there is a single two-sided PCB with components mounted on it.
The upper part is taken by the power section, while the lower part is dominated by a microcontroller p/n LPC4357JBD208 manufactured by NXP. Attached to it is a Micrel p/n KSZ8041, a 10/100 Ethernet PHY transceiver. The 8-pin SOIC part near the vertical USB connector was manufactured by TI, with the exact part number proving to be challenging to identify.
On the other side of the board, we have a variety of passives, an opto-isolator, and two memory chips: a W9812G6KH-6I 128M SDRAM manufactured by Winbond and IS29GL064-70SLET [PDF] 64M parallel Flash manufactured by ISSI.
While there are quite a few obvious test points on the board, probing them with an oscilloscope yielded no signals of interest like a serial console. However, on the top side of the board, there is a conspicuous-looking 2x5-pin 50mil pitched pin array close to the microcontroller. In this instance, however, software extraction via this route proved to be redundant as the manufacturer kindly provided downloadable software updates via their website. At the time of writing, however, downloads appear to be behind a login; allowing open access to firmware updates is always preferable as it promotes research and easy patching by end users
Software analysis
Those blessed with access would be able to download a certain 855.zip, containing a PDF file with instructions—just as described by the manufacturer—and several binaries:
· A855-01.bin appears to contain an exception vector table
· B855-01.bin contains the bulk of software—the whopping 439KB
· C855-01.bin looks like default configuration data, and
· M855-01.rfs proved to be a file system with a lot of XML files.
By trimming the first 8 bytes of B855-01.bin and loading it into Ghidra at address 0x1B000000, it is possible to explore the software in further detail. The address was hinted at by the contents of A855-01.bin, where many non-zero little-endian words started with 1B00.
The software was then explored both statically in Ghidra and dynamically by interacting with the exposed web server to confirm our hypotheses, but more on that in the text below.
Now, without any further ado, let’s get right to the issues that were discovered and look at them in more detail. Note: all symbol names listed below are based on the reverse engineering effort and the fact that the software, at least the web server part, was based on the embOS/IP middleware package from Segger as evidenced by the contents of the Server HTTP header.
ZDI-24-671: Configuration backup missing authentication
This was the initial report by Gjoko Krstic of Zero Science Lab, which prompted further investigation by the ZDI team.
The gist of the issue is dead simple: accessing a certain URL will result in obtaining a configuration backup, identical in format and size to what is supplied in the software update—like so:
The lucky person could then proceed to abuse the disclosed credentials to access everything else the device has to offer.
Searching the software blob for the URL revealed a table of handlers for dynamically generated content:
Finding the corresponding handler, let’s explore its code:
All that it does is supply some value, a pointer, and a size to another function. And the size looks suspiciously similar to the amount of data returned. Looking at the called function reveals:
For comparison, here is a different CGI handler:
So the expected authentication pattern would be to get the SID parameter, convert it to an integer, call session_find()
, and check the result. Note how this is absent in the offending handler, leaving the critical backup function unprotected by any form of authentication.
ZDI-24-675 and ZDI-24-676: Two more missing authentication bugs
All the endpoint handlers were checked for this pattern, and two more handlers were found where authentication was missing. These handlers allow unauthenticated attackers to a) restart the device normally and b) cause a factory reset of the device.
Let’s have a look at the handler code.
All other endpoints made an apparent attempt at authenticating the user. This meant it was time to change the tune. A quick fuzzing session aimed at the HTTP protocol handling triggered several crashes of the device, all of which seemed to do with the handling of multipart requests.
ZDI-24-672: Stack-based buffer overflow when handling boundary values in multipart requests
The first issue turned out to be a classic stack-based buffer overflow, where code does not restrict the amount of attacker-controlled data being copied. Specifically, the overflow occurs in the following fragment of code, believed to be part of the ExecMethod()
routine, which processes supplied HTTP request headers:
The code does not ensure the supplied value will fit within the fixed-length array before copying the boundary value into a buffer within the memory region pointed to by the ctx
variable. This results in an overflow when values longer than 30 characters are supplied.
The overflown buffer is allocated on the stack and initialized as part of the ctx
structure in the _Start()
routine:
Note the contents of this structure also include function pointers (pfSend, pfReceive) that can be overwritten with controlled values and subsequently dereferenced, resulting in full control over execution. As is typical for embedded systems, no exploit mitigations like the NX bit or any kind of layout randomization are present.
ZDI-24-674: Stack-based buffer overflow when handling values in multipart requests
Here is another issue discovered through fuzzing. Specifically, the overflow occurs in the following fragment of code, which handles multipart form variables submitted with the request:
The code does not properly verify whether there is enough space left in the buffer before copying attacker-controlled data into it, leading to a buffer overflow. The copying loop terminates only when '\r' is encountered.
The affected buffer is allocated on the stack in the _Start()
function, part of the very same object as in the previous issue.
Notably, the affected function does not appear to be part of the embOS/IP code base in what little code ZDI could find online to examine.
ZDI-24-673: DoS when handling multipart requests
This is a particularly curious issue. A logic error exists within the handling of boundary values supplied as part of multipart HTTP requests, resulting in an infinite loop.
Specifically, the following fragment of code is affected which is responsible for processing the multipart request body:
The intention behind this code is to locate the boundary line before processing the content that follows, skipping lines containing CR-LF only. However, when this code is presented with a boundary value different from the one provided in the request headers, the loop never terminates as the buffer data is only consumed on empty lines.
The following, rather minimalistic, request triggers this condition:
Timeline
Here’s the timeline of our interactions with Deep Sea:
01/21/24 – ZDI requested a vendor PSIRT contact.
01/22/24 – The vendor provided contact information.
01/23/24 – ZDI reported the vulnerability to the vendor
02/05/24 – The vendor states the report was blocked by IT and asked ZDI to resend the report.
02/12/24 – ZDI resent the report using an alternative method.
02/13/24 – The vendor asked why we performed tests on their products.
02/13/24 – ZDI provided the vendor with additional details about the ZDI program.
02/14/24 – The vendor asked what initiated the ZDI to look at the DSE855.
02/14/24 – ZDI emphasized our intent to responsibly disclose this vulnerability to Deep Sea for remediation. The ZDI also offered additional resources about coordinated vulnerability disclosure, as well as feedback on implementing a proper incident response process. We also reiterated our 120-day disclosure policy to ensure the vendor was aware they needed to respond with a patch within the allotted time.
05/24/24 – ZDI informed the vendor that since we never received a response we have assumed this vulnerability remains unpatched, and that we’re publishing this case as a zero-day advisory on 06/13/24.
Summary
The embedded world continues to supply us with a lot of fun bugs, and we at ZDI are always interested in hearing about them from your reports just as much as finding them ourselves—especially in today’s world where a lot of bug classes are mitigated on major platforms like Linux, MacOS, and Windows, a good stack-based buffer overflow exploit writing session presents a welcome respite and a solid return to basics.
I hope you enjoyed this short write-up. Thanks to your continuous submissions, we will continue to post more of that. Until then, you can follow me on Mastodon and the team on Twitter, Mastodon, LinkedIn, or Instagram for the latest in exploit techniques and security patches.