Correct, spec-compliant Active Directory password change, reset, and policy read over SAMR, Netlogon, LSA, Kerberos, and LDAP — including the AES paths impacket lacks.
Project description
PassWolf
Correct, spec-compliant Active Directory password change, reset, and policy read over SAMR, Netlogon, LSA, Kerberos, and LDAP - including the AES paths impacket lacks.
Getting started • Choosing a method • Method matrix
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 account password or hash over SAMR, Netlogon, LSA, Kerberos kpasswd, and LDAP, and for reading the effective password policy. A Windows Server 2025 domain controller hardens off the legacy RC4 SAMR change opcodes and accepts only the AES SAMR change (SamrUnicodeChangePasswordUser4, opnum 73) in their place; passwolf speaks that AES change, and a good deal more.
Why separate tools
A change and a reset are different operations with different security models, and conflating them is the root of several sharp edges in the bundled tooling. passwolf keeps them apart on purpose:
passwolf changeproves the account's current secret (password or NT hash) and needs no privilege on the target. It is subject to the full domain password policy.passwolf resetis a privileged overwrite that proves nothing about the old secret. It bypasses minimum password age and history and requires a caller with reset rights.passwolf policyreads the effective password and lockout policy (the domain policy, any applicable PSO, and the configured SYSVOL GPO intent) so you can see the constraints a change has to satisfy before you attempt one.
What it covers
Every method below is mapped to its Microsoft Open Specification section and validated against live domain controllers (Server 2022 build 20348 and Server 2025 build 26100).
Change methods (passwolf change)
| Method | Protocol / opnum | Notes |
|---|---|---|
samr-aes |
SAMR 73, SamrUnicodeChangePasswordUser4 |
AES; the only SAMR change Server 2025 accepts |
samr-rc4 |
SAMR 55, SamrUnicodeChangePasswordUser2 |
legacy RC4; pass-the-hash capable |
samr-oem |
SAMR 54, SamrOemChangePasswordUser2 |
OEM/LM RC4; needs a stored LM hash and a password ≤14 chars |
samr-des |
SAMR 38, SamrChangePasswordUser |
DES OWF cross-encryption; needs a user handle; can set the new password by NT hash with --target-new-hash |
samr-diag |
SAMR 63, SamrUnicodeChangePasswordUser3 |
undocumented; returns the structured policy rejection reason |
kpasswd |
Kerberos 464, version 0x0001 |
RFC 3244 change protocol |
ldap |
LDAP unicodePwd delete + add | defaults to sealed 389, no certificate needed |
netlogon-aes |
Netlogon 30, NetrServerPasswordSet2 |
machine/trust, AES NL_TRUST_PASSWORD over a sealed channel |
netlogon-des |
Netlogon 6, NetrServerPasswordSet |
machine/trust, DES OWF; still accepted on Server 2025 |
rap |
RAP opcode 115, NetUserPasswordSet2 over SMB1 |
obsolete cleartext LM-only path; not NTLM-usable on modern hosts |
rap-oem |
RAP opcode 214, SamOEMChangePasswordUser2 over SMB1 |
legacy OEM/LM RC4; works on SMB1 hosts that store an LM hash |
Reset methods (passwolf reset)
| Method | Protocol / opnum | Notes |
|---|---|---|
samr-aes |
SAMR 58 + UserInternal7 | AES cleartext reset (the UserInternal7 info level) |
samr-rc4 |
SAMR 58 + UserInternal4InformationNew | legacy RC4 + MD5-salt cleartext reset |
samr-hash |
SAMR 37 + UserInternal1 | set the NT OWF directly (full policy bypass) |
kpasswd |
Kerberos 464, version 0xFF80 |
RFC 3244 set protocol, with target name and realm |
ldap |
LDAP unicodePwd replace | defaults to sealed 389 |
dsrm (--dsrm) |
SAMR 66, SamrSetDSRMPassword |
the DC-local recovery (RID 500) password; selected with the dedicated --dsrm flag |
auto is the default for both operations. For passwolf change it prefers the strongest SAMR change the DC accepts (AES) and falls back to RC4 only when AES is genuinely unavailable, never merely for compatibility. For passwolf reset it walks a cross-method ladder - kpasswd, LDAPS, LDAP, then the SAMR resets (AES, RC4, RC4-unsalted, set-hash) - and takes the first that succeeds.
On Windows Server 2025, the Kerberos set (kpasswd), the LDAP unicodePwd replace (ldap), and the AES SAMR RPC reset (samr-aes, SamrSetInformationUser2 opnum 58 + UserInternal7) all work - confirmed live against a Server 2025 domain controller. The 2025 RC4 hardening (CVE-2021-33757 / KB5004605) blocks the legacy SAMR changes, not resets, so no passwolf reset method is blocked there.
When the target's password is expired or flagged must-change-at-next-logon, passwolf change retries the SAMR bind over a null session and completes the change. This covers the buffer-based methods (samr-aes, samr-rc4, samr-oem, samr-diag, and auto); the handle-based samr-des cannot use the null-session path.
Authentication
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.
Installation
passwolf is managed with uv.
uv tool install git+https://github.com/StrongWind1/PassWolf
Or run it from a checkout without installing:
uv run passwolf change --help
uv run passwolf reset --help
uv run passwolf policy --help
Examples
# 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 on a change (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!'
Output formats
--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.
What sets it apart
- The AES change (opnum 73) and the AES reset info levels (UserInternal7) are what a Server 2025 DC requires, and both are implemented here.
- The LDAP path defaults to plain 389 with a SASL sign-and-seal bind, so it works without an LDAPS certificate instead of requiring
ldaps://. - The undocumented SAMR opnum 63 returns the structured reason a change was rejected.
- Precise NTSTATUS decoding and a strict change-versus-reset separation.
Limitations and notes
- The reset cleartext info levels use the SMB session key as their content-encryption key, so they require the SMB named-pipe transport, not direct TCP.
- The OEM change and the LDAP change need the cleartext old password (the LM hash and the unicodePwd delete value cannot be formed from an NT hash).
- Netlogon and the DSRM reset address machine/trust and the RID-500 recovery account respectively, not arbitrary users.
Development
make install-dev # uv sync
make check # ruff check + ruff format --check + ty + pytest + docs
make format # ruff format + ruff check --fix
Credits
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]).
Related tools
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
Disclaimer
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.
License
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file passwolf-0.3.0.tar.gz.
File metadata
- Download URL: passwolf-0.3.0.tar.gz
- Upload date:
- Size: 158.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
60a105e48511391716189a95d03e4457db66609feb047dff7becc317215aa3d9
|
|
| MD5 |
67aa23c7e2972082ced7b9c5592376b9
|
|
| BLAKE2b-256 |
dbd83f55905dd0dcc8391f41b943d09c5a8411f3931cdf7d705b51f08191be38
|
Provenance
The following attestation bundles were made for passwolf-0.3.0.tar.gz:
Publisher:
publish.yml on StrongWind1/PassWolf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
passwolf-0.3.0.tar.gz -
Subject digest:
60a105e48511391716189a95d03e4457db66609feb047dff7becc317215aa3d9 - Sigstore transparency entry: 2121629813
- Sigstore integration time:
-
Permalink:
StrongWind1/PassWolf@3f0ff62ec12b2cece119e3bd7ad281530cefedb1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/StrongWind1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3f0ff62ec12b2cece119e3bd7ad281530cefedb1 -
Trigger Event:
release
-
Statement type:
File details
Details for the file passwolf-0.3.0-py3-none-any.whl.
File metadata
- Download URL: passwolf-0.3.0-py3-none-any.whl
- Upload date:
- Size: 91.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17fe20f95c074e59bc50286143997dcf72cbb19d75d4989661b543d6caa9dc76
|
|
| MD5 |
981532077e93f0a951d9cf8593825aac
|
|
| BLAKE2b-256 |
5857b0d5cffc081f84ae3b9dda2c97a3cd6760bbd367ccb1e04f7c68abf1a130
|
Provenance
The following attestation bundles were made for passwolf-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on StrongWind1/PassWolf
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
passwolf-0.3.0-py3-none-any.whl -
Subject digest:
17fe20f95c074e59bc50286143997dcf72cbb19d75d4989661b543d6caa9dc76 - Sigstore transparency entry: 2121629892
- Sigstore integration time:
-
Permalink:
StrongWind1/PassWolf@3f0ff62ec12b2cece119e3bd7ad281530cefedb1 -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/StrongWind1
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@3f0ff62ec12b2cece119e3bd7ad281530cefedb1 -
Trigger Event:
release
-
Statement type: