Fuite d'Ingrédient Critique

Arthur P., Elsa F., Francois L., Joey G., Julie D., Raphael G.

← Fuite d'Ingrédient Critique · Au cœur de la base de données / Une activité bien trop suspecte / Un vol et une attaque des plus communs / Un binaire rendant malade...

Une activité bien trop suspecte

Des activités réseau suspectes ont été identifiées, concernant une personne qui s'est déjà fait remarquer. Que se passe-t-il ?

Deux personnes viennent donc d'être identifiées : il s'agit d'un responsable de la production et d'un prestataire externe.

Les administrateurs réseaux nous indiquent avoir identifié des activités inhabituelles concernant le premier quelques jours avant la fuite, et viennent de faire le rapprochement. Investiguez pour savoir de quoi il en retourne.

Cote 21 pts

Indices

Les erreurs 401 semblent intéressantes
Concentrez-vous à regarder quelles fonctions peuvent être appelées lorsque l'endpoint précédemment identifié est contacté.
Inspectez plus particulièrement les interactions avec le stockage local du navigateur. Vous pourriez trouver quelque chose de louche...

Faire son rapport

Résolution

Identification des activités suspectes dans les logs réseau

Nous avons à notre disposition un fichier log d'un serveur Nginx, nous devons faire un script qui détecte des requêtes identiques réalisées dans un intervalle de temps réduit par la même IP.

Voici un exemple très simple de script qui peut réaliser cette tâche :

endpoint = ""
count = 0
prev = ""

# Affiche les endpoints qui apparaissent 5 fois ou plus à la suite
with open("nginx_access.log") as file:
    for line in file:
        # Vérifier si c'est une requête GET ou POST
        if "GET" in line:
            endpoint = line.split("GET")[1].split("HTTP")[0].strip()
        elif "POST" in line:
            endpoint = line.split("POST")[1].split("HTTP")[0].strip()
        else:
            # Si ni GET ni POST, passer à la ligne suivante
            continue

        if endpoint == prev:
            count += 1
            if count == 5:
                print(f"Endpoint répété 5 fois: {endpoint}")
        else:
            count = 1
            prev = endpoint

On obtient en sortie :

La sortie de ce script

En utilisant grep pour filtrer les logs sur ces deux points d'entrée, on constate que les accès à /login (à gauche), semblent tout à fait légitimes, tandis que les accès à /recipe/20/version/1 de l'API (à droite) sont bien plus suspects : ils sont très rapprochés et réguliers dans le temps, et finissent toujours par renvoyer une erreur 401.

Les logs filtrés selon ces deux points d'entrée

Nous pouvons donc en déduire que les activités suspectes que l'on cherche à identifier sont les nombreux appels à ce point d'entrée.

Analyse du code JavaScript

Pour la deuxième moitié de l'exercice, nous nous retrouvons avec le code correspondant au front de l'application concernée, qui est l'interface de visualisation des recettes, dont la base de données a été extraite à l'étape précédente.

Comme l'indique la note de l'équipe de développement dans le fichier README.md qu'ils ont ajouté, cette application vient d'être décommissionnée, c'est pourquoi on ne peut l'inspecter que de cette façon. On y apprend aussi que l'authentification des utilisateurs se fait par OIDC, une information qui pourrait nous servir plus tard.

L'archive contenant des centaines de lignes de JavaScript et HTML peu propres et "anciennes" (testé et fonctionnel avec Firefox 12 sur Windows 2000 !), n'utilisant pas les fonctionnalités les plus récentes (API fetch, ES6...), il est peu lisible. Il vaut donc mieux s'orienter directement vers ce qui nous intéresse.

Pour cela, on part de ce qu'on a découvert lors de la première moitié de l'exercice : de nombreux appels à /recipe/20/version/1 de l'API. On commence donc par chercher où se fait cet appel dans le code JavaScript(1). Cela nous amène à la fonction requestPrecise(2).

On remarque que celle-ci prépare simplement la requête, pour l'envoyer à authenticate qui réalise effectivement l'appel API, avant d'appeler la fonction de retour preciseResponse(3). Dans cette dernière fonction, une faute de frappe discrète est présente : elle peut se voir directement, ou apparaître plus clairement en activant un correcteur orthographique dans son éditeur, à la ligne 209(4).

Aperçu des fonctions mentionnées ci-dessus

Mais en quoi cette simple erreur peut-elle poser problème ? Puisque cette ligne se situe dans une condition indiquant que le jeton a expiré, nous allons nous placer dans cette situation.

Imaginons que le jeton soit expiré lors de l'appel API réalisé dans authenticate qui appelle notre fonction preciseResponse en retour. La fonction est donc appelée avec le code d'erreur 401 et le message Token Expired.

On voudrait alors retirer le jeton d'accès du stockage local, et rejouer la requête. De cette façon, la fonction authenticate ne disposant plus de jeton d'accès pour contacter l'API, en demandera au préalable un nouveau à partir de celui de rafraîchissement.

Cependant, à cause de cette faute de frappe, le jeton d'accès n'est pas retiré du stockage local, ainsi l'appel à authenticate rejoue exactement la même requête, qui va à nouveau appeler la fonction de retour, et ainsi de suite. Nous arrivons dans des appels de fonctions de retour à l'infini, dont l'une réalise un appel API, d'où les très nombreux appels effectués sur la même URL.

En résumé :

  • Une faute de frappe s'est glissée dans le code JavaScript : la ressemblance entre les deux lettres la rend difficile à détecter
  • En conséquence, la demande d'un nouveau jeton de rafraîchissement OIDC est mal réalisée dans certains cas : celui d'un appel à /api/recipe/:id/version/:id. Une telle erreur sur un appel précis de l'API peut facilement passer inaperçu lors des tests fonctionnels
  • Cela a pour effet que des fonctions de rappel (callback) peuvent s'appeler entre elles à l'infini, causant par la même occasion des appels en boucle à l'API

L'activité suspecte provient donc bien d'un problème dans l'application, et non d'une action humaine. La personne est donc innocentée, il s'agissait d'une fausse piste.