Forum Discussion
RE: How do you identify 'non-privileged' users...
Looking to generate a KQL query or Analytics rule to identify 'Multiple failed user logon attempts' from Windows PCs only and the user is classified as 'non-privileged'.
Just looking for the most effective way to define a 'non-privileged' user from either security alerts/events and/or AAD related logs.
Any best practice advice welcomed.
- Hey Jason, I believe you could write the logic to query the Watchlist and only add new members but believe it would be easier to just load everyone each time, and then query your Watchlist on the same frequency as your Logic App. For example – if you add 10 members on the first run, then add 3 members and run it again 4 hours later, your Watchlist will have 23 total items (original 10, plus 13 – the original 10 again and the new 3), but if you query your Watchlist on items added in the last 4 hours it will only show 13. If you then remove 5 users and run it again 4 hours later, your watchlist will have 31 total items (original 10, then the 13 we added, then the 8 current members), but if you query your Watchlist on the last 4 hours then you will just see the current 8 members and it’s accurate.
12 Replies
- m_zorichIron ContributorSo most of your log sources like Security Events / AAD sign on logs aren't going to be able to tell you if a user is privileged or not, because that is going to be pretty unique to your environment. There are some products like Defender for Identity that can do some of the work for you for sure, but really you are going to have to feed that information in to Sentinel yourself because it will be unique to your company. There are a few posts around about adding AAD Info to Sentinel - there is a great blog post here https://techcommunity.microsoft.com/t5/azure-sentinel/enriching-azure-sentinel-with-azure-ad-information/ba-p/1288805 and a shameless promotion here for my blog - https://learnsentinel.blog/2021/07/04/enrich-hunting-with-data-from-ms-graph-and-azure-ad/
Is there a reason why you want to identify non-privileged users instead of the opposite? I would guess your list of privileged users is smaller. In that case you may want to enrich Sentinel with the info about your privileged users, then you can exclude them from your queries.- JMSHW0420Iron Contributor
Hello m_zorich.
Thank you very much for the reply. It is very much appreciated.
The reason for searching for 'non-privileged' status is to identify those logging onto resources that are short of permissions or privilege. Also the list of privileged users is smaller.
Ideally I want to review the EventID == 4625 from SecurityEvent where I can then 'join' or 'union' those failed user logons (account) against a(n) user (account record) that can identify if they are a privileged user or not.
So for a user (account) entity is there a property that can 'state' if a user has privileged status, OR if they are NOT a member of a privileged access group, OR a member added to a security enabled group (EventIDs in "4728", "4732", or "4756") ?
So a simple starting point for failed logons being:
SecurityEvent
| where TimeGenerated >= ago(1d)
| where EventID == 4625
| summarize FailedLogons=count() by Account, Computer
| sort by FailedLogons descI will of course look at your links, but any further feedback to the questions I ask, would be very much welcomed.
Jason
- m_zorichIron ContributorSure I get your use case a little more now, so you will still need to enrich Sentinel with the right information about what users & groups are privileged, a 4625 event for a non privileged vs privileged user is going to look the same. If you have a list of users who are privileged, you could add them to a Watchlist - https://docs.microsoft.com/en-us/azure/sentinel/watchlists. Then you use that Watchlist as part of your query, and for your example you want results that aren't in the Watchlist. If you create a Watchlist called PrivilegedUsers with a column named Account, then a list of accounts, your query would look like -
let PrivilegedUsers = (_GetWatchlist('PrivilegedUsers') | project Account);
SecurityEvent
| where TimeGenerated >= ago(1d)
| where EventID == 4625
| where Account !in (PrivilegedUsers)
| summarize FailedLogons=count() by Account, Computer
| sort by FailedLogons desc
You could do the same for group membership changes, create a Watchlist called PrivilegedGroups with TargetAccount as the column header, then the list of names of the groups you want to monitor, then your query will be
let PrivilegedGroups = (_GetWatchlist('PrivilegedGroups') | project TargetAccount);
SecurityEvent
| where EventID in (4728, 4732, 4756)
| where TargetAccount in (PrivilegedGroups)
You can combine the two as well, so something like
let PrivilegedGroups = (_GetWatchlist('PrivilegedGroups') | project TargetAccount);
let PrivilegedUsers = (_GetWatchlist('PrivilegedUsers') | project Account);
SecurityEvent
| where EventID in (4728, 4732, 4756)
| where TargetAccount in (PrivilegedGroups) and Account !in (PrivilegedUsers)
Would give you a non privileged user being added to a privileged group
Instead of watchlists the alternate would be ingest this info as a custom table, PrivilegedGroups_CL and PrivilegedUsers_CL (see the examples in the last post), then you could join & lookup that way. The key will be keeping those lists current.