Aller au contenu
  1. Writeups/

YesWeHack: Dojo 36

588 mots·3 mins·
Fayred
Auteur
Fayred
Passionné par l’informatique en général et plus particulièrement par la sécurité informatique. Amateur de CTFs et débutant en Bug Bounty, je cherche avant tout à apprendre, m’amuser et partager ce que j’ai appris.
Sommaire

Énoncé
#

A friend of yours has created a web application that allows you to check the availability of your locally hosted services. He assured you that it is secure and even allowed you to run it as a test user!

Prove him wrong by reading the flag.txt file on the server.

~ The flag can be found in the file: /tmp/flag.txt

Authors: Owne, Brumens

Aperçu
#

L’application fournit son code source et prend deux entrées : cmd et token. La première est censée contenir l’adresse IP ou le nom de domaine à tester avec ping. La seconde détermine l’utilisateur associé à la requête.

Résolution
#

Détourner la recherche du token
#

Le traitement de cmd dépend de l’utilisateur. Le compte dev passe par PreProd_Sanitize(), tandis que les autres utilisent Prod_Sanitize() :

def Run(self):
        if self.user == "dev":
            cmd_sanitize = self.PreProd_Sanitize(self.command)
        else:
            cmd_sanitize = self.Prod_Sanitize(self.command)

        # At the moment we don't have internet access.
        # We should only ping localhost to avoid server timeout
        result = subprocess.run(["/bin/ash", "-c", f"ping -c 1 {cmd_sanitize}"], capture_output=True, text=True)
        if result.returncode == 0:
            return result.stdout
        else:
            return result.stderr

Il faut donc comprendre comment la valeur user est choisie. Elle correspond au premier résultat de cette requête SQL :

# Get user that holds the given token
r = cursor.execute('SELECT username FROM users WHERE token LIKE ?', (token,))
try:
    user = r.fetchone()[0]
except:
    user = "test"

command = Command(cmd, user)

Le paramètre est bien préparé, mais il reste interprété comme un motif par l’opérateur LIKE. Dans ce contexte, _ représente exactement un caractère et % une suite de zéro ou plusieurs caractères. Le token _% correspond donc à toute valeur non vide.

La requête ne contenant aucun ORDER BY, l’exploitation repose sur le fait que le premier enregistrement renvoyé appartient ici à dev :

L’application nous traite désormais comme dev et sélectionne la fonction de préproduction.

Atteindre l’injection de commandes
#

Voici le filtre appliqué à notre commande :

def PreProd_Sanitize(self, s:str) -> str:
        """My homemade secure sanitize function"""
        if not s:
            return "''"
        if re.search(r'[a-zA-Z_*^@%+=:,./-]', s) is None:
            return s
        return "'" + s.replace("'", "'\"'\"'") + "'" 

Si l’entrée contient un caractère de la regex, la fonction l’entoure de quotes simples et échappe les quotes déjà présentes. En revanche, une entrée qui n’en contient aucun est renvoyée telle quelle. Il faut donc construire une commande uniquement avec les caractères autorisés par cette seconde branche.

Le point-virgule n’est pas filtré : 0; fournit d’abord une cible valide à ping, puis ouvre une seconde commande. Comme /bin/ash exécute la chaîne avec l’option -c, $0 contient ici /bin/ash. Le payload 0;$0 1 permet de vérifier ce comportement sans employer de lettre ni de slash :

La réponse confirme que $0 invoque bien /bin/ash. Il reste à lui passer le fichier à exécuter. Le glob ? représente exactement un caractère dans un nom de fichier ; huit ? correspondent donc à flag.txt. Le payload final est 0;$0 ????????.

Lire le flag
#

La seconde commande lance ainsi /bin/ash flag.txt. Le shell tente d’interpréter le contenu du fichier et renvoie la ligne du flag dans son message d’erreur :

Flag: FLAG{W3lc0me_T0_Th3_Oth3r_S1de!}

La vulnérabilité vient donc de l’enchaînement de deux faiblesses : le motif LIKE permet d’obtenir le traitement réservé à dev, puis la liste noire de PreProd_Sanitize() laisse passer une commande construite sans caractères alphabétiques.

Bonus : il était aussi possible d’exécuter des commandes en représentant les caractères en octal. Par exemple, ; $'\143\141\164' $'\146\154\141\147\056\164\170\164' correspond à ; cat flag.txt.

Référence
#

Articles connexes