15:34
1/2/2011

Okazuje się, że nie tylko PHP jest podatne na opisane przez nas niedawno ataki DoS wynikające z błędu konwersji liczby 2.2250738585072011e-308. Co więcej, Java czyni ten bug jeszcze ciekawszym!

Java: 2.2250738585072011e-308

Zarówno Javowy runtime jak i kompilator (wersje 32- i 64-bit), wpadają w nieskończoną pętle przy próbach kowersji dziesiętnej wartości 2.2250738585072011e-308 na double-precision binary floating-point. Wartość powinna zostać przekonwertowana do 0x1p-1022 (DBL_MIN) ale Java zwiesza się przy 0x0.fffffffffffffp-1022 (largest subnormal double-precision floating-point number).

Podobnie jak w przypadku PHP inne formy zapisu tej liczby również powodują problemy:

  • 0.00022250738585072012e-304
  • 00000000002.2250738585072012e-308
  • 2.225073858507201200000e-308
  • 2.2250738585072012e-00308
  • 2.2250738585072012997800001e-308)

Aby wypróbować błąd na własnej skórze możecie skompilować i uruchomić dwa poniższe programy. Pierwszy ubije środowisko uruchomieniowe, drugi kompilator:

class runhang {
public static void main(String[] args) {
System.out.println("Test:");
double d = Double.parseDouble("2.2250738585072012e-308");
System.out.println("Value: " + d);}}

class compilehang {
public static void main(String[] args) {
double d = 2.2250738585072012e-308;
System.out.println("Value: " + d);}}

Błąd został znaleziony przez Konstantina Preißera, a jego przyczyna została zawężona do “pętli naprawczej” w FloatingDecimal.java. Koto zauważył świetny komentarz w w/w pliku w okolicach correction loop — sprawdźcie linię 676 ;-)

Bład w Java

A być powinien, wtedy nie byłoby tego błędu

Uwaga na webaplikacje pisane w JSP

Przewidujemy ciężkie dni dla właścicieli webaplikacji pisanych w Javie. Skutki błędu już są odczuwalne m.in. w Google Spreadsheets — wpisanie 2.2250738585072011e-308 do komórki i np. dodanie 1 spowoduje zawieszenie się aplikacji do momentu timeoutu backendu.

Radzimy szybko sprawdzić wszystkie zmienne przekazywane do waszych javowej webaplikacji (m.in. GET/POST/Cookie), których wartości mogą ustawiać internauci. A gdybyście chcieli wiedzieć na co jeszcze zwrócić uwagę w kontekście bezpieczeństwa webaplikacji, to zapraszamy na kolejną edycją naszego szkolenia :-)


Przeczytaj także:



41 komentarzy

