Blogs & Stories

SpiderLabs Blog

Attracting more than a half-million annual readers, this is the security community's go-to destination for technical breakdowns of the latest threats, critical vulnerability disclosures and cutting-edge research.

Reversing (and Recreating) Cryptographic Secrets Found in .NET Assemblies Using Python

Picture the scene - you’re on a penetration test, somehow you’ve got hold of a bunch of .NET assemblies for the application you’re assessing, be it a web application or thick client. On a thick client test, getting a hold of these files is somewhat trivial as they’re right there in front of you. On a web application test, however, things are not as easy - but it still is possible, depending on permissions and such. I won’t go into "the how-to" in order get these in this blog post, instead I will assume you’re sitting there, a cup of coffee in hand, staring at a bunch of .DLL files decompiled in something like dotPeek, ILSpy, etc.

Looking at the source code of decompiled assemblies relating to an application you’re assessing is an eye-opening experience. You’ll learn the inner workings of the application which will support the testing, and often, as a bonus, you’ll find hardcoded secrets, mostly in the form of user credentials – ranging from database connection strings to domain accounts. Sometimes this process can make you work a little bit harder for the rewards.

You may, for example, find no quick wins - no plaintext credentials, no advance to go and collect $200. Instead, you may find something like a reference in the source code to an external configuration file. The application may use this configuration file to look up some values and then utilise them. Imagine a reference in the source code to something like the below, we’ll call the file it references spiderconfig.xml.


Let’s imagine this spiderconfig.xml file relates to a .NET web application, which utilises the domain account “SPIDER\SpideyAdmin” to carry out privileged functionality. The “==” padding right at the end of the password value should scream out right away that this is likely to be base64 encoded. Decode it and profit? Not quite! Mostly unprintables are returned – not a password in this form.


It is also worth stating at this point that the password may actually be the value in the config file as is, without any encoding and actually contains the characters “==” at the end. I wouldn’t rule this completely out!

Assuming that this is not actually the password, we must go back to the assembly in question and find some cryptographic references. After some browsing, a class called “crypto_component” is found, byte object names such as c_key (32 bytes) and c_iv (16 bytes), references to Rijndael, etc. I think we’re onto a winner here.


We have the initialisation vector (IV) and encryption key. The “Rijndael” reference reveals it to be using Advanced Encryption Standard (AES), a symmetric block cipher. We have everything we need to be able to decrypt the password value in the spiderconfig.xml file, we are just missing magical crypto stuff.

Enter Python. *faint trumpet noise can be heard in the background*

We import AES from Crypto.Cipher, together with a bunch of useful things from binascii.
[1] Both the encryption key (c_key) and iv (c_iv) are loaded into bytes objects.
We setup AES using our key and IV and configure AES in cipher block chaining (CBC) mode.
[2] We provide the base64 encoded value from the configuration file, call “a2b_base64” from binascii on it to turn it back into binary data.
[3] We then call AES decrypt on the ciphertext (which uses our key and IV).


We pull the magic crypto levers and something which more likely resembles a password is returned: Welcome1
(note: the \x08 at the end is just padding due to the block size of 16 and this being an 8 character password)

Just for fun, let’s reverse things around and encrypt this back, validating that our IV works.

[4] The plaintext “Welcome1” is passed back into AES but this time encrypting it (with our key and IV).
[5] The result of this is then passed into “b2a_base64” from binascii (but not a2b_ like last time), this converts a line of ASCII characters into base64 encoding, as per our configuration file.


The final result matches the base64 encoded value in our configuration file from the start, so this confirms that both the key and IV we have work as intended.

One privileged domain account obtained. Time for a cup of tea and biscuits.