Office 365 & AAD baseline security one-sliders

Tips

  • The following one-sliders (kinda) work also together:
    • Authentication, authorization and session compartments align with the triangle when you place the inverted triangle to side-by-side with the bottom slide
Click picture for larger version
Click picture for larger version

External references

https://docs.microsoft.com/en-us/office365/enterprise/microsoft-cloud-it-architecture-resources

https://aka.ms/M365GoldenConfig

https://query.prod.cms.rt.microsoft.com/cms/api/am/binary/RE2MHP5

https://gallery.technet.microsoft.com/Cybersecurity-Reference-883fb54c

https://cloudblogs.microsoft.com/microsoftsecure/2019/01/10/best-practices-for-securely-using-microsoft-365-the-cis-microsoft-365-foundations-benchmark-now-available/

You can’t hide things in AAD

If there is single sentence I would emphasize regarding all things AAD, then it would be ”You can’t hide things in AAD” – This sentence is based on the premise, that in order for services and users to work and interact there has to be visibility between objects in the directory.

One of the most common examples is, that sometimes a new hire in a large company directory has been ”hidden” by setting ”msExchHideFromAddressLists” value to true. This done in order to hide the user from Exchange Address List, but that really is the only thing that setting does. It does absolutely nothing to hide the user from AAD, (or from on-prem AD),

Azure AD Directory Members can enumerate lot of stuff in AAD

  1. All other users in the tenant, including guest users
    1. In a large tenant, there might be external users in as non-guest members, they can query all the same stuff than the internal users. For example, they can see who of their competitors are also working at the same customer, and if the customer is using Office 365 Groups, then they might by just looking at group names, descriptions and members deduct some insights of the project
  2. Groups, and group members
  3. Applications and permissions registered for the tenant
  4. All visible user attributes (this mainly only excludes the Authentication attributes)
    1. Mobile Phones
    2. Alternative Email Address
    3. Alias Addresses
  5. And also see occurred changes in all above objects by using the delta listeners in Graph API

Graph API

GraphAPI

Lets start by saying that Graph API is The most powerful invention Microsoft has devised in the last few years. You can already see that many of the Microsoft Client Apps fetch their oAuth2 Access Tokens with resource graph defined as their preferred resource. If you’re AAD or Office Dev this thing is going make you’re life so much easier! For me it represents the greatest thing since sliced bread :)…

But…

This means, that when such powerful API exists, there must be also in-depth understanding of it’s capabilities:

Below is demo of a standard user creating a ”listener for all delta changes” in the directory.

One can only imagine, how much an persistent presence having attacker would benefit on this kind of listener, that lists delta changes such as addition of new groups, phone numbers, title changes, changes in group memberships etc.

 

What standard non-privileged user can get by using delta subscription?

  • All new attribute changes

Attrs.PNG

  • All new groups, and their members, or just group member changes

DeltaObjects3

  • New invited users from other tenants

External.PNG


PoC Script

Description

Query Any Delta Change for directory objects, after you’ve defined what to listen first in the initial request

#User permissions
Any internal user will work, regardless Directory Role = Normal, even unlicensed user account can do this

#ADAL
you must have ADAL library files to run this example

#App parameters and endpoints
Global Powershell Multitenant = ClientID 1b730954-1685-4b74-9bfd-dac224a7b894
redirect URI’s = ”urn:ietf:wg:oauth:2.0:oob”
Endpoint = https://login.windows.net/common/

#Scopes
”scp”: ”AuditLog.Read.All Directory.AccessAsUser.All Directory.ReadWrite.All Group.ReadWrite.All
-When you use Powershell as ClientID you will get these static scopes for all users, regardless of the user permissions (User Permissions are separately then checked against AAD)

#Microsoft documentation
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/user_delta
https://developer.microsoft.com/en-us/graph/docs/api-reference/beta/api/group_delta

  • Read MS docs to understand how delta subscriptions work.
  • You can’t just run the script with F5 in ISE, or execute directly. The script is meant to be used so that you create subscription, and then run another row on the script to get deltas added into existing array
  • I could finesse the script bit more, but for now its used to demonstrate how non-privileged user can use it just like an admin user

 