Dodaj komentarz
  1. wygląda na to, że docsy już naprawione (z automatu przestawia na 0)

  2. Prawie ta sama sytuacja co w przypadku PHP.

  3. Ciekawostki:
    runhang zawiesza interpretera java, ale gij nie.
    compilehang zawiesza kompilatora javac, ale gcj nie.
    compilehang skompilowany przez gcj działa pod java i gij.

  4. Ja tam jakoś Google Spreadsheets wykrzaczyć nie potrafię.
    Albo coś jest ze mną, albo google już jakiegoś patcha walnęło.

    • vizzdoom: przed wpisaniem liczby użyj na niej podwójnego ROT-13, może to pomoże ;)

  5. działa działa, zablokowali tylko tą podstawową reprezentację, pozostałe wykładają spreadsheeta.

  6. Czy tego typu konwersje nie są czasem dokonywane sprzętowo, przez procesor i koprocesor?

  7. Podwójny ROT-13 w Emacsie i przez sendmail

  8. @Bartosz najpierw trzeba złamać potrójną zaporę przez toolkity ;D

  9. Agrrr, co za badziewie, mordercza liczba! w AS3 też się wywala…
    var tempNumber:Number=2.2250738585072012e-308;
    trace(“test: “+tempNumber);

  10. @Bartosz: a nie Winampem na port Notatnika?

    Nie mogą zapatchować np. przepisując algorytm z gcj albo czegoś innego? Albo zaimplementować test asymetryczny, po 14 latach ;)

  11. vizzdoom: to przez szkole hakerow :)

  12. Czy to oznacza też zagrożenie dla wszystkich telefonów z Anrdoidem?

    • Marek: sprawdź, wysyłając do siebie wiadomość z a) tylko tą liczbą b) z numerem nadawcy ustawionym na tę liczbę. :-)

  13. @Piotr
    Ale treść wiadomości będzie stringiem, czyli chyba się nie wykrzaczy?

    • L/\N|)O: zakładam, że w Androidzie jest jakiś moduł “wykrywania numerów telefonicznych” i może on tu zadziałać. Ale architektury Androida nie znam i w tym wypadku IMHO szybszym rozwiązaniem jest próba wysłania, niż próba przeczytania manuala ;)

  14. Niezbyt ciekawy bug. Dla finansów zupełnie bez znaczenia. Nikt nie używa floating pointa do obliczeń walutowych.

  15. Ogólnie liczby w Javie są chyba trochę niedopracowane. Na przykład rzuca się w oczy brak konsekwencji w komunikatach błędów. Przy kompilacji tego:

    public class LongCasting {
    public static void main(String[] args) {
    double result = 0.0;
    long t = Long.MAX_VALUE;

    result = t;
    System.out.println(result);

    t = t – 10;
    result = t;
    System.out.println(result);
    }
    }

    Nie wyświetli się żaden błąd. Programik wyświetli dwie takie same liczby, bo dochodzi do ograniczenia precyzji (long i double są 64 bitowe). Brak konsekwencji jest dlatego że jak przypiszesz longa do inta to też będziesz miał ograniczenie precyzji a mimo to wywali Ci się błąd kompilacji.

  16. W końcu coś ciekawego dla javovca :) Piotrek macie coś dedykowanego dla javovców ?

    • Koziołek — a czego potrzebujesz? :-) Szkolenia z bezpieczeństwa tylko pod kątem Javy to zbyt wąski kawałek tortu, zarówno jeśli chodzi o zapotrzebowanie jak i materiał. Godzina, dwie i pozamiatane ;-)

  17. @Konrad, wywali się, bo w javie w przeciwieństwie do np. PHP jest kontrola typów w trakcie kompilacji i kompilator wykryje, że chcesz niejawnie rzutować z long na int. To jest niedozwolone.
    @Piotr, szkoda :D Jakoś jak czytam różne informacje o zagrożeniach to utwierdzam się w przekonaniu, że java jest aż zbyt bezpieczna.

  18. No tak, wywali się, ale mi o to chodzi. Chodzi o to że powinno się też wywalić jak się przypisuje longa do double’a. To też jest ograniczanie precyzji.

  19. @Konrad nie. Ponieważ Double.MAX_VALUE> Long.MAX_VALUE w javie wynika, że każdy Long może być zapisany jako Double bez utraty precyzji, bo wszystkie Long należą do Double. Dlatego się nie wywali. Typy idą Short-Integer-Long-Foat-Double, a do poważnych obliczeń i tak używa się BigInteger i BigDecimal.

  20. @Koziołek: Jak piszesz aplikację webową, to z czego korzystasz? Bo frameworków “javowych” jest od groma i trochę. I praktycznie w każdym z nich można sobie strzelić z łuku w kolano w inny sposób. Natomiast podstawy/zasady tworzenia bezpiecznego kodu są takie same w PHP/C#/Java/(…).

  21. No tak, ale to nie prawda, jak zresztą pokazuje mój przykład. Każdego longa nie zapiszesz w float ani w double, bez utraty precyzji. Zresztą jakby to mogło być możliwe skoro float ma 32-bity w Javie a double 64-bit. Nawet jeśli przyjmiemy że double i long mają tę samą pojemność, to liczby zmiennoprzecinkowe mają inną organizację. Te 64 bity dzielą się na mantysę i na cechę.

  22. @Konrad, tu to tru :D

    @Paweł, Vaadin ostatnio. Co do zasad ogólnych to też nie do końca, bo nie wyobrażam sobie SQL Injection w przypadku np. JPA.

  23. @Koziołek: Doprawdy? Nie do końca? :) http://cwe.mitre.org/data/definitions/89.html i pierwsze zalecenie w Potential Mitigations. Użycie rozwiązań typu JPA służy właśnie do ograniczenia prawdopodobieństwa wystąpienia podatności na SQL injection. Ale cały czas zostaje temat walidacji danych wejściowych i braku zaufania do danych pochodzących od użytkownika, patrz: SQL Injection: Hibernate (http://cwe.mitre.org/data/definitions/564.html).

  24. @Koziołek: I jeszcze drugi przykład (pierwszy czeka na moderatora): http://blogs.sun.com/carolmcdonald/entry/owasp_top_10_number_2 i “Don’t do this with JPA”. Czym to się różni od “zwykłego” SQL injection? :)

  25. @Paweł, tak na prawdę to niczym, ale dawno nie widziałem takiego kodu. Takie coś tworzą właśnie phpowcy (sorry, ale taka prawda) i osoby nieobeznane z API JDBC.
    To jest kwestia przyzwyczajeń z “ojczystego” języka.

  26. @Koziołek: Ja bym powiedział, że to nie jest kwestia przyzwyczajeń z ojczystego języka, tylko braku świadomości, że dane otrzymane od użytkownika mogą być ZŁE i nigdy nie należy ich używać bez odpowiedniej walidacji i “neutralizacji” (encodingu), niezależnie od tego, czy będą używane w kontekście HTML (bo XSS), SQL (bo SQL Injection), tworzenia polecenia OS (bo OS command injection), LDAP (bo LDAP Injection), XML/XPath (bo XPath injection) i tak dalej, i tak dalej. Osoba, która taką świadomością się wykaże raczej takich potworków tworzyć nie będzie. Raczej.

  27. @Paweł, ale nie spotkałem się z tutorialem, który by nauczał tworzenia zapytań HQL/JPQL/EQL w ten sposób. To, że “się da” to nie znaczy, że naucza się pisania takiego kodu. Kwestia wyrobienia użytkownika. Weź podręcznik do PHP i będziesz miał tam ręczne tworzenie zapytań. Weź do javy to będziesz miał omówione PreparedStatements, a o “ręcznym” wstawianiu parametrów nie będzie mowy.

  28. PHPpowcy mają prepared statement i delikatnie mówiąc nie jest to w PHP ostani krzyk mody ani specjalna wiedza tajemna. Oczywiście partacze i nieuki istnieją, ale kogo obchodzą partacze? Biznesmenów z bożej łaski, którzy kombinują jakby tu zrobić konkurencję Naszej Klasy za 500zł.

  29. @Koziolek. Już same pytania na głupi egzamin na ZCE pokazują czego należy się uczyć. Kurcze, jak ktoś chce być prowadzony przez tutki dla początkojących i jeszcze na tych tutkach skończyć, to co on sobą reprezentuje?

  30. @zan, to co 80% tzw. rynku, byle jak byle tanio.

  31. @Paweł Goleń

    Swiadomość świadomością, ale jak ktoś nie poćwiczył danego tematu, spieszy się albo ma kiepski dzień, to jakieś injection sobie zafunduje. Poza tym są techniki pisania, które wymagają większego skupienia i są takie, które wymagają mniejszego. Programista świadomy problemu, ale składający z escapowanych napisów ma większą szansę coś przeoczyć.

  32. @Koziołek

    A to racja niestety. Rynek wydaje się duży, ale jak się odsieje te wszytskie oferty “…ze zmysłem estetycznym i prawem jazdy”, to zosataje niewiele. Ja po prostu nauczyłem się tą strefę ignorować.

    • zan, koziołek: zapominacie, że część kodu może pochodzić “z zewnątrz” (pracownicy zatrudnieni na kontrakt, do dopisania funkcji X) lub co gorsza, programista przejmuje kod po kimś… Nie zawsze w powyższych przypadkach jest czas na dogłębne code review, zwłaszcza jeśli projekt tworzył się przez X lat ;)

  33. @Piotr, wiesz, ale jak patrzę na bardzo stary kod to tam nie ma takich baboli. Może kwestia innego podejścia do pisania kodu kiedyś, a dziś.

  34. Sprawdziłem andrioda. Nie mam telefonu z tym systemem, więc sprawdziłem na emulatorze. Skompilowałem kod z funkcji runhang podanej tutaj. Błąd nie występuje.

  35. @ Konrad:
    double last = 1.7976931348623157E308;// ==Double.MAX_VALUE
    double pre = 1.7976931348623156E308;
    double delta = last-pre;
    double check =pre+delta;
    println “Max :”+Double.MAX_VALUE
    println “delta:”+delta
    println “check:”+check
    Wynik:
    Max :1.7976931348623157E308
    delta:1.9958403095347198E292 (kosmos)
    check:1.7976931348623157E308
    —-
    Liczby zmiennoprzecinkowe nie mają stałego kroku na osi wzrostu wartości.
    Jeżeli oś dla inta wygląda tak: ( | – krok o 1)
    0-(0+delta=1)-(1+delta=1)-|-|-|-|-|-|-|-|-|-|-|-(int.MAX)

    to oś dla zmiennego przecina wygląda tak:
    (1-delta=2E-1022)-1-(1+delta=2E-1022)-|–|—|—-|—–|——|——– … (Double.MAX_VALUE-1.9958403095347198E292)—(Double.MAX_VALUE)

    Procesor/java zaokrągla do najbliższej delty.
    Teraz zgadnij ile należy odjąć od max long by ruszyło float o to …001E+iles.

    Java istnieje bardzo długo i nikt się takimi błędami nie przejmuje. Zmienny przecinek wykorzystywany jest tylko w obliczeniach naukowych (i to nie wszystkich!). Natomiast to do czego jest Java- finanse nie używa się takich liczb ze względu na kolosalne błędy w obliczeniach w przypadku łączenia wyrażeń w których występują bardzo duże rozbieżności wartości w parametrach. Własnie ze względu na duży krok dla dużych liczb.

    Dziwię się, że ten błąd wypłynął po tylu latach.

    Google Spredsheet / Excel / Calc cięzko popsuć bo tam nie używa się zmiennego przecinka. Z tego powodu który opisałem powyżej.

  36. Jak to słusznie zauważyli tu: http://news.ycombinator.com/item?id=2164863 i http://www.jug.poznan.pl/2011/02/spotkanie-poznan-jug-aspectj-w-praktyce-08-02-2011/ nie trzeba mieć parse w swoim kodzie. Wystarczy curl -H “Accept-Language: en;q=2.2250738585072012e-308” na dowolny servlet, który robi getLocale()… A wygląda na to, że niektóre duże banki w Polsce są na to podatne… Ciekawie kiedy “Anonymous” tego gdzieś użyją :P

Twój komentarz

Zamieszczając komentarz akceptujesz regulamin dodawania komentarzy. Przez moderację nie przejdą: wycieczki osobiste, komentarze nie na temat, wulgaryzmy.

RSS dla komentarzy: