Challenge Description

  • Difficulty : Medium
  • Points : 250
  • Categoty : Web

Walkthrough

The Meme Generator website allows you generate a Meme by seaching a term on any one of the 3 below search engines.

  • Google
  • DuckDuckGo
  • Search Encrypt

Meme Gen

When searching on any of the search engines, the search URL would look like this.

# Google
https://google.com/?q=<MEME_SEARCH_TERM>

# DuckDuckGo
https://DuckDuckGo.com/?q=<MEME_SEARCH_TERM>

# Search Encrypt
https://searchencrypt.com/?q=<MEME_SEARCH_TERM>

# General Format
https://<SEARCH_ENGINE>.com/?q=<MEME_SEARCH_TERM>

The backend server prepends https:// and appends .com to the search engine we specify to form the search query.

The following multipart POST request is sent to the backend API endpoint /api/generate when generating a Meme.

API

We also have the source code of the Meme Generator logic by going to /source.

Source

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)

After reviewing the code, it is clear that, in order to get the flag, we should make the backend server issue a request to the /flag endpoint and the URL should start with http://l0calhost.

Vulnerability #1 - SSRF in Search Engine Field

We have a very obvious Server Side Request Forgery in the search engine multipart form data field. We can specify any domain name omitting the TLD part. For example, if we want to make the backend server issue a request to https://example.com, we would specify only example as the search engine. So, to get a callback on burp collaborator, set the search engine as follows.

  • <collaborator_unique_id>.oastify

Somehow, I couldn’t come up with a way to exploit this to get the flag.

Vulnerability #2 - JavaScript Injection in Search Term

I noticed that the we can inject arbitrary JavaScript in the search term and would get evaluated by the chrome engine. I specified the following search term to see if the search term is evaluated in some way.

  • query string : abc" + " test

If my theory was right I would get a Meme with abc test as search term.

Initial 01

And to my surprise, the search term was indeed abc test.

Initial 02

I had doubts at this point whether is it Python code injection or JavaScript code injection. To confirm my doubts, I tried the following payload which will form a search query containing current date. I used JavaScript Date() function in the query string as follows.

  • abc" + Date() + " test"

JS Date 01

Voila, the Meme showed search term as abc <Current Date> test.

JS Date 02

If we want to redirect the Chrome headless browser to arbitrary URLs, we will need access to JavaScript objects such as document or window.

Confirming access to window.location object with following query string.

  • abc " + window.location + " test

Window.Location 01

The meme now shows the window.location value in the search field.

Window.Location 01

Now that I had arbitrary JavaScript execution and have access to JavaScript window object, I can make the Chrome browser redirect to any URL. To confirm this, I used following query string which will redirect the Chrome headless browser to http://example.com.

  • abc"; window.location="http://example.com"; var s = "test

Example.com 01

The generated meme showed that we have successfull redirection to https://example.com.

Example.com 02

Finally, its time to get the flag. Sending the following payload satisfied all conditions and gave the flag.

  • abc"; window.location="http://l0calhost.localhost:8080/flag"; var s = "test

Flag 01

Flag: BlackHatMEA{196:15:b15003a794eec95eb3d93f3ee55398d81c4ed0ab}

Flag 02