While these days its getting harder to block yourself from Azure ad via Conditional Access misconfiguration its still fairly easy to do it 🙂
I wanted to explore a way to create account, which in the first place cant be included in Conditional Access. This kind of Account would be an Application/SPN which signs in without delegated user context.
- This stuff is still experimental testing, below is the best practice

- At the moment, you cant monitor the spn’s, but anyone who has taken a peek to current azure ad sign-in logs export feature can guess that this feature is coming
- I also wanted to ensure, that this is native passwordless account, we are in luck as SPN with Certificate can be just like that 🙂
- Please note SPN’s could also be some where along the future included in CA’s :)…
Demo

- Create Azure AD Application, and assign it permissions
- Run the NodeJS sample
- If there is any interest I will include the dependency list, for now the depedencies are only shown in code
Poc Run
- This one is assigned with very heavy permissions, to be able to recover tenant from an utter disaster. It can also create an utter disaster (never run these demos in production tenant)


Create SPN with Powershell
$Subject = "CN=" + "BreakGlassAccountSecureCloud" $Expiration = (get-date).AddYears(2) $pass = Read-Host -Prompt "PFX exporting Password" $cert = New-SelfSignedCertificate -CertStoreLocation "Cert:\CurrentUser\My" -Subject $Subject -KeySpec KeyExchange -NotAfter $Expiration $AADKeyValue = [System.Convert]::ToBase64String($cert.GetRawCertData()) $cert| Export-PfxCertificate -FilePath (($Subject -split "=")[1] + ".pfx") -Password ($pass | ConvertTo-SecureString -AsPlainText -Force) #Navigate to directory where OpenSSL is installed $certnfile = (($Subject -split "=")[1] + ".pem") openssl.exe pkcs12 -in (($Subject -split "=")[1] + ".pfx") -passin "pass:$pass" -out $certnfile -nodes $data = get-content $certnfile $data[$data.IndexOf("-----BEGIN PRIVATE KEY-----")..$data.IndexOf("-----END PRIVATE KEY-----")] | Out-File $certnfile-encoding "DEFAULT" $naming = ($Subject -split "=")[1] connect-azuread $application = New-AzureADApplication -DisplayName $naming -IdentifierUris ("https://" + $naming + "recover.dewi.red") $sp = New-AzureADServicePrincipal -AppId $application.AppId Add-AzureADDirectoryRoleMember -ObjectId (Get-AzureADDirectoryRole | where-object {$_.DisplayName -eq "Company Administrator"}).Objectid -RefObjectId $sp.ObjectId New-AzureADApplicationKeyCredential -ObjectId $application.ObjectId -CustomKeyIdentifier ($Subject -split "=")[1] -Type AsymmetricX509Cert -Usage Verify -Value $AADKeyValue -EndDate ($Expiration | get-date -format "dd.M.yyyy" ) @{ "thumbprint" = $cert.Thumbprint "subject" = $Subject "appid" = $application.appId "tenantinfo" = (Get-AzureADTenantDetail).objectID "CertificatePath" = $certnfile } | ConvertTo-Json -Depth 4 | Out-File .\nodeconfig.JSON -Force -Encoding utf8
- Assign the following permissions

NodeJS code example

- Code will show the targeted policy and change its state to reporting only


Node.JS code with references to the certificate credentials flow
//https://github.com/pnp/office365-cli/blob/dev/docs/manual/docs/user-guide/connecting-office-365.md //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow //https://docs.microsoft.com/en-us/azure/cosmos-db/certificate-based-authentication //https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-certificate-credentials //https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow#second-case-access-token-request-with-a-certificate const fs = require('fs') var config = require('./nodeconfig.json') var chalk = require('chalk') var request = require('request') console.log(config) console.log(chalk.yellowBright(fs.readFileSync('./loading.txt').toString())) var {decode} = require('jsonwebtoken'); //Creds var {appid,tenantinfo,thumbprint,CertificatePath} =config console.log(appid) const {AuthenticationContext} = require('adal-node') var authorityHostUrl = 'https://login.windows.net' var tenant = tenantinfo; var authorityUrl = authorityHostUrl + '/' + tenant var resource = 'https://graph.microsoft.com' var context = new AuthenticationContext(authorityUrl) //Read Certificate into key var ks = fs.readFileSync(CertificatePath).toString('UTF8') //console.log(ks) context.acquireTokenWithClientCertificate(resource,appid,ks,thumbprint,(error,tokenresponse) => { //console.log(error,tokenresponse) console.log(decode(tokenresponse.accessToken)) var kvOpt = { json:true, uri:"https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies", headers:{ "Authorization": "Bearer " + tokenresponse.accessToken } } console.log(kvOpt) request.get(kvOpt,(error,response2) => { //Remove [1] to show all policies console.log(response2.body.value[1]) //this is the targeted policy var patchob = response2.body.value[1].id //turn to reportOnly kvOpt.method = "patch" kvOpt.uri = "https://graph.microsoft.com/v1.0/identity/conditionalAccess/policies/" + patchob console.log(kvOpt) kvOpt.patch = "Enabled" kvOpt.headers['content-type'] = "application/json" kvOpt.json = { state:"enabledForReportingButNotEnforced" } request(kvOpt, (error,response3) => console.log(response3.statusCode, response3.statusMessage)) }) })
- Successful run will turn the selected array.item (policy) state to reportOnly


- If there is interest I will share NodeJS Express App, that automates the steps above. This is just POC:ng it!
Errors
- If you have insufficient permissions when calling graph api, you might see any of the following messages
error: { code: 'AccessDenied', message: 'You cannot perform the requested operation, required scopes are missing in the token.', innerError: { date: '2020-08-16T07:04:45', 'request-id': '2e557a9f-4ef0-4ab4-9e1c-7be3159f8808' } } error: { code: 'AccessDenied', message: 'Insufficient privileges to create or update policy. Application.Read.All scope is required to add/edit application condition.', innerError: { date: '2020-08-16T07:18:45', 'request-id': '747a0f8c-fef2-4ca6-a0d6-ac7418ac350f' }
0 comments on “Alternative take on Azure AD ‘Break Glass’ account”