StrongWind1/AD-SecretGen
GitHub: StrongWind1/AD-SecretGen
AD-SecretGen 是一个从明文密码确定性推导 Active Directory 密码哈希和 Kerberos 密钥的参考工具,为 NTDSWolf 的逆操作。
Stars: 0 | Forks: 0
AD-SecretGen
从密码推导出 AD 密码哈希和 Kerberos 密钥。
文档 •
安装说明 •
CLI 参考
`ad-secretgen` 接收一个**密码**(明文或原始密码 blob)以及一个**账户身份**,并计算出 Active Directory 为该账户**在 `NTDS.dit` 中以 PEK 加密存储**的所有派生自密码的密钥——与域控逐字节存储的内容完全一致。
它是 **NTDSWolf 的逆操作**:NTDSWolf 从数据库中*解密*密钥,而本工具则是从密码中*推导*出相同的密钥。它是一个确定性的参考 oracle,通过三条独立路径(NTDSWolf 读取 `.dit`、DSInternals `Get-ADRePLAccount` 通过网络传输,以及 RFC3961/3962 + MS 已知测试向量)与真实 AD 进行逐字节交叉验证。
它是一个*哈希工具*,而不是解密器:它从不涉及 bootkey/PEK 链、基于 KDS 的 gMSA 推导、LAPS 或 BitLocker——那些是提取/解密的工作(NTDSWolf 的职责)。
## 功能
- 计算 AD 以 PEK 加密存储的所有密码派生密钥:NT、LM、RC4-HMAC、Kerberos DES / AES128 / AES256,以及全部 29 个 WDigest 哈希。
- NTDSWolf 的逆操作——从密码中*推导*密钥,而不是从 `.dit` 中*解密*它们。
- 确定性的参考 oracle,经过与真实 AD 的逐字节验证(NTDSWolf、DSInternals 以及 RFC3961/3962 + MS 已知测试向量)。
- 单个独立的 PEP 723 文件——使用 `uv` 直接通过 URL 运行,无需 clone 或安装任何东西。
- 处理用户、计算机、gMSA / dMSA 和信任账户,每种账户都应用正确的 Kerberos salt 规则。
- 支持 text、JSON 和丰富的 `pretty` 输出格式。
## 计算内容
| 输出 | 算法 | 规范 |
|--------|-----------|------|
| **NT hash** (NTOWFv1) | `MD4(UTF-16LE(password))` | [MS-NLMP] §3.3.1 |
| **LM hash** (LMOWFv1) | 对大写的 OEM-14 密码执行 `DES("KGS!@#$%")` | [MS-NLMP] §3.3.1 |
| **RC4-HMAC** (etype 23) | = NT hash | [RFC4757] |
| **Kerberos DES** (etype 1 = 3) | `mit_des_string_to_key` | [RFC3961] §6.2 |
| **Kerberos AES128 / AES256** (etype 17 / 18) | `DK(PBKDF2-HMAC-SHA1, "kerberos")`,4096 次迭代 | [RFC3962] §4 |
| **WDigest** (29 x MD5) | 对 29 种身份组合执行 `MD5("name:realm:password")` | [MS-SAMR] §3.1.1.8.11.3.1 |
## 示例
```
$ ad-secretgen --password 'P@ssw0rd!' --user alice --realm corp.local --netbios CORP
[meta]
account-type : user
realm : CORP.LOCAL
Kerberos salt: CORP.LOCALalice
[ntlm]
nt : <32-hex NT hash>
lm : <32-hex LM hash>
[kerberos]
aes256-cts-hmac-sha1-96 (18) : <64 hex>
aes128-cts-hmac-sha1-96 (17) : <32 hex>
rc4-hmac (23) :
[wdigest]
wdigest[01..29] : <29 x 32-hex>
```
## 安装说明
使用 [uv](https://docs.astral.sh/uv/) 安装(提供 `ad-secretgen` 命令及简短别名 `adsg`):
```
uv tool install git+https://github.com/StrongWind1/AD-SecretGen
```
它也是一个独立的 [PEP 723](https://peps.python.org/pep-0723/) 单文件,因此无需安装即可运行——`uv` 会直接从 URL 获取其依赖(`pycryptodome` + `rich`):
```
uv run https://raw.githubusercontent.com/StrongWind1/AD-SecretGen/main/ad_secretgen.py \
--password 'P@ssw0rd!' --user alice --realm corp.local --netbios CORP
```
## 快速开始
```
# 明文用户账户
ad-secretgen --password 'P@ssw0rd!' --user alice --realm corp.local --netbios CORP
# 从 stdin 读取密码(避免其保留在 argv / shell history 中)
ad-secretgen --password - --user alice --realm corp.local
# gMSA: 通过 LDAP 使用 bloodyAD 拉取 managed password,然后推导 keys
B64=$(bloodyAD --host DC -d corp.local -u you -p pw get object 'svc$' --attr msDS-ManagedPassword | sed -n 's/^msDS-ManagedPassword: //p')
ad-secretgen --password-b64 "$B64" --managed-blob --user 'svc$' --realm corp.local --account-type computer
# 机器账户(作为 hex 的原始 UTF-16LE password blob)
ad-secretgen --password-hex 4100620063... --user 'WS01$' --realm corp.local --account-type computer
# JSON 输出(包含一个回显计算出的 salt 的 meta block)
ad-secretgen --password 'P@ssw0rd!' --user alice --realm corp.local --netbios CORP --format json
```
## 参数
| 参数 | 含义 |
|----------|---------|
| `--password STR` | 明文密码。`-` 从 stdin 读取一行。 |
| `--password-hex HEX` | 以十六进制表示的原始密码 **blob**(例如机器账户的 UTF-16LE 密码)。NT 直接为 `MD4(blob)`。 |
| `--password-b64 B64` | 同上,但采用 base64 格式——即 `bloodyAD` 打印 `msDS-ManagedPassword` 时的格式。 |
| `--managed-blob` | 将该 blob 视为 `MSDS-MANAGEDPASSWORD_BLOB`([MS-ADTS] §2.2.19)并使用其 256 字节的 `CurrentPassword`。适用于 gMSA/dMSA。 |
| `--user SAM` | `sAMAccountName`。**大小写对用户/信任账户的 salt 很重要**——请参阅下文的*大小写*部分。 |
| `--realm DNS` | DNS 域名。在 AD/Kerberos 中,域*即是* **realm**——两者是同一个东西。Kerberos 会将其大写,因此输入任何大小写形式都可以。 |
| `--account-type {user,computer,trust}` | 选择 salt 规则。对于 `--managed-blob` 或以 `$` 结尾的 `--user`(机器/gMSA),**默认为 `computer`**;否则为 `user`。显式传入以进行覆盖——尤其是 **`trust`**(同样以 `$` 结尾,但使用 krbtgt salt)以及任何以 `$` 结尾的*用户*账户。 |
| `--netbios NAME` | NetBIOS 域名。生成 29 个 WDigest 哈希时必填。 |
| `--dns-domain FQDN` | 用于 WDigest 的 DNS 域名 FQDN(默认为 `--realm`)。 |
| `--upn UPN` | 用于 WDigest 的 `userPrincipalName`(默认为 `@`)。 |
| `--salt SALT` | **原样**覆盖计算出的 Kerberos salt。对于那些无法从身份推导出 salt 的账户(见*大小写*),这是一个逃生通道。 |
| `--format {text,json,pretty}` | 输出格式(默认为 `text`)。 |
必须且只能提供 `--password` / `--password-hex` / `--password-b64` 中的一个。身份输入仅用于需要它们的密钥:如果没有 `--realm`,则跳过 Kerberos 密钥;如果没有 `--netbios`,则跳过 WDigest 哈希(每次跳过都会在 stderr 输出提示)。
## 大小写——请阅读此部分
`NT`、`LM` 和 `RC4-HMAC` **仅取决于密码**——`--user`/`--realm` 的大小写对它们没有影响。但是 **Kerberos 密钥(DES/AES)是加盐的(salted)**,并且 salt 的大小写规则因账户类型而异:
| 账户类型 | salt | 大小写行为 |
|--------------|------|------------------|
| **user** | `UPPER(realm) + sAMAccountName` | `sAMAccountName` **保留原始大小写**——你必须传入完全一致的存储大小写 |
| **computer / gMSA** | `UPPER(realm) + "host" + lower(name) + "." + lower(dns)` | name 和 DNS 被**转为小写**——输入任何大小写形式都可以 |
| **trust** | `UPPER(realm) + "krbtgt" + name-without-$` | 合作方 flat-name 保留大小写(按存储状态) |
realm——即 Kerberos 对 AD **domain** 的称呼——总是被转为大写,因此 `--realm corp.local`、`CORP.LOCAL` 和 `Corp.Local` 是等价的。
**易错点:**对于 **user** 和 **trust** 账户,传入错误的 `sAMAccountName` 大小写(例如 `ALICE` 与 `alice`)会产生一个*错误但看似有效*的 salt,从而导致 AES/DES 密钥错误——而且**不会报错**。如果你不知道确切的存储大小写,请从 KDC 获取(见下一节),并通过 `--salt` 传入,这将完全跳过 salt 的构造过程。
**WDigest 在设计上是不区分大小写的。**这 29 个哈希*正是*大小写的排列组合——针对 NetBIOS、DNS 和 `DOMAIN\user` 形式的按原样存储、全小写、全大写以及混合模式。这恰恰是摘要认证能匹配任何大小写用户名的原因:AD 预存了所有常见的大小写组合。(为了逐字节复现这 29 个哈希,你仍然需要知道用于哈希 #1 的原始存储大小写,但全小写/全大写的条目无论大小写如何都能匹配。)
## 查找大小写——让 KDC 告诉你 (CredWolf)
你永远不必猜测大小写。**KDC 会在其对裸 AS-REQ 响应的 `PA-ETYPE-INFO2` 预认证数据中返回权威的 salt**——其中包含了精确的 realm 和用户名大小写。这**不是登录尝试**:它不会增加密码错误计数,也不会锁定账户,而且你只需要**用户名**,不需要密码。
[CredWolf](https://github.com/StrongWind1/CredWolf) 实现了这一点。当它执行 Kerberos AES 认证时,它会发送裸 AS-REQ,读取 salt,从中恢复正确大小写的用户名,并报告如下内容:
```
[VERBOSE] Username case corrected by KDC: ALICE -> alice
```
已在活跃的 KDC 上进行验证——你输入的任何大小写最终都会返回那个唯一存储的 salt:
```
typed --user ALICE -> KDC salt = CORP.LOCALalice corrected username = 'alice'
typed --user Alice -> KDC salt = CORP.LOCALalice corrected username = 'alice'
typed --user alice -> KDC salt = CORP.LOCALalice (already correct)
```
要将其与 `ad-secretgen` 结合使用:
```
# 1) 从 KDC 获取 salt(带有 AES + -v 的 CredWolf 会打印修正后的名称;或者直接读取其 salt)
# 2) 将其传回 - 要么作为修正后的用户名...
ad-secretgen --user alice --realm corp.local --password 'P@ssw0rd!'
# ...或者,最稳妥地,原样作为 salt:
ad-secretgen --salt 'CORP.LOCALalice' --password 'P@ssw0rd!' --user alice --realm corp.local
```
`--salt` 途径是绝对可靠的:它同样适用于那些 salt 根本**不是** `REALM+username` 的账户——最典型的是内置的 **Administrator**(以及 **krbtgt**),它们的 salt 被冻结在 DC *最初安装时的主机名*上(例如 `WIN-ABC123Administrator`)。对于这些账户,用户名大小写恢复无能为力——但直接馈入 KDC 的原始 salt 给 `--salt` 是非常精确的。
注意事项(由 CredWolf 记录):此技巧需要 **AES**(这是触发 salt 检索的条件),并且**不**适用于 **AS-REP-roastable** 账户——在禁用预认证的情况下,KDC 返回的 AS-REP 不包含 `PA-ETYPE-INFO2`,因此没有可供读取的 salt。对于这些情况,请自行提供确切的大小写,或者使用 RC4(它没有 salt)。
## 通过 LDAP 提取 gMSA 密码 (bloodyAD)
如果你有权限读取 gMSA 的 `msDS-ManagedPassword`(你在 `PrincipalsAllowedToRetrieveManagedPassword` 中,或者你是 Domain Admin),你就可以完全离线构造它的 Kerberos 和 NTLM 密钥:
```
B64=$(bloodyAD --host DC -d corp.local -u you -p pw get object 'svc$' --attr msDS-ManagedPassword | sed -n 's/^msDS-ManagedPassword: //p')
ad-secretgen --password-b64 "$B64" --managed-blob --user 'svc$' --realm corp.local --account-type computer
```
`--managed-blob` 会解析 `MSDS-MANAGEDPASSWORD_BLOB`,提取出 256 字节的 `CurrentPassword`,并使用 **computer**(`host/`)salt 推导 `NT = MD4(CurrentPassword)` 以及 AES 密钥。因为 gMSA 属于计算机类别,所以此处 `--user`/`--realm` 的大小写无关紧要。托管密码会轮换(约 30 天),因此在轮换后需要重新提取。(dMSA 有所不同——它的托管密码无法通过这种方式从 LDAP 获取。)
## 输出格式
这三种格式都包含**相同的信息**,并组织成相同的部分——**meta**(输入项:account-type、realm/domain、`sAMAccountName`、回显的**密码**、`Kerberos salt`,以及提供的任何 `--netbios`/`--dns-domain`/`--upn`),**ntlm**(`nt`、`lm`),**kerberos**(enctype,按 etype 顺序排列,括号内带有数字:`des-cbc-crc (1)`、`des-cbc-md5 (3)`、`aes128-cts-hmac-sha1-96 (17)`、`aes256-cts-hmac-sha1-96 (18)`、`rc4-hmac (23)`),以及 **wdigest**(29 个 `wdigest[01..29]` 哈希)——随后是任何**被跳过**的条目。没有任何信息会在一种格式中显示而在另一种格式中隐藏;只有当某一行或某一节没有值时,它才会被舍弃(在所有格式中一致)。它们仅在布局上有所不同:
- **text**(默认)——由对齐的 ` : value` 行组成的 `[section]` 块,最后是一个 `[skipped]` 块。
- **json**——每个部分都是一个嵌套对象(`meta` / `ntlm` / `kerberos`),其中 `wdigest` 和 `skipped` 为数组;适合机器读取。
- **pretty**——每个部分都是一个分组的 `rich` 面板(META / NTLM / KERBEROS / WDIGEST)。
PBKDF2 迭代次数固定为 4096([RFC3962] §4),此处仅作文档说明而非回显参数。
## 限制与说明
- **非 ASCII 密码的 DES 和 WDigest** 依赖于区域设置:DES 使用 DC 的 ANSI 代码页(cp1252),WDigest 使用 ISO-8859-1。不在该代码页内的密码(例如西里尔文)或**二进制 gMSA blob** 无法复现这两种算法的结果——但 NT 和 AES 仍然可以。此类字段将被跳过并在 stderr 中输出提示。
- **Administrator / krbtgt** 带有冻结的安装时 salt——请使用 `--salt`。
- **NoLMHash**(现代默认设置)意味着 AD 存储一个空白的 `LM`;本工具输出的是该密码的*真实* LM 哈希。
- **不在范围内**(设计使然):AES-SHA2 (etype 19/20)、`Primary:CLEARTEXT`、随机的 `NTLM-Strong-NTOWF`、NetNTLM 网络响应、DCC1/DCC2、DPAPI 预置密钥。
## 验证
已与真实 AD 进行逐字节交叉验证:**NTDSWolf**(读取 `ntdsutil` IFM `.dit`) **= DSInternals**(`Get-ADRePLAccount` dcsync) **= AD-SecretGen**(从密码推导),涵盖了 NT、RC4、DES、AES128、AES256、salt 以及所有 29 个 WDigest 哈希——适用于 user、computer、trust、gMSA 和子域账户。测试套件(`tests/`)利用实验室固件以及已发布的 RFC3961/3962 和 NT/LM 已知测试向量来确保这一点。
```
make check # ruff + ty + pytest + docs
# ...或者单独地:
uv run pytest # fixtures + KATs
uv run ruff check . && uv run ty check
```
## 相关工具
此合集内的其他项目:
- [NTDSWolf](https://github.com/StrongWind1/NTDSWolf) - 离线 NTDS.dit 解析器和凭据提取器
- [CredWolf](https://github.com/StrongWind1/CredWolf) - Active Directory 凭据验证
- [KerbWolf](https://github.com/StrongWind1/KerbWolf) - Kerberos roasting 和哈希提取工具包
- [Kerberos](https://github.com/StrongWind1/Kerberos) - Active Directory 中的 Kerberos:协议、安全性与攻击
## 免责声明
AD-SecretGen 仅旨在用于授权的安全测试、研究和教育。请仅在你有授权测试的凭据和账户上使用它。未经授权访问计算机系统是违法的。作者不对使用此工具进行的任何误用或造成的损害负责。
## 许可证
[Apache License 2.0](LICENSE)标签:Linux安全, Python, 凭据验证, 哈希生成, 密码学, 手动系统调用, 无后门, 活动目录, 逆向工具