JAK WŁAMAŁEM SIĘ DO BANKU
Czy jesteś pewien, że twoja aplikacja jest bezpieczna?
Sławomir Jasek
SecuRing
4Developers, Warszawa, 7.04.2014 r.
Sławomir Jasek
Konsultant bezpieczeństwa (~ 10 lat), setki projektów, głównie
różnego typu aplikacje
SecuRing (od 2003)
Testowanie i doradztwo dotyczące bezpieczeństwa aplikacji i systemów IT
Jeśli to możliwe w ramach„white-box” (przegląd konfiguracji, kodu,
konsultacje), a także już na etapie definiowania architektury
Wynikiem testu jest dokładny raport opisujący szczegółowo znalezione
podatności (oraz wykonane testy), wraz z rekomendacjami/sposobami
naprawy
Disclaimer ;-)
● Wszelkie podobieństwo do istniejących systemów i
podatności jest przypadkowe
● Próby ataku na aplikacje bez zgody jej właściciela
mogą prowadzić do poważnych konsekwencji!
http://freestock.ca/signs_symbols_g43-grunge_warning_sign__do_not_read_this_sign_p1723.html
Perspektywa intruza
Tradycyjne napady na placówki już jakoś nie wychodzą ;)
http://en.wikipedia.org/wiki/Olsen_Gang
Bankowość Internetowa!
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
7-cyfrowy identyfikator, hmm...
● 7 cyfr to 10 milionów możliwości. Trochę dużo...
● A, spróbujmy coś wpisać
Uwierzytelnienie
● Sprawdzenie wszystkich identyfikatorów na
przeciętnym łączu zajmie maksymalnie kilka dni.
● Mamy istniejące identyfikatory. I co dalej?
● Wszystko jedno do którego konta się włamiemy.
Wystarczy, że zgadniemy hasło do dowolnego.
● Dla każdego identyfikatora próbujemy najprostsze
dopuszczalne hasła
● Po 3 próbach konto się blokuje. Sprawdzamy 2
hasła, żeby nie narobić szumu.
Uwierzytelnienie - hasła
• Najpopularniejsze na świecie:
password, 123456, abc123 ...
• A tymczasem w Polsce:
d**a, k**a, polska, legia…
• Minimalne wymogi złożoności (8 znaków, 1 cyfra,
znak specjalny), i mamy kandydata:
d**ak**a1!
Autoryzacja transakcji
● OK, możemy się zalogować, zobaczyć ile klient
zarabia, na co wydaje...
● Ale nadal nie możemy ukraść pieniędzy :-(
● Bank wymaga autoryzacji kodem SMS, przesyłanym
na zaufany numer
● Hmm... gdyby tak się dało zmienić numer telefonu do
SMS na swój?
Zmiana numeru telefonu do autoryzacji
● Hmm... a gdyby tak zmienić nr telefonu do
kodów SMS na swój?
● Krok 1
Przelew z cudzego konta
● No dobra, tego klienta wyczyściliśmy. Ale jest
jeszcze wielu innych, gdzie nie zgadliśmy
hasła...
● Przelew. A gdyby tak z cudzego konta? ;-)
● Hmm... zobaczmy.
Jak to się stało? ● Wewnętrzny stan aplikacji może być nadpisany w inny
sposób niż spodziewał się programista. Framework
czasami działa nawet zbyt dobrze ;)
● Enterprise Java Beans okiem pentestera:
http://despicableme.wikia.com/wiki/Minions/Gallery
Automagia: Expression Language
Dotychczas:
<%=HTMLEncoder.encode(((Person)person.getAddress().getStreet())%>
Expression Language (OGNL):
<c:out value="person.address.street" />
Ale potęga warstwy abstrakcji może być
wykorzystana do ataku na aplikację.
Eskalujemy dalej
● Bankowość internetowa została napisana ze
szczególną dbałością o bezpieczeństwo
● Programiści znają problem SQL injection, wiedzą jak
należy poprawnie budować zapytania do bazy
● Kod źródłowy został poddany dokładnej analizie pod
tym kątem
Prepared statement / call
String sql = "select * from users where
firstname=? and lastname=?";
query = conn.prepareStatement(sql);
query.setString(1, "Jan");
query.setString(2, "Kowalski");
result = query.executeQuery();
Czy coś tu może być nie tak?
String sql = "{call
USERS.search(" + "?" + ", ?)}";
call = conn.prepareCall(sql);
call.setString(1, "Jan");
call.setString(2, "Kowalski");
call.execute();
Pod maską: procedura składowana
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim(' || P_FIRSTNAME || ')) ';
END IF;
Pod maską: procedura składowana
PROCEDURE search(
p_firstname IN T_STRING,
p_lastname IN T_STRING,
) IS
(...)
v_sql_select := ' SELECT distinct a.USER_ID';
v_sql_from := ' FROM APP_WEB.USERS a ';
v_sql_where := ' WHERE a.USER_ID is not null ';
IF p_firstname is not null THEN
v_sql_where := v_sql_where || ' and lower(trim(a.FIRSTNAME)) =
lower(trim('|| 'adam')) union select version,'x' from v$instance-- || '))';
END IF;
Atak na parser XML
● Parametry przesyłane z przeglądarki klienta przy
składaniu wniosku do banku są w formie XML
data=%3C%3Fxml+version%3D%221.0%22%3F%3E%0A%3Cuser%3E%0A%C2%A0%C2%A0%C2%A0%C2%A0%3Cfirstname%3EJan%3C%2Ffirstname%3E%0A%C2%A0%C2%A0%C2%A0%C2%A0%3Clastname%3EKowalski%3C%2Flastname%3E%0A%3C%2Fuser%3E
● czyli
<?xml version="1.0"?> <user> <firstname>Jan</firstname> <lastname>Kowalski</lastname> </user>
Atak na parser XML
● Co zrobi parser jeśli dopiszemy nowy DOCTYPE?
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> ]> <lolz>&lol;</lolz> <lolz>lol</lolz>
Atak na parser XML
● Co zrobi parser jeśli dopiszemy nowy DOCTYPE?
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ELEMENT lolz (#PCDATA)>
]> <lolz>&lol1;</lolz> <lolz>lollollollollollollollollollol</lolz>
Atak na parser XML
● Co zrobi parser jeśli dopiszemy nowy DOCTYPE?
<?xml version="1.0"?> <!DOCTYPE lolz [ <!ENTITY lol "lol"> <!ELEMENT lolz (#PCDATA)> <!ENTITY lol1 "&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;"> <!ENTITY lol2 "&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;"> <!ENTITY lol3 "&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;"> <!ENTITY lol4 "&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;"> <!ENTITY lol5 "&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;"> <!ENTITY lol6 "&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;"> <!ENTITY lol7 "&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;"> <!ENTITY lol8 "&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;"> <!ENTITY lol9 "&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;"> ]> <lolz>&lol9;</lolz> <user> <firstname>Jan</firstname> <lastname>Kowalski</lastname> </user>
http://en.wikipedia.org/wiki/Billion_laughs
Atak na parser XML - XXE
● A gdyby tak:
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<foo>&xxe;</foo>
● Tylko w 2014 r. poległy już w ten sposób:
Google, Facebook, Ebay...
Cross Site Scripting
Nasz framework automagicznie koduje na wyjściu
wszystkie znaki specjalne do kontekstu HTML
<bean:write name="transferFormId" property="trn_recipient"/>
trn_recipient="<script>alert('xss')</script>
<input type="text" name="trn_recipient" value=""<script>alert('xss')</script>"
Cross Site Scripting
● Ale w przyrodzie występują również m.in.
<bean:write name="transferFormId" property="trn_recipient" filter="false"/>
<input value="${trnRecipient}"
● Lub wklejenie bezpośrednio w kontekst javascript:
<script> var split='<bean:write name="transferFormId" property="trn_recipient">'; splitRecipient(split); </script>
trn_recipient=';alert('xss');--
<script> var split='';alert('xss');--
Wnioski
● Myśl o bezpieczeństwie, patrz na aplikację z
pozycji intruza (modelowanie zagrożeń)
● Zasada ograniczonego zaufania do zewnętrznych
bibliotek, frameworków, warstw abstrakcji. RTFM,
łatki, konfiguracja...
● Nawet teoretycznie drobne błędy mogą przy
sprzyjających warunkach doprowadzić do
poważnych konsekwencji
● Bezpieczeństwo w SDLC – np. OpenSAMM
● Testy penetracyjne wykazują podatności, których
można nie zauważyć nawet przy przeglądzie kodu
https://www.flickr.com/photos/arenamontanus/2125942630/
Inne błędy autoryzacji
● Krok 1 – wprowadzenie przelewu
● from=29466256723999805678149498&to=92940458930436651352719727&title=test&amount=100¤cy=PLN&action=INITIALIZE
● Krok 2 – podpisanie
● code=1133245&action=SIGN
● Krok 3 – wysłanie
● action=SEND
Inne błędy autoryzacji
● Krok 1 – wprowadzenie przelewu
● from=29466256723999805678149498&to=92940458930436651352719727&title=test&amount=100¤cy=PLN&action=INITIALIZE
● Krok 2 – podpisanie
● code=1133245&action=SIGN
● Krok 3 – wysłanie
● action=SEND
Ukryta funkcjonalność
● Formularz wnioskowy we flash-u. Parametry
wywołania – flashvars:
Form_RunContent( "src", "static/swf/Form","width", "100%","height", "100%","align",
"middle", "quality", "high","bgcolor", "#FFFFFF","type", "application/x-
shockwave-flash", "pluginspage", "http://www.adobe.com/go/getflashplayer", "flashVars",
"formID=57&internalDialog=false&localeChain=pl_PL&"
● internalDialog=false? A jak ustawię na true?
Top Related