Obchód SMS-2FA przez brute-force: analiza pentestu banku
W trakcie zatwierdzonego testu penetracyjnego typu black-box w dużym systemie bankowym (4000 hostów, 4500 użytkowników) wykryto lukę umożliwiającą obchód dwustopniowej autoryzacji SMS. Złośliwy użytkownik z numerem telefonu ofiary mógł całkowicie automatycznie przeprowadzić próbę odgadnięcia 4-cyfrowego kodu bez ograniczeń liczby prób, uzyskując dostęp do danych osobowych, historii wniosków i narzędzi do oszustw.
Uzyskanie dostępu pozwalało na składanie kredytów, finansowanie projektów oraz potwierdzanie wypłat poprzez obsługę klienta — bez dodatkowych sprawdzeń. Entropia kodu (10 000 kombinacji) umożliwiała jego złamanie w ciągu kilku sekund.
Analiza API i logiki uwierzytelniania
Proces logowania opierał się na dwóch punktach końcowych:
- Wysłanie SMS-a:
POSTzwraca"repeat":180, blokując ponowne wysłanie przez 3 minuty. - Sprawdzenie kodu:
POST /api/*****/loginz ciałem"{code":"0548","phone":"+7(111)222-22-22"}.
Odpowiedź na niepoprawny kod to HTTP 400 z komunikatem "Numer telefonu lub kod jest nieprawidłowy". Poprawna odpowiedź to HTTP 200 z ciasteczkami (JSESSIONID, PHPSESSID). Brakowało liczników prób, CAPTCHA, blokady IP lub sesji. Każdy żądanie był przetwarzany niezależnie.
POST /api/*****/login HTTP/1.1
Host: ****
Content-Type: application/json
{"code":"0548","phone":"+7(111) 222-22-22"}
Implementacja ekspluatacji w JavaScript
Proof-of-concept (PoC) wykorzystywał sesję przeglądarkową do obejścia CORS i SameSite. Główny cykl to równoległy permutacyjny atak w 20 wątkach:
await fetch('https://roga_and_copyta/*****/api/*****/login', {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({code: codeStr, phone: phone})
});
for (let i = 0; i < 20; i++) {
turboThread(i, start, end); // Zakres kodów
}
if (response.status === 200) {
foundCode = codeStr;
// Automatyczne uzupełnianie pola
}
Powodzenie określano po statusie 200. Podobny skrypt działał w Pythonie. Czas ataku: 35 sekund na 10 000 żądań.
Klasyfikacja i przyczyny luki
Luka odpowiada CWE-307 (Niewłaściwe ograniczenie nadmiernych prób uwierzytelniania). CVSS 3.1: 9.1 (Krytyczny) — sieć, niska złożoność, brak uprawnień, brak interakcji użytkownika.
Kluczowe błędy projektowe:
- Priorytet wygody: brak blokad dla uniknięcia reklamacji o błędne wpisywanie.
- Ignorowanie automatyzacji: 4 cyfry są trudne dla człowieka, ale łatwe dla skryptu.
- Brak testów przeciwko brute-force.
Zalecenia dotyczące usunięcia luki
Dla wielowarstwowej ochrony należy wdrożyć:
- Limit 3–5 prób na kod z rotacją SMS.
- CAPTCHA po 2–3 błędach.
- 6-cyfrowe kody (1 milion kombinacji, czas przebrania — godziny).
- Monitorowanie anomalii: częstotliwość żądań, reputacja IP.
- Ograniczanie szybkości (rate limiting) według numeru telefonu i sesji.
// Przykład rate limiting na serwerze (pseudokod)
if (attempts[phone] > 5) {
invalidateCode(phone);
requireCaptcha();
}
Co jest ważne
- Obchód 2FA daje pełny dostęp do konta: dane osobowe, wnioski, dane bankowe, potwierdzenie wypłat.
- 4-cyfrowy kod bez limitów — 10 000 kombinacji, złamanie w 35 sekund.
- CWE-307 (CVSS 9.1): brak ochrony przed automatycznym przebraniem.
- Zalecenie: limity + CAPTCHA + 6 cyfr + monitorowanie.
- Pentest wykrył lukę przed wykorzystaniem; wszystko zostało zamknięte.
— Editorial Team
Brak komentarzy.