Rédaction CTFZone Quals 2019: Poulet

Malgré le report de la conférence OFFZONE 2020 , il y aura une finale CTFZone ! Cette année, il se tiendra pour la première fois en ligne et sera activement diffusé sur les réseaux sociaux.

Nous annoncerons les détails plus tard, mais pour l'instant, nous suggérons d'explorer le démarrage de la tâche Web à partir de la phase de qualification. L'analyse de la solution nous a été envoyée par Devand MacLean du Canada. Surtout pour Habr, nous avons traduit le texte de la startup locale et nous vous invitons à découvrir quelle chaîne de vulnérabilités les participants ont rencontrée et qu'est-ce que le poulet a à voir avec cela.



informations générales


Auteur de la tâche: Pavel Sorokin
Points: 470
Nombre d'équipes ayant résolu la tâche: 2

Liens utiles:




, - :


  • (/Home/Index);
  • (/Home/Hens);
  • (/Home/Contact);
  • (/Auth/Login).

,   , :


  • ;
  •  API ,   .


La page / Accueil / Poules contient des liens pour télécharger des «passeports» de poulet.


Après avoir décodé Base64, nous trouvons le paramètre nom de fichier: 1.txt. En utilisant CyberChef, nous encodons / etc / passwd dans Base64 et suivons le lien:

http://web-chicken.ctfz.one/File/Download?filename=L2V0Yy9wYXNzd2Q=

Le contenu du fichier / etc / passwd sur le serveur est ouvert dans le navigateur , ce qui signifie que nous avons le droit de lire des fichiers arbitraires dans le système.

Lorsque vous essayez de trouver d'autres fichiers dans le système, vous pouvez remarquer une erreur qui apparaît à chaque fois que vous demandez un fichier inexistant (ou un fichier qui ne peut pas être lu avec les droits d'utilisateur de l'application):


Après avoir vu que l'erreur fait référence à la variable d' environnement ASPNETCORE_ENVIRONMENT , nous commençons à étudier les dispositions des applications Web ASP.NET Core. Et nous voyons ce qui suit:


Pour convertir les chemins et noms de fichiers nécessaires et les extraire en Base64, nous écrivons un petit script Python ( fetch.py ):


En utilisant ces connaissances sur la structure de l'application Web MVC créée dans ASP.NET Core, en utilisant fetch.py, nous obtenons le code source des fichiers suivants:


  • ../Views/Shared/_Layout.cshtml
  • ../Views/Home/Index.cshtml
  • ../Views/Home/Contact.cshtml
  • ../Views/Home/Hens.cshtml
  • ../Views/Auth/Login.cshtml

Après avoir fait cela et étudié le code source de chaque page, nous trouvons un détail intéressant dans Hens.cshtml


La ligne 1 contient l'instruction d'inclusion @ utilisant StackHenneryMVCAppProject, qui dans ASP.NET signifie un lien vers un fichier DLL.

Nous ouvrons ../StackHenneryMVCAppProject.dll - mais pas via l'utilitaire Python dans Kali Linux, mais dans le navigateur sous Windows, car nous allons décompiler ce fichier sous Windows. Grâce à l'utilisation de tels l'URL:

http://web-chicken.ctfz.one/File/Download?filename=Li4vU3RhY2tIZW5uZXJ5TVZDQXBwUHJvamVjdC5kbGw=

ouverture fichier DLL dans dnSpy (décompilateur .NET), voir immédiatement que dans la méthode initialize () classe Config. La configuration , qui s'exécute au démarrage de l'application Web, lit le contenu du fichier chicken_domains_internal.txt . A l'aide d'un script Python, nous extrayons le contenu de ce fichier:

web-chicken-flag
web-chicken-auth

La méthode Initialize () se connecte à  web-chicken-flag via le port 4321 et reçoit plusieurs paramètres: secret_token , RSA_key et  flag .

Malheureusement, il n'est pas possible d'établir une connexion à web-chicken-flag. Mais, lorsque nous définissons l'enregistrement des hôtes DNS locaux pour traduire web-chicken-auth en l'adresse IP externe du serveur ( 34.89.232.240 ), nous parvenons à ouvrir http: // web-chicken-auth / avec l' interface Swagger UI utilisée pour décrire et exécuter des commandes via l'API REST.


En creusant dans le fichier DLL décompilé, vous pouvez remarquer que le contrôleur AuthController avait non seulement la méthode Login () , mais aussi la méthode Change_Password_Test () :


Lorsque nous allons dans  / Auth / Change_Password_Test, nous voyons ce formulaire:


Lorsque nous étudions la méthode Change_Password_Test () , nous comprenons qu'elle traite quatre paramètres de type String via une requête HTTP POST:


  • Nom d'utilisateur;
  • nouveau mot de passe;
  • ancien mot de passe;
  • base_url.


