Attacking Azure Developers: Easy Mode

In keeping with our posts about finding methods to jump the "Cloud Gap", let's take a look at how we can leverage access gleaned from DevOps engineers and developers. As Adversarial Operators, it is important to realize "how" to hunt, "where" to hunt, and "what" to look for when we are entrenched in what I consider a hostile environment.

This is what I intend to provide by discussing a couple of methods used to store secrets on the victim DevOp system. The assumption here is that you, the adversary, have already gained access to the enterprise network and/or DevOp system and are looking for useful information necessary to gain access to the Azure infrastructure.

This is a two part post. The first is going to discuss locations that store secrets in cleartext. The second will discuss more involved (i.e., encrypted) methods of storing secrets.

Environment and System Variables

I am just going to fall on my sword up front and say that this is the obvious, but I want to get it out of the way early. Environment and system variables can maintain a wealth of information. DevOps engineers will add just about anything from database connection strings to authentication credentials to API keys, so don't pass up a potential opportunity by overlooking the obvious. Here are a few ways to glean environment and system variables using set, powershell, and registry utilities.

1. Using the set command

2. Using a PowerShell cmdlet


Use Get-Children Env: to pull all of the variables. I just printed the contrived SECRETDATABASE variable for brevity.

3. Using the reg command

Stay along for the ride, it gets more interesting I promise. Let's take a look at the AzureRM PowerShell module.

PowerShell: AzureRM Module

Most anyone enlisted to perform DevOps responsibilities wants to automate and simplify the processes and their lives as much as possible. I don't blame nor judge; this is a safe place, it is human nature. Enter the AzureRM PowerShell module.

AzureRM provides a means to remotely manage and administer an Azure infrastructure exclusively through the PowerShell command line using a robust set of Cmdlets. A couple of convenient Cmdlets often used are Save-AzureRmContext and Import-AzureRmContext, respectively. The reason being, the DevOps engineer can use these to save an authentication profile to local filesystem thus eliminating the need to provide credentials to access Azure. Yeah I thought you would like that.

The Save-AzureRmContext cmdlet takes Azure credentials, in the form of username and password, as input. In turn, a TokenCache is generated that consists of bearer tokens in the format of JSON Web Tokens. I noted three (3) individual tokens with the primary token containing both kid and x5t header properties indicating the first token was used to enforce authenticity while subsequent tokens relied on the primary token's signature validation. These are stored in an arbitrary JSON-formatted, user-defined file at the time of creation.

The Import-AzureRmContext uses a -PATH flag to read in the aforementioned JSON file. PROTIP: Again, DevOps folks like to stuff things into environment variables. This is a perfectly ideal situation to hunt and locate this access token file.

But is this token machine specific? Can I replay it on another system? No and yes, but with a caveat. First the token is generated per Azure user account so it is system independent. Second, there doesn't seem to be a JWT jti payload property preventing replay, so we are free to steal and us it for our own bidding.

The caveat is that the token is time based; however, that time is approximately 65 minutes as defined by the following JWT payload properties:

  "iat": 1518471374,
  "nbf": 1518471374,
  "exp": 1518475274,

This is ((exp - iat)seconds/60seconds) which gives us 65 minutes. Although, this is somewhat short lived, I can envision a situation of being situationally aware via keylogging/idletimes and then replaying a token to achieve Azure access.

Once you manage to acquire this JSON file, just drop into PowerShell and call Import-AzureRmContext -PATH 'path to jsonfile' and the gates to Azure shall welcome you.

Alright, onto the next progressively more interesting method of credential storage and thievery, the Secret Manager utility.

Microsoft Secret Manager Utility

Implementation details

