Contents

Your service connection credentials are mine

Like with the two previous posts Hacking Azure DevOps and I am in your pipeline reading all your secrets! I want to continue to raise awareness and understanding about pipeline security in Azure DevOps. In the previous post I have explained how secure / marked as secret variables are handled during pipeline runtime.
In this post I want to show how an Azure Resource Manager service connection configuration is handled during pipeline runtime. And which sensitive information is exposed through this service connection.
Like I mentioned in the previous posts, without proper security configuration for pipelines this information could be abused by attackers.

But lets start with creating an out of the box Azure Resource Manager service connection.

New Service Connection

In your Azure DevOps project click on the setting icon in the bottom left corner. Next in the setting menu select the service connection option. On the new page in the top right corner click on the New Service Connection button. A list of the available service connection type should be visible on you right hand side. Select the Azure Resource Manager as shown in the following image and click next.

/posts/2022/your-service-connection-credentials-are-mine/1-new-service-connection.svg
New service connection - Azure resource manager

Next, as shown in the following image select the recommended option Service principal (automatic) and click next.

/posts/2022/your-service-connection-credentials-are-mine/2-new-service-connection-automatic.svg
New service connection - select automatic

On the next page you should be able to see the subscriptions and resource groups to which your current account has access. Select the preferred subscription and resource group and click save. It should look as follows.

/posts/2022/your-service-connection-credentials-are-mine/3-new-service-connection-subscription.svg
New Azure service connection
Note
You need at least owner permissions on a single resource group and your user must be able to register applications in the Azure AD to which your subscription is linked. In case more trouble shooting is required please have a look at MS docs Troubleshoot ARM service connections

When done correctly the newly created service connection should be visible in the list. See following example.

/posts/2022/your-service-connection-credentials-are-mine/4-new-service-connection-created.svg
Successfully created service connection

Service connection un the hood

Now that we have our service connection created, lets have a look under the hood. Click on the newly created service connection and the click on the managed service principal option as show in the following image.

/posts/2022/your-service-connection-credentials-are-mine/5-new-service-connection-properties.svg
New service connection properties

You should be redirected to Azure AD page of the underlying App Registration as show in the following image.

/posts/2022/your-service-connection-credentials-are-mine/6-new-service-connection-principal.svg
New service connection principal

To facilitate the service connection an App registration is created under the identity of the currently logged in account. We will ignore the awful looking naming that has been done to the App registration ;-). In addition a secret is also created to facilitate the use of this App registration by the service connection we just created in Azure DevOps. Click on the Certificates and secrets option in the left hand menu, the secret in question should be visible. Naturally the secret value is hidden. This is also show in the following image.

/posts/2022/your-service-connection-credentials-are-mine/7-new-service-connection-principal-secret.svg
New service connection principal secret

In addition this app registration is also added as contributor to the resource group or any other scope that is selected during the creation process. In the case of this example I have selected the resource group called demo-ado-scraper. Have a look at the following image.

/posts/2022/your-service-connection-credentials-are-mine/8-new-service-connection-principal-permissions.svg
New service connection principal permissions

So an App registration with a secret is created and contributor permissions are applied to the selected scope. Meaning that if it would be possible to obtain the App registration details including secret from within a pipeline, it would allow an attacker to at the very least gain contributor permissions over all the resources available under the selected permissions scope. In my case that would be the resource group.
Lets have have a look how we can achieve this.

Getting the App registration secret

Ideally to get any kind of authentication and authorization going the following values would be required.

  • Secret set for the the service connection app registration
  • App registration Id of the service connection app registration
  • Tenant Id of the service connection app registration

Since it is actually possible to get all 3 values using PowerShell a simple pipeline with a Powershell task which exposes the SYSTEM_ACCESSTOKEN should be more then sufficient to get the job done. The clue lies within the warning that is given when using the Connect-AzAccount -ServicePrincipal which reads as following

Warning
The provided service principal secret will be included in the ‘AzureRmContext.json’ file found in the user profile ( C:\Users\DevJev.Azure ). Please ensure that this directory has appropriate protections.

It tells us that when logging in using an App registration with a secret this secret is stored inside a file on the system. This file can be retrieved and parsed. The following script does exactly that.

Take a closer look at the file we just retrieved, you will notice that each context is stored under its own name. We can get the name of our current context by simply using \$contextName = \$(Get-AzContext).Name command. With the context name sorted its simply down to sifting through the json properties. And the ones we where interested in are as follows.

  • The app registration Id is retrieved by \$azureRmContextJson.Contexts.$contextName.Account.Id
  • The app registration secret is retrieved by \$azureRmContextJson.Contexts.$contextName.Account.ExtendedProperties.ServicePrincipalSecret
  • The tenant Id is retrieved by \$azureRmContextJson.Contexts.$contextName.Tenant.Id

The full script combined with the yaml task is as follows.

When the above task is executed in a pipeline the pipeline output will contain the Base64 encoded strings for each of the variable values. As show in the following clipping.

/posts/2022/your-service-connection-credentials-are-mine/9-pipeline-result.svg
Pipeline result

After using the decoding script from I am in your pipeline reading all your secrets! post the decoded values are as how in the following image.

/posts/2022/your-service-connection-credentials-are-mine/10-pipeline-result-decoded.svg
Pipeline result decoded

With these values exfiltrated an attacker is now able to access your Azure resources at with contributor permissions. For example by using the Connect-AzAccount

/posts/2022/your-service-connection-credentials-are-mine/11-example-connect.svg
Example connect

Please make sure to secure your pipeline access and use this blog post as an example to grow understanding in your organization about why pipeline security is a crucial security topic. Thank you for taking the time to read this post!