Hackvens
<-- home

Barbhack 2023 / Intleaks

August 26, 2023

INTLEAKS

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.

image-20230901133133813

Ce bouton requête le endpoint /api/auth, nous délivrant un jeton JWT :

image-20230901132724512

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.

image-20230901132741789

Il est possible d’identifier certains endpoints de l’API depuis le code source de la page.

image-20230901132754256

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 ».

image-20230901132829256

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.

image-20230901132842161

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.

image-20230901132905960

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.

image-20230901132931988

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"

image-20230901132956918

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.

image-20230901133015565

Nous validons ensuite que le JWT forgé est bien valide en nous connectant à l’application.

image-20230901132132341

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 ».

image-20230901133036443

image-20230901132048027

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 :

Nous avons ainsi gagné accès en lecture au fichier recherché et résolu ce challenge :

image-20230901133052785