Add-Type -Path $env:USERPROFILE\AzureADToken\Microsoft.IdentityModel.Clients.ActiveDirectory.2.12.111071459\lib\net45\Microsoft.IdentityModel.Clients.ActiveDirectory.dll 

$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList "https://login.windows.net/common/"
$ThisPromptBehavior = [Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior]::Always
$authResult = $AuthContext.AcquireToken("https://graph.microsoft.com","1b730954-1685-4b74-9bfd-dac224a7b894", "urn:ietf:wg:oauth:2.0:oob", $ThisPromptBehavior)

#URI to use the access token
$InitialURI = "https://graph.microsoft.com/v1.0/users/delta"
$GroupsURI = "https://graph.microsoft.com/beta/groups/delta"

 $Headers = @{
 "Authorization" = ($authResult.AccessTokenType) +" "+ ($authResult.AccessToken)
 }

#Subscribe for changes you're interested user objects
$req = Invoke-RestMethod -Method Get -Uri $uri -UseBasicParsing -Headers $headers -Verbose
#Subscribe for changes you're interested in groups
$greq = Invoke-RestMethod -Method Get -Uri $GroupsURI -UseBasicParsing -Headers $headers -Verbose
#query for all changes, and populate results to array
$ChangeArray = @();$count=$null

do {
$count = $count + $req.value.Count;$count
 $req = Invoke-RestMethod -Method Get -Uri $req.'@odata.nextLink' -UseBasicParsing -Headers $headers -Verbose

 $ChangeArray += $req.value
 $req.value

 Write-Host "Next Link"

    } while ($req.'@odata.nextLink' -ne $null)

do {
$count = $count + $greq.value.Count;$count
 $greq = Invoke-RestMethod -Method Get -Uri $greq.'@odata.nextLink' -UseBasicParsing -Headers $headers -Verbose

 $ChangeArray += $greq.value
 $greq.value

 Write-Host "Next Link"

    } while ($greq.'@odata.nextLink' -ne $null)

Br Joosua!

Demonstration – Illicit consent grant attack in Azure AD / Office 365

Disclaimer: All information contained within this post is common knowledge, given you grasp basic concepts of AAD default behavior,  OAuth2 Authorizations and how Javascript works in browsers.


 

How does it work?

Attacker creates multi-tenant app, and adds non-admin requiring API permissions for the desired API’s.  Attacker then delivers the link to end user, which after clicking the link only has to consent (Approve) the API permissions for the attacker.

Attacker has then access to user mail, this happens because attacker exfiltrates in the background the Access Token and uses it against the Exchange Online API

Down below I describe why it might be hard for the user to discern good app from the bad app.

Why it works?

Multi-tenants app by default have access to end users data, unless your Azure AD Admin have disabled the particular defaults that enable this behavior.

 

Exfil.jpg

Graph API + Other API’s

PoC

  • Create multi-tenant Azure AD Web-App/API + Azure Web App that delivers the JS. client side code to browser from Node.JS Web App
  • I am using minified ADAL.JS as <script src in the code. Other than that, I’ve just added few custom functions to the code, and studied wide variety of existing Vanilla ADAL JS Apps in GitHub
  • Figure out a way to deliver the link to the victim/victims in other AAD / Office 365 tenants
  • I’ve opted to forge existing Microsoft Newsletter with link to malicious Azure Web App to
  • When user clicks the link consent dialog is prompted.

JSA.PNG

  • Consent – This as any other dialog, you would get prompted for when consent framework kicks in (This is where the confidence part happens)

Newsletter.PNG

The process is same for benign apps, so there is superficially nothing to separate the good app from the bad (except caution, which is often unheeded, if you consider how often an mobile application presenting similar dialog is approved for multiple permissions in mobile phone)

  • User is redirected to the malicious app with seemingly benign OIDC-Flow (response_type=id_token)… no mention here of the OAuth2 Implicit Grant Flow (yet)

Redir

  • Hidden iFrame executes the OAuth2 Implicit Grant Flow, and gains Access Token to resource

Tokens

  • AJAX HTTP is used to post the Access Token as Payload to external exfil service
    • In case you wonder, that I’d left the function key there :)… Just try

JAX

  • User is redirected to the original link requested in the mail

OH.PNG

<- Users timeline stops here


 

– >Perpetrators timeline begins here

  • Token is copied by the malicious actor from Azure Functions where it was posted