Microsoft has introduced the Secret Manager as a means to prevent sensitive information within ASP.NET Core codebases. This is the abstract from the official Microsoft documentation, "This document shows how you can use the Secret Manager tool in development to keep secrets out of your code. The most important point is you should never store passwords or other sensitive data in source code, and you shouldn't use production secrets in development and test mode. You can instead use the configuration system to read these values from environment variables or from values stored using the Secret Manager tool. The Secret Manager tool helps prevent sensitive data from being checked into source control. The configuration system can read secrets stored with the Secret Manager tool described in this article."

So what exactly is the referenced configuration system? The answer is that it is a special assembly that can bind the actual secrets stored on the filesystem without implementing the literal values within the codebase. Let's take a look at a fundamental implementation.

Say for instance that we want to add a reference to an object called VictimPassword. We will use this as the key for our key value store coming up here shortly. First we declare a Secrets.cs class that contains a simple constructor and a couple of default accessor and mutator methods (i.e., get and set).

Further, the CoreSample.csproj must be edited in order to generate and insert a valid GUID within the UserSecretsId tags. This will be unique for each project and will be used to construct a unique namespace relative to our application secrets.

A couple of additional lines are important here. The Microsoft.Extensions.SecretManager.Tools assembly is used to store our configuration secrets and the Microsoft.Extensions.Configuration.UserSecrets assembly is used to access the secrets within our code. I have installed both via the inherent Visual Studio NuGet package manager system.

Now in our Programs.cs class we can reference the respective VictimPassword object. The first line references the Microsoft.Extensions.Configuration assembly. Line 15 through 17 loads our Secrets.cs class. Line 20 accesses the value associated with our key. As you can see, at no point is the password actually declared within our code. This is actually quite nice and I commend Microsoft for making this available to developers.

We haven't talked about how the secrets are actually created, so about time we did. Quite simply start a powershell instance, navigate to the root of your project and use the built-in dotnet.exe utility as follows:

PS C:\CoreSample> dotnet user-secrets set VictimPassword "Foobarbaz"
Successfully saved VictimPassword = Foobarbaz to the secret store.

Attacks against the Secret Manager

What could we do against this solution in order to glean the secrets? Well here are a few ideas for your hunt, both directly and indirectly.

1. Hunt the Source Code Repository

The assumption is that you are on the network and searching for DevOps folks, but you may also have access to an internal company portal. These portals often contain details regarding significant technology resources. Hunt for the source code repository (e.g., internal gitlab server, source control, etc.) and do keyword searches, such as password or connection-string from the root location. This could yield some quick wins.

Further, leverage filetype searches and look for .csproj files. If they contain the Microsoft.Extensions.SecretManager.Tools and Microsoft.Extensions.Configuration.UserSecrets assemblies then you know you are working towards credentials. Look at the commits and try to reconcile the usernames (often domain account names) to details provided by net user queries. Locate the account and their system.

What do we do once on the system?

2. Hunt the Filesystem

Once you have located the developer's system there are a couple of ways to extract the secrets from the filesystem. The Secret Manager stores secrets in a default location on Microsoft systems. Microsoft also uses a default name called secrets.json. Very thoughtful, I know.

%AppData%\Roaming\Microsoft\<GUID>\secrets.json

The contents of the file should look something like the following:

{
  "VictimPassword": "Foobarbaz"
}

3. Or Just Ask Nicely with PowerShell

You guessed it, there is a cmdlet that we can leverage to obtain the same secrets. Again, drop into the root of the projects directory and issue the following command:

PS C:\CoreSample> dotnet.exe user-secrets list
VictimPassword = Foobarbaz

Conclusion

This was actually pretty interesting to research, and I hope you found it valuable as well. I discussed environment and system variables, the PowerShell AzureRM module, and finally the Secret Manager utility. I am not finished yet. Microsoft implements a robust solution called the Azure Key Vault as a production-ready secrets safe.

It's a vault and those that know me, know I like physical deterrents. This has vault in the name so close enough, let's crack it. That's just what we are going to attempt in a follow-on post. In the meantime, kick some ass, hunt some devs and remember "Always Forward"!

References

Microsoft Secret Server
Microsoft AzureRM