Windows 域控笔记

本篇介绍一些基础概念和相关知识

什么是域?

Windows域通常用于大型网络——公司网络、学校网络和政府网络。除非你有雇主或学校提供的笔记本电脑,否则你在家里不会遇到这种情况。

典型的家用计算机是一个孤立的实体。您可以控制计算机上的设置和用户帐户。连接到域的计算机是不同的——这些设置是在域控制器(DC, domain controller) 上控制的。

Windows域为网络管理员提供了一种方法来管理大量的pc机,并从一个地方控制它们。一个或多个服务器(称为域控制器)控制域及其上的计算机。

域通常由在同一本地网络上的计算机组成。但是,连接到某个域的计算机可以通过VPN或Internet连接继续与域控制器通信。这使得企业和学校能够远程管理他们提供给员工和学生的笔记本电脑。

当计算机连接到一个域时,它不会使用自己的本地用户帐户,用户帐户和密码都是在域控制器上统一管理。当您登录到该域中的计算机时,计算机将使用域控制器验证您的用户帐户名称和密码。这意味着您可以在任何连接到该域的计算机上使用相同的用户名和密码登录。

网络管理员可以更改“域控制器”上的组策略设置。域上的每台计算机将从“域控制器”获得这些设置,它们将覆盖用户在其pc上指定的任何本地设置。所有的设置都是从一个地方控制的。这也“锁定”了计算机。您可能不允许更改连接到域的计算机上的许多系统设置。

域环境搭建

http://blog.sina.com.cn/s/blog_6ce0f2c901014okt.html

http://www.it165.net/os/html/201306/5493.html

Windows安全认证机制

Windows安全认证机制主要有两种:NTLM和Kerberos

基于ntlm的认证方式,主要用在早期的windows工作组环境中,认证的过程也相对比较简单。

另一种是基于Kerberos的认证方式,主要用在域环境中。

Windows 2000和后续的操作系统使用Kerberos为其默认认证方法。RFC 3244 “微软Windows 2000 Kerberos变更密码与设置密码协议” 记录整理一些微软对Kerberos协议软件包的添加。RFC 4757 记录整理微软对RC4密码的使用。虽然微软使用Kerberos协议,却并没有用麻省理工的软件。

维基

https://en.wikipedia.org/wiki/NT_LAN_Manager

https://zh.wikipedia.org/wiki/Kerberos

其他资料

能用通用的语言介绍下 Kerberos 协议么?(通俗解释)

Kerberos Authentication Explained

kerberos认证原理

Steal or Forge Kerberos Tickets (对Kerberos的一些攻击方式)

Windows安全认证是如何进行的?[Kerberos篇]

Windows安全认证是如何进行的?[NTLM篇]

windows protocol(对Kerberos、NTLM、LDAP的一些介绍,比较详细,可以多看看)

Mimikatz

Mimikatz是一款开源应用程序,允许用户查看和保存Kerberos tickets之类的身份验证凭据。本杰明·德尔皮(Benjamin Delpy)继续领导Mimikatz的开发,因此该工具集可与当前版本的Windows配合使用,并包括最新的攻击。

Mimikatz能做什么

  • Pass-the-Hash: Windows used to store password data in an NTLM hash. Attackers use Mimikatz to pass that exact hash string to the target computer to login. Attackers don’t even need to crack the password, they just need to use the hash string as is. It’s the equivalent of finding the master key to a building on the floor. You need that one key to get into all the doors.
  • Pass-the-Ticket: Newer versions of windows store password data in a construct called a ticket. Mimikatz provides functionality for a user to pass a kerberos ticket to another computer and login with that user’s ticket. It’s basically the same as pass-the-hash otherwise.
  • Over-Pass the Hash (Pass the Key): Yet another flavor of the pass-the-hash, but this technique passes a unique key to impersonate a user you can obtain from a domain controller.
  • Kerberos Golden Ticket: This is a pass-the-ticket attack, but it’s a specific ticket for a hidden account called KRBTGT, which is the account that encrypts all of the other tickets. A golden ticket gives you domain admin credentials to any computer on the network that doesn’t expire.
  • Kerberos Silver Ticket: Another pass-the-ticket, but a silver ticket takes advantage of a feature in Windows that makes it easy for you to use services on the network. Kerberos grants a user a TGS ticket, and a user can use that ticket to log into any services on the network. Microsoft doesn’t always check a TGS after it’s issued, so it’s easy to slip it past any safeguards.
  • Pass-the-Cache: Finally an attack that doesn’t take advantage of Windows! A pass-the-cache attack is generally the same as a pass-the-ticket, but this one uses the saved and encrypted login data on a Mac/UNIX/Linux system.

Mimikatz github

https://github.com/gentilkiwi/mimikatz

其他资料

What is Mimikatz: The Beginner’s Guide

[后渗透]Mimikatz使用大全

九种姿势运行Mimikatz(包含了一些绕过杀软的手段)

深入分析Mimikatz:WDigest

深入分析Mimikatz:SSP

刨根问底:Hash传递攻击原理探究

Unofficial Guide to Mimikatz & Command Reference(介绍了mimikatz的使用方法和检测方法)

LDAP

什么是LDAP

轻量目录访问协议(Lightweight Directory Access Protocol)是直接运行在TCP/IP栈上的目录服务协议,LDAP有关联的API可以简化编写互联网目录服务应用程序的过程。LDAP一般使用在企业内部用于保存员工信息和组织架构,同时可以作为个人电脑以及应用系统的统一认证和登录。

Searching a Directory

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
#include <iostream>

//----------------------------------------------
// Performing an LDAP Synchronous Search.
//
// Be aware that you must set the command prompt screen buffer
// height to 350 and the width to 90.
//-------------------------------------------------------------

#include <windows.h>
#include <winldap.h>
#include <winber.h>
#include <rpc.h>
#include <rpcdce.h>
#include <stdio.h>

#pragma comment(lib,"Wldap32.lib")

//-----------------------------------------------------------
// This subroutine must have validated credentials (name and
// password) passed to it.
//-----------------------------------------------------------
int MyLDAPSearch(PCHAR pUserName, PCHAR pPassword)
{
//---------------------------------------------------------
// Initialize a session. LDAP_PORT is the default port, 389.
//---------------------------------------------------------
PCHAR hostName = "corp.vantasy.com";
LDAP* pLdapConnection = NULL;

pLdapConnection = ldap_initA(hostName, LDAP_PORT);

if (pLdapConnection == NULL)
{
printf("ldap_init failed with 0x%x.\n", LdapGetLastError());
ldap_unbind(pLdapConnection);
return -1;
}
else
printf("ldap_init succeeded \n");


//-------------------------------------------------------
// Set session options.
//-------------------------------------------------------
ULONG version = LDAP_VERSION3;
ULONG numReturns = 10;
ULONG lRtn = 0;

// Set the version to 3.0 (default is 2.0).
lRtn = ldap_set_option(
pLdapConnection, // Session handle
LDAP_OPT_PROTOCOL_VERSION, // Option
(void*)&version); // Option value

if (lRtn == LDAP_SUCCESS)
printf("ldap version set to 3.0 \n");
else
{
printf("SetOption Error:%0lX\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}

// Set the limit on the number of entries returned to 10.
lRtn = ldap_set_option(
pLdapConnection, // Session handle
LDAP_OPT_SIZELIMIT, // Option
(void*)&numReturns); // Option value

if (lRtn == LDAP_SUCCESS)
printf("Max return entries set to 10 \n");
else
{
printf("SetOption Error:%0lX\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}


//--------------------------------------------------------
// Connect to the server.
//--------------------------------------------------------

lRtn = ldap_connect(pLdapConnection, NULL);

if (lRtn == LDAP_SUCCESS)
printf("ldap_connect succeeded \n");
else
{
printf("ldap_connect failed with 0x%lx.\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}


//--------------------------------------------------------
// Bind with credentials.
//--------------------------------------------------------
PCHAR pMyDN = "DC=corp,DC=vantasy,DC=com";
SEC_WINNT_AUTH_IDENTITY_A secIdent;

secIdent.User = (unsigned char*)pUserName;
secIdent.UserLength = strlen(pUserName);
secIdent.Password = (unsigned char*)pPassword;
secIdent.PasswordLength = strlen(pPassword);
secIdent.Domain = (unsigned char*)hostName;
secIdent.DomainLength = strlen(hostName);
secIdent.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;

lRtn = ldap_bind_sA(
pLdapConnection, // Session Handle
pMyDN, // Domain DN
(PCHAR)&secIdent, // Credential structure
LDAP_AUTH_NEGOTIATE); // Auth mode
if (lRtn == LDAP_SUCCESS)
{
printf("ldap_bind_s succeeded \n");
secIdent.Password = NULL; // Remove password pointer
pPassword = NULL; // Remove password pointer
}
else
{
printf("ldap_bind_s failed with 0x%lx.\n", lRtn);
ldap_unbind(pLdapConnection);
return -1;
}

//----------------------------------------------------------
// Perform a synchronous search of fabrikam.com for
// all user objects that have a "person" category.
//----------------------------------------------------------
ULONG errorCode = LDAP_SUCCESS;
LDAPMessage* pSearchResult;
PCHAR pMyFilter = "(&(objectCategory=person)(objectClass=user))";
PCHAR pMyAttributes[6];

pMyAttributes[0] = "cn";
pMyAttributes[1] = "company";
pMyAttributes[2] = "department";
pMyAttributes[3] = "telephoneNumber";
pMyAttributes[4] = "memberOf";
pMyAttributes[5] = NULL;

errorCode = ldap_search_sA(
pLdapConnection, // Session handle
pMyDN, // DN to start search
LDAP_SCOPE_SUBTREE, // Scope
pMyFilter, // Filter
pMyAttributes, // Retrieve list of attributes
0, // Get both attributes and values
&pSearchResult); // [out] Search results

if (errorCode != LDAP_SUCCESS)
{
printf("ldap_search_s failed with 0x%0lx \n", errorCode);
ldap_unbind_s(pLdapConnection);
if (pSearchResult != NULL)
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("ldap_search succeeded \n");

//----------------------------------------------------------
// Get the number of entries returned.
//----------------------------------------------------------
ULONG numberOfEntries;

numberOfEntries = ldap_count_entries(
pLdapConnection, // Session handle
pSearchResult); // Search result

if (numberOfEntries == NULL)
{
printf("ldap_count_entries failed with 0x%0lx \n", errorCode);
ldap_unbind_s(pLdapConnection);
if (pSearchResult != NULL)
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("ldap_count_entries succeeded \n");

printf("The number of entries is: %d \n", numberOfEntries);


//----------------------------------------------------------
// Loop through the search entries, get, and output the
// requested list of attributes and values.
//----------------------------------------------------------
LDAPMessage* pEntry = NULL;
PCHAR pEntryDN = NULL;
ULONG iCnt = 0;
char* sMsg;
BerElement* pBer = NULL;
PCHAR pAttribute = NULL;
PCHAR* ppValue = NULL;
ULONG iValue = 0;

for (iCnt = 0; iCnt < numberOfEntries; iCnt++)
{
// Get the first/next entry.
if (!iCnt)
pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
else
pEntry = ldap_next_entry(pLdapConnection, pEntry);

// Output a status message.
sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
if (pEntry == NULL)
{
printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
ldap_unbind_s(pLdapConnection);
ldap_msgfree(pSearchResult);
return -1;
}
else
printf("%s succeeded\n", sMsg);

// Output the entry number.
printf("ENTRY NUMBER %i \n", iCnt);

// Get the first attribute name.
pAttribute = ldap_first_attributeA(
pLdapConnection, // Session handle
pEntry, // Current entry
&pBer); // [out] Current BerElement

// Output the attribute names for the current object
// and output values.
while (pAttribute != NULL)
{
// Output the attribute name.
printf(" ATTR: %s", pAttribute);

// Get the string values.

ppValue = ldap_get_valuesA(
pLdapConnection, // Session Handle
pEntry, // Current entry
pAttribute); // Current attribute

// Print status if no values are returned (NULL ptr)
if (ppValue == NULL)
{
printf(": [NO ATTRIBUTE VALUE RETURNED]");
}

// Output the attribute values
else
{
iValue = ldap_count_valuesA(ppValue);
if (!iValue)
{
printf(": [BAD VALUE LIST]");
}
else
{
// Output the first attribute value
printf(": %s", *ppValue);

// Output more values if available
ULONG z;
for (z = 1; z < iValue; z++)
{
printf(", %s", ppValue[z]);
}
}
}

// Free memory.
if (ppValue != NULL)
ldap_value_freeA(ppValue);
ppValue = NULL;
ldap_memfreeA(pAttribute);

// Get next attribute name.
pAttribute = ldap_next_attributeA(
pLdapConnection, // Session Handle
pEntry, // Current entry
pBer); // Current BerElement
printf("\n");
}

if (pBer != NULL)
ber_free(pBer, 0);
pBer = NULL;
}

//----------------------------------------------------------
// Normal cleanup and exit.
//----------------------------------------------------------
ldap_unbind(pLdapConnection);
ldap_msgfree(pSearchResult);
ldap_value_freeA(ppValue);
return 0;

}

int main(int argc, char *argv[])
{
if (argc != 3)
{
printf("Usage: LDAPTest.exe username password\n");
return 0;
}

MyLDAPSearch(argv[1], argv[2]);

return 0;
}

DCShadow

揭秘DCShadow

相关书籍

《内网安全攻防:渗透测试实战指南》