Marrying client-side Windows-based CryptEncrypt and server-side,Linux-based Crypt::OpenSSL::RSA

Time flies and it does so very quickly. The story I am about to tell you is 8 years old, but it does feel like I wrote it yesterday.

In 2014 a client asked me to develop a never-seen-before prototype of a new type of an endpoint agent that would be code-minimal, position-independent, 32- and 64- architecture-aware and talk to a backend using strong encryption. Yes, kinda EDR or C2-like agent and we had discussions about using it for both blue and red team engagements, if it worked.

Anyone who tried to make Windows crypto primitives talk to (typically Linux-based) server-side crypto primitives knows that it is an awful coding experience. After googling around, and trying different things I eventually developed the prototype. I can’t share the code for obvious reasons, but I can at least describe what it did.

On a client side, I had a routine that would talk to the socket (not proxy aware at that time) and follow a basic data protocol exchanging encrypted data blobs with my server. The data was encrypted with a public key that only server could decrypt. Nothing really ground breaking.

What was annoyingly, frustratingly hard to develop was the actual decryption part. The server part was using Crypt::OpenSSL::RSA (yes, perl!) primitive, and I couldn’t force it to decrypt the CryptEncrypted message I was sending.

After many hours of debugging and googling around I eventually figured it out. After I used CryptEncrypt I just had to reverse the data blob delivered by the function: byte, by byte. Yup, it was that simple.

Commander Minority Report

This is an idea I have not tested in practice, but it emerged in response to a simple question:

  • What if sysmon, 4688, EDR command line logging couldn’t catch a thing?

I am not the first one to ask this question and @_xpn_ has a great post about an argument spoofing trick one can use to fool command line interceptors: create a suspended process, then inject a proper command line argument after the process creation event has been intercepted, then resume the process with the new command line injected.
Brilliant!

So… that made me think about what command line really is.

And command line is basically a string that is being parsed to set up a internal state for the program that parses it. And with that we can ask another question: is there a way to manipulate the process state and assign appropriate values to internal engine of the targeted tool and make it run as if a command line argument was provided, but without providing that command line? Of course, to do so one needs to know intricacies of the process that is being manipulated but it’s relatively straightforward for targets like powershell (source code available), or even v|cbscript / cmd.

Perhaps there is a scope for a completely new type of offensive engine that takes instrumentation to a completely new level…