Active Directory password change, reset, and policy read over SAMR, Netlogon, LSA, Kerberos, and LDAP.
Getting started • Choosing a method • Method matrix • CLI reference • Documentation
One console command, passwolf, with three subcommands - passwolf change, passwolf reset, and passwolf policy - that implement every documented and undocumented Windows method for changing or resetting an Active Directory account password over SAMR, Netlogon, LSA, Kerberos kpasswd, and LDAP, and for reading the effective password policy. 11 change methods, 6 reset methods, and full policy read across 5 protocols - including the AES SAMR paths that Windows Server 2025 requires and no other public tool implements.
The AD password-change landscape is fragmented across tools that each cover a few methods and skip the rest. PassWolf consolidates every documented (and undocumented) change and reset path into one tool, with the AES methods Server 2025 demands.
| Capability | PassWolf | impacket | bloodyAD | NetExec | rpcclient |
|---|---|---|---|---|---|
| AES SAMR change (Server 2025) | yes | no | no | no | no |
| RC4 / OEM / DES SAMR changes | 4 methods | 1 | no | 1 | 1 |
| Diagnostic change (structured rejection reason) | yes | no | no | no | no |
| Kerberos kpasswd change | yes | yes | no | no | no |
| Kerberos kpasswd set (privileged reset) | yes | no | no | no | no |
| LDAP change + reset (sealed 389, no cert needed) | both | both | reset only | no | no |
| SAMR reset (AES / RC4 / set-hash) | 3 methods | no | no | no | partial |
| Netlogon machine / trust password | AES + DES | no | no | no | no |
| DSRM reset (DC recovery account) | yes | no | no | no | no |
| RAP legacy change (SMB1) | 2 methods | no | no | no | no |
| Pass-the-hash change | yes | yes | no | yes | no |
| Expired / must-change password handling | yes | no | no | no | no |
| Password policy read (domain + PSO + GPO) | yes | no | partial | partial | partial |
| Auto method selection with fallback | yes | no | no | no | no |
| Server 2025 validated | yes | partial | no | no | no |
| JSON / text / rich output | yes | no | no | no | no |
- 17 methods across 5 protocols - every SAMR, Netlogon, Kerberos kpasswd, LDAP, and RAP path that Windows exposes for changing or resetting a password, including the undocumented diagnostic opnum 63
- Server 2025 ready - the AES SAMR change (opnum 73,
SamrUnicodeChangePasswordUser4) and AES reset (UserInternal7) that Server 2025 requires are both implemented and validated live - Strict change vs reset separation - a change proves the current secret and needs no privilege; a reset is a privileged overwrite that bypasses minimum age and history. Two operations, two subcommands, no confusion
- Pass-the-hash - the RC4 and DES SAMR changes accept an NT hash as the old secret, and the set-hash reset writes a hash directly
- Expired password handling - when the target password is expired or flagged must-change-at-next-logon,
passwolf changeretries over a null session and completes the change - Password policy read - reads the domain default policy, any applicable fine-grained PSO, and the configured SYSVOL GPO intent, so you see the constraints before you attempt a change
- Precise error decoding - every NTSTATUS is decoded so a wrong old password, a policy rejection, and a disabled method are distinguishable
- Machine-parseable output -
--format text(greppable),--format json(structured), or--format pretty(rich panel) - LDAP over sealed 389 - the LDAP paths default to a SASL sign-and-seal bind on port 389, so they work without an LDAPS certificate
- Spec-traced - every method is mapped to its Microsoft Open Specification section and validated against live domain controllers (Server 2022 build 20348 and Server 2025 build 26100)
| Method | Protocol / opnum | Auth | Notes |
|---|---|---|---|
samr-aes |
SAMR 73, SamrUnicodeChangePasswordUser4 |
password | AES-256; the only SAMR change Server 2025 accepts |
samr-rc4 |
SAMR 55, SamrUnicodeChangePasswordUser2 |
password, NT hash | legacy RC4; pass-the-hash capable |
samr-oem |
SAMR 54, SamrOemChangePasswordUser2 |
password | OEM/LM RC4; needs a stored LM hash and password ≤14 chars |
samr-des |
SAMR 38, SamrChangePasswordUser |
password, NT hash | DES OWF cross-encryption; can set the new password by NT hash |
samr-diag |
SAMR 63, SamrUnicodeChangePasswordUser3 |
password | undocumented; returns the structured policy rejection reason |
kpasswd |
Kerberos 464, version 0x0001 |
password | RFC 3244 change protocol |
ldap |
LDAP unicodePwd delete + add | password | defaults to sealed 389, no certificate needed |
netlogon-aes |
Netlogon 30, NetrServerPasswordSet2 |
password | machine/trust account, AES over a sealed Netlogon channel |
netlogon-des |
Netlogon 6, NetrServerPasswordSet |
password | machine/trust account, DES OWF; still accepted on Server 2025 |
rap |
RAP 115, NetUserPasswordSet2 over SMB1 |
password | obsolete cleartext LM-only; not NTLM-usable on modern hosts |
rap-oem |
RAP 214, SamOEMChangePasswordUser2 over SMB1 |
password | legacy OEM/LM RC4; works on SMB1 hosts that store an LM hash |
auto is the default. It prefers the strongest SAMR change the DC accepts (AES) and falls back to RC4 only when AES is genuinely unavailable.
| Method | Server 2022 | Server 2025 |
|---|---|---|
samr-aes |
yes | yes |
samr-rc4 |
yes | blocked |
samr-oem |
yes | blocked |
samr-des |
yes | blocked |
samr-diag |
yes | blocked |
kpasswd |
yes | yes |
ldap |
yes | yes |
netlogon-aes |
yes | yes |
netlogon-des |
yes | yes |
Server 2025 blocks the three legacy SAMR changes (RC4, OEM, DES) and the diagnostic opnum as part of its RC4 hardening (CVE-2021-33757 / KB5004605). AES-SAMR, Kerberos, LDAP, and Netlogon remain available.
| Method | Protocol / opnum | What it sets | Notes |
|---|---|---|---|
samr-aes |
SAMR 58 + UserInternal7 | cleartext password | AES-256 reset (the modern path) |
samr-rc4 |
SAMR 58 + UserInternal4InformationNew | cleartext password | legacy RC4 + MD5-salt reset |
samr-hash |
SAMR 37 + UserInternal1 | NT hash directly | full policy bypass (length, complexity, history, minimum age) |
kpasswd |
Kerberos 464, version 0xFF80 |
cleartext password | RFC 3244 set protocol, with target name and realm |
ldap |
LDAP unicodePwd replace | cleartext password | defaults to sealed 389 |
dsrm |
SAMR 66, SamrSetDSRMPassword |
NT hash | DC-local recovery (RID 500) password; selected with --dsrm |
auto walks a cross-method ladder (kpasswd, LDAPS, LDAP, then SAMR AES/RC4/hash) and takes the first that succeeds. Unlike changes, no reset method is blocked on Server 2025.
Reads the effective password and lockout policy so you can see the constraints a change has to satisfy before you attempt one. 10 methods across 4 protocols, each running independently with its reachability recorded - an anonymous run shows exactly which channels leak the policy and which deny it.
| Method | Protocol | Auth required | What it reads |
|---|---|---|---|
samr-query |
SAMR opnum 46 (fallback op 8) | yes | domain default: minimum/maximum age, length, history, complexity, reversible encryption, lockout, force-logoff |
samr-getdompwinfo |
SAMR opnum 56 | no | handle-light: minimum password length + password properties flags |
samr-getusrpwinfo |
SAMR opnum 44 | yes | per-user, PSO-resolved: effective minimum length + properties for --target-user |
samr-diag |
SAMR opnum 63 | yes | change-failure oracle: submits a guaranteed-violating password and reads the structured rejection (PSO-effective policy of the authenticated principal) |
kpasswd |
Kerberos 464 SOFTERROR | yes | change-failure oracle: parses the SOFTERROR policy blob from the KDC (works on Server 2025) |
ldap-domain-head |
LDAP domainDNS attributes | yes | most complete single-shot domain-default read: all policy fields from the domain head object |
ldap-pso |
LDAP msDS-PasswordSettings | yes | enumerates every fine-grained password settings object (PSO) in the Password Settings Container |
ldap-resultant |
LDAP msDS-ResultantPSO | yes | the winning PSO for --target-user, dereferenced to its full policy values |
ldap-uac |
LDAP msDS-User-Account-Control-Computed | yes | live account state: lockout, password expiry, and bad-password count for --target-user |
sysvol |
SMB SYSVOL GptTmpl.inf | yes | configured intent per GPO: the [System Access] settings from the Default Domain Policy's security template, cross-checked against the live values |
all is the default and runs every method. The oracles (samr-diag, kpasswd) report the policy effective for the authenticated principal. The per-user methods (samr-getusrpwinfo, ldap-resultant, ldap-uac) resolve the fine-grained policy for --target-user.
Self-change on a Server 2025 DC (auto selects the AES change):
$ passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--target-old-password 'OldPass1!' --target-new-password 'NewPass1!'
[+] SNOW/jdoe password changed (samr-aes)Install from PyPI:
uv tool install passwolf # recommended
pip install passwolf # or with pipOr install from source:
uv tool install git+https://github.com/StrongWind1/PassWolfOr run from a checkout without installing:
uv run passwolf change --help
uv run passwolf reset --help
uv run passwolf policy --help# Self-change on a Server 2025 DC (auto selects the AES opnum 73)
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--target-old-password 'OldPass1!' --target-new-password 'NewPass1!'
# Pass-the-hash change
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--target-old-hash 47c4cc3a368a4a0fa79a7bf059b7adba --target-new-password 'NewPass1!'
# Set the new password by NT hash (DES change, proves the old secret, no privilege)
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--target-old-password 'OldPass1!' --target-new-hash 47c4cc3a368a4a0fa79a7bf059b7adba
# Change an expired or must-change-at-next-logon password (retries over a null session)
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--target-old-password 'Expired1!' --target-new-password 'NewPass1!'
# Privileged reset, AES cleartext path, as an admin
passwolf reset --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--auth-as-user Administrator --auth-as-password 'Admin1!' \
--target-new-password 'NewPass1!'
# Set the NT hash directly (no Kerberos keys regenerated)
passwolf reset --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--auth-as-user Administrator --auth-as-password 'Admin1!' \
--target-new-hash 47c4cc3a368a4a0fa79a7bf059b7adba
# Rotate a computer account password over the Netlogon secure channel
passwolf change --target-domain SNOW --target-user 'WS01$' --dc dc.snow.lab \
--account machine --target-old-password 'curr3nt' --target-new-password 'n3wer' \
--netbios SNOW
# LDAP change over sealed 389 (works without an LDAPS certificate)
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
--method ldap --target-old-password 'OldPass1!' --target-new-password 'NewPass1!'
# Kerberos bind using the ticket in KRB5CCNAME (no bind password needed)
passwolf change --target-domain SNOW --target-user jdoe --dc dc.snow.lab \
-k --target-old-password 'OldPass1!' --target-new-password 'NewPass1!'
# Read the effective password policy before attempting a change
passwolf policy --target-domain SNOW --dc dc.snow.lab \
--auth-as-user Administrator --auth-as-password 'Admin1!'All three subcommands bind with NTLM by default, or with Kerberos via -k / --kerberos. Under -k the tool uses the ticket cache named by KRB5CCNAME if it holds a usable TGT, otherwise it fetches one from --dc with the supplied password or NT hash; either way the interactive bind-password prompt is skipped.
--format text (default) prints one greppable status line, --format json prints a single JSON object, and --format pretty renders a rich panel. The result always decodes the NTSTATUS precisely so a wrong old password, a policy rejection, and a disabled method are distinguishable.
passwolf change - change a password by proving the current secret (no privilege required)
| Argument | Description |
|---|---|
--target-user NAME |
(required) the account whose password to change |
--target-domain NAME |
(required) the AD domain the account belongs to |
--target-old-password PASS |
the current password; prompted if neither this nor --target-old-hash is given |
--target-old-hash [LM:]NT |
the current NT hash, for a pass-the-hash change |
--target-new-password PASS |
the new password; prompted if neither this nor --target-new-hash is given |
--target-new-hash [LM:]NT |
set the new password by raw NT hash (pins --method samr-des; bypasses policy, drops Kerberos keys) |
--dc HOST |
domain controller hostname or IP (defaults to --target-domain) |
--method METHOD |
auto (default), samr-aes, samr-rc4, samr-oem, samr-des, samr-diag, kpasswd, ldap, netlogon-aes, netlogon-des, rap, rap-oem |
--account {user,machine,trust} |
account kind (default user); machine and trust route to the Netlogon change |
--transport {smb,tcp} |
RPC transport: SMB named pipe (default) or direct TCP |
--netbios NAME |
NetBIOS domain name for machine/trust accounts (derived from --target-domain if omitted) |
--ldaps |
use LDAPS on port 636 instead of sealed LDAP on 389 |
--auth-as-user NAME |
authenticate as a different principal (defaults to the target account) |
--auth-as-password PASS |
that principal's password |
--auth-as-hash [LM:]NT |
that principal's NT hash |
--auth-as-domain NAME |
that principal's domain (defaults to --target-domain) |
-k, --kerberos |
bind with Kerberos instead of NTLM; uses KRB5CCNAME or fetches a TGT |
--format {text,json,pretty} |
output format (default pretty) |
-v, --verbose |
detailed logging |
passwolf reset - reset a password by privileged overwrite (requires reset rights)
| Argument | Description |
|---|---|
--target-user NAME |
(required) the account whose password to reset |
--target-domain NAME |
(required) the AD domain the account belongs to |
--target-new-password PASS |
the new password; prompted if neither this nor --target-new-hash is given |
--target-new-hash [LM:]NT |
set the NT hash directly (written as NT or LM:NT; skips length, complexity, history) |
--dc HOST |
domain controller hostname or IP (defaults to --target-domain) |
--auth-as-user NAME |
(required) the privileged account performing the reset |
--auth-as-password PASS |
that account's password; prompted if neither this nor --auth-as-hash is given |
--auth-as-hash [LM:]NT |
that account's NT hash |
--auth-as-domain NAME |
that account's domain (defaults to --target-domain) |
-k, --kerberos |
bind with Kerberos instead of NTLM |
--method METHOD |
auto (default), samr-aes, samr-rc4, samr-rc4-unsalted, samr-hash, kpasswd, ldap |
--transport {smb,tcp} |
RPC transport (default smb); all SAMR resets are pipe-only |
--ldaps |
use LDAPS on port 636 instead of sealed LDAP on 389 |
--expire / --no-expire |
require (default) or skip a password change at next sign-in |
--dsrm |
reset the DC-local Directory Services Restore Mode recovery password instead |
--reset-info-class CLASS |
(advanced) send an exact USER_INFORMATION_CLASS: internal1, userall, internal4, internal5, internal4new, internal5new, internal7, internal8 |
--reset-opnum {37,58} |
(advanced) pick opnum 37 (SamrSetInformationUser) or 58 (SamrSetInformationUser2, default); only with --reset-info-class |
--format {text,json,pretty} |
output format (default pretty) |
-v, --verbose |
detailed logging |
passwolf policy - read the password policy (mutates nothing)
| Argument | Description |
|---|---|
--target-domain NAME |
(required) the AD domain to read the policy from |
--dc HOST |
domain controller hostname or IP (defaults to --target-domain) |
--auth-as-user NAME |
the account to sign in as (required unless --anonymous) |
--auth-as-password PASS |
that account's password |
--auth-as-hash [LM:]NT |
that account's NT hash |
--auth-as-domain NAME |
that account's domain (defaults to --target-domain) |
-k, --kerberos |
bind with Kerberos instead of NTLM |
--anonymous |
connect with no credentials to see what an unauthenticated user can read |
--target-user NAME |
resolve this account's fine-grained (PSO) effective policy; defaults to the signed-in principal |
--method METHOD |
all (default), samr-query, samr-getdompwinfo, samr-getusrpwinfo, samr-diag, kpasswd, ldap-domain-head, ldap-pso, ldap-resultant, ldap-uac, sysvol |
--transport {smb,tcp} |
RPC transport for the SAMR reads (default smb) |
--ldaps |
use LDAPS on port 636 instead of sealed LDAP on 389 |
--format {text,json,pretty} |
output format (default pretty) |
-v, --verbose |
detailed logging |
git clone https://github.com/StrongWind1/PassWolf.git
cd PassWolf
uv sync # install dev dependencies
make check # run lint + typecheck + tests + docs
make format # auto-fix formattingConventional commit messages (feat:, fix:, docs:); run make check before every commit.
Built on Impacket and PyCryptodome. The AES SAMR change (opnum 73) and the AES cleartext reset info levels (UserInternal7) that impacket does not implement are traced directly to the Microsoft Open Specifications ([MS-SAMR], [MS-NRPC], [MS-LSAD], [MS-ADTS]).
Other projects in this collection:
- AD-SecretGen - derive AD password hashes and Kerberos keys from a password
- NTDSWolf - offline NTDS.dit parser and credential extractor
- CredWolf - Active Directory credential validation
- KerbWolf - Kerberos roasting and hash extraction toolkit
- Kerberos - Kerberos in Active Directory: protocol, security, and attacks
PassWolf is intended for authorized penetration testing, red team engagements, and security audits only. You must have explicit written permission from the system owner before changing or resetting any account secret in an Active Directory environment. Unauthorized access to computer systems is illegal. The authors are not responsible for any misuse or damage caused by this tool.