<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>thrts &amp;mdash; Paweł Goleń, blog</title>
    <link>https://wampir.mroczna-zaloga.org/tag:thrts</link>
    <description></description>
    <pubDate>Sat, 02 May 2026 17:28:17 +0000</pubDate>
    <item>
      <title>Bootamp XXIV: co było nie tak w przykładzie</title>
      <link>https://wampir.mroczna-zaloga.org/bootamp-xxiv-co-bylo-nie-tak-w-przykladzie</link>
      <description>&lt;![CDATA[Dzisiaj pierwsza część rozwinięcia rozwiązania zadania XXIV z bootcamp.&#xA;&#xD;&#xA;!--more--&#xD;&#xA;Przykłady były ostatecznie dwa, dostępne są one pod adresami:&#xA;&#xA;  http://bootcamp.threats.pl/lesson24/,&#xA;  http://bootcamp.threats.pl/lesson24a/,&#xA;&#xA;Przykłady te różnią się w bardzo niewielkim stopniu, ale o tym później.&#xA;&#xA;Pierwsza, podstawowa sprawa - skąd pochodzą dane pobierane przez skrypt i wstawiane na stronę? W tym przypadku dane pochodzą z blipa, skrypt pobiera statusy oznaczone tagiem #thrts. Ze specyfiki serwisu wynika, że autorem statusu może być każdy jego użytkownik. Jest to istotna różnica w stosunku, do sytuacji z wyświetlaniem mojego własnego statusu, którego autorem mogę być tylko ja, przynajmniej teoretycznie (przypomnę: Spoofing SMS, czyli fałszowanie nadawcy SMS-a).&#xA;&#xA;Skoro dane na pewno będą pochodziły od niezaufanego odbiorcy, należy je traktować jako niezaufane. W zasadzie nawet jeśli nadawca jest zaufany, same dane i tak powinny być traktowane jako niezaufane, choćby dlatego, że:&#xA;&#xA;  serwis jest pod kontrolą strony trzeciej, nie koniecznie zaufanej,&#xA;  dane między serwerem (serwisem) a aplikacją są przesyłane w sposób, który nie wyklucza ewentualnego tamperingu,&#xA;&#xA;Czy przykłady zawierały jakiś mechanizm oczyszczania lub encodingu danych? Tak, przy czym ten mechanizm był (specjalnie) nieskuteczny. W pierwszym przypadku wyglądał on mniej więcej tak:&#xA;    &#xA;    &#xD;&#xA;    body = body.replace(/(\?)  /g, &#39;&amp;lt;$1&amp;gt;&#39;);&#xD;&#xA;    &#xA;&#xA;W założeniu ten regexp miał dokonywać następującej zamiany:&#xA;    &#xA;    &#xD;&#xA;    scriptalert(/XSS/);/script&#xD;&#xA;    &amp;lt;script&amp;gt;alert(/XSS/);&amp;lt;/script&amp;gt;&#xD;&#xA;    &#xA;&#xA;Problem w tym, że choć zamiana była globalna (flaga g, zamieniane były wszystkie dopasowania), to nie była ona rekursywna. W rezultacie kod postaci:&#xA;    &#xA;    &#xD;&#xA;    img onerror=&#34;javascript:alert(/Wanna play?/);&#34; src=&#34;./notfound&#34; id=&#34;&lt;&#34; /  był przekształcany na postać:&#xA;    &#xA;    &#xD;&#xA;    img onerror=&#34;javascript:alert(/Wanna play?/);&#34; src=&#34;./notfound&#34; id=&#34;&amp;lt;&amp;gt;&#34; / &#xD;&#xA;    &#xA;&#xA;Dlaczego właśnie tak? Wynika to z konstrukcji regexpa. Znajduje on fragmenty, które zaczynają się od znaku  i kończą znakiem  przy czym w środku zawierają zero lub więcej znaków, które nie są  oraz . Dopasowanie to robione jest w sposób niezachłanny (dlatego jest fragment \\? w regexp).&#xA;&#xA;Tak skonstruowany regexp w powyższym przykładzie operację zamiany wykonywał wyłącznie na wartości id, czyli na fragmencie . Całość tagu img (...)  pozostaje natomiast bez zmian. Cóż, regexpy są fajne, ale trzeba umieć z nich korzystać... Poza tym z tego, że regexpy są fajne, wcale nie wynika, że nadają się do wszystkiego. Przykład? Proszę bardzo, choćby to: BBCode won&#39;t protect you from XSS.&#xA;&#xA;Druga wersja przykładu &#34;naprawia&#34; błąd z wersji pierwszej. Funkcja replace wołana jest w pętli tak długo, jak długo jej wywołanie ma jakikolwiek efekt (następuje zamiana fragmentu stringu). W tym drugim wariancie sytuacja wygląda w sposób następujący (kolejne iteracje):&#xA;    &#xA;    &#xD;&#xA;    img onerror=&#34;javascript:alert(/Wanna play?/);&#34; src=&#34;./notfound&#34; id=&#34;&lt;&#34; /  img onerror=&#34;javascript:alert(/Wanna play?/);&#34; src=&#34;./notfound&#34; id=&#34;&amp;lt;&amp;gt;&#34; /  &#xD;&#xA;    &amp;lt;img onerror=&#34;javascript:alert(/Wanna play?/);&#34; src=&#34;./notfound&#34; id=&#34;&amp;lt;&amp;gt;&#34; /&amp;gt; &#xD;&#xA;    &#xA;&#xA;W pierwszej linii znajdują się dane oryginalne. W drugiej - po pierwszej iteracji. Łatwo zauważyć, gdzie nastąpiła zmiana. W kolejnej iteracji następuje kolejna (i ostatnia przy okazji) zmiana danych. W rezultacie dane są &#34;bezpieczne&#34;. Ale czy na pewno?&#xA;&#xA;Oczywiście, że nie. W przykładowym rozwiązaniu payload został podzielony na dwa statusy:&#xA;    &#xA;    &#xD;&#xA;    &lt;img src=&#34;./notfound&#34; onerror=&#34;javascript:alert(/Have fun/);&#34; alt=&#34;&#xD;&#xA;    &#34; /  W takim przypadku regexp nie wykona żadnego podstawienia. W efekcie po połączeniu tych dwóch fragmentów na przykładowej stronie zostanie wstawiony następujący kod:&#xA;    &#xA;    &#xD;&#xA;    #thrts img src=&#34;./notfound&#34; onerror=&#34;javascript:alert(/Have fun/);&#34; alt=&#34;&lt;/pp&#34;  #thrts&#xD;&#xA;    &#xA;&#xA;Efekt zgodny z oczekiwaniami: XSS.&#xA;&#xA;Oczywiście temu przykładowi krzywdę można zrobić na więcej niż jeden sposób. O innym ciekawym sposobie napiszę w kolejnym odcinku.&#xA;&#xA;Tak, zamiast budować dziwne regexpy, równie dobrze można było zamienić po prostu wystąpienia znaków  oraz  na stosowne encje, ale nie o to chodziło w tym przykładzie.&#xA;&#xD;&#xA;&#xD;&#xA;Oryginał tego wpisu dostępny jest pod adresem Bootamp XXIV: co było nie tak w przykładzie&#xA;&#xA;smallAutor: Paweł Goleń/small]]&gt;</description>
      <content:encoded><![CDATA[<p>Dzisiaj pierwsza część rozwinięcia <a href="//wampir.mroczna-zaloga.org/archives/940-bootcamp-xxiv-rozwiazanie.html">rozwiązania zadania XXIV z bootcamp</a>.</p>



<p>Przykłady były ostatecznie dwa, dostępne są one pod adresami:</p>
<ul><li><a href="http://bootcamp.threats.pl/lesson24/">http://bootcamp.threats.pl/lesson24/</a>,</li>
<li><a href="http://bootcamp.threats.pl/lesson24a/">http://bootcamp.threats.pl/lesson24a/</a>,</li></ul>

<p>Przykłady te różnią się w bardzo niewielkim stopniu, ale o tym później.</p>

<p>Pierwsza, podstawowa sprawa – skąd pochodzą dane pobierane przez skrypt i wstawiane na stronę? W tym przypadku dane pochodzą z blipa, skrypt pobiera statusy oznaczone tagiem <a href="http://blip.pl/tags/thrts"><a href="https://wampir.mroczna-zaloga.org/tag:thrts" class="hashtag"><span>#</span><span class="p-category">thrts</span></a></a>. Ze specyfiki serwisu wynika, że autorem statusu może być każdy jego użytkownik. Jest to istotna różnica w stosunku, do sytuacji z wyświetlaniem <em>mojego własnego</em> statusu, którego autorem mogę być tylko ja, przynajmniej teoretycznie (przypomnę: <a href="http://niebezpiecznik.pl/post/spoofing-sms-czyli-falszowanie-nadawcy-sms-a/">Spoofing SMS, czyli fałszowanie nadawcy SMS-a</a>).</p>

<p>Skoro dane <em>na pewno</em> będą pochodziły od niezaufanego odbiorcy, należy je traktować jako niezaufane. W zasadzie nawet jeśli nadawca jest zaufany, same dane i tak powinny być traktowane jako niezaufane, choćby dlatego, że:</p>
<ul><li>serwis jest pod kontrolą strony trzeciej, nie koniecznie zaufanej,</li>
<li>dane między serwerem (serwisem) a aplikacją są przesyłane w sposób, który nie wyklucza ewentualnego tamperingu,</li></ul>

<p>Czy przykłady zawierały jakiś mechanizm oczyszczania lub encodingu danych? Tak, przy czym ten mechanizm był (specjalnie) nieskuteczny. W pierwszym przypadku wyglądał on mniej więcej tak:</p>

<p>    body = body.replace(/&lt;([^&lt;&gt;]*?)&gt;/g, &#39;&lt;$1&gt;&#39;);</p>

<p>W założeniu ten regexp miał dokonywać następującej zamiany:</p>

<p>    
    &lt;script&gt;alert(/XSS/);&lt;/script&gt;</p>

<p>Problem w tym, że choć zamiana była globalna (flaga g, zamieniane były wszystkie dopasowania), to nie była ona rekursywna. W rezultacie kod postaci:</p>

<p>    <img src="./notfound" id="&lt;&gt;” /&gt;&lt;/p&gt;

&lt;p&gt;był przekształcany na postać:&lt;/p&gt;

&lt;p&gt;    &lt;img onerror=" src="./notfound" id="&lt;&gt;"/></p>

<p>Dlaczego właśnie tak? Wynika to z konstrukcji regexpa. Znajduje on fragmenty, które zaczynają się od znaku &lt; i kończą znakiem &gt; przy czym w środku zawierają zero lub więcej znaków, które nie są &lt; oraz &gt;. Dopasowanie to robione jest w sposób niezachłanny (dlatego jest fragment \*? w regexp).</p>

<p>Tak skonstruowany regexp w powyższym przykładzie operację zamiany wykonywał wyłącznie na wartości id, czyli na fragmencie &lt;&gt;. Całość tagu  pozostaje natomiast bez zmian. Cóż, regexpy są fajne, ale trzeba umieć z nich korzystać... Poza tym z tego, że regexpy są fajne, wcale nie wynika, że nadają się do wszystkiego. Przykład? Proszę bardzo, choćby to: <a href="http://blog.kotowicz.net/2010/09/bbcode-wont-protect-you-from-xss.html">BBCode won&#39;t protect you from XSS</a>.</p>

<p>Druga wersja przykładu “naprawia” błąd z wersji pierwszej. Funkcja <em>replace</em> wołana jest w pętli tak długo, jak długo jej wywołanie ma jakikolwiek efekt (następuje zamiana fragmentu stringu). W tym drugim wariancie sytuacja wygląda w sposób następujący (kolejne iteracje):</p>

<p>    <img src="./notfound" id="&lt;&gt;” /&gt;
    &lt;img onerror=" src="./notfound" id="&lt;&gt;"/><br>
    &lt;img onerror=“javascript:alert(/Wanna play?/);” src=“./notfound” id=”&lt;&gt;” /&gt;</p>

<p>W pierwszej linii znajdują się dane oryginalne. W drugiej – po pierwszej iteracji. Łatwo zauważyć, gdzie nastąpiła zmiana. W kolejnej iteracji następuje kolejna (i ostatnia przy okazji) zmiana danych. W rezultacie dane są “bezpieczne”. Ale czy na pewno?</p>

<p>Oczywiście, że nie. W przykładowym rozwiązaniu payload został podzielony na dwa statusy:</p>

<p>    <img src="./notfound" alt="
    "/></p>

<p>W takim przypadku regexp nie wykona żadnego podstawienia. W efekcie po połączeniu tych dwóch fragmentów na przykładowej stronie zostanie wstawiony następujący kod:</p>

<p>    <a href="https://wampir.mroczna-zaloga.org/tag:thrts" class="hashtag"><span>#</span><span class="p-category">thrts</span></a> <img src="./notfound" alt="&lt;/p&gt;&lt;p&gt;”&gt; &lt;a href=" class="hashtag"><span>#</span><span class="p-category">thrts</span></a></p>

<p>Efekt zgodny z oczekiwaniami: XSS.</p>

<p>Oczywiście temu przykładowi krzywdę można zrobić na więcej niż jeden sposób. O innym ciekawym sposobie napiszę w kolejnym odcinku.</p>

<p>Tak, zamiast budować dziwne regexpy, równie dobrze można było zamienić po prostu wystąpienia znaków &lt; oraz &gt; na stosowne encje, ale nie o to chodziło w tym przykładzie.</p>

<p><em>Oryginał tego wpisu dostępny jest pod adresem <a href="https://archive.mroczna-zaloga.org/archives/941-bootamp-xxiv-co-byo-nie-tak-w-przykadzie.html">Bootamp XXIV: co było nie tak w przykładzie</a></em></p>

<p><small>Autor: <a href="https://wampir.mroczna-zaloga.org/o-mnie">Paweł Goleń</a></small></p>
]]></content:encoded>
      <guid>https://wampir.mroczna-zaloga.org/bootamp-xxiv-co-bylo-nie-tak-w-przykladzie</guid>
      <pubDate>Thu, 04 Nov 2010 06:31:00 +0000</pubDate>
    </item>
    <item>
      <title>Bootcamp XXIV: rozwiązanie</title>
      <link>https://wampir.mroczna-zaloga.org/bootcamp-xxiv-rozwiazanie</link>
      <description>&lt;![CDATA[Mój niezbyt trudny przykład doczekał się rozwiązania. Jego autorem jest (http://radekk.blip.pl), a zobaczyć je można (podobnie jak wcześniejsze (http://koto.blip.pl/)) na blipie (tag #thrts). Jak wspominałem, sam przykład nie jest specjalnie trudny, ciekawsza może być stojąca za nim historia.&#xA;&#xD;&#xA;!--more--&#xD;&#xA;Pierwszy impuls do tego przykładu pojawił się, gdy w wykorzystywanym na blogu szablonie znalazłem potencjalny XSS. Opisałem to we wpisie Nie jestem purystą, ale... czyli spot the bug. By ten błąd wykorzystać, należało utworzyć wpis o określonej nazwie, a w dodatku zmieścić się z payloadem w ograniczonej długości znaków. Oczywiście - można. Tylko po co? Po co, skoro osoba, która ma możliwość nadania wpisowi określonego tytułu, XSS może osadzić również na kilkanaście innych, łatwiejszych sposobów. Cała sytuacja uległaby zdecydowanej zmianie, gdyby tytuły były na przykład brane z zewnętrznego źródła.&#xA;&#xA;Drugi impuls pojawił się wówczas, gdy pisałem krótki kod do wstawiania statusu z blipa. Standardowy gadżet mi się nie podobał, więc na szybko napisałem taką prostą funkcję:&#xA;    &#xA;    &#xD;&#xA;    function blipwidgetrun(w) {&#xD;&#xA;      var blipStatus = document.getElementById(&#39;blip-status&#39;);&#xD;&#xA;              &#xD;&#xA;      blipStatus.innerHTML = w.body;&#xD;&#xA;      blipStatus.href = &#39;http://blip.pl/s/&#39; + w.id;&#xD;&#xA;    }&#xD;&#xA;    &#xA;&#xA;Co jest z nią nie tak? Dokładnie to samo, co w obecnym przykładzie z bootcamp. No, prawie to samo, bo w przykładzie zasymulowałem encoding znaków  oraz  by nie było tak prosto. Do kilku szczegółów związanych z tym przykładem wrócę zresztą w najbliższej(?) przyszłości.&#xA;&#xA;Jeśli ktoś nie zorientował się, co jest nie tak z tym kodem - wyjaśniam. Korzystając z innerHTML wpisuję fragment kodu, który może zawierać znaczniki HTML. Innymi słowy - proszę się o XSS, wspomina o tym MSDN. Ten fragment kodu długo czekał na poprawkę, specjalnie zresztą mi się z nią nie spieszyło, bo musiałbym sam sobie tego XSS zafundować, a mam ku temu sporo innych sposobów. Ewentualność wysłania przez kogoś czegoś w moim imieniu chwilowo tu pomijam.&#xA;&#xA;Temat ponownie zaświtał mi w głowie wówczas, gdy Gynvael zastanawiał się nad prasówką, a ja w całej swojej leniwości, nad automatycznym sposobem jej realizacji. W tym wariancie mamy &#34;full wypas&#34; czyli dane wstawiane na stronę i przetwarzane przez hipotetyczny gadżet pochodzą z niezaufanego źródła i potencjalnie są ZŁE.&#xA;&#xA;Trzeci impuls, choć chronologicznie powinien być wymieniony dużo wcześniej, to pewien specyficzny przykład XSS, na który się kiedyś natknąłem. Strona tworzona była w dość dziwny sposób, w szczególności niektóre jej fragmenty były dość intensywnie przekształcane przy pomocy jQuery. Serwer zwracał dane wpisane przez klienta zakodowane w odpowiedni sposób, ale... co zrobi(ł) następujący kod:&#xA;    &#xA;    &#xD;&#xA;    $(&#39;#test2&#39;).html($(&#39;#test1&#39;).text());&#xD;&#xA;    &#xA;&#xA;I czego się nauczyliśmy?&#xA;&#xD;&#xA;&#xD;&#xA;Oryginał tego wpisu dostępny jest pod adresem Bootcamp XXIV: rozwiązanie&#xA;&#xA;smallAutor: Paweł Goleń/small]]&gt;</description>
      <content:encoded><![CDATA[<p>Mój <a href="http://threats.pl/bezpieczenstwo-aplikacji-internetowych/dane-z-roznych-zrodel">niezbyt trudny przykład</a> doczekał się rozwiązania. Jego autorem jest <a href="http://radekk.blip.pl">^radekk</a>, a zobaczyć je można (podobnie jak wcześniejsze <a href="http://koto.blip.pl/">^koto</a>) na blipie (tag <a href="http://blip.pl/tags/thrts"><a href="https://wampir.mroczna-zaloga.org/tag:thrts" class="hashtag"><span>#</span><span class="p-category">thrts</span></a></a>). Jak wspominałem, sam przykład nie jest specjalnie trudny, ciekawsza może być stojąca za nim historia.</p>



<p>Pierwszy impuls do tego przykładu pojawił się, gdy w wykorzystywanym na blogu szablonie znalazłem potencjalny XSS. Opisałem to we wpisie <a href="//wampir.mroczna-zaloga.org/archives/791-nie-jestem-purysta-ale-czyli-spot-the-bug.html">Nie jestem purystą, ale... czyli spot the bug</a>. By ten błąd wykorzystać, należało utworzyć wpis o określonej nazwie, a w dodatku zmieścić się z payloadem w ograniczonej długości znaków. Oczywiście – można. Tylko po co? Po co, skoro osoba, która ma możliwość nadania wpisowi określonego tytułu, XSS może osadzić również na kilkanaście innych, łatwiejszych sposobów. Cała sytuacja uległaby zdecydowanej zmianie, gdyby tytuły były na przykład brane z zewnętrznego źródła.</p>

<p>Drugi impuls pojawił się wówczas, gdy pisałem krótki kod do wstawiania statusu z blipa. Standardowy gadżet mi się nie podobał, więc na szybko napisałem taką prostą funkcję:</p>

<p>    function blip<em>widget</em>run(w) {
      var blipStatus = document.getElementById(&#39;blip-status&#39;);</p>

<p>      blipStatus.innerHTML = w.body;
      blipStatus.href = &#39;<a href="http://blip.pl/s/&#39;">http://blip.pl/s/&#39;</a> + w.id;
    }</p>

<p>Co jest z nią nie tak? Dokładnie to samo, co w obecnym przykładzie z bootcamp. No, prawie to samo, bo w przykładzie zasymulowałem encoding znaków &lt; oraz &gt; by nie było tak prosto. Do kilku szczegółów związanych z tym przykładem wrócę zresztą w najbliższej(?) przyszłości.</p>

<p>Jeśli ktoś nie zorientował się, co jest nie tak z tym kodem – wyjaśniam. Korzystając z <a href="http://developer.mozilla.org/pl/DOM/element.innerHTML">innerHTML</a> wpisuję fragment kodu, który może zawierać znaczniki HTML. Innymi słowy – proszę się o XSS, <a href="http://msdn.microsoft.com/en-us/library/ms533897%2528VS.85%2529.aspx">wspomina o tym MSDN</a>. Ten fragment kodu długo czekał na poprawkę, specjalnie zresztą mi się z nią nie spieszyło, bo musiałbym sam sobie tego XSS zafundować, a mam ku temu sporo innych sposobów. Ewentualność wysłania przez kogoś czegoś w moim imieniu chwilowo tu pomijam.</p>

<p>Temat ponownie zaświtał mi w głowie wówczas, gdy <a href="http://gynvael.coldwind.pl/?id=346">Gynvael zastanawiał się nad prasówką</a>, a ja w całej swojej leniwości, nad automatycznym sposobem jej realizacji. W tym wariancie mamy “full wypas” czyli dane wstawiane na stronę i przetwarzane przez hipotetyczny gadżet pochodzą z niezaufanego źródła i potencjalnie są ZŁE.</p>

<p>Trzeci impuls, choć chronologicznie powinien być wymieniony dużo wcześniej, to pewien specyficzny przykład XSS, na który się kiedyś natknąłem. Strona tworzona była w dość dziwny sposób, w szczególności niektóre jej fragmenty były dość intensywnie przekształcane przy pomocy <a href="http://jquery.com/">jQuery</a>. Serwer zwracał dane wpisane przez klienta zakodowane w odpowiedni sposób, ale... co zrobi(ł) następujący kod:</p>

<p>    $(&#39;<a href="https://wampir.mroczna-zaloga.org/tag:test2" class="hashtag"><span>#</span><span class="p-category">test2</span></a>&#39;).html($(&#39;<a href="https://wampir.mroczna-zaloga.org/tag:test1" class="hashtag"><span>#</span><span class="p-category">test1</span></a>&#39;).text());</p>

<p>I czego się nauczyliśmy?</p>

<p><em>Oryginał tego wpisu dostępny jest pod adresem <a href="https://archive.mroczna-zaloga.org/archives/940-bootcamp-xxiv-rozwizanie.html">Bootcamp XXIV: rozwiązanie</a></em></p>

<p><small>Autor: <a href="https://wampir.mroczna-zaloga.org/o-mnie">Paweł Goleń</a></small></p>
]]></content:encoded>
      <guid>https://wampir.mroczna-zaloga.org/bootcamp-xxiv-rozwiazanie</guid>
      <pubDate>Tue, 02 Nov 2010 07:06:00 +0000</pubDate>
    </item>
  </channel>
</rss>