Double Take Zero Day (CVE-2023–40459)
Last updated
Was this helpful?
Last updated
Was this helpful?
This blog post is about the process of discovering CVE-2023–40459 DoS vulnerability in SierraWireless Airlink product (RV50) which was discovered by my friend and I as well as ForeScout Vendere Labs. folks at ForeScout Labs reported this vulnerability and a few more before us and published this document. Although the vulnerability was reported before, the published document does not contain any details about the discovery process and i felt that writing about the details can be useful for newbies ( such as myself ) who want to get their hands dirty in this field.
This is my first dive into the world of vulnerability research with the help of my friend Majid and it felt like stepping into uncharted territory. you can read the Persian version of this blog post at this link.
Long story short, our findings were out of curiosity. we were randomly searching and looking at blogs and write-ups about IoT exploitation (because of the lack of exploit mitigation technologies and old software found in these platforms ) which led us to this post. in summary, its about decrypting the firmware of AirLink devices as well as discovering a command injection and LPE (local privilege escalation) vulnerability.
The AirLink® Raven RV50 is the industry’s lowest power LTE gateway. Simple to install and easy to manage, the Raven RV50 industrial gateway is designed to connect critical assets and infrastructure. Ideal for industrial-grade applications in energy, utilities and smart-city infrastructure, the Raven RV50 provides real-time remote connectivity for SCADA, distribution management systems and metering.
The firmware decryption part took our attention, we wanted to test it just to see if it works on all versions of the firmware. so as a test, we went on to vendor’s website and started looking for the same version of the firmware to begin with. unfortunately, we hit a wall right at the beginning, because the page only showed download links for latest version of firmware binary which was 4.16.0.021 at that time.
But a quick search on Internet archive (wayback machine) reveals a snapshot from February 2020 which shows that older firmware files (4.13.0.017) are still accessible via direct download links:
When it comes to firmware analysis, there are a few familiar names that come to mind. automated firmware analysis frameworks such as FAT, make life easier for reverse engineers and are very popular. but in this case, we had to go manual and get our hands dirty.
After downloading and extracting the firmware with binwalk :
we face some encrypted files including firmware filesystem and kernel image:
Following the blog post mentioned earlier, we used the same decryption code along with the firmware version to decrypt these files:
Examining the file with binwalk again, we see that the decrypted filesystem is squashfs and the firmware is using a 32bit ARM Linux Kernel.
After extracting the decrypted filesystem, we moved on to the emulation part.
Since this blog post is not about emulator configuration and setup, i’ll skip the tiny details. emulation was a real pain in the ass and took a lot of time, because we didn’t have access to a physical device for dynamic analysis and black box fuzzing. it also caused a lot of unnecessary rabbit holes along the way.
Here is the summary of what we did for firmware emulation:
cross-compiled an ARM-based Linux Kernel for QEMU emulator using buildroot tools (the originally extracted kernel caused a kernel panic)
Created a new squashfs filesystem for emulating the binary (we needed more space for dynamic analysis tools such as gdbserver)
Configured QEMU emulation framework for running the firmware (use custom ARM Kernel and setup port forwarding for GDB remote debugging)
Copied all firmware binaries and analysis tools to the newly created filesystem
A few tweaks here and there
Started the emulator with our fingers crossed…
After extracting the firmware, we started looking for initng files that are responsible for initiating system daemons. while searching these files, we found one that was responsible for starting the web interface for device management console:
/etc/initng/daemon/acemanager.i
the exec command indicates:
web server binary called ACEmanager in /sbin/ directory
Public and private SSL keys for HTTPS connection in /etc/ACEmanager/certs/
Static web page files (cgi, html, js, etc.) in /www/
Shared objects needed by the web server in /lib/
To find out which libraries and shared objects are used by the web server, we can use readelf command:
/etc/initng/daemon/acemanager.i
the exec command indicates:
web server binary called ACEmanager in /sbin/ directory
Public and private SSL keys for HTTPS connection in /etc/ACEmanager/certs/
Static web page files (cgi, html, js, etc.) in /www/
Shared objects needed by the web server in /lib/
To find out which libraries and shared objects are used by the web server, we can use readelf command:
readelf –d /sbin/ACEmanager
For building the ARM kernel and emulating the firmware on a x64 Linux host, we used QEMU and Buildroot as mentioned before. the main pick here is that after building a new filesystem and Kernel image, we have to move the files from decrypted filesystem (binaries, static scripts, shared objects, etc.) to the new filesystem and do not replace anything related to built-in Linux binaries because the system would crash and emulation would fail.
After a lot of configurations/tweaks, here comes the working ACEmanager web console:
When it comes to IoT systems (specially gateway devices), the first things that a remote adversary would target are:
Open network services
Web-based management console
With that in mind, considering we don’t have access to a physical instance of the device for network protocol fuzzing and testing network services (which is not possible on an emulator), we were left with the web-based console as the only attack surface we could actually work on.
Starting with static analysis, we examined the ACEmanager binary in Ghidra, looking for useful strings, execution flow, unsafe functions, program inputs, etc. aside from running SonarQube and Semgrep (which returned nothing useful), analyzing the disassembly/decompiler output shows that ACEmanager binary is not stripped and function names are visible. bad function usage had a long list but none of them are accessible from the web console.
Black box fuzzing of the ACEmanager binary was first performed using Qiling binary instrumentation framework and the famous AFL in a user-mode emulation scenario. this approach led to a crash but after farther examination it didn’t reveal a vulnerability or useful code coverage.
The next option, was using QEMU full-system emulation with Valgrind. the fuzz parameters were the pre-authentication parameters in the POST web request sent from web login panel. this is where we hit the jackpot :)
To fuzz the binary in a more controllable fashion, we wrote a python fuzzer script using pyradamsa library to create test cases for user authentication POST request parameters including username and password fields.
The next step was remote debugging the binary to find out what is causing the segfault.
The strategy was remote debugging of ACEmanager using gdbserver on QEMU side and connecting to it using gdb client (GEF/pwndbg) from Ghidra debugger console so we can analyze both the execution flow and decompiler at the same time.
The steps:
Running ACEmanager with command line arguments under gdbserver:
2. Opening ACEmanager binary in Ghidra debugger window:
3. Attaching gdb-multiarch from Ghidra debug console to remote gdbserver:
4. Running the fuzzer script to hit the segfault again and analyzing the disassembly/decompiler related to that code section.
The normal web authentication request sent to the web server, is something like this:
running the fuzzer script shows a crash is caused by an empty password tag in the XML data:
The picture shows that ACEmanager receives the username/password XML data from web requests, then checks the credential using PAM modules. this happens repeatedly until we send an empty password tag (<password></password>
) which causes a segment violation that indicates a memory corruption in some place. as you can see, the crash is in ACEmanager::Connect()
function which means this function or one of its callees is parsing the XML data to extract username and password for PAM authentication.
Recalling from static analysis phase, the TinyXML library was imported and used in ACEmanager binary.
TinyXML is a simple, small, minimal, C++ XML parser that can be easily integrating into other programs. It reads XML and creates C++ objects representing the XML document. The objects can be manipulated, changed, and saved again as XML.
Since the segfault is happening during XML parsing process, this library took our attention. we started searching for possible vulnerabilities related to TinyXML itself. a few searches showed us the answer we were looking for:
CVE-2021–42260 has been identified in the TinyXML library and the TiXmlParsingData::Stamp
module related to processing operations for web requests and XML messages. This vulnerability arises due to an infinite loop caused by infinite recursion, which never reaches the necessary condition to exit. To exploit this vulnerability, an attacker must send a web server an XML message with a malformed structure, causing the program to enter an infinite loop.
As mentioned, crashing the service occurs in the Connect()
function of ACEmanager. This function is responsible for receiving requests and sending them to other functions for request type validation and establishing communication. Essentially, this function plays the role of a parent function for implementing XML Parser and PAM Authentication functions, and among the functions called from ACEmanager::Connect()
, the TiXmlParsingData::Stamp
function is observed. In the current scenario, exploiting this vulnerability involves using an HTTP request with an empty “password”
tag, as XML processing in ACEmanager is performed by this library.
To validate our assumptions, we set a breakpoint on ACEmanager main function and right after hitting it, the TinyXML library is loaded into the program.
we set a break point on stamp function as well. in this state, ACEmanager awaits HTTP requests and it hits the breakpoint after receiving it. to validate the breakpoint, we check the disassembly instructions at that memory address in Ghidra:
in the first request, we sent an XML data with values in the password tag just to see how the data is parsed in the function. after hitting the second breakpoint, we toke a look at the call stack:
You can clearly see that ACEmanager::Connect() function is the caller of TiXmlParsingData and TiXmlElement. checking the function parameters proves the point as well:
As observed, the stamp function receives the XML request as an input parameter from the connect function. according to CVE-2021–42260 if we send an empty password tag, the program enters the infinite loop. if we compare the TinyXML source code with Ghidra decompiler output, we can confirm this:
after sending the XML data with empty password tag, we see this in gdb:
When username and password tags are passed to the stamp function, the third breakpoint is hit and following instructions cause the infinite loop:
if we disable this breakpoint, the infinite loop will cause a resource exhaustion condition and hits the limit Linux kernel parameters that don’t allow any program to consume more than a specific amount of system resources, this in turn, will cause the stamp function to be terminated thus raising an exception in connect function and finally, causing a segmentation fault and program crash.
Sending the malformed XML data in the POST web request parameters will lead to a crash in ACEmanager binary and cause a Denial of Service (DoS) condition, but the initng script will restart the service after a few minutes. an attacker could continue the attack by re-sending the payload over and over again. here is the final PoC exploit for this attack:
You can also find the PoC on GitHub at this link.
As my first real-world experience in bug hunting, this was so hard, time consuming and had a huge learning curve as well. but it also shows that binary exploitation is not always about being an opcode ninja and digging deep inside the OS. sometimes its about finding the right way of looking at the system and even using the previous findings of other researchers or older vulnerabilities to get what you want.