Resources
/
Blog
Video

Weaponizing Cryptography Mistakes to Achieve RCE and Administrative Access: Kill Chains and Coffee

29 Oct 2026
WRITTEN BY
ETHAN WILKINS
Weaponizing Cryptography Mistakes to Achieve RCE and Administrative Access: Kill Chains and Coffee
Contents

Key Takeaways

  • This post covers one of Armadin’s latest kill chain discoveries.
  • We chained five vulnerabilities in a commercial ASP.NET application into unauthenticated remote code execution (RCE) and administrative access.
  • Three cryptography decisions produced the chain: a reversible encoding presented to administrators as encryption, cryptographic keys compiled into shipped binaries, and encryption applied to client-side state in place of integrity validation.

Cryptography fails quietly. There is no warning, because the application keeps serving traffic exactly as it did before. The control that was supposed to be holding the line simply isn’t.

The application in this particular engagement was a commercial ASP.NET document management platform deployed in a customer environment. It made three cryptographic decisions:

  1. It presented a reversible encoding to administrators as encryption.
  2. It compiled static cryptographic keys into its shipped binaries.
  3. It encrypted its client-side state instead of validating the integrity of that state.

Together, these decisions produced an unauthenticated path from the open internet to code execution on the web server.

Decision One: Calling an Encoding “Encryption”

The application routed document downloads through a file-retrieval handler that accepted an opaque, encoded file path parameter. An administrator-facing configuration menu described this behavior as query string encryption, with the stated goal of keeping server-side file paths out of the browser. The mechanism behind that toggle was not encryption, and the Armadin team deduced the encoding scheme through a separate endpoint in the application that returned encoded strings for plaintext paths supplied by the caller.

By providing multiple different inputs to this encoding oracle and observing the output, we determined that the custom encoding scheme Base64-encoded the input, transposed the characters in groups of four, and rotated the resulting string by half its length. The transformation was deterministic and used no key, so reversing it required no secret material. We implemented the inverse in a script that decoded live parameters back to absolute server paths and encoded arbitrary paths of our choosing into values the application would accept.

The difference between obfuscation and encryption is whether reversing it requires a key the attacker doesn’t have. The configuration menu promised the second and shipped the first.

What the Decoder Opened

With a working encoder, we were able to address the file-retrieval handler directly, and the handler did not require authentication to serve what it was asked for. A path-disclosure weakness had become a local file read (LFR) vulnerability, exposing application source code and secrets, and it took no credentials.

Decision Two: Compiling Keys into the Binary

We used this LFR vulnerability to download a utility DLL from the application and load it into a public .NET decompiler. The decompiled source contained a static TripleDES key and initialization vector, which were written directly into a utility class and compiled into the shipped assembly. These keys were not generated per installation, and they were not stored in a protected configuration section. The same values shipped in every deployment of that software. Because of the LFR vulnerability, they were recoverable by anyone on the internet.

Decision Three: Encrypting State Instead of Validating It

The application had replaced ASP.NET’s standard ViewState feature with its own custom implementation. The homemade ViewState implementation encrypted the ViewState data with that hardcoded key rather than relying on the platform’s MAC validation. On each POST request, a shared base page class decoded, decrypted, and decompressed the ViewState field, then passed the result to a .NET object deserializer backed by `BinaryFormatter` with no type validation.

Encryption answers the question, “Can the client read this?” Integrity validation answers the question, “Did the client write this?”

The application needed the second question answered and instead answered the first. Because the deserializer sits directly behind that check, the key was the only thing standing between an anonymous HTTP request and the server’s object graph.

We generated a well-known .NET gadget chain with a code execution payload, compressed and encrypted it with the application’s own key and IV, encoded it to match the custom transport format, and submitted it in the custom ViewState field of a POST to the unauthenticated login page. The server accepted the payload as its own work, deserialized it, and executed the code. Every page inheriting from that base class was affected, including pre-authentication pages.

Proving a Blind Compromise

The RCE was blind. The application did not include the command output in the HTTP response, and egress restrictions prevented Armadin from receiving HTTP or DNS callbacks from commands like curl or ping. Additionally, the app’s execution context did not have permission to write inside the web root.

In order to prove that code execution occurred, we fell back on a response differential in the same file-retrieval handler, which worked on the Windows temp directory (`C:\Windows\Temp`). Requests for files that existed produced an HTTP 302, whereas requests for files that did not exist produced an HTTP 200 with a verbose error. 

We used that behavior as an assertion over three requests:

  • The first targeted a nonexistent path in the system temp directory and established the “cannot be found” baseline.
  • The second included a command execution payload instructing the server to write a file at exactly that path.
  • The third checked for that file again. The response changed to an HTTP 302, proving that Armadin’s payload could run system commands and write files to disk. 

The Other Branch: Forging Authentication Tokens

In addition to achieving RCE, we discovered that the same key protected the authentication tokens the application carried in its client-side state. We built tooling to intercept the field, decrypt it, modify its contents, and re-encrypt it for submission. Because the server treated successful decryption as proof of authenticity, whatever we wrote back was accepted as the application’s own state. We exploited this weakness to forge a token for a privileged account and obtain administrative access without a password. 

Two Critical Lessons for Security Teams

1. Let the platform handle the cryptography, and make sure every installation gets its own keys.

Modern web frameworks ship with built-in protection for the data an application hands to a browser and later accepts back. That protection checks whether the returned data is the same data the server sent, and the control stops an attacker from rewriting it. When a development team replaces that mechanism with one of its own, it takes on the responsibility for a problem the framework has already solved. In this case the replacement checked only whether the data could be decrypted rather than whether it had been altered.

The key matters just as much as the mechanism. A key written into source code and compiled into the product ships to every customer, so a single copy of the software exposes the key protecting every installation. Armadin recommends that teams generate key material per installation, store it outside the application code, and ask third-party vendors directly how their products handle this before deployment occurs.

2. Protect files with authentication, not with a hard-to-read URL.

Scrambling a file path so that it looks meaningless in the address bar hides information from a casual observer, but it does not decide who is allowed to retrieve the file. Anyone who figures out the scrambling can ask for whatever they want.

We recommend that file download endpoints verify the requestor's identity and permissions on every request, and that they serve content only from a defined directory for user-facing files. That second step keeps configuration files, application binaries, and other system content out of reach, even when an attacker can control the requested path.

Dive Deeper into an Agentic Hyperattack

Discover how a safe, managed Hyperattack™ from Armadin gives you the tools and confidence to eliminate truly exploitable risk across your environment. Learn more about Hyperattacks.

Continue reading
General Paul M. Nakasone Joins the Armadin Board of Directors
News
Press Release
10.30.26
General Paul M. Nakasone Joins the Armadin Board of Directors
Beyond Cairn: The Glass Houses of AI Attackers
Blog
10.21.26
Beyond Cairn: The Glass Houses of AI Attackers
Kill Chains and Coffee Episode 5: Why AI Beats Human Pentesting
Blog
Podcast
Video
9.3.26
Kill Chains and Coffee Episode 5: Why AI Beats Human Pentesting