Exfil

JWTIO

  • A Separate Powershell session running in some dark corner of the world is now searching users mail content for several keywords, and playing with the Access Token for the next 3600 seconds

Passwords

 

See it live:

Evil side:

User side

 

 

 

Microsoft References

https://docs.microsoft.com/en-us/office365/securitycompliance/detect-and-remediate-illicit-consent-grants 

https://blogs.technet.microsoft.com/office365security/defending-against-illicit-consent-grants/

Microsoft has issued multiple items in Secure Score controls about the scenario

Stay tuned for PT2 (How to defend against it)

Don’t try this at home (Or configuring AD FS against Azure AD Domain Services)

 

Fair warning: While I have disclaimer in the bottom of the page, and blog title basically emphasizes it… Do not try this in production unless you’re in very comfortable terms with Azure AD and Active Directory in general.


Background

Azure AD Domain Services is Azure Managed version of Active Directory – Basically in exchange for your domain admin credentials, you get two managed endpoints to direct your resources at.

When to use it?

Example: You don’t want to extend your on premises network to Azure, but you still want to offer LDAP & Kerberos to your services deployed in the cloud.

More at FAQ

And more

 

The curious case of AD FS and AAD DS 

Every now and then I’ve wondered whether its possible provide these endpoints to VM running Active Directory Federation Services -> Based on my tests it is possible with some limitations

Good to know before proceeding

  • There exists no officially supported scenario of deploying AD FS, where you won’t need Domain Admin credentials to install AD FS as a member server to the target forest. 
  • Options 1 & 2 require Domain Admin
    • Option 1:
      • You can let the wizard create the objects given you’re armed with domain admin privileges
    • Option 2
      • Or ask for domain admin to pre-create the required objects, and then install AD FS with -AdminConfig switch, where you detail the pre-configured container for AD FS 

    • Option 3
      • By combining the two following guides, you can get AD FS working on AAD DS:
        • Guide 1: ”Create an Organizational Unit (OU) on an Azure AD Domain Services managed domain”
        • Guide 2:  Starting with AD FS in Windows Server 2016, you can run the cmdlet Install-AdfsFarm as a local administrator on your federation server, provided your Domain Administrator has prepared Active Directory

From Guide 1

  • Create an OU, where you create the container for DKM

AADDS2

$initialPath = ”CN=fscon,OU=NotInLocalDomain,DC=azure4d,DC=onmicrosoft,DC=com”

From Guide 2

  • Modify the original script to accumulate the new OU ($InitialPath) – Or alternatively bind new parameters to pass the $InitialPath
# THIS IS NOT THE FULL SCRIPT- original @ https://docs.microsoft.com/fi-fi/windows-server/identity/ad-fs/deployment/Install-AD-FS-Delegated-Admin

# The OU Name is a randomly generated Guid
[string]$guid = [Guid]::NewGuid()
write-host ("OU Name" + $guid)

$ouName = $guid
$initialPath = "CN=fscon,OU=NotInLocalDomain,DC=azure4d,DC=onmicrosoft,DC=com"

Install AD FS

  • Create certificates before the install.
    • If you don’t create certs before the install, the install will fail (in theory it should use the same path for CERTS, but in my tests it didn’t. I might try again at some point, if there is valid case for such install, but for now I am satisfied with creating the certificates before hand)
    • In this case I used the public cert for all three certificates.

$adminConfig = .\NewOU.ps1 -AcctToAclDkmContainer "AZURE4D\fsacc"

Install-AdfsFarm -FederationServiceName $fsname `
-CertificateThumbprint $thubmprint -FederationServiceDisplayName $fsname -ServiceAccountCredential $svcaccount -OverwriteConfiguration -Credential $credentials -AdminConfiguration $adminConfig -SigningCertificateThumbprint $thubmprint -DecryptionCertificateThumbprint $thubmprint

AADDS

Install AD FS Farm with modified script

 

Limitations

Any service where AD FS has to write data into synced object, such as RegisteredDevices OU, or similar functionality won’t be in the scope of this installation. In this form AD FS is only able to authenticate, and authorize accounts.

See it in action

  • Synced on-prem user logging into Azure VM, and using integrated auth to login into Claims Xray (adfshelp.com)

 

Br, Joosua!