Récapitulatif des Challenges Techniques Réalisés
Pour cette première explication, ce premier write-up, je vais détailler tous les challenges que j'ai pu réaliser. Même s'il y en a qui sont assez simples, il est utile de tout expliquer, car cela peut servir à n'importe qui.
Challenge 1 : Analyse de Paquets Réseau
La première chose sur laquelle je me suis lancé concerne les challenges réseau.
Analyse avec Wireshark
J'ai utilisé Wireshark pour analyser une capture très courte contenant des paquets mal formés. Le seul indice initial était que les paquets étaient défectueux, mais qu'il était possible de retrouver une trace à l'intérieur.
En analysant la capture, j'ai remarqué plusieurs éléments :
- En descendant dans l'analyse, j'ai identifié un ACK.
- En regardant spécifiquement le protocole TCP, une fois la barre déroulée, nous avons repéré le flag ACK.
- Il était indiqué "TCP flag", ce qui est une information qu'on cherche depuis le début.
En examinant les données, j'ai constaté que les envois de requêtes DNS ou TCP étaient mal formés, avec quelques informations qui ne semblaient pas bouger.
Découverte des Informations Cachées
J'ai ensuite remarqué qu'au niveau du GET, correspondant au paquet/capture 10, qui est une requête HTTP, nous avions le flag. En descendant jusqu'à la toute fin, nous trouvions celui-ci.
Extraction et Décryptage
La seconde méthode pouvait consisté à déchiffrer la valeur de l'Authorization.
Cette valeur, lorsqu'elle est soumise à un encodeur/décodeur Base64 sur n'importe quel site, donne le résultat attendu. J'ai pu vérifier mon historique.
L'indice le plus important à retenir est que la présence de "==" à la fin d'une chaîne signifie toujours qu'il s'agit de Base64. C'est ce qui nous permet de déterminer le type d'encodage. Généralement, ces décodages sont très simples et des outils en ligne sont facilement trouvables.
Ceci a constitué le premier challenge.
Challenge 2 : Analyse d'une Exfiltration de Données par DNS
J'ai ensuite travaillé sur un deuxième cas, qui concernait également un réseau. Je suis tombé dessus par hasard, car les challs n'étaient pas forcément séparés par catégorie classique (Reverse, Web, Misc, etc.). Ce second cas était beaucoup plus long et contenait beaucoup plus d'informations.
Identification de l'Exfiltration
L'énoncé indiquait uniquement qu'il y avait une exfiltration de données. Cela implique nécessairement qu'il y a du bruit (à nos yeux) et que très peu de données sortent progressivement.
Analyse initiale du trafic
La première chose à regarder est évidemment la destination ou les destinations. Parmi l'ensemble des paquets, certains peuvent nous aider. Je suis passé dans l'onglet Statistics, puis j'ai filtré sur IPv4.
La première chose que j'ai vue était beaucoup de trafic vers 10.0.0.45. Bien que cela soit possible, certaines adresses n'étaient pas très intéressantes. Généralement, les plus grosses quantités de paquets ne sont pas les plus pertinentes.
Il faut toujours regarder les dernières :
- 8.8.8.8 : Les serveurs de Google.
- 1.1.1.1 : Cloudflare.
Pour une exfiltration, il s'agit soit de l'un ou de l'autre, parmi les serveurs externes. J'ai donc appliqué un filtre sur l'adresse 8.8.8.8. Mon intuition m'a orienté vers Google, donc je me suis concentré sur ce dernier. Nous sommes passés de 1238 échanges initiaux à 110...c'est un peu plus simple tout de suite :)
Détection des séquences de données
La première chose qui a sauté aux yeux était la présence de sortes de suites de caractères. Je l'ai remarqué en regardant la partie inférieure à droite, mais aussi Flag (ça sautait aux yeux sur l'écran principal, mais je n'avais pas levé la tête comme une nouille). J'ai ensuite examiné les Queries.
J'ai pris toutes ces requêtes et j'ai appliqué un second filtre. L'adresse 10.0.0.45 est présente dans 100 % des échanges, il ne devait n'y avoir qu'elle de compromise.
En faisant la somme totale, environ 10 % du trafic était dédié uniquement au DNS, ce qui suggère fortement une exfiltration de données par DNS (ou tunneling).
Méthode d'Inspection et Extraction
Les attaquants contournent souvent les pare-feu en encodant les données volées, généralement en Base64 ou en hexadécimal. L'hexadécimal est beaucoup plus difficile à gérer.
Le but était de filtrer via dns.query.name pour repérer ces données encodées.
J'ai utilisé la commande : dns.query.name contains.
Une fois que j'ai réussi à filtrer et à extraire les paquets, j'ai exporté les données au format .txt. Ce fichier contenait toutes les informations.
Traitement des données extraites
J'ai constaté que les numéros de séquence étaient triés de 0 à 29 (environ). Donc pas besoin de les réorganiser.
Idéalement, un script aurait pu supprimer les lignes inutiles, mais j'ai fait cette étape à la mano pour gagner du temps, car mon script Python n'avait pas fonctionné correctement.
Si jamais, ça ressemble à ça (les données étaient encodées en Base32) :
data = """PCOJLE3LKTJAOGGG5GZDEN3VZXFI5V3ZELYJRU54J26JD
44SVRAZDEB2ZJSWHGASTA2GH72KLOTJT26MRQRBWF3JY5
FASAODAOCITJUW52XSHW6BZQV4ZJBYCMC4KSJCBGRU766
46PQTT3734X3Z553ZZ5NX2AFT27G5LWYVAKA3RIJSKIHU
EIDLNKOTLULFVXJ6D7EFZ4QZB5LUKRVDF3RHZ3OBRIJJH
7JXPY545HUKLKJOS6CT3UDIVCVQONPPODXVD72PDEE5WI
3SOULXJFU7WOAD37S3G2D5DKUD5LPJA7G7CIV3YAFMSE3
OBOIZ3M4WTFYPR3NBYSH54DS4RUREF2DCYVS2YY5LKK7S
S7TU2UWHE3EIPKJF4XQZPCWVZ2NAWIDVX4YSMPJEDXU4M
FDHZB3BN3JUYHVEVPAPGPP3CZS7KJC7WNP3JAGC7RV2PD
XOZQKK332SZBQQ6QA5WTU3YLYGVZDUHDMDWJXFVPYVS2W
CEK6VIAQD5ZGZNJE2GGJFGYVU3RAWLIKCR6ZVONO3IIFT
26H4HMML7L4B7ETPW3OKXZIF5GAWOHH27CS2XPNGZYYNO
ELI2EKJT5SRGPMHLG5CKYQ2GMFWPPJB5M5AKHRJ7EAZNQ
3N46CIRTII6BTG4VKDW677J3JLTFMUVRELKU3HADP64FX
OAWCHVTH2B7AC4WNZUABVNGRCEOTQCW5BOFXAPY36DRLN
FYRFYW44C5ZM4NZ53HOYFAGPN3NLD3QKZIYTNTXBHTTSP
BADB5OB7PIUGFA3KVHRHML6N3IB3WLU6H4ZWOHLIXLC66
VQ2G7SGBWDCI2F2AJIKAE4CN3BSTV75HLHKLKQ5SNAUKO
UTIL7JGV7QGRH44VOV7ZMC7HR62HUMTNNAGZE2V74KIOW
HNUIKFQ2NATAF3MBVGSBNXVJMELTZRURFMSYFYCTGUYSY
PIB377ATZARWVOU2ZIGWHGQETSPT2X7JACPGQON7DM643
6VURYLREZ3G5IG3LPZ7D52T2O5VYJG2PTSW2GA2N7USST
ZCNHEELAINNTHES3LIH6JDKNFTUPSLKPKD275MYAMADBU
2LYOMQVRFJ34SZJRTHLHPABTZFSP3JP4WMDYJA6JFGKEG
GDELZ2FKJ6WNJCN4MHDFZNJ5HT3Z4HZY7XOEZAUNLFW3D
2KLTKJEHM4JMXG24Z2IQELFX4EIM44OXWXVYKWSJPKPEU
R2BFFODVKQZGL4CN5FJ553BLYGVSFDUDQBJUT3E5FFKG4
7H4QFO65QDPHIEXO42UCQCWCJWJUPGJ3OJQSN2MCWGTCS
Y3FA6UWTUIYLP4IRVFLMZH2PQZL4IZQ72ZBWOEKZ5TLPY
ZKEMFZKBMDYHRKNUKCZILUKWVNX7SKYGKLIMO3TK2CB7P
QLJ5H6WLZKM7DMUXWN6ZT57VUTJ3YNUAQLON77AORFP23
LEYSSNBIO67FSDIC4MS524LNXFHHN7OJUGONNDLTJPPL3
RPPO65V67F4PS3DCZV36MTAKUJGVORU4JCMGMIO5N3X3C
F7QXTYAIB4T2ARXSU5L7L5AY7LFDFQZJZLDKVBMCIC4BO
QR7UKQUWQQEK4BROQ6PBWXB5ZPDRZS2FLSBV6VWBPCJW7
33N3THD3XKS6TU4EG3WHYMTQ6D26ISEQIBPUYUTVC5KSQ
GO27Z5TMEY7MWCEGCRLHTR42XNCQFH5DJ6ZYYHILM74BM
GFRHITM22NNNW7CLKDVDHSLJHM2FYL43PCF3SIMXETDWU
OTL73AJSKSDRFTPTDRAZEINLIJ7HOZX2XH7MRRZJOW5H
QI7PBJP7S4255PVW4FY4VQ2LJ4TYZ333KQNCMVPU2XI7F
7YAKVMEMNA"""
Une fois fait, un second script Python m'a permis d'avoir de nouvelles infos pour reconstruire le fichier.
Reconstruction du Fichier
J'avais un fichier décompressé sauvegardé sous le nom fichier-final.bin.
J'ai obtenu un hexa qui indiquait un format/extension PNG, via le magic byte.
Nous avions donc un fichier bin, et il ne restait plus qu'à le traiter. En appliquant le format PNG, cela nous a donné un fichier final, qui était une petite image.
Stratégie de résolution des challenges en CTF
Rappel/Tips : Gestion du temps et des priorités
Concernant les challenges, il est important de savoir quand abandonner un sujet.
- Si, par ego, vous vous plongez automatiquement Ă fond dans un challenge et que vous y passez deux, trois, quatre heures, cela ne vaut pas le coup.
- Vous risquez de gâcher votre CTF, de perdre du plaisir et de perdre du temps sans gagner de points.
- Il est donc préférable, si vous ne sentez pas que vous pouvez avancer, de passer directement à autre chose. Au pire, vous pourrez y revenir plus tard.
En réalité, la plupart des CTF comportent tellement de challenges que vous ne reviendrez pas forcément dessus, sauf si toute l'équipe a quasiment tout clear.
Exemple personnel d'un ciao
Je ne sentais pas que je pouvais avancer sur le challenge en cours que j'avais choisi après ceux de réseaux. J'avais testé des injections, car le sujet était lié au web, mais ces tests ne m'avaient rien donné. Je n'avais pas d'intuition ni d'indices probants.
J'ai donc lâché l'affaire, sans jamais y revenir (spoiler alert : c'était une bonne chose :))
Challenge 3 : Reverse Engineering sur driver.sys
Tout au long de la semaine, j'avais entendu parler de reverse... Ça me travaillait trop pour passer à côté pour ce CTF.
Je suis directement parti sur ce côté-là .
Résolution du premier challenge en Reverse
Je suis tombé sur le tout premier challenge, qui était simple.
Il y a eu 3 étapes :
J'ai lancé Ghidra, mais je n'ai pas fait de recherche avancée. J'ai directement fouillé et lu le code.
Je suis tombé dessus par hasard donc c'est cool MAIS, pour les prochaines fois, je pense que la solution la plus simple est de faire un Ctrl+F et de bien configurer le champ de recherche contextuelle.
Il est préférable de tout sélectionner par défaut dans les champs de recherche.
Au lieu de passer cinq minutes, voire plus, ce challenge est pliable en 15 secondes (utile pour une épreuve de rapidité :)).
Challenge 4 : Reverse Engineering d'un fichier Half-Life 3
Après avoir résolu le premier challenge, j'ai décidé de continuer sur le reverse et passer au second challenge.
Pour ce nouveau challenge de Reverse Engineering, nous disposions d'un fichier half_life_3.luac.
L'énoncé indiquait que nous n'avions qu'une partie du code, mais que cela était largement suffisant pour découvrir le flag.
Analyse initiale du code
En arrivant sur la page, j'ai vu qu'il y avait très peu de code. Cependant, il y a une ligne contenant un ensemble de valeurs. J'ai tenté d'examiner cet ensemble de valeurs via l'onglet Data Type Manager, les labels, etc., pour voir ce qui pourrait être intéressant, mais je n'ai rien trouvé d'exploitable.
J'ai donc décidé de me concentrer sur ces valeurs. J'ai noté chaque valeur une par une, ce qui a permis d'obtenir un seul fichier contenant toutes ces informations. Le contexte de ce challenge, lié à ce fichier, me fait dire qu'il n'y a que ça à prendre.
Identification des éléments clés
Une partie de ce que j'ai pris ressemble à ça :
LJ@main.lua .4))M69B)B OLsub tonumber hex "bytes" i94) M69- 98$ 8 BA O 69 concat table b xor char string bit a key res i l 6 ' B33'' B BB6 B2K print-46167253bac2f5566e16d5cb8cdde9d9c3cf8ae1-13473310c1a0c62f5e78b194fced9beef7a3bf9c bit require bit
J'épargne le reste de l'atrocité, mais on peut voir plusieurs choses :
- Mentions XOR Byte
- Mentions XSTR
- Mentions Key Byte
- Mentions Data Byte
Il était évident que les mentions XOR Byte suggéraient qu'une opération XOR devait être effectuée.
Mise en œuvre de l'opération XOR
J'ai isolé les deux longues chaînes hexadécimales. L'objectif était de créer un petit code, exécutable via Python.
Autant vous dire que c'est un code très long :
data = bytes.fromhex("46167253bac2f5566e16d5cb8cdde9d9c3cf8ae1")
key = bytes.fromhex("13473310c1a0c62f5e78b194fced9beef7a3bf9c")
# Opération XOR byte par byte
decoded = bytes([a ^ b for a, b in zip(data, key)])
print(decoded.decode('utf-8'))
Le principe est simple :
- Nous avons la donnée et la clé sous forme de deux codes hexadécimaux.
- Il faut effectuer une opération XOR sur ces données.
- Une fois le résultat affiché et imprimé dans le terminal, nous obtenons le fichier final.
- Enfin, il ne restait plus qu'à convertir le résultat en format hexadécimal.
Une fois cette dernière étape réalisée, le flag était obtenu.
Challenge 5 : Résolution d'un Challenge de Code Morse
Le prochain challenge était hyper intéressant et assez marrant parce qu'il était vraiment unique en son genre. Il s'agissait d'un petit train qui passait, et à chaque passage, une lumière clignotait. Cette lumière était en fait un signal que nous devions écouter et capturer pour obtenir le code Morse et découvrir les trois mots qui s'y cachaient.
Tentatives initiales et approche technique
Le code Morse était transmis par des clignotements lumineux successifs. Au début, j'ai essayé de le prendre directement à la main, mais cela ne fonctionnait pas du tout. Spoiler : C'était trop compliqué vu la vitesse. Je me suis alors demandé comment capturer ce code Morse.
La première chose que j'ai faite a été d'ouvrir l'outil développeur de mon navigateur (Ctrl+Maj+C). Immédiatement, j'ai vu le code, et j'ai remarqué quelque chose qu'il y avait une div qui changeait à chaque clignotement.
Je me suis dit que si j'arrivais à capter ce timing, j'arriverais à déchiffrer le Morse. Évidemment, un petit nombre de millisecondes correspondrait à un point, tandis qu'un grand nombre de millisecondes correspondrait à un tiret. En fonction de cela, on pourrait déterminer le mot et capturer le flag.
Je suis donc parti sur cette analyse, car je sentais que c'était forcément la bonne approche, et je ne voyais pas d'autres solutions.
Analyse du DOM et exécution du script
Je suis passé par la Console : Le DOM n'était pas disponible. Le but était donc de taper Allow Pasting dans la console, appuyer sur Entrée, puis coller un code JavaScript nous permettant de récupérer ce code Morse pour déchiffrer le message.
Une fois que nous avions ce code Morse, la seule difficulté était d'exécuter le script ou de copier-coller au bon moment pour obtenir le signal Morse correct.
Problèmes de capture et déchiffrage
J'ai fait une première capture, mais elle était incomplète. Il semble que ma capture ait commencé au milieu d'une boucle avec des fragments, contenant des séquences comme "t3 t3 r" qui n'avaient pas de sens. Les mots précédents n'étaient pas forcément présents.
Afin de montrer à quoi ça ressemble, voici une partie de la sortie :
VM26133:13 État ALLUMÉ (ON) pendant 202 ms
VM26133:13 État ÉTEINT (OFF) pendant 201 ms
VM26133:13 État ALLUMÉ (ON) pendant 200 ms
VM26133:13 État ÉTEINT (OFF) pendant 201 ms
VM26133:13 État ALLUMÉ (ON) pendant 601 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 201 ms
VM26133:13 État ÉTEINT (OFF) pendant 600 ms
VM26133:13 État ALLUMÉ (ON) pendant 201 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 601 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 201 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 201 ms
VM26133:13 État ÉTEINT (OFF) pendant 601 ms
VM26133:13 État ALLUMÉ (ON) pendant 200 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 601 ms
VM26133:13 État ÉTEINT (OFF) pendant 601 ms
VM26133:13 État ALLUMÉ (ON) pendant 601 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 601 ms
VM26133:13 État ÉTEINT (OFF) pendant 200 ms
VM26133:13 État ALLUMÉ (ON) pendant 201 ms
VM26133:13 État ÉTEINT (OFF) pendant 1002 ms
VM26133:13 État ALLUMÉ (ON) pendant 200 ms
On voit tout de suite une chose quand on fait la capture :
- 200 millisecondes correspondaient Ă un point.
- 600 millisecondes correspondaient Ă un tiret.
- + de 600ms correspondaient Ă une pause entre deux mots.
C'est ainsi que j'ai réussi à reconstituer les mots. Le message était "M0RS3 FOR 3V3R", ce qui correspondait aux trois mots attendus.
J'ai effectué une deuxième et une dernière capture pour être absolument certain.
Conclusion du challenge
Bien que ce challenge ait été assez long à réaliser par rapport au temps d'écoute et à la nécessité de faire la bonne capture, le dilemme était finalement assez simple et très intuitif. Une fois que j'avais trouvé cette solution, je n'en voyais pas d'autre et il suffisait de foncer droit, ce qui a fonctionné.
Avec l'exfiltration DNS, je crois que c'est celui que j'ai le plus aimé.
Challenge 6 : Challenge de Mathématiques d'un vieux savant
Description du Challenge
Le titre/texte du challenge était le suivant : "Un savant possède un secret que vous souhaitez."
Pour obtenir ce secret, un serveur est mis en place qui pose des questions. Il faut y répondre dans un temps imparti, sans quoi la réponse ne sera jamais donnée.
Détails Techniques
- Netcat : 34.12.76.21 sur le port 1337.
- Objectif : Répondre à un code qui teste des opérations arithmétiques.
Méthode de Résolution
En accédant à la console, le message affiché était : "=== Oracle Arithmetics === Solve 20 equations. You have exactly 1.0 seconds for each! Operations allowed: +, -, *, / If the result is a decimal, send it back exactly as it evaluates in Python (or rounded to 2 decimal places). "
Le processus était simple :
- Appuyer sur "Enter to restart".
- Observer la première opération, qui était : 52 * (75 / 2).
- Initialement, j'ai répondu au hasard pour provoquer un "times up".
Une fois ces informations obtenues, ce type de challenge devient extrêmement simple. Il suffit de créer un code qui remplisse la tâche à notre place, en envoyant les requêtes nécessaires.
Implémentation du Code
Le code doit :
- Être capable de gérer les requêtes (ce qui implique forcément le package request).
- D'extraire l'équation avec une expression régulière.
- De la faire passer dans la fonction eval() (Fonction intégrée qui analyse une expression sous forme de chaîne de caractères (ou d'objet code compilé) et retourne le résultat de cette évaluation).
- Effectuer le calcul arithmétique demandé.
- Renvoyer le résultat.
Il est important de conserver ce type d'implémentation, car ce type de chall apparaît souvent en CTF.
Finish him
Une fois que notre code est prêt, il suffit de le lancer en ciblant l'adresse et le port spécifiés. Il ne reste qu'à attendre la fin du processus.
Point de Vigilance : La Connexion
Il est crucial de noter l'importance de la connexion. Si votre connexion n'est pas stable pour ce genre de challenge, vous risquez d'être rapidement bloqué (normalement ça n'arrive jamais, car les orgas mettent le paquet sur le réseau sans mauvais jeu de mots).
Challenge 7 : Le vieux savant et ses Automates
Le challenge suivant après les mathématiques était différent, mais il conservait le même principe : répondre à 20 questions assez rapidement.
Introduction au Concept d'Automate
Le texte du challenge était le suivant : "Le savant a été stun par votre capacité à faire des calculs de base. Pour le prochain secret, vous allez devoir vous attaquer à des automates héhé"
Bon et bien c'est parti...
Le terme automate, en informatique, fait référence aux automates finis, soit les DFA (Deterministic Finite Automata) ou les NFA (Non-deterministic Finite Automata).
En substance, il s'agit de machines à états avec des transitions. Pour illustrer cela, on peut prendre l'exemple d'une machine à café : c'est un automate fini. Vous effectuez une action, la machine prend en compte cette action, elle commande votre café, elle vous le sert, puis elle recommence.
Le Défi Spécifique : DFA
Dans le cadre du challenge, l'énoncé était :
=== Oracle Automate (Speed Parsing) ===
I will give you 20 Deterministic Finite Automata (DFA).
For each one, you have exactly 1.0 seconds to tell me if it Accepts (A) or Rejects (R) the given string!
L'état de la première machine... `insert issou gif`
Start State: 0
Accept States: [2, 5]
Transitions:
0 --0--> 1
0 --1--> 4
1 --0--> 1
1 --1--> 0
2 --0--> 2
2 --1--> 6
3 --0--> 0
3 --1--> 2
4 --0--> 0
4 --1--> 1
5 --0--> 0
5 --1--> 2
6 --0--> 0
6 --1--> 2
String: 11111101
Answer (A/R) >
Comme précédemment, il fallait utiliser un script Python, en jouant sur les mêmes principes de base de question-réponse et d'implémentation de code.
Méthodologie de Résolution
La démarche consistait à :
- Faire un import socket pour se connecter au serveur et lire les réponses.
- Extraire dynamiquement l'état initial et la liste des états d'acceptation.
- Construire un dictionnaire des transitions.
- Simuler la lecture de la chaîne (ce qui aide, même si c'est rare, il faut le souligner).
Challenge plié.
Challenge 8 : Résolution d'un Challenge de Sécurité Web
Le texte était le suivant : "Oh sympa ce nouveau blog pour des jeux vidéo. Les bulles proposées pour Balatro sont vraiment fortes... http://34.158.170.152/"
Approche du Challenge
J'ai abordé ce challenge qui concernait un site avec une connexion utilisateur. J'ai mon pote qui est venu m'aider en m'informant qu'en tapant user, user pour le nom d'utilisateur et le mot de passe, nous pouvions nous connecter. Cependant, nous étions initialement en tant que User pour le rôle.
S/O pour cette information, car elle m'a permis de résoudre très rapidement le challenge grâce à cette donnée de connexion.
Utilisation de Burp Suite pour l'Interception
Une fois que nous avions la méthode de connexion, la suite était simple. On capture l'échange avec Burp Suite pour intercepter les requêtes. Pour l'interception, nous pouvons configurer Burp Suite pour qu'il soit connecté à notre navigateur de base. Comme je n'ai pas fait cette configuration, le navigateur intégré peut prendre du temps à démarrer. En pleine course contre-la-montre d'un CTF, bon...c'est du temps de perdu pour rien.
Bref, je me suis connecté avec la paire user, user en utilisant l'option "Intercept" dans l'onglet "Proxy". Cela permet de rejouer les requêtes ou, du moins, de voir les requêtes qui sont envoyées. C'est assez intéressant, car cela permet d'analyser les requêtes envoyées et ce que nous recevons en retour.
J'ai donc configuré Proxy / Intercept en mode Intercept On. Bien qu'il existe un Repeater pour tester plus de choses, je n'ai pas eu besoin de passer par là .
Analyse des Réponses et Exploitation
En passant par cette méthode, j'ai vu une réponse contenant le rôle et le session id.
Nous avions aussi le cookie qui nous donnait ces informations. On pouvait le voir parce que Burp Suite fait automatiquement le décodage quand on passe la souris sur lui.
Pour expliquer plus en détail, concernant le cookie, je me suis demandé ce que cela pouvait être. Il s'agissait en réalité d'un code en Base64 qu'il fallait fournir pour la session. Cette session ID nous donnait tout ce dont on avait besoin.
Pourquoi j'ai tout de suite vu que c'était du base64, et bien comme j'ai pu le dire dans les précédents WU : Si == alors Base64.
Décodage et Connexion en tant qu'Administrateur
D'après toutes ces informations, peu importe le nom d'utilisateur ou le mot de passe que j'utilisais, si je modifiais le rôle pour le mettre en Admin avec un "A" majuscule, par exemple, mais alors vraiment à tout hasard, je pouvais bypasser la sécurité et être en mode admin.
J'ai donc décodé le cookie, modifié le rôle en Admin, puis recoder en Base64 et jackpot. Nous étions bien entrés en tant qu'administrateur. Une fois connecté en tant qu'admin, il était simple de se déplacer partout et de trouver le flag.
Pause et retour sur du Reverse
Avant de parler du prochain challenge, il faut faire une petite parenthèse sur le Kahoot qu'ils nous ont permis de faire en milieu de session.
Le Défi du Kahoot
Ce challenge était hyper intéressant, même s'il comportait 50 questions. Le but : Nous faire perdre du temps.
Cependant, si l'on se classait dans le top 5, on pouvait quand mĂŞme gagner des points.
Concernant ma performance :
- Entre la question 10 et la question 35-40, j'étais top 1.
- Le downfall est arrivé et j'ai terminé 4ème.
- C'est hors contexte, mais va falloir me retrouver la personne qui a mis des questions sur du reverse de logiciels Unity
Finalement, j'ai fini à la quatrième place, à un point du troisième. Malgré les questions de NICHE (Unity, SDIM, etc.), dans le fond, c'est une épreuve sympa qui permet de faire une pause et de sortir la tête de son écran.
Challenge 9 : La planète des images
Analyse du Code et Découverte de l'Indice
Le but était de trouver une valeur permettant de chiffrer ou déchiffrer, mais il ne s'agissait pas forcément de comprendre le code, juste de trouver une valeur.
En me baladant dans le code, j'ai réalisé qu'un indice était présent, caché dans le nom de l'épreuve, dans le texte, ou dans la manière dont il était expliqué. Je ne l'ai pas vu immédiatement, mais je l'ai compris après l'avoir trouvé.
En examinant les différentes parties du code, j'ai remarqué une fonction contenant la fonction strcmp. Cette notion de comparaison ne m'était pas immédiatement évidente.
C'est une erreur fréquente quand on est la tête sous l'eau, et qu'on travaille depuis plusieurs heures : on ne lit pas forcément le challenge en entier ou on ignore les indices cachés.
Cependant, une fois que l'on identifie la comparaison comme un mot-clé important, il est facile de retrouver la valeur parmi toutes les fonctions.
Résolution du Challenge
La bonne valeur était 9888c6369bad50f1, présente dans une fonction booléenne. Je ne me souviens plus exactement comment je l'ai trouvée à l'époque, mais j'avais fait le tour des fonctions à la recherche d'une valeur correspondante.
Ce n'est qu'en descendant dans le code que j'ai pu voir ce passage. J'étais passé par l'onglet Symbol Tree/Functions, où se trouvent généralement toutes les fonctions, ce qui est souvent ma première étape.
J'ai réussi à résoudre le défi assez facilement à la fin. Je ne sais plus s'il fallait simplement insérer cette valeur ou autre chose, car je n'ai plus l'énoncé complet du challenge. Mais je crois que trouver cette valeur était suffisant pour effectuer le changement ou servir de flag, et tout était bon.
Challenge 10 : Exploitation d'un Accès SSH sur Système Linux
Le dernier challenge de la soirée consistait à exploiter un accès SSH obtenu sur le compte d'un développeur.
Vous avez obtenu un accès SSH au compte d'un développeur. Il se peut que vous puissiez lire des fichier auquels vous n'êtes pas censé avoir accès trouver comment !
SSH Access: developer:devpass123 port 1337 35.234.174.163
Phase Initiale et Analyse de l'Environnement
Nous sommes propulsés sur une machine Linux. La première étape a été de recueillir des informations initiales.
J'ai exécuté les commandes suivantes pour évaluer l'environnement :
lscat TODO.txt
Le fichier TODO.txt contenait une information cruciale : Note to self: The python interpreter has been granted special permissions for the audit script.
L'interpréteur Python possède donc des permissions spéciales permettant d'effectuer des audits.
Compréhension de la Vulnérabilité Python
Ceci a immédiatement orienté la résolution du chall. Il est expliqué que Python permet d'effectuer des actions telles que :
- Lire des répertoires adjacents, même sans permission d'accès explicite.
- Lire des fichiers en effectuant un changement de répertoire (
change directory).
J'ai donc essayé de vérifier cela via des commandes pour en apprendre plus sur python.
Stratégie d'Exploitation
La stratégie suivante s'est articulée autour de la vérification des capacités de l'installation Python.
Identification de l'Interpréteur
Il était essentiel de déterminer la version de Python utilisée, afin de s'assurer que nous travaillions avec Python 3, car nous utiliserions des commandes spécifiques à cette version.
J'ai utilisé which python3 pour savoir où était l'exécutable, ce qui a renvoyé le chemin : /usr/bin/python3. Ce chemin est un lien symbolique, mais j'ai décidé de me concentrer sur ce que nous pouvions faire avec cet interpréteur.
Test des Permissions et Découverte de la Faille
En examinant les droits via ls -la, nous avons confirmé les permissions (-rwxr-xr-x 1 root root 5941864 Aug 31 10:18 /usr/bin/python3.10).
Enfin, j'ai fait la commande getcap /usr/bin/python3.10 pour vérifier sa capacité à lire où je veux : /usr/bin/python3.10 cap_dac_read_search=ep.
La faille principale identifiée est la capacité de contourner les vérifications standards de lecture de fichiers et de recherche dans les répertoires, ce qui est lié au Discretionary Access Control (DAC).
Théoriquement, tout script ou commande exécuté via cet interpréteur Python spécifique hérite de cette capacité, lui permettant de lire le contenu des répertoires normalement restreints.
En utilisant la fonction open() de Python, il est donc possible d'accéder aux ressources du répertoire auquel nous ne pouvons pas y accéder.
Résolution du Challenge
J'ai commencé par tester des commandes pour trouver des fichiers comme flag.txt ou flag, sans succès initial.
J'ai donc lu le répertoire :
python3.10 -c "import os; print(os.listdir('/home/security-manager'))"
J'ai eu l'ensemble des fichiers Ă savoir : ['.bashrc', '.profile', '.bash_logout', 'flag2.txt', 'audit_report.txt']
La correction a été de réaliser que le fichier cible était flag2.txt, et non flag.txt.
En ciblant correctement le répertoire, j'ai pu lire le fichier :
python3.10 -c "print(open('/home/security-manager/flag2.txt').read())""
et, par conséquent, trouver le flag.