Files
NetSPI-MicroBurst/REST/Get-AzDomainInfoREST.ps1
2021-05-05 13:36:11 -05:00

385 lines
23 KiB
PowerShell

#Offload nextLink parsing to a separate function to avoid bloat
Function Get-RESTReq {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true,
HelpMessage="The management scoped token")]
[string]$managementToken,
[Parameter(Mandatory=$true)]
[string]$resourceURI
)
$runningList = @()
$list = ((Invoke-WebRequest -Uri $resourceURI -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | ConvertFrom-Json)
$runningList += $list.value
$nextKeys = $list.nextLink
#If we have a nextKeys param, loop until we exhaust it
while($nextKeys -ne $null){
$getNext = ((Invoke-WebRequest -Uri $nextKeys -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).Content | ConvertFrom-Json)
$nextKeys = $getNext.nextLink
$list += $getNext.value
}
$runningList
}
Function Get-AzDomainInfoREST {
[CmdletBinding()]
Param(
[Parameter(Mandatory=$false,
HelpMessage="Subscription ID")]
[string]$SubscriptionId,
[Parameter(Mandatory=$false,
HelpMessage="The management scoped token")]
[string]$managementToken,
[parameter(Mandatory=$false,
HelpMessage="Dump list of Storage Accounts.")]
[ValidateSet("Y","N")]
[String]$StorageAccounts = "Y",
[parameter(Mandatory=$false,
HelpMessage="Dump list of Resources.")]
[ValidateSet("Y","N")]
[String]$Resources = "Y",
[parameter(Mandatory=$false,
HelpMessage="Dump list of Virtual Machines.")]
[ValidateSet("Y","N")]
[String]$VMs = "Y",
[parameter(Mandatory=$false,
HelpMessage="Dump list of Network Information.")]
[ValidateSet("Y","N")]
[String]$NetworkInfo = "Y",
[parameter(Mandatory=$false,
HelpMessage="Dump list of RBAC Users/Roles/etc.")]
[ValidateSet("Y","N")]
[String]$RBAC = "Y"
)
if ($SubscriptionId -eq ''){
# List all subscriptions for a tenant
$subscriptions = ((Invoke-WebRequest -Uri ('https://management.azure.com/subscriptions?api-version=2019-11-01') -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | ConvertFrom-Json).value
# Select which subscriptions to dump info for
$subChoice = $subscriptions | out-gridview -Title "Select One or More Subscriptions" -PassThru
if($subChoice.count -eq 0){Write-Verbose 'No subscriptions selected, exiting'; break}
$SubscriptionId = $subChoice.subscriptionId
}
else{$subChoice = $SubscriptionId; $noLoop = 1}
Write-Verbose "Dumping information for subscription $SubscriptionID"
# Folder Parameter Checking - Creates Az folder to separate from MSOL folder
if ($folder){
if(Test-Path $folder){
if(Test-Path $folder"\Az"){}
else{New-Item -ItemType Directory $folder"\Az"|Out-Null}}
else{New-Item -ItemType Directory $folder|Out-Null ; New-Item -ItemType Directory $folder"\Az"|Out-Null}; $folder = -join ($folder, "\Az")}
else{if(Test-Path Az){}else{New-Item -ItemType Directory Az|Out-Null};$folder= -join ($pwd, "\Az")}
$resourceGroups = ((Invoke-WebRequest -uri (-join ("https://management.azure.com/subscriptions/",$SubscriptionId,"/resourcegroups?api-version=2020-10-01") ) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | ConvertFrom-Json).value
$subName = $subChoice.displayName
#Quirk here: not sure if it's possible to enumerate files via REST
if($StorageAccounts -eq "Y"){
#Get list of storage accounts
Write-Verbose "Getting Storage Accounts..."
$storageACCTs = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,'/providers/Microsoft.Storage/storageAccounts?api-version=2019-06-01'))
# Check Output Path
if(Test-Path $folder"\Files"){}
else{New-Item -ItemType Directory $folder"\Files" | Out-Null}
foreach($account in $storageACCTs){
#Check output path
if(Test-Path $folder"\Files\"$strgName){}
else{New-Item -ItemType Directory $folder"\Files\"$strgName | Out-Null}
$strgName = $account.name
Write-Verbose "`tGetting containers for $strgName storage account"
#Get list of containers and iterate through them
$containers = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com", $account.id,"/blobservices/default/containers?api-version=2021-01-01"))
foreach($container in $containers){
$pathName = "\Files\"+$strgName+"\Blob_Files_"+$container.Name
#Check if the container is public and write it to a file
if($container.properties.publicAccess -eq "Container"){
$containerName = $container.name
$blobUrl = (-join ("https://$strgName.blob.core.windows.net/$containerName/"))
Write-Verbose "`t`t$containerName container is Public"
$blobUrl >> $folder"\Files\ContainersFileUrls.txt"
}
}
#Enumerate through other storage resources
$shares = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com", $account.id,"/fileServices/default/shares?api-version=2021-01-01"))
foreach($share in $shares){
#Write-Output (-join ("Storage account ",$strgName, " has share ", $share.name))
}
$tables = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com", $account.id,"/tableServices/default/tables?api-version=2021-01-01"))
foreach($table in $tables){
#Write-Output (-join ("Storage account ", $strgName, " has share ", $table.name))
}
$queues = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com", $account.id,"/queueServices/default/queues?api-version=2021-01-01"))
foreach($queue in $queues){
#Write-Output (-join ("Storage account ", $strgName, " has queue ", $queue.name))
}
}
$storageCount = $StorageACCTs.count
Write-Verbose "`t$storageCount storage accounts were found."
}
if($Resources -eq "Y"){
if(Test-Path $folder"\Resources"){}
else{New-Item -ItemType Directory $folder"\Resources\" | Out-Null}
Write-Verbose "Getting AzureSQL Resources..."
$sqlServers = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Sql/servers?api-version=2020-08-01-preview"))
Write-Output $sqlServers
$count = $sqlServers.count
Write-Verbose "`t$azureSQLServersCount AzureSQL servers were enumerated."
#Need to loop back to this since I don't have any spun up on my infra
#foreach($server in $sqlServers){
# $databases = (((Invoke-WebRequest -Uri (-join ('https://management.azure.com',$server.id,'/databases?api-version=2020-08-01-preview')) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).Content) | ConvertFrom-Json).value
#}
Write-Verbose "Getting Azure App Services..."
$appServices = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com/subscriptions/",$SubscriptionId,"/providers/Microsoft.Web/sites?api-version=2019-08-01"))
foreach($app in $appServices){
$app.properties | select State,@{name="HostNames";expression={$_.HostNames}},RepositorySiteName,UsageState,Enabled,@{name="EnabledHostNames";expression={$_.EnabledHostNames}},AvailabilityState,@{name="HostNameSslStates";expression={$_.hostNameSslStates}},ServerFarmId,Reserved,LastModifiedTimeUtc,SiteConfig,TrafficManagerHostNames,ScmSiteAlsoStopped,targetSwapSlot,hostingEnvironmentProfile,ClientAffinityEnabled,ClientCertEnabled,HostNamesDisabled,OutboundIpAddresses,PossibleOutboundIpAddresses,ContainerSize,DailyMemoryTimeQuota,SuspendedTill,MaxNumberOfWorkers,CloningInfo,SnapshotInfo,ResourceGroup,IsDefaultContainer,DefaultHostName,SlotSwapStatus,HttpsOnly,Identity,@{name="ID";expression={$app.id}},@{name="Name";expression={$app.name}},@{name="Kind";expression={$app.kind}},@{name="Location";expression={$app.location}},@{name="Type";expression={$app.type}},@{name="Tags";expression={$app.tags}} | Export-Csv -NoTypeInformation -LiteralPath $folder"\Resources\AppServices.CSV"
}
$count = $appServices.count
Write-Verbose "`t$count App Services enumerated"
Write-Verbose "Getting Azure Disks..."
$disks = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com/subscriptions/",$SubscriptionId,"/providers/Microsoft.Compute/disks?api-version=2020-12-01"))
$disks | Export-CSV -NoTypeInformation -LiteralPath $folder"\Resources\Disks.CSV"
$disks | ForEach-Object{if($_.properties.encryption -eq ""){$_.Name | Out-File -LiteralPath $folder"\Resources\Disks-NoEncryption.txt"}}
$diskDT = New-Object System.Data.DataTable("disks")
$columns = @("ResourceGroupName", "TimeCreated", "OsType", "DiskSizeGB", "DiskSizeBytes", "UniqueId", "ProvisioningState", "DiskIOPSReadWrite", "DiskMBpsReadWrite", "DiskState", "Id", "Name", "Location", "Encryption")
foreach($col in $columns){$diskDT.Columns.Add($col) | Out-Null}
foreach($disk in $disks){
$diskProps = $disk.properties
$diskDT.Rows.Add($disk.name, $diskProps.timeCreated, $diskProps.osType, $diskProps.diskSizeGB, $diskProps.diskSizeBytes, $diskProps.uniqueId, $diskProps.provisioningState, $diskProps.diskIOPSReadWrite, $diskProps.diskMBpsReadWrite, $diskProps.diskState, $disk.id, $disk.name, $disk.location, $diskProps.encryption) | Out-Null
}
$diskDT | Export-Csv -NoTypeInformation -LiteralPath $folder"\Resources\Disks.csv"
$count = $disks.count
Write-Verbose "`t$count Disks were enumerated."
#TODO: Deployments
Write-Verbose "Getting Key Vault Policies..."
$keyVaults = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/resources?`$filter=resourceType eq 'Microsoft.KeyVault/vaults'&api-version=2019-09-01"))
foreach($vault in $keyVaults){
$vaultDetails = ((Invoke-WebRequest -Uri (-join ("https://management.azure.com",$vault.id,"?api-version=2019-09-01")) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | convertfrom-json)
$vaultPerms = $vaultDetails.properties.accessPolicies.permissions
#Bit of a hack to expand the objects in our CSV
[pscustomobject]@{
Keys = $vaultPerms.keys -join ','
Secrets = $vaultPerms.secrets -join ','
Certificates = $vaultPerms.certificates -join ','
} | Export-Csv -NoTypeInformation -LiteralPath (-join ($folder,'\Resources\',$vault.name,'-Vault_Policies.csv'))
}
#Write-Verbose "Getting Resource Groups..."
#$resourceGroups = ((Invoke-WebRequest -uri (-join ("https://management.azure.com/subscriptions/",$SubscriptionId,"/resourcegroups?api-version=2020-10-01") ) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | ConvertFrom-Json).value
#foreach($rg in $resourceGroups){
# $rg | select ResourceGroupName,Location,ProvisioningState,ResourceId | Export-CSV -NoTypeInformation -LiteralPath $folder"\Resources\Resource_Groups.CSV"
#}
Write-Verbose "Getting Automation Account Runbooks and Variables..."
$automationAccounts = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com/subscriptions/",$SubscriptionId,"/providers/Microsoft.Automation/automationAccounts?api-version=2015-10-31"))
if($automationAccounts){
if(Test-Path $folder"\Resources\AutomationAccounts"){}
else{New-Item -ItemType Directory $folder"\Resources\AutomationAccounts" | Out-Null}
}
foreach($account in $automationAccounts){
if(Test-Path (-join ($folder,"\Resources\AutomationAccounts\",$account.name))){}
else{New-Item -ItemType Directory (-join ($folder,"\Resources\AutomationAccounts\",$account.name)) | Out-Null}
$runbooks = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com" + $account.id + "/runbooks?api-version=2015-10-31"))
foreach($runbook in $runbooks){
try{
$content = (Invoke-WebRequest -uri (-join ("https://management.azure.com" + $runbook.id + "/content?api-version=2015-10-31") ) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing)
if($content.Headers["Content-Type"] -eq "text/powershell"){
$content.Content | Out-File -FilePath (-join ($folder,"\Resources\AutomationAccounts\",$account.name,"\",$runbook.name,".txt"))
}
}
#We get a 404 on runbooks that haven't been published yet, might change this to just create an empty text file
catch [System.Net.WebException]{
}
}
$variables = Get-RESTReq -managementToken $managementToken -resourceURI (-join ("https://management.azure.com" + $account.id + "/variables?api-version=2015-10-31") )
$variables | Select name, properties | Out-File -FilePath (-join ($folder,"\Resources\AutomationAccounts\",$account.name,"\","Variables.txt")) #-Append
}
$count = $automationAccounts.count
Write-Verbose "`t$count Automation Accounts were enumerated."
}
if($NetworkInfo -eq "Y"){
Write-Verbose "Getting Network Interfaces..."
if(Test-Path $folder"\Interfaces"){}
else{New-Item -ItemType Directory $folder"\Interfaces\" | Out-Null}
$networkInterfaces = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Network/networkInterfaces?api-version=2020-11-01"))
foreach($interface in $networkInterfaces){
$NICName = $interface.name
foreach($prop in $interface.properties){
$prop.ipConfigurations.properties | select PrivateIpAddressVersion,Primary,LoadBalancerBackendAddressPoolsText,LoadBalancerInboundNatRulesText,ApplicationGatewayBackendAddressPoolsText,ApplicationSecurityGroupsText,PrivateIpAddress,PrivateIpAllocationMethod,ProvisioningState,SubnetText,PublicIpAddressText,Name,Etag,Id | Export-Csv -NoTypeInformation -LiteralPath $folder"\Interfaces\"$NICName"-ipConfig.csv"
}
}
Write-Verbose("`tGetting Public IPs for each network interface...")
$publicIpTbl = New-Object System.Data.DataTable
$publicIpTbl.Columns.Add("Name") | Out-Null
$publicIpTbl.Columns.Add("IPAddress") | Out-Null
$publicIpTbl.Columns.Add("PublicIPAllocationMethod") | Out-Null
$publicIpTbl.Columns.Add("ResourceGroup") | Out-Null
$publicIps = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Network/publicIPAddresses?api-version=2020-11-01"))
$publicIpCount = 0
foreach($ip in $publicIps){
if($ip.properties.ipAddress){
$publicIpTbl.Rows.Add($ip.name, $ip.properties.IPAddress, $ip.properties.publicIPAllocationMethod, $ip.id.split('/')[4]) | Out-Null
$publicIpCount += 1
}
}
$publicIpTbl | Export-Csv -NoTypeInformation -LiteralPath $folder"\PublicIPs.csv"
Write-Verbose("`t$publicIPCount Public IP Addresses were found")
Write-Verbose "`tGetting Network Security Groups..."
# Create data table to house results
$RulesTempTbl = New-Object System.Data.DataTable
$RulesTempTbl.Columns.Add("NSGName") | Out-Null
$RulesTempTbl.Columns.Add("ResourceGroupName") | Out-Null
$RulesTempTbl.Columns.Add("Location") | Out-Null
$RulesTempTbl.Columns.Add("RuleName") | Out-Null
$RulesTempTbl.Columns.Add("Protocol") | Out-Null
$RulesTempTbl.Columns.Add("SourcePortRange") | Out-Null
$RulesTempTbl.Columns.Add("DestinationPortRange") | Out-Null
$RulesTempTbl.Columns.Add("SourceAddressPrefix") | Out-Null
$RulesTempTbl.Columns.Add("DestinationAddressPrefix") | Out-Null
$RulesTempTbl.Columns.Add("Access") | Out-Null
$RulesTempTbl.Columns.Add("Priority") | Out-Null
$RulesTempTbl.Columns.Add("Direction") | Out-Null
$networkSecurityGroups = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Network/networkSecurityGroups?api-version=2020-11-01"))
foreach($group in $networkSecurityGroups){
$NSGName = $group.name
$NSGRG = $group.id.split('/')[4]
$NSGLocation = $group.location
foreach($rule in $group.properties.securityRules){
$RulesTempTbl.Rows.Add($NSGName, $NSGRG, $NSGLocation, $rule.name, $rule.properties.protocol, $rule.properties.sourcePortRange -join ' ', $rule.properties.DestinationPortRange -join ' ', $rule.properties.SourceAddressPrefix -join ' ', $rule.properties.DestinationAddressPrefix -join ' ', $rule.properties.Access, $rule.properties.priority, $rule.properties.direction) | Out-Null
}
}
$RulesTempTbl | Export-Csv -NoTypeInformation -LiteralPath $folder"\FirewallRules.csv"
$RulesTempTbl | where Direction -EQ 'Inbound' | where SourceAddressPrefix -eq '*' | where Access -EQ 'Allow' | Export-Csv -NoTypeInformation -LiteralPath $folder"\FirewallRules-AnySourceInboundAllow.csv"
$RulesTempTbl | where Direction -EQ 'Inbound' | where SourceAddressPrefix -eq '*' | where Access -EQ 'Allow' | where DestinationAddressPrefix -EQ '*' | Export-Csv -NoTypeInformation -LiteralPath $folder"\FirewallRules-AnyAnyInboundAllow.csv"
$AnyAnyRules = $RulesTempTbl | where Direction -EQ 'Inbound' | where SourceAddressPrefix -eq '*' | where Access -EQ 'Allow' | where DestinationAddressPrefix -EQ '*'
$AnyRulesCounter = $AnyAnyRules | measure
$AnyRulesCount = $AnyRulesCounter.Count
$RulesCounter = $RulesTempTbl | measure
$RulesCount = $RulesCounter.Count
Write-Verbose "`t$RulesCount Network Security Group Firewall Rules were enumerated."
Write-Verbose "`t`t$AnyRulesCount Inbound 'Any Any' Network Security Group Firewall Rules were enumerated."
}
if($VMs -eq "Y"){
# Create folder for VM Info for cleaner output
if(Test-Path $folder"\VirtualMachines"){}
else{New-Item -ItemType Directory $folder"\VirtualMachines\" | Out-Null}
$virtualMachines = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Compute/virtualMachines?api-version=2020-06-01"))
$vmDT = New-Object System.Data.DataTable("vms")
$columns = @("ResourceGroupName","Name","Location","ProvisioningState")
foreach($col in $columns){$vmDT.Columns.Add($col) | Out-Null}
foreach($vm in $virtualMachines){
$vmDetails = (Invoke-WebRequest -uri (-join ("https://management.azure.com" + $vm.id + "?api-version=2020-06-01") ) -Verbose:$false -Method GET -Headers @{ Authorization ="Bearer $managementToken"} -UseBasicParsing).content | convertfrom-json
$vmDT.Rows.Add($vmDetails.id, $vmDetails.name, $vmDetails.location, $vmDetails.properties.provisioningState) | Out-Null
}
$vmDT | Export-Csv -NoTypeInformation -LiteralPath $folder"\VirtualMachines\VirtualMachines-Basic.csv"
$count = $virtualMachines.count
Write-Verbose "`t$count Virtual Machines enumerated."
}
if($RBAC -eq "Y"){
if(Test-Path $folder"\RBAC"){}
else{New-Item -ItemType Directory $folder"\RBAC\" | Out-Null}
$principalPermissionsTbl = New-Object System.Data.DataTable
$principalPermissionsTbl.Columns.Add("PrincipalID") | Out-Null
$principalPermissionsTbl.Columns.Add("RoleName") | Out-Null
$principalPermissionsTbl.Columns.Add("Scope") | Out-Null
#Can just grab our identity's principal ID from our JWT
$tokenPayload = $managementToken.split('.')[1]
while($tokenPayload.Length % 4){$tokenPayload += "="}
$tokenJson = [System.Text.Encoding]::ASCII.GetString([System.Convert]::FromBase64String($tokenPayload)) | ConvertFrom-Json
$currentPrincipalID = $tokenJson.oid
$roleDefinitions = Get-RESTReq -managementToken $managementToken -resourceURI (-join('https://management.azure.com/subscriptions/',$SubscriptionID,'/providers/Microsoft.Authorization/roleDefinitions?api-version=2015-07-01'))
$ownerGUID = ($roleDefinitions | ForEach-Object{ if ($_.properties.RoleName -eq 'Owner'){$_.name}})
$rbacAssignments = Get-RESTReq -managementToken $managementToken -resourceURI (-join ('https://management.azure.com/subscriptions/',$SubscriptionId,"/providers/Microsoft.Authorization/roleAssignments?api-version=2015-07-01"))
foreach($def in $rbacAssignments.properties){
$roleDefID = $def.roleDefinitionId.split("/")[6]
$roleName = ($roleDefinitions | foreach-object {if ($_.name -eq $roleDefID){$_.properties.RoleName}})
if($roleName){
$principalPermissionsTbl.Rows.Add($def.principalId, $roleName, $def.scope) | out-null
if($def.principalId -eq $currentPrincipalID){
Write-Verbose (-join ("Current identity has permission ", $roleName, " on scope ", $def.scope))
}
else{
#Write-Verbose (-join ("Principal ", $def.principalId, " has permission ", $roleName, " on scope ", $def.scope))
}
}
}
$readers = $principalPermissionsTbl.Select("RoleName = 'Reader'")
if($readers){$readers | Export-CSV -NoTypeInformation -LiteralPath $folder"\RBAC\Readers.csv"}
$contributors = $principalPermissionsTbl.Select("RoleName = 'Contributor'")
if($contributors){$contributors | Export-CSV -NoTypeInformation -LiteralPath $folder"\RBAC\Contributors.csv"}
$owners = $principalPermissionsTbl.Select("RoleName = 'Owner'")
if($owners){$owners | Export-CSV -NoTypeInformation -LiteralPath $folder"\RBAC\Owners.csv"}
$currentIdentityPerms = $principalPermissionsTbl.Select("PrincipalID = '$currentPrincipalID'")
if($currentIdentityPerms){$currentIdentityPerms | Export-CSV -NoTypeInformation -LiteralPath $folder"\RBAC\CurrentIdentity.csv"}
}
}