Un peu plus bas dans la méthode Change_Password_Test () , nous voyons: sans recevoir de valeur pour base_url , la méthode prend la valeur conf.auth_server , c'est-à-dire web-chicken-auth , puis ajoute / auth / login à la fin de la valeur base_url , en affectant la chaîne résultante à la variable requestUri :


Dans la prochaine partie importante du code, les valeurs de chaîne des paramètres username et  old_password de la requête HTTP POST, et avec eux le  secret_token de la configuration, sont insérés dans la chaîne JSON. Ensuite, un objet JSON StringContent est créé à partir de ces données , qui sont envoyées en tant que données HTTP POST à ​​la variable requestURI créée à l'étape précédente.


Maintenant, le serveur attend la requête HTTP POST pour  requestUri pour renvoyer l'objet JSON avec le paramètre de code égal à 0. Si cela ne se produit pas, nous arrivons à la branche de code qui commence à partir de la ligne 6 et renvoie la vue / Views / Auth / Login avec l'erreur Login non valide / mot de passe .

Si la demande HTTP POST renvoie toujours un objet JSON avec le paramètre de code égal à 0, nous allons à la branche de code à partir de la ligne 11. Ensuite, la variable requestUri2 obtient la valeur http: // web-chicken-auth , un autre objet JSON StringContent est crééavec les paramètres de la requête HTTP POST d'origine, comme dans la partie précédente, et ces données sont envoyées à l'adresse requestUri2  dans la requête HTTP PUT. Enfin, le message Mot de passe modifié est renvoyé au client .


En bref: tout le processus commence par une requête HTTP POST à  / Auth / Change_Password_Test , qui attend un certain nombre de paramètres. Il prend ces valeurs et crée un objet JSON pour l'envoyer à  <base_url> / auth / login via HTTP POST. Si en réponse, il reçoit JSON avec le paramètre de code égal à 0, il crée un autre objet JSON et envoie une demande HTTP PUT à  http: // web-chicken-auth / auth / change_password .

Lors de l'apprentissage de l'API REST dans l'interface utilisateur de Swagger, nous voyons que dans le fichier DLL décompilé, il y avait non seulement les routes / auth / login et  / auth / change_password , mais également la route pour / auth / password_recovery .


Cette route d'API attendait un objet JSON avec deux paramètres: email et  secret_token . S'il l'a reçu, il a renvoyé un objet JSON avec une propriété de code de 0 et des chaînes de message et de  jeton .


Il s'avère que si nous forçons d'une manière ou d'une autre la méthode Check_Password_Test () à demander immédiatement l'itinéraire / auth / password_recovery au lieu de / auth / login , alors pour entrer dans la branche de changement de mot de passe, il suffira d'entrer une adresse e-mail valide.

Nous étudions à nouveau la partie du code dans laquelle la méthode Check_Password_Test () attribue la valeur requestUri dans la requête HTTP POST d'origine, et nous comprenons que nous devons envoyer la valeur http: // web-chicken-auth / auth / password_recovery # en tant que paramètre base_url et requestUri dans la finale le résultat sera http: // web-chicken-auth / auth / password_recovery # / auth / login .

Le caractère # dans l'URL HTTP indique qu'une partie de l'adresse de demande est terminée. Par conséquent, le serveur qui reçoit la demande ignorera simplement la partie / auth / login .


Bien sûr, cela est bien, mais nous devons juste comprendre comment le point de terminaison REST, où nous redirigeons maintenant la demande, recevra la valeur de l' e - mail . Sans cette valeur, la demande échouera et nous n'irons pas plus loin.

Nous examinons attentivement comment la méthode Check_Password_Test () crée des données JSON, et nous voyons que les données d'entrée ne sont pas effacées du tout, ce qui signifie que vous pouvez facilement implémenter vos paramètres. Il suffit d'envoyer les paramètres suivants dans la requête HTTP POST:

username = admin old_password
= pwned "," email ":" admin@chicken.ctf.zone "," lol ":"
new_password = p0tat0
base_url = http: // web-chicken-auth = http: // web-chicken-auth = http: // web-chicken-auth / auth / password_recovery #

L'objet JSON résultant est envoyé à  / auth / password_recovery :


Avec ces données JSON, nous avons satisfait aux exigences que / auth / password_recovery doit renvoyer une réponse, après quoi le serveur entrera dans la branche de changement de mot de passe (l'objet JSON contient la propriété email ). Maintenant, nous n'avons plus besoin de la valeur de old_password . Dans cette branche de code, nous avons fourni toutes les valeurs nécessaires pour changer le mot de passe (la propriété username est admin et la propriété password est p0tat0 ). La demande est terminée et nous obtenons un mot de passe modifié en réponse.


Il ne reste plus qu'à entrer un nouveau nom d'utilisateur et mot de passe sur la page de connexion!


Et voici notre drapeau:


All Articles