Giter Club home page Giter Club logo

domainpasswordspray's People

Contributors

dafthack avatar egypt avatar fullmetalcache avatar iisresetme avatar nidem avatar rileyzink avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

domainpasswordspray's Issues

Users with badPwdCount = $null are excluded by default

This causes users that have badPwdCount = $null to be excluded from the password spray.
Is there a reason for this or just a mistake?

Value could for example be $null if:

  1. User never logged on
  2. User never typed password wrong
  3. We have 'deny read' on the attribute

The last one seems a bit dangerous to ignore.

Fix would be to add else statement, where $attemptsuntillockout is still checked but $observation_window is ignored.

No issue - lots of code changes

@dafthack Hey Beau,

No code issue, just getting in touch. I did a rewrite of DomainPasswordSpray, heavily borrowing from your codebase: https://github.com/mdavis332/DomainPasswordSpray

I changed many things including modularizing the code, having it accept arrays of strings, having it output objects, and having it process usernames in parallel to reduce runtime - overall making it feel more "PowerShell-ey" (I think).

The changes are so sweeping, I didn't want to be presumptive on your repo and make a pull request. Wanted to get your feelings on whether you preferred it stay a separate fork, and also get your blessing before I publicized the code changes on Twitter or did any presentations on it.

Thanks in advance for your time,
Michael

Some logic errors in the lockout thresholds handling

There seems to be some errors in the handling of account lockout thresholds.

  • First, the variable $SmallestLockoutThreshold is defined as the minimum value of all account lockout threshold (from the domain policy and other fine-grained password policies), as follows :

[int]$SmallestLockoutThreshold = $AccountLockoutThresholds | sort | Select -First 1

For each of these policies, a 0 value means that the lockout policy is disabled. However, if the lockout policy is disabled for a specific perimeter but not all accounts, the "minimum" value computed above is wrong (0), since it is hinted after that this value means no lockout policy for all accounts :

https://github.com/dafthack/DomainPasswordSpray/blob/94cb72506b9e2768196c8b6a4b7af63cebc47d88/DomainPasswordSpray.ps1#L385:L388

  • As a consequence, some accounts are not tested password spraying on a domain where there is no lockout policy (on at least one perimeter). Indeed, the way the $attemptsuntillockout variable is computed does not take the $SmallestLockoutThreshold=0 case into account :
    $attemptsuntillockout = $SmallestLockoutThreshold - $userbadcount

This value will never be greater than 0 (thus than 1), so the second part of the condition tested to add a specific user to the list of targets will never be True :

if (($timedifference -gt $observation_window) -or ($attemptsuntillockout -gt 1))

So, every account with a bad password attempt made in the last $observation_window minutes is skipped.

To solve all these problems, I suggest you add some arbitrary high value in to the $AccountLockoutThresholds list instead of 0 when $objDeDomain.Properties.lockoutthreshold or $PSOLockoutThreshold is equal to 0.
Here :

$AccountLockoutThresholds += $objDeDomain.Properties.lockoutthreshold

And here :
$AccountLockoutThresholds += $PSOLockoutThreshold

Script fails while using --verbose

] Using .\north.users.txt as userlist to spray with
[
] Warning: Users will not be checked for lockout threshold.
Cannot index into a null array.
At C:\ad\tools\DomainPasswordSpray.ps1:569 char:5

  • $lockObservationWindow_attr = $DomainEntry.Properties['lockoutObs ...
    
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (:) [], RuntimeException
    • FullyQualifiedErrorId : NullArray

Method invocation failed because [System.String] does not contain a method named 'ConvertLargeIntegerToInt64'.
At C:\ad\tools\DomainPasswordSpray.ps1:570 char:5

  • $observation_window = $DomainEntry.ConvertLargeIntegerToInt64($lo ...
    
  • ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    • CategoryInfo : InvalidOperation: (:) [], RuntimeException
    • FullyQualifiedErrorId : MethodNotFound

[] The domain password policy observation window is set to minutes.
[
] Setting a minute wait in between sprays.
[] Password spraying has begun with 1 passwords
[
] This might take a while depending on the total number of users
[] Now trying password --verbose against 15 users. Current time is 12:34 AM
[
] Password spraying is complete
[*] Any passwords that were successfully sprayed have been output to ./north-sprayed-creds.txt

I also had to change a line with write-host $message: to $message :

Variable reference is not valid

PS C:\Tools> Import-Module .\DomainPasswordSpray\DomainPasswordSpray.ps1

At C:\Tools\DomainPasswordSpray\DomainPasswordSpray.ps1:261 char:21
+         Write-Host "$Message: Waiting for $($Seconds/60) minutes. $($ ...
+                     ~~~~~~~~~
Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to delimit the
name.
    + CategoryInfo          : ParserError: (:) [], ParseException
    + FullyQualifiedErrorId : InvalidVariableReferenceWithDrive

Write-Host "$Message: Waiting for $($Seconds/60) minutes. $($Seconds - $Count)"

Fix:

Change line 261 to

Write-Host "${Message}: Waiting for $($Seconds/60) minutes. $($Seconds - $Count)"

Variable reference is not valid

Update - If you don't need it, you can just delete or comment out the function timer and the script works fine.

After a recent update the script fails with the following error

Iex : At line:261 char:21

  •     Write-Host "$Message: Waiting for $($Seconds/60) minutes. $($ ...
    
  •                 ~~~~~~~~~
    

Variable reference is not valid. ':' was not followed by a valid variable name character. Consider using ${} to
delimit the name.
At line:1 char:1

  • Iex (new-object net.webclient).downloadstring('https://raw.githubuser ...
  •   + CategoryInfo          : ParserError: (:) [Invoke-Expression], ParseException
      + FullyQualifiedErrorId : InvalidVariableReferenceWithDrive,Microsoft.PowerShell.Commands.InvokeExpressionCommand
    

Divide by zero error when calculating delay

As a note here, I didn't set a -Delay value, because it previously defaulted to 30 minutes, which was acceptable. It looks like that default is still there, if I'm reading the code correctly. However, when running the script, I received a divide by zero error and no delay. The error message is reproduced below, please let me know if there is any other information I can provide.

I just noticed a "ConvertLargeIntegerToInt64" error prior to this that may be part of the problem. I missed it earlier because I thought it was another "Cannot index into null array" error. I also received a null array error for line 347 (in addition to the two for 538 and 539) when running Get-DomainUserList.

PS C:\Users\<snip>\Desktop\DomainPasswordSpray> Invoke-DomainPasswordSpray -UserList userlist.txt -Domain <snip> -PasswordList passlist.txt -OutFile sprayed-creds.txt
[*] Using userlist.txt as userlist to spray with
[*] Warning: Users will not be checked for lockout threshold.
[*] WARNING - Be very careful not to lock out accounts with the password list option!
Cannot index into a null array.
At C:\Users\<snip>\Desktop\DomainPasswordSpray\DomainPasswordSpray.ps1:538 char:5
+     $lockObservationWindow_attr = $DomainEntry.Properties['lockoutObs ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : NullArray

Method invocation failed because [System.String] does not contain a method named 'ConvertLargeIntegerToInt64'.
At C:\Users\<snip>\Desktop\DomainPasswordSpray\DomainPasswordSpray.ps1:539 char:5
+     $observation_window = $DomainEntry.ConvertLargeIntegerToInt64($lo ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : MethodNotFound

[*] The domain password policy observation window is set to  minutes.
[*] Setting a  minute wait in between sprays.

Confirm Password Spray
Are you sure you want to perform a password spray against <snip> accounts?
[Y] Yes  [N] No  [?] Help (default is "Y"): y
[*] Password spraying has begun with  27  passwords
[*] This might take a while depending on the total number of users
[*] Now trying password <snip> against <snip> users. Current time is 10:41 AM
[*] Writing successes to sprayed-creds.txt
[*] SUCCESS! User:<snip> Password:<snip>
Attempted to divide by zero.
At C:\Users\<snip>\Desktop\DomainPasswordSpray\DomainPasswordSpray.ps1:245 char:96
+ ... atus "Waiting for $($Seconds/60) minutes. $($Seconds - $Count) second ...
+                                                 ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

Attempted to divide by zero.
At C:\Users\<snip>\Desktop\DomainPasswordSpray\DomainPasswordSpray.ps1:245 char:96
+ ... atus "Waiting for $($Seconds/60) minutes. $($Seconds - $Count) second ...
+                                                 ~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (:) [], RuntimeException
    + FullyQualifiedErrorId : RuntimeException

[*] Now trying password <snip>! against <snip> users. Current time is 11:06 AM
[*] Writing successes to sprayed-creds.txt

PS C:\Users\<snip>\Desktop\DomainPasswordSpray> ^C
PS C:\Users\<snip>\Desktop\DomainPasswordSpray> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      5.1.17763.1490
PSEdition                      Desktop
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
BuildVersion                   10.0.17763.1490
CLRVersion                     4.0.30319.42000
WSManStackVersion              3.0
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1

Edit: Updated to contain more information

Delay and Jitter

seem to be calculated using random values. Setting a given minimum and maximum delay can be useful, for example:

`
function Get-CalculatedDelay
{
param(
[int] $MinDelay,
[int] $MaxDelay,
[int] $JitterPercentage
)

$jitterFactor = (Get-Random -Minimum (-$JitterPercentage) -Maximum $JitterPercentage) / 100.0
$baseDelay = Get-Random -Minimum $MinDelay -Maximum $MaxDelay
$delay = [math]::Round($baseDelay * (1 + $jitterFactor))

return $delay

}

`

Passwords containing spaces don't work in password file

The title is a presumption of what the issue is based on my results below. It appears that when you have a password file, and a password within that file contains spaces, it does not return proper results (false negative).

In this sample, I'm using a PasswordList containing a single line, "a soapsud Touts the climber3". Note that there is no success message.

PS C:\Users\USER\Desktop\DomainPasswordSpray-master> Invoke-DomainPasswordSpray -UserList C:\Users\USER\Desktop\single_user.txt -PasswordList C:\Users\USER\Desktop\single_pass.txt -OutFile C:\Users\USER\Desktop\results.txt -Debug
[*] Using C:\Users\USER\Desktop\single_user.txt as userlist to spray with
[*] Warning: Users will not be checked for lockout threshold.
[*] The domain password policy observation window is set to 30 minutes.
[*] Setting a 30 minute wait in between sprays.

Confirm Password Spray
Are you sure you want to perform a password spray against 1 accounts?
[Y] Yes  [N] No  [?] Help (default is "Y"): y
[*] Password spraying has begun with  1  passwords
[*] This might take a while depending on the total number of users
[*] Now trying password " against 1 users. Current time is 11:36 AM
[*] Writing successes to C:\Users\USER\Desktop\results.txt
[*] Password spraying is complete
[*] Any passwords that were successfully sprayed have been output to C:\Users\USER\Desktop\results.txt

In this sample, I've tried the same user and same password, but now I'm using the password in-line and enclosed in quotes. This time there is a success message.

PS C:\Users\USER\Desktop\DomainPasswordSpray-master> Invoke-DomainPasswordSpray -UserList C:\Users\USER\Desktop\single_user.txt -Password "a soapsud Touts the climber3" -OutFile C:\Users\USER\Desktop\results.txt
[*] Using C:\Users\USER\Desktop\single_user.txt as userlist to spray with
[*] Warning: Users will not be checked for lockout threshold.
[*] The domain password policy observation window is set to 30 minutes.
[*] Setting a 30 minute wait in between sprays.

Confirm Password Spray
Are you sure you want to perform a password spray against 1 accounts?
[Y] Yes  [N] No  [?] Help (default is "Y"): y
[*] Password spraying has begun with  1  passwords
[*] This might take a while depending on the total number of users
[*] Now trying password a soapsud Touts the climber3 against 1 users. Current time is 11:37 AM
[*] Writing successes to C:\Users\USER\Desktop\results.txt
[*] SUCCESS! User:USER Password:a soapsud Touts the climber3
[*] Password spraying is complete
[*] Any passwords that were successfully sprayed have been output to C:\Users\USER\Desktop\results.txt

Running in a loop with multiple domains

Hello,

I noticed when running in a loop with multiple domains it will fail. I solved this by commenting out the lines below as they will change the context from the provided $domain variable to actually pulling the current domain in the context of the powershell.exe window.

$DirEntry = New-Object System.DirectoryServices.DirectoryEntry
$UserSearcher.SearchRoot = $DirEntry

Function Get-ObservationWindow get troubles on -verbose

Errors when using -Verbose on lines 565/572

Powershell advices -
Error: Cannot index into a null array.
Error in method invocation because [System.String] does not contain a method named 'ConvertLargeIntegerToInt64'.

PosibleFix:
Change the function Get-ObservationWindow on lines 565/572 to

function Get-ObservationWindow($DomainEntry)
{
    # Check if $DomainEntry is null
    if ($null -eq $DomainEntry) {
        Write-Host "Error: DomainEntry is null."
        return $null
    }

    # Check if 'Properties' exists and is not null
    if ($null -eq $DomainEntry.Properties) {
        Write-Host "Error: Properties is null."
        return $null
    }

    # Check if 'lockoutObservationWindow' exists in the object's properties
    if ($DomainEntry.Properties.ContainsKey('lockoutObservationWindow')) {
        # Get the value of the 'lockoutObservationWindow' attribute
        $lockObservationWindow_attr = $DomainEntry.Properties['lockoutObservationWindow']

        # Check if the attribute has an assigned value
        if ($null -ne $lockObservationWindow_attr -and $lockObservationWindow_attr.Count -gt 0) {
            # Convert the value from LargeInteger to TimeSpan
            $observation_window = [System.TimeSpan]::FromTicks($lockObservationWindow_attr.Value)

            # Return the observation window in minutes
            return $observation_window.TotalMinutes
        } else {
            Write-Host "lockoutObservationWindow attribute has no value."
            return $null
        }
    } else {
        Write-Host "lockoutObservationWindow attribute not found."
        return $null
    }
}

I am not sure if it modifies much the behavior of the original function but it seems to work.
Feel free to modify it and comment

Also on line 265 be sure you´ve changed the parameter $Message to ${Message}

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.