AzureTracks.com investigating using Microsoft Sentinel, KQL, and logs.
Andrew Posted on 7:18 am

Find Actions Taken by an Administrator in Azure Logs

In this post, I explore how to find meaningful actions taken by an administrator in our environment. Commonly known as the audit, we will take a look at how to find what changes were made by an individual account. Join me as we dive into auditing logs in Azure and determine if the administrator account is responsible for actions in our environment.

Why audit?

Audit logs are in place for all of the things we do, generally speaking. If those logs are being uploaded and retained in a Log Analytics Workspace, then we can use them to help secure our environment. We audit because it is good practice and because we need to prove a hypothesis to solve a problem.

The Problem at Hand

An IT Manager has come to us at the SOC with concerns about a storage account. We have already taken the steps to rotate keys, but the Manager is convinced that someone leaked the storage key.

Starting with the suspected administrators userid makes this a lot easier, but we can use the same shared in this post to reverse that process a bit and find the actions to associate a userid. Let’s see what we can find!

Getting Started

First up, let’s get logged in to our Azure portal at https://portal.azure.com and head to our Log Analytics Workspace.

Next, we should start to explore our data a little bit to determine if we are looking in the correct tables.

AzureActivity

Set your Time Range to 24 hours and let’s see what we get back here. I had to set my query a few days longer since I’m using a lab environment.

Alright, we can see that we have some useful data here, but I don’t see a way to restrict to a single login or userid right away. Let’s expand some fields and see what we can find here.

Although I have blurred a few fields here, we can see the Caller field is readily accessible to use. This field holds the calling userid that performed this action in the log entry.

The next thing that I notice here is that we can use the field OperationNameValue to filter on too! This is looking pretty helpful in a hurry!

Great stuff so far. Let’s add a couple of filters to our code:

AzureActivity
| where TimeGenerated > ago(14d) // adjust the time range as needed
line)
| where Caller == "xxxxxxxxxx"

Swap out my x’s above in the query for a username in your environment and let’s see what results we can get now.

Notice that I’m using the query to set my query time now. I’ve set it to 14 days. You can change this to match what you might investigate. I often start very small to keep results fast, and remember that if you have many thousands of events coming back in a 5m query, use “take 15” to just get a random set of results returned limited to 15 rows. This will save your whole day!

Ok, so I can see a lot of different types of operations happening here, but we are narrowing things down now!

Let’s limit the operations as well:

AzureActivity
| where TimeGenerated > ago(14d) // adjust the time range as needed
| where OperationNameValue contains "Storage" // Storage operations
| where Caller == "xxxxxxxxxx"

Ok, now we are seeing a picture come together:

There is one more field I’d like to explore if you scroll over in these results — Authorization.

It looks like there could be some useful data in here, let’s expand this field and explore this.

I cleaned the display up a bit, but you can see that we get the action AND the role used in this case. This is exactly what we need to see.

Evidence

We have now find our first piece of evidence to evaluate. We can see our Administrator user did in fact list storage keys. Our log entry will have the subscription, userid, timestamp and other data available, let’s look at what we can use to really clean up this display here.

AzureActivity
| where TimeGenerated > ago(14d) // adjust the time range as needed
| where OperationNameValue contains "Storage" // Storage operations
| where Caller == "xxxxxxxxx"
| extend Authorization = parse_json(Authorization)
| project TimeGenerated, OperationNameValue, ActivityStatusValue, Authorization.evidence.role, _ResourceId

The query above should return a nice clean results set telling us the story of what was done here.

Results

We can see in the screenshot above that our specified userid for the Administrator did list the storage keys in the timeframe the IT Manager provided to us. We can include this in our information package for the Manager.

It’s critical to educate the Manager in this case too! They must understand that listing keys is not a compromise on it’s own. We could further investigate to determine other storage operations and interactions to show files were downloaded, moved, or deleted if that is required in our case.

In the 2 results above we can see that the Success entry is the line we are after in our case.

In this case, our hypothesis was that the administrator userid listed keys. In this case, that was found to be true!

Closing

The full query I use to help me filter different types of actions in Azure is:

AzureActivity
| where TimeGenerated > ago(14d) // adjust the time range as needed
//| where OperationNameValue contains "SecurityInsights" // Write, Change, Create, Delete Operations (Toggle this line)
//| where OperationNameValue contains "Authorization" // Login & Signin Operations (Toggle this line)
| where OperationNameValue contains "Storage" // Storage operations (Toggle this line)
| where Caller == "xxxxxxxx"
| extend Authorization = parse_json(Authorization)
| project TimeGenerated, OperationNameValue, ActivityStatusValue, Authorization.evidence.role, _ResourceId

Toggle the comments and values to filter by different operation types such as Create, Delete, Authorizations, and Storage. This allows your investigations to move quickly if you use something similar for your environment in a saved query for your SOC Team.

Happy Hunting!

Thanks for joining me on this journey through some audit data, and I hope that this saves you a bit of time in your investigations!