Advertisement
Guest User

Untitled

a guest
Mar 19th, 2018
441
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 21.82 KB | None | 0 0
  1. CTF (Capture The Flag) de la Toulouse Hacking Convention. Le principe est le suivant : Vous prenez des gens avec des ordinateurs, regroupés en équipe, vous les mettez dans une salle pendant toute une nuit et vous leur soumettez des challenges de sécurité informatique, de différentes sortes :
  2.  
  3. accéder à un serveur via un site web comportant une vulnérabilité
  4. résoudre des énigmes de logiques, comprendre le fonctionnement d'un algorithme
  5. retrouver des informations dissimulées dans un fichier (une image, une vidéo ou autre)
  6. etc.
  7.  
  8. Chaque challenge réussi donne un code secret (un flag), qui permet d'obtenir des points, un classement, et des goodies pour les meilleurs.
  9.  
  10. Je vous propose ici le récit de ce que j'ai réussi à faire sur un challenge en particulier. Émotions, rebondissements, doutes existentiels et scripts en langage python sont au rendez-vous.
  11. Oracle au désespoir
  12.  
  13. Vers les 25 heures du soir, après m'être acharné (avec des résultats plus ou moins probants) sur les challenges web, je me suis intéressé aux challenges de type "oracle". Le fonctionnement, que j'ai découvert sur place, consiste à dialoguer avec un ordinateur proposant une énigme. Le flag est obtenu lorsque celle-ci est résolue . Le rapport avec la sécurité informatique est assez éloigné, ça n'en est pas moins amusant.
  14.  
  15. L'énoncé du challenge est simple : "quelqu'un vous attend ici : challs.thcon.party:7121"
  16.  
  17. Je vous passe les détails techniques (connexion socket au port indiqué avec la commande netcat). Les échanges d'information sont effectués via une console en ligne de commande. C'est comme du chat, mais avec un ordinateur.
  18.  
  19. Je me connecte et suis accueilli par un message sybillin me demandant 32 octets :
  20.  
  21. root@kali:~/recher: nc challs.thcon.party 7121
  22.  
  23. ...do computers have free will?
  24.  
  25. Let’s find out: ( 0/128)
  26. - Please send 32 bytes:
  27.  
  28. Soit. J'écris 32 fois la lettre "a" et appuie sur Entrée. Le serveur répond que je dois envoyer "64 nibbles", puis coupe la connexion.
  29.  
  30. Je demande de l'aide à un collègue. S'ensuit alors une conversation bizarre :
  31.  
  32. − Ouh mais il est érotique, ton challenge !
  33.  
  34. − C'est écrit "nibbles", pas "nipples".
  35.  
  36. (Les conversations avec mes collègues deviennent souvent bizarres, mais ce n'est jamais de ma faute).
  37.  
  38. Les échanges entre machines ajoutent parfois des caractères à la fin, pour les sauts de ligne. Je recommence et écrit 31 "a", puis 30 "a", puis la même chose avec des zéros, sans plus de résultats.
  39.  
  40. J'en viens à me demander si ce n'est pas des histoires bizarres d'encodage et de conversion caractères -> octets. Je teste des commandes tel que : "echo -e '\x80\x80\x80...' | nc challs.thcon.party 7121", avec 32 fois "\x80", puis 31, puis 30. (Je vous passe à nouveau les détails techniques).
  41.  
  42. Je finis par appeler au secours l'un des organisateurs, qui m'explique qu'il faut envoyer 64 caractères hexadécimaux. Bon, c'était tout simple. Et c'était presque clairement énoncé.
  43. Sous le soleil des entropiques
  44.  
  45. J'envoie 64 caractères, le serveur m'en répond 64 autres, qui ne semblent correspondre à aucune logique. Puis il me demande si c'est de "l'entropie" ou pas.
  46.  
  47. Ce mot a des significations diverses, dans plusieurs domaines scientifiques. Mais lorsqu'un informaticien l'utilise, ça veut juste dire "le hasard". On a l'air beaucoup plus savant lorsqu'on utilise des mots rares et compliqués, même pour désigner des concepts simples.
  48.  
  49. Comme je n'en ai aucune idée, je réponds au hasard que ce n'est pas de l'entropie (j'aurais aussi pu répondre entropiquement que ce n'était pas du hasard). Coup de bol, c'est la bonne réponse. Le serveur me redemande alors 32 octets, mais le compteur s'est incrémenté et affiche maintenant 1/128, et ainsi de suite jusqu'à ce que ma réponse soit incorrecte.
  50.  
  51. Je recommence quelques fois, afin d'avoir suffisamment de données de tests. Voici la session la plus longue que j'ai pu obtenir :
  52.  
  53. root@kali:~/recher: nc challs.thcon.party 7121
  54. ...do computers have free will?
  55.  
  56. Let's find out: ( 0/128)
  57.  
  58. # " J'écris 64 caractères hexadécimaux. "
  59. - Please send 32 bytes: 0123456789012345678901234567890123456789012345678901234567890123
  60.  
  61. # " Ceci est la réponse du serveur : "
  62. - Here is my choice: 48f14855c629f4c9316201c5f4717117098b75978ff9f70881fcb7277139a4bd
  63.  
  64. - Was it entropy?
  65. - Yes (0)
  66. - No (1) 0 <-- "Ceci est ma réponse : 'oui, c'est de l'entropie'."
  67.  
  68. # " Le compteur augmente de 1, la même question est posée à nouveau. "
  69. # " À partir de maintenant, je décide de n'envoyer que des 0, "
  70. # " juste pour tester. "
  71. Let's find out: ( 1/128)
  72. - Please send 32 bytes: 0000000000000000000000000000000000000000000000000000000000000000
  73. - Here is my choice: 1b3e137c6f17fd570d4854731c37d5671b3e137c6f17fd570d4854731c37d567
  74. - Was it entropy?
  75. - Yes (0)
  76. - No (1) 1
  77.  
  78. Let's find out: ( 2/128)
  79. - Please send 32 bytes: 0000000000000000000000000000000000000000000000000000000000000000
  80. - Here is my choice: b19e785dc5a6b603b9e96930f452289b27813290e8967cf23ca0a89af769cad4
  81. - Was it entropy?
  82. - Yes (0)
  83. - No (1) 0
  84.  
  85. Let's find out: ( 3/128)
  86. - Please send 32 bytes: 0000000000000000000000000000000000000000000000000000000000000000
  87. - Here is my choice: 5f714af4848475d109c1071c27e1df125f714af4848475d109c1071c27e1df12
  88. - Was it entropy?
  89. - Yes (0)
  90. - No (1) 1
  91.  
  92. Let's find out: ( 4/128)
  93. - Please send 32 bytes: 0000000000000000000000000000000000000000000000000000000000000000
  94. - Here is my choice: 92043b718a3b076d12b4c2d287031e6f92043b718a3b076d12b4c2d287031e6f
  95. - Was it entropy?
  96. - Yes (0)
  97. - No (1) 1
  98.  
  99. Let's find out: ( 5/128)
  100. - Please send 32 bytes: 0000000000000000000000000000000000000000000000000000000000000000
  101. - Here is my choice: d8d228fd8c44dda81339a2f65b86a304444e30cb17848e77950b98b3f7af37ef
  102. - Was it entropy?
  103. - Yes (0)
  104. - No (1) 1
  105.  
  106. ...eh, told ya!
  107.  
  108. Your oracle failed, computers do have free will, right?
  109.  
  110. Vous pouvez répéter la réponse ?
  111.  
  112. L'un des démarches possibles pour résoudre cette énigme aurait été de créer beaucoup de données de tests, et de balancer le tout à notre collègue intelligence-artificielier (c'est un vrai nom de métier). Il s'en serait débrouillé et m'aurait certainement ressorti la logique sous-jacente. Malheureusement, celui-ci n'était pas disponible.
  113.  
  114. J'ai décidé d'étudier uniquement les cas où j'envoie 64 fois le caractère "0". C'est une valeur particulière, et avec un peu de chance le comportement associé serait plus simple. Comme on peut envoyer plusieurs fois la même chose durant une même session, si je ne comprends que ce cas particulier, ce sera suffisant.
  115.  
  116. J'ai également décidé de ne pas m'attarder sur les réponses entropiques, puisque c'est supposé être du hasard complet. J'ai donc rassemblé les réponses non-entropiques obtenues avec 64 zéros, afin d'essayer d'y trouver un motif particulier :
  117.  
  118. '1b3e137c6f17fd570d4854731c37d5671b3e137c6f17fd570d4854731c37d567'
  119.  
  120. '5f714af4848475d109c1071c27e1df125f714af4848475d109c1071c27e1df12'
  121.  
  122. '92043b718a3b076d12b4c2d287031e6f92043b718a3b076d12b4c2d287031e6f'
  123.  
  124. Si vous avez un œil de lynx, vous remarquerez tout de suite quelque chose. Ça n'a pas été mon cas, j'ai donc utilisé ma fonction "plop()", codée en langage python. Celle-ci affiche une suite d'octets sous différents formats. Voici le plop de la première réponse :
  125.  
  126. >>> snippets.plop("1b3e137c6f17fd570d4854731c37d5671b3e137c6f17fd570d4854731c37d567")
  127.  
  128. entier : 12322125917677961190078392629443594210871360495881596387589866311484110787943
  129. liste entier : [27, 62, 19, 124, 111, 23, 253, 87, 13, 72, 84, 115, 28, 55, 213, 103, 27, 62, 19, 124, 111, 23, 253, 87, 13, 72, 84, 115, 28, 55, 213, 103]
  130. hexa upcase : 1B3E137C6F17FD570D4854731C37D5671B3E137C6F17FD570D4854731C37D567
  131. hexa lowcase : 1b3e137c6f17fd570d4854731c37d5671b3e137c6f17fd570d4854731c37d567
  132. hexa-space upcase : 1B 3E 13 7C 6F 17 FD 57 0D 48 54 73 1C 37 D5 67 1B 3E 13 7C 6F 17 FD 57 0D 48 54 73 1C 37 D5 67
  133. hexa-space lowcase : 1b 3e 13 7c 6f 17 fd 57 0d 48 54 73 1c 37 d5 67 1b 3e 13 7c 6f 17 fd 57 0d 48 54 73 1c 37 d5 67
  134. str ascii : fail
  135. str utf-8 : fail
  136.  
  137. # Désolé pour la coloration syntaxique totalement anarchique.
  138. # Linkedin n’est pas très bon sur ce sujet.
  139.  
  140. La liste d'entier commence par 27. J'ai cherché à tout hasard s'il y en avait un autre, que j'ai trouvé un peu plus loin. Encouragé par cette découverte, je cherche un deuxième 62, que je trouve à côté du deuxième 27, et ainsi de suite. Le même paquet de valeurs est répété deux fois !
  141.  
  142. On aurait pu s'en apercevoir en jouant un peu avec les données. La somme des entiers de chaque réponse donne systématiquement un nombre pair, ce qui peut mettre la puce à l'oreille. Et si on trie les entiers d'une réponse, on voit tout de suite que chacun d'eux apparaît deux fois. Ça se fait très facilement avec un petit bout de python :
  143.  
  144. sorted([27, 62, 19, 124, 111, 23, 253, 87, 13, 72, 84, 115, 28, 55, etc. ])
  145.  
  146. [13, 13, 19, 19, 23, 23, 27, 27, 28, 28, 55, 55, 62, 62, 72, 72, etc. ]
  147.  
  148. Il ne reste plus qu'à confirmer la présence de ce motif dans les autres réponses. Pas la peine de les ploper. On ajoute juste des espaces au milieu :
  149.  
  150. '1b3e137c6f17fd570d4854731c37d567 1b3e137c6f17fd570d4854731c37d567'
  151.  
  152. '5f714af4848475d109c1071c27e1df12 5f714af4848475d109c1071c27e1df12'
  153.  
  154. '92043b718a3b076d12b4c2d287031e6f 92043b718a3b076d12b4c2d287031e6f'
  155.  
  156. Ce qui nous amène à la règle suivante :
  157.  
  158. Lorsqu'on envoie 64 fois le caractère "0", la réponse n'est pas de l'entropie si elle est constituée d'un groupe de 32 caractères répétés deux fois.
  159.  
  160. Je ne sais pas si cette règle est applicable lorsqu'on envoie autre chose que des zéros. D'après les logs que j'ai gardés, il semblerait que non. Mais ça n'a aucune importance, le but étant d'obtenir le flag, pas de comprendre le fonctionnement dans son intégralité.
  161. 128 fois sur le métier remettez votre ouvrage...
  162.  
  163. Il reste à automatiser tout cela, à l'aide d'un script quelconque capable d'établir une connexion avec une machine distante et d'envoyer/recevoir des données.
  164.  
  165. Je m'étais préalablement renseigné sur les Capture The Flag en général et la THCON en particulier, j'avais trouvé des challenges de l'année précédente, dont certaines solutions utilisaient la librairie python "pwntools".
  166.  
  167. Il s'agit d'une librairie spécifiquement destinée aux CTF, avec des fonctions de décompilation, hash, communication, etc. C'est l'occasion d'apprendre à s'en servir. Je commence par un gentil petit test :
  168.  
  169. from pwn import *
  170. r = remote("challs.thcon.party", 7121)
  171. for _ in range(3):
  172. print(r.recvline())
  173. r.sendline("0" * 64)
  174.  
  175. (N'importe quel développeur python vous dira que c'est vilain de faire des "import *", mais quand on fait du CTF, les scripts sont rapides et sales. Je ne manquerais pas de vous le démontrer dans la suite de cet article).
  176.  
  177. Et là, ça plante. Je ne saurais précisément vous dire pourquoi. Mon script était peut-être incorrect, ou bien c'était normal que ça plante puisque je termine la connexion brutalement, où c'est le serveur qui commençait à saturer. Bref, je laisse tomber pwntools.
  178.  
  179. Un collègue me suggère de faire un script expect. Mais c'est un langage que je ne connais que très peu, je n'avais pas vraiment le temps de m'y replonger, et après avoir parcouru diverses documentations, je ne trouvais toujours pas d'exemple simple effectuant des actions différentes en fonction de ce que renvoie la machine distante. Exit expect, donc.
  180.  
  181. S'ensuit un passage de ma vie dont j'ai un peu honte, où je tente de résoudre l'énigme manuellement. Je me suis trompé à 60/128.
  182.  
  183. J'implore la pitié d'un des organisateurs, lui demandant s'il ne connaîtrait pas un moyen technique simple de scripter des échanges entre machines. Il me renvoie à pwntools, avec la fonction recvuntil(), qui récupère tout ce qu'envoie le serveur jusqu'à atteindre un mot spécifique, passé en paramètre.
  184.  
  185. Je teste rapidement. Ça semble fonctionner, mais pas tout le temps. Étrange...
  186. Veuillez nous excuser pour la gêne occasionnée
  187.  
  188. J'avance progressivement dans le codage de mon script, mais chaque test est chaotique, la connexion se coupe parfois inopinément.
  189.  
  190. Un organisateur m'informe alors que "on a un petit souci technique parce que les serveurs de challenges ont été codés avec le cul, et il y a une fork bomb" (je cite de mémoire). Il me rassure plus ou moins en précisant qu'ils recherchent activement l'origine du problème.
  191.  
  192. J'ai la confirmation de ce fait, en réalisant que la connexion n'est plus du tout possible. Je termine mon script, sans aucune certitude sur son fonctionnement, puisque je ne peux plus le tester.
  193.  
  194. En attendant la résurrection du serveur, je tente de me concentrer sur d'autres challenges, mais je n'y parviens pas. J'envisageais le risque qu'il n'y aurait plus que des périodes de disponibilités très courtes. Je me suis donc retrouvé à tester régulièrement, pendant une petite dizaine de minutes.
  195. Vous pouvez re-répéter la re-réponse ?
  196.  
  197. Le serveur revient enfin parmi les vivants ! Je lance fébrilement mon script. Miraculeusement, il fonctionne du premier coup, et je vois défiler à toute vitesse les 128 questions.
  198.  
  199. Et boum, ça plante.
  200.  
  201. La fonction recvuntil() récupère correctement les données, mais lorsque la connexion se termine sans que le mot attendu apparaisse, elle balance une exception sans rien restituer. Pour résumer : le serveur venait de m'envoyer le flag, mais mon script ne l'avait pas affiché. Bravo.
  202.  
  203. Je corrige, en ajoutant vite-fait-mal-fait une boucle avec des recvline(). C'est moins intelligent comme fonction, mais au moins ça restitue.
  204.  
  205. Mon script replante parce que j'ai mal compté les itérations. Il considère qu'il faut répondre 129 fois et non pas 128 avant de finir par les recvline(). Quand on prend le temps de réfléchir 2 secondes, on ne fait pas des bourdes de ce genre. Mais là, il est 25h30 du matin, le serveur peut retomber en panne d'un instant à l'autre, je suis très près du but, ma bière est vide, ce n'est donc pas le moment de perdre 2 secondes à réfléchir.
  206.  
  207. Après suppression de la bourde et re-exécution tout aussi fébrile, les derniers échanges s'affichent, contenant le précieux flag. Je le copie-colle dans le scoreboard et valide le challenge. Enfin !
  208. Fort sale
  209.  
  210. Voici mon script dans sa version finale. Si vous entendez un bruit dans le fond, c'est un développeur python qui vient de s'évanouir. Le même bloc de code est répété trois fois, il y a des données de tests sous forme de commentaire jetées un peu partout, les logs ne sont pas homogènes, ça plante quand même à la fin, ça a été programmé avec des parties du corps que je n'ose même pas mentionner ici, mais ça sort le flag.
  211.  
  212. from pwn import *
  213.  
  214. r = remote("challs.thcon.party", 7121)
  215. print('-' * 50)
  216. print(r.recvuntil("bytes:"))
  217. print('-' * 50)
  218. r.sendline("0000000000000000000000000000000000000000000000000000000000000000")
  219. print('-' * 50)
  220.  
  221. answer = r.recvuntil("(1)")
  222. # Here is my choice: bc9d65f48352864a31c71bc772a1bb2822cc0c2f5250012e071ffe2906207103\n - Was it entropy?\n - Yes (0)\n - No (1)
  223. # pifpaf choice: ba8aeb0ff152efb909ab87ac231db654ba8aeb0ff152efb909ab87ac231db654\n pouet
  224. print(answer)
  225.  
  226. answer = answer.decode("ascii")
  227. start, middle, end_answer = answer.partition('choice:')
  228. end_answer = end_answer.strip()
  229. print(end_answer)
  230. end_answer = end_answer.split()[0]
  231. print(end_answer)
  232. if len(end_answer) != 64:
  233. raise Exception("Bleuargh")
  234. answer_processed = end_answer
  235. if answer_processed[:32] == answer_processed[32:]:
  236. entropy = "1"
  237. else:
  238. entropy = "0"
  239. print(entropy)
  240.  
  241. print('-' * 50)
  242. r.sendline(entropy)
  243. print(r.recvuntil("bytes:"))
  244.  
  245. # Et donc ici, il faut bien mettre 126, et non pas 127. Sinon ça fait une question de trop.
  246. for count in range(126):
  247.  
  248. print('#'*10 + str(count) + '#'*10)
  249. print('-' * 50)
  250. r.sendline("0000000000000000000000000000000000000000000000000000000000000000")
  251. print('-' * 50)
  252.  
  253. answer = r.recvuntil("(1)")
  254. # Here is my choice: bc9d65f48352864a31c71bc772a1bb2822cc0c2f5250012e071ffe2906207103\n - Was it entropy?\n - Yes (0)\n - No (1)
  255. print(answer)
  256.  
  257. answer = answer.decode("ascii")
  258. start, middle, end_answer = answer.partition('choice:')
  259. end_answer = end_answer.strip()
  260. print(end_answer)
  261. end_answer = end_answer.split()[0]
  262. print(end_answer)
  263. if len(end_answer) != 64:
  264. raise Exception("Bleuargh")
  265. answer_processed = end_answer
  266. if answer_processed[:32] == answer_processed[32:]:
  267. entropy = "1"
  268. else:
  269. entropy = "0"
  270. print(entropy)
  271.  
  272. print('-' * 50)
  273. r.sendline(entropy)
  274. print(r.recvuntil("bytes:"))
  275.  
  276. print('#'*50 + "last")
  277. print('-' * 50)
  278. r.sendline("0000000000000000000000000000000000000000000000000000000000000000")
  279. print('-' * 50)
  280.  
  281. answer = r.recvuntil("(1)")
  282. # Here is my choice: bc9d65f48352864a31c71bc772a1bb2822cc0c2f5250012e071ffe2906207103\n - Was it entropy?\n - Yes (0)\n - No (1)
  283. print(answer)
  284.  
  285. answer = answer.decode("ascii")
  286. start, middle, end_answer = answer.partition('choice:')
  287. end_answer = end_answer.strip()
  288. print(end_answer)
  289. end_answer = end_answer.split()[0]
  290. print(end_answer)
  291. if len(end_answer) != 64:
  292. raise Exception("Bleuargh")
  293. answer_processed = end_answer
  294. if answer_processed[:32] == answer_processed[32:]:
  295. entropy = "1"
  296. else:
  297. entropy = "0"
  298. print(entropy)
  299.  
  300. print('-' * 50)
  301. r.sendline(entropy)
  302.  
  303. # La bonne vieille boucle infinie de recvline().
  304. while True:
  305. print(r.recvline())
  306.  
  307. #print(r.recvline())
  308. #print(r.recvline())
  309.  
  310. Et voici la même chose en version propre (mais je n'ai pas pu la tester).
  311.  
  312. import time
  313. import pwn
  314.  
  315. KNOWN_NIBBLES_TO_SEND = '0' * 64
  316.  
  317. def between(data, token_before, token_after):
  318. """
  319. Récupère les caractères entre deux tokens.
  320. Renvoie une chaîne vide si l'un des deux token n'est pas présent.
  321. Le cas où l'un des deux tokens est présent plusieurs fois dans data
  322. n'est pas pris en compte.
  323. """
  324. _, __, stripped_before = data.partition(token_before)
  325. stripped_both, _, __ = stripped_before.partition(token_after)
  326. return stripped_both
  327.  
  328. def compute_entropy(nibbles_sent, nibbles_received):
  329.  
  330. if nibbles_sent != KNOWN_NIBBLES_TO_SEND:
  331. raise Exception("Désolé, on ne sait faire que avec 64 zéros.")
  332.  
  333. if nibbles_received[:32] == nibbles_received[32:]:
  334. return '1'
  335. else:
  336. return '0'
  337.  
  338. def answer_one_question(connection, nibbles_to_send, iteration_script):
  339.  
  340. data_received = connection.recvuntil('bytes:')
  341. data_received = data_received.decode('ascii')
  342. iteration_oracle = between(data_received, 'out: (', '/128)')
  343. iteration_oracle = iteration_oracle.strip()
  344. iters = (iteration_oracle, iteration_script)
  345. # Print des index, pour être sûr qu'on n'est pas décalé.
  346. print("Numéro de la question. Oracle : %s. Script : %s" % iters)
  347.  
  348. connection.sendline(nibbles_to_send)
  349. data_received = connection.recvuntil('(1)')
  350. data_received = data_received.decode('ascii')
  351. nibbles_received = between(data_received, 'choice:', '\n')
  352. if len(nibbles_received) != 64:
  353. print(data_received)
  354. print(nibbles_received)
  355. raise Exception("Impossible de récupérer les nibbles de l'Oracle.")
  356.  
  357. entropy = compute_entropy(nibbles_to_send, nibbles_received)
  358. print("Nibbles reçus : %s" % nibbles_received)
  359. print("Entropie : %s" % entropy)
  360. connection.sendline(entropy)
  361.  
  362. def main():
  363.  
  364. connection = pwn.remote('challs.thcon.party', 7121)
  365. print('-' * 50)
  366. print("Connexion OK")
  367.  
  368. for iteration_script in range(128):
  369.  
  370. print('-' * 50)
  371. answer_one_question(
  372. connection,
  373. KNOWN_NIBBLES_TO_SEND,
  374. iteration_script)
  375. # Petit temps d'attente pour ne pas trop agresser le serveur
  376. time.sleep(0.1)
  377.  
  378. print('-' * 50)
  379. print("Fin des questions. Affichage des infos finales.")
  380.  
  381. try:
  382. while True:
  383. print(connection.recvline())
  384. except EOFError:
  385. print("Fin de la connexion.")
  386.  
  387. if __name__ == '__main__':
  388. main()
  389.  
  390. Pris en flag'
  391.  
  392. Le flag était : THC{t3hT0rto1se1s4lway5r1ght}. Ils sont tous sous la forme THC{xxxxx}, avec du leet speak à l'intérieur, pour se donner un genre geek et classe.
  393.  
  394. La traduction en texte humainement lisible donne "The tortoise is always right", ce qui est peut-être une vague référence à la divination Shang ou bien aux cosmologies impliquant des tortues. Je n'ai pas cherché à comprendre.
  395.  
  396. Finalement, le plus dur n'était pas l'énigme en elle-même, mais le contexte autour : cette histoire de nibbles, les outils pour scripter des échanges entre machines, les problèmes techniques.
  397.  
  398. Les challenges de type oracle fonctionnent tous selon le même principe, si je m'y étais un peu plus préparé et si j'avais testé la librairie pwntools un minimum, j'aurais été beaucoup moins perturbé par ce contexte. La leçon à retenir, c'est que pour faire des bons scores dans les CTF, il faut s'entraîner. Cela dit, celui de la THCON est réputé pour être particulièrement difficile.
  399. Et maintenant, en route vers de nouvelles aventures !
  400.  
  401. Après cette victoire, je me suis tourné vers le deuxième challenge oracle, que je n'ai pas réussi à comprendre. Voici un lien vers mes logs, je ne sais pas si on peut en tirer grand chose. Tant qu'à faire, je vous mets également mon code python avec la fameuse fonction plop().
  402.  
  403. Le reste de la nuit s'est tout de même révélé fructueux, puisque nous avons ensuite réussi le troisième challenge de type Web. Ça a été plus difficile, plus technique, mais toute l'équipe s'y est impliqué. C'est une autre histoire, que je raconterais peut-être à l'occasion, si j'ai le temps.
  404.  
  405. https://pastebin.com/raw/jmT56KvG
  406. https://pastebin.com/DsHHw29H
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement