Barbhack 2023 / Intleaks
August 26, 2023INTLEAKS
Nous nous attaquons au challenge Web INTLEAKS. Il s’agit d’une application dont le but est de partager des documents dévoilés par des lanceurs d’alertes. La page d’accueil indique que deux serveurs interne hébergent des fichiers dévoilés. L’un d’entre eux comprend les fichiers approuvés, et l’autre ceux qui ne l’ont pas été. L’objectif de ce challenge est d’atteindre un fichier secret qui n’a pas encore été approuvé et dévoilé.
Dans un premier temps, nous remarquons sur la page d’accueil qu’un bouton permet aux utilisateurs de s’authentifier à l’API en tant qu’anonyme.
Ce bouton requĂŞte le endpoint /api/auth
, nous délivrant un jeton JWT :
Nous analysons le contenu de celui-ci. Nous constatons qu’il est signé à l’aide de l’algorithme HS512, symétrique, dont la clé est ici à priori stockée dans le fichier indiqué par l’entête « kid ». Nous notons également la présence d’un paramètre "acl" :  "anon"
dans le corps de ce jeton.
Il est possible d’identifier certains endpoints de l’API depuis le code source de la page.
Le endpoint /api/me
permet d’afficher nos privilèges en se basant sur notre JWT.
Toujours depuis la page d’accueil, nous pouvons accéder à des documents. Ceux-ci correspondent aux documents ayant été « dévoilés et approuvés ».
Nous remarquons un paramètre dans l’URL correspondant au document à lire. En manipulant ce paramètre, un message indique que le nom de fichier doit se terminer par .disclosed.
Nous parvenons également à déclencher d’autres messages d’erreurs, indiquant que cette fonctionnalité émet des requêtes HTTP de la forme suivante :
- http://files.internal{location}
files.internal correspond donc au serveur dont les fichiers ont été « dévoilés et approuvés ».
Nous remarquons ensuite que les caractères « ? » et « # » nous permettent d’échapper la vérification de l’extension. Le directory listing étant activé sur files.internal, nous parvenons ainsi à lister les fichiers présents sur ce serveur web, et à y accéder en lecture. Nous parvenons notamment à lire les fichiers source app.py et jwt.py, également présents sur ce serveur et contenant le code source des fonctionnalités de l’API.
A partir de ce code source, nous pouvons comprendre comment sont générés les jetons JWT servant à gérer l’authentification. A partir du code source, nous identifions une LFI (Local File Inclusion) dans l’entête JKI du JWT.
Cet entête prend en entrée un fichier correspondant à la clé permettant de signer le jeton JWT. Ainsi, sélectionner en entrée un fichier dont nous connaissons le contenu revient à connaitre la clé permettant de signer le jeton.
De plus, nous remarquons qu’un JWT est considéré comme administrateur s’il contient, dans son corps, l’entrée suivante :
"acl"Â : "admin"
Nous cherchons donc à modifier le contenu de notre JWT. Ayant obtenu le contenu de quelques fichiers sur le serveur, par exemple « app.py », nous pouvons utiliser ce fichier comme clé pour générer une signature valide, et spécifier le chemin vers ce même fichier dans l’entête « kid » à l’aide de la LFI.
Nous réalisons donc un script python afin de générer un jeton JWT administrateur valide, prenant en compte les éléments précédemment énoncés.
Nous validons ensuite que le JWT forgé est bien valide en nous connectant à l’application.
Nous sommes à présent en mesure de requêter le endpoint /api/leaks
. Le serveur nous retourne le nom du fichier « caché » recherché, et d’après le code source, nous pouvons comprendre que celui est hébergé sur le serveur « whisteblower.internal ».
N’ayant pas accès directement à ce serveur, nous cherchons à forcer l’application à le requêter via une attaque de type SSRF.
Pour ce faire, nous pouvons réutiliser le endpoint « /api/document » et manipuler le paramètre location afin de forger une requête vers le serveur whistleblower.internal :
- http://[email protected]/{leak_file}?.disclosed
Nous avons ainsi gagné accès en lecture au fichier recherché et résolu ce challenge :