❌

Normal view

Received β€” 28 October 2026 ⏭ Black Hills Information Security, Inc.

No PoC, No Problem: Rediscovering CVE-2025-29902 in the Telex RDC Server

On a recent external network penetration test, the client's attack surface was quite small. From a tester’s perspective, that is usually bad news, because a handful of exposed services means only a handful of chances to find a way in. Luckily one host in particular jumped out at me, a Bosch Telex Remote Dispatch Console (RDC) Server.

A little research turned up Bosch advisory BOSCH-SA-992447, which maps to CVE-2025-29902, a CVSS 10.0 unauthenticated remote code execution vulnerability in the RDC's REST API, fixed in version 1.3.0 in May of 2025. The patch date on the web server's home page was "2023-07-29", leading me to believe the server was vulnerable. The bad news for me was that there were no public exploits available. I notified the customer who challenged me to demonstrate impact with a working proof of concept. This post walks through my journey trying to track down where the vulnerability was and how to exploit it.

Telex RDC is a dispatch console server used to drive radio communications, the kind of system you find behind dispatch centers for utilities, transit, and public safety. Per the Bosch advisory, the flaw lives in the REST API, affects all versions below 1.3.0, requires no authentication, and was responsibly disclosed by external researcher Omer Shaik. I managed to get in touch with the author of the exploit, but they were not willing to share a proof of concept.

Before I could analyze anything, I needed a copy of the old, v1.2 vulnerable software. The vendor's site offered the latest patched release, v1.3, which already had the fix implemented. Luckily, I was able to track down a download URL for the pre-patched version of Telex RDC, v1.2.

https://telex-downloads.s3.us-east-1.amazonaws.com/Releases/230809_v120_RDC_Server.zip

Now that I had both binaries, it was time to find out what had changed.

The textbook next move is patch diffing where you load both versions into a decompiler, compare them, and let the delta point you straight at the fix. I dropped both binaries into a fresh Ghidra project and ran a version diff, expecting a tidy little patch to jump out.

Ghidra version diff between v1.2 and v1.3

It did not. There were two full years and a complete UI overhaul separating v1.2 from v1.3. The diff was enormous, with thousands of changes and almost none of them related to the security fix. Trying to isolate the one patched code path in that sea of churn was a genuine needle-in-a-haystack problem.

This is the honest reality of patch diffing. It is fantastic when the vendor ships a small, targeted fix, and nearly useless when the vulnerable version is buried under a couple of years of unrelated feature work. Rather than burn the rest of the engagement squinting at decompiled functions, I switched approaches entirely.

If static analysis was going to fight me, I would let the application tell me where the bug was. I installed the RDC Server v1.2 on a Windows VM and stood up a local instance that I could poke at, without any risk to the client's production system.

Telex RDC Server local instance

I went back and reread the security advisory which gave me two extremely useful hints: the vulnerability was triggered over HTTP, and it did not require prior authentication. That narrowed the hunt to one question: which parts of the REST API can I reach without logging in?

So, I automated the question. I configured Autorize, the Burp Suite extension, to replay every authenticated request with the Authorization header removed, flagging any endpoint that answered the unauthenticated copy the same way it answered the authenticated one. Clicking through the entire admin interface with Autorize running produced a nice list of endpoints that were not enforcing authentication at all.

The endpoints reachable with no Authorization header are shown below.

/api/v1/resources?type=IMAGE_FILES
/SystemAdmin/views/activityLog.html
/SystemAdmin/views/selectorchooser.html
/SystemAdmin/views/audiofilechooser.html
/SystemAdmin/views/keylistener.html
/SystemAdmin/views/systemStatus.html
/SystemAdmin/views/radioChannels.html
/api/v1/ssl/status

One of the endpoints stood out from the others.

Autorize flagging endpoints that ignore authentication

The /api/v1/resources endpoint accepted uploads. My next step was to intercept the request in Burp Suite and look over the attack surface. I sent the unauthenticated POST and set the filename to a basic path traversal payload aimed at the root of the drive.

..\..\..\..\..\test.exe

To my amazement, the server responded with "204 No Content", indicating that no error had been encountered, so it was possible the file had been created on the server.

Unauthenticated arbitrary path traversal

Sure enough, browsing to C:\ on the VM showed my test.exe file sitting right there in the root of the drive. I was dumbfounded.

Unauthenticated path traversal successful

So, there it was. An unauthenticated attacker can write an arbitrary file anywhere on disk. That is the core of CVE-2025-29902. It is also, on its own, not remote code execution. Writing a file is only half the battle, because you still need something on the box to actually execute it.

My first idea was classic DLL hijacking. I fired up Process Monitor from the Sysinternals Suite and filtered on "Load Image" operations targeting DLLs under C:\Program Files\Telex\RDC Server\. The application loaded plenty of DLLs, but every one of them was already present on disk, so there was no missing DLL I could simply drop in. Exploiting that would have required DLL proxying.

ProcMon Load Image results, no missing DLLs to hijack

I kept exploring the application looking for a binary that would launch on demand. I found one. The web UI has a feature to regenerate the SSL configuration, and when you trigger it, the application shells out to a bundled openssl.exe living in the install directory.

The app launches its bundled openssl.exe during cert generation

That seemed to be a perfect target, a known, fixed path to an executable that the application itself runs, and one I can overwrite. The only catch was that triggering certificate regeneration requires being logged in, which brings us to the authentication bypass, courtesy of the same file write.

Poking around the install directory, I found SystemConfiguration.txt, which included the administrator username and password. The password appeared to be encrypted and Base64 encoded.

System configuration file with credentials on disk

I created a new Telex RDC server instance with a different username and password and made a copy of that configuration file.

Custom system configuration file

Next, I used the path traversal to overwrite the SystemConfiguration.txt with my modified version.

Overwriting the system configuration with new credentials

Once the service restarted, I logged into the administrator portal with username "jack" and password "admin". With admin access in hand, I used the file write one more time to overwrite openssl.exe with a compiled binary of my own. For a clean and harmless proof of concept, that binary just launches the Windows calculator.

#include <iostream>
#include <cstdlib>

int main() {
system("calc.exe");
std::cout << "Calculator opened. Press Enter to exit..." << std::endl;
std::cin.get();
return 0;
}

Overwriting the bundled openssl.exe as an unauthenticated user

Finally, I logged in with my credentials, browsed to System, then Certificates, then Install Self-Signed Certificate. The application dutifully invoked openssl.exe, which was now my binary, and up popped the most glorious calculator. Notably, it ran with administrator privileges, which meant full compromise of the server. The exploit chain was complete using an unauthenticated file write, authentication bypass, and another file write to finally achieve remote code execution as local administrator.

Proof of concept: arbitrary code execution as administrator on the RDC server

Back in my lab, everything worked but this exploit was scrappy, and there was no chance I was going to run the full chain against a live dispatch server. Instead, I proved reachability with the least invasive check possible, a single unauthenticated GET to /api/v1/resources against the production host. It responded just as the lab instance had, confirming that the vulnerable endpoint was exposed and unauthenticated, which was enough to prove exploitability without writing a single byte to the customer's system. I'm sure there are more elegant ways to get RCE with this path traversal, but this was the way I came up in the two days of testing available.

If you are running a Telex RDC Server, the mitigations are straightforward.

  • Patch to RDC Server v1.3.0 or later, and VLink 6.6.0 or later. This is the single most important step.

To check if you are currently vulnerable, send an unauthenticated GET request to the /api/v1/resources endpoint. If you receive a "200 OK" response, the server is vulnerable.

❌