AzureAD/Applications/Invoke-CKAttackGraph.ps1
function Invoke-CKAttackGraph { <# .SYNOPSIS Collect information from Azure AD such as users, applications, service principals, groups and directory roles and send it to a cosmosDB graph database via the Gremlin API. Author: Roberto Rodriguez (@Cyb3rWard0g) License: MIT Required Dependencies: None Optional Dependencies: None .DESCRIPTION Invoke-CKAttackGraph is a PowerShell wrapper to collect information from Azure AD such as users, applications, service principals, groups and directory roles and send it to a cosmosDB graph database via the Gremlin API. .PARAMETER gremlinHostname FQDN of Gremlin endpoint (e.g. cosmosxxxxxx.gremlin.cosmos.azure.com). .PARAMETER cosmosDBRWKey Azure CosmosDB Read-Write primary key. .PARAMETER cosmosDBName Azure CosmosDB database name. .PARAMETER cosmosDBGraphName Azure CosmosDB graph collection name. .PARAMETER partitionKey Azure CosmosDB graph partition key. .PARAMETER accessToken Access token used to access the API. .LINK https://docs.microsoft.com/en-us/graph/api/application-list-owners?view=graph-rest-1.0&tabs=http https://docs.microsoft.com/en-us/graph/api/serviceprincipal-list-owners?view=graph-rest-1.0&tabs=http https://docs.microsoft.com/en-us/graph/api/group-list-members?view=graph-rest-1.0&tabs=http https://docs.microsoft.com/en-us/graph/api/directoryrole-list-members?view=graph-rest-1.0&tabs=http .EXAMPLE Invoke-CKAttackGraph -accessToken $accessToken #> [cmdletbinding()] Param( [parameter(Mandatory = $true)] [String]$gremlinHostname, [parameter(Mandatory = $true)] [String]$cosmosDBRWKey, [parameter(Mandatory = $true)] [String]$cosmosDBName, [parameter(Mandatory = $true)] [String]$cosmosDBGraphName, [parameter(Mandatory = $false)] [String]$partitionKey = 'pk', [parameter(Mandatory = $true)] [String]$accessToken ) write-host "[ACTIVITY] Collecting Azure AD users, applications, service Principals, groups and directory roles" $users = Get-CKAzADUsers -selectFields "id,displayName" -accessToken $accessToken $users | ForEach-Object {Add-Member -InputObject $_ -MemberType NoteProperty -Name 'label' -Value 'user'} $applications = Get-CKAzADApplications -selectFields "id,displayName" -accessToken $accessToken $applications | ForEach-Object {Add-Member -InputObject $_ -MemberType NoteProperty -Name 'label' -Value 'application'} $serviceprincipals = Get-CKAzADServicePrincipals -selectFields "id,displayName" -accessToken $accessToken $serviceprincipals | ForEach-Object {Add-Member -InputObject $_ -MemberType NoteProperty -Name 'label' -Value 'serviceprincipal'} $groups = Get-CKAzADGroups -selectFields "id,displayName" -accessToken $accessToken $groups | ForEach-Object {Add-Member -InputObject $_ -MemberType NoteProperty -Name 'label' -Value 'group'} $directoryroles += Get-CKAzADDirectoryRoles -accessToken $accessToken $directoryroles | ForEach-Object {Add-Member -InputObject $_ -MemberType NoteProperty -Name 'label' -Value 'directoryrole'} # Graph Creation write-host "[ACTIVITY] Defining Graph object" $resourceList = $applications + $serviceprincipals + $groups + $directoryroles $edges = @() foreach ( $resource in $resourceList ) { $metadata = Switch ($resource.label) { 'application' { @{ type = 'applications'; relationship = 'owner_of' } } 'serviceprincipal' { @{ type = 'servicePrincipals'; relationship = 'owner_of' } } 'group' { @{ type = 'groups'; relationship = 'member_of' } } 'directoryrole' { @{ type = 'directoryRoles'; relationship = 'member_of' } } } Write-verbose "[ACTIVITY] >> Listing $($metadata.relationship) $label $($resource.displayName) .." $relationObjects = Switch ($metadata.relationship) { 'owner_of' { Get-CKOwners -resourceType $metadata.type -objectId $resource.id -accessToken $accessToken } 'member_of' { Get-CKMembers -resourceType $metadata.type -objectId $resource.id -accessToken $accessToken } } if ($relationObjects.value){ $edges += @{ objectName = $resource.displayName id = $resource.id relationship = $metadata.relationship relationObjects = $relationObjects } } } $vertices = $users + $resourceList # Add graph to CosmosDB graph write-host "[ACTIVITY] Adding vertices and edges to CosmosDB graph.." Import-Module PoshGremlin write-host "[ACTIVITY] Defining CosmosDB client connection.." $authKey = ConvertTo-SecureString -AsPlainText -Force -String "$cosmosDBRWKey" $gremlinParams = @{ Hostname = $gremlinHostname Credential = New-Object System.Management.Automation.PSCredential "/dbs/$cosmosDBName/colls/$cosmosDBGraphName", $authKey } # Process vertices write-host "[ACTIVITY] Importing vertices.." foreach ($v in $vertices) { $label = $v.label Write-Verbose "[ACTIVITY] >> Creating $label vertex: $($v.displayName) - $($v.id)" "g.V().has('$label','id','$($v.id)').fold().coalesce(unfold(),addV('$label').property('id','$($v.id)').property('displayName','$($v.displayName)').property('$partitionKey', '$partitionKey'))" | Invoke-Gremlin @gremlinParams | Out-Null } Start-Sleep 10 # Process Edges write-host "[ACTIVITY] Importing edges.." foreach ($e in $edges) { foreach ($m in $e.relationObjects) { Write-Verbose "[ACTIVITY] >> Creating Edge: From $($m.displayName) to $($e.objectName)" "g.V('$($m.id)').as('source').V('$($e.id)').as('target').not(__.in('$($e.relationship)').where(eq('source'))).addE('$($e.relationship)').from('source').to('target')" | Invoke-Gremlin @gremlinParams | Out-Null } } } |