LatePoint, wtyczka do rezerwacji wizyt w WordPressie z ponad 100 tysiącami instalacji, pozwala klientom logować się jednorazowym kodem OTP. Kod ma sześć cyfr, a serwer blokuje go po dziesięciu błędnych próbach. Do wersji 5.7.4 włącznie licznik prób był jednak zwiększany nieatomowo. Kilka równoległych żądań widzi tę samą starą wartość i blokada nie zadziała. Wordfence ocenia to na 9.1: bez logowania, ze znajomością e-maila lub telefonu klienta, można przejąć jego konto w LatePoint. Changelog wersji naprawczej mówi tylko „Addressed security bugs”. Kod pokazuje więcej.
auth__verify_otp).AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N, CWE-287. Brak oceny NVD.exploitation: none, automatable: yes, technical impact: total. EPSS 0,40% (22.10.2026). Nie znalazłem publicznego PoC.Na czym polega błąd
Opis Wordfence jest wyjątkowo precyzyjny. Funkcja verifyOTP wybiera aktywny kod z warunkiem attempts < 10, sprawdza go, a potem zapisuje attempts + 1, licząc od wartości wczytanej do pamięci. UPDATE nie ma warunku na liczbę prób i nie ma blokady wiersza (SELECT … FOR UPDATE). To klasyczny TOCTOU: między odczytem a zapisem mieści się wiele równoległych żądań, a każde widzi licznik poniżej limitu. Nonce na endpoincie niczego nie zmienia, bo według CNA zwracają go publicznie endpointy request_otp i resend_otp.
To dobry przykład kontroli, która przechodzi każdy test funkcjonalny. Test „11 złych kodów po kolei, potem blokada” daje wynik pozytywny w 5.7.4. Błąd ujawnia dopiero test współbieżności. Ten sam typ problemu, w innej skali, opisywałem przy GitHub Enterprise Server.
Zakres skutku: konto klienta, nie administrator
Wektor C:H/I:H/A:N dotyczy konta klienta LatePoint: rezerwacji, danych osobowych, historii wizyt i tego, co klient może zmienić w profilu. Szkic VMRT Radar słusznie to podkreślał: to nie jest przejęcie administratora WordPressa ani wykonanie kodu. Do oceny dla konkretnej instalacji potrzebujesz trzech informacji, których źródła nie podają:
- czy logowanie klientów kodem OTP jest u Ciebie włączone (czy jest domyślne, nie potwierdziłem),
- czy klienci LatePoint są powiązani z kontami WordPress i jakie te konta mają role,
- jakie dane trzymasz w profilu klienta. Gabinet medyczny, kancelaria czy poradnia psychologiczna mają tu zupełnie inny profil ryzyka niż salon fryzjerski. W UE to także pytanie o naruszenie ochrony danych osobowych.
Warunek „atakujący zna e-mail lub telefon klienta” nie jest silną barierą. Dla wybranej osoby to zwykle informacja publiczna albo łatwa do zdobycia. Dla masowego ataku trzeba listy klientów konkretnego serwisu, co ogranicza skalę, ale nie wektor.
Poprawka: changelog milczy, kod mówi
Changelog 5.7.5 nie wymienia CVE ani tego błędu, a dziękuje i WPScan, i Wordfence, więc wydanie zawiera co najmniej dwie poprawki. Porównałem w SVN WordPress.org plik lib/helpers/otp_helper.php w tagach 5.7.4 i 5.7.5:
- W 5.7.5 przed sprawdzeniem kodu jest wywołanie
increment_attempts_within_limit()z komentarzem „Claim one attempt atomically before the check; zero rows means the cap is reached, even under concurrency”. - Nowa metoda w modelu OTP wykonuje jedno zapytanie
UPDATE … SET attempts = attempts + 1 … WHERE id = … AND attempts < …i sprawdza, czy zmieniło wiersz. Zwiększenie licznika i kontrola limitu są teraz jedną operacją w bazie, a próba jest „zużyta” przed porównaniem kodu, a nie po nim.
To poprawny wzorzec i odpowiada dokładnie przyczynie opisanej przez CNA. Dla VM to rzadki komfort: nie trzeba wierzyć changelogowi, wystarczy wskazać zmianę. Nie zwalnia to z testu po wdrożeniu, ale kryterium zamknięcia „wersja ≥ 5.7.5” ma tu pokrycie w kodzie.
Co zostaje po poprawce
Z kodu 5.7.5 wynikają też granice, w których poprawka działa. Limit to 10 prób na kod. Nowy kod unieważnia poprzednie. Wygenerować można do 60 kodów na godzinę dla jednego kontaktu. W najgorszym przypadku daje to więc około 600 prób na godzinę wobec miliona możliwych kodów, czyli rzędu 0,06% szansy na godzinę dla jednego konta. To nie zero, ale to już ryzyko projektowe, a nie podatność. Dwie uwagi:
- Limit generowania jest liczony zapytaniem
COUNTprzed zapisem nowego kodu. Czy jest odporny na współbieżność, nie sprawdzałem. Jeśli nie, budżet prób jest większy niż powyżej. - Każdy wygenerowany kod jest wysyłany do ofiary e-mailem lub SMS-em. Seria kodów, o które klient nie prosił, to najlepszy sygnał wykrycia, jaki masz, także dla prób sprzed aktualizacji. Klienci zgłaszający „dziwne kody logowania” powinni trafiać do bezpieczeństwa, nie tylko do supportu.
Co powinien zrobić VE
- Znajdź instalacje LatePoint i ustal wersję z działającej instancji, to, czy logowanie OTP jest włączone, oraz jakie dane są w profilach klientów. Na tej podstawie ustaw priorytet: wysoki dla serwisów z danymi wrażliwymi.
- Aktualizuj do 5.7.5 lub nowszej. Wydanie zawiera też inne poprawki bezpieczeństwa (WPScan), więc celem jest najnowsza wersja, a nie minimalna. Najpierw staging, bo 5.7.5 dodaje też obsługę REST API.
- Jeśli aktualizacja musi poczekać, ogranicz liczbę równoczesnych żądań do
auth__verify_otpz jednego źródła na WAF lub reverse proxy albo czasowo wyłącz logowanie OTP. Rozproszony atak z wielu adresów może taki limit ominąć, więc to tylko mitigacja. - Przejrzyj logi: serie żądań
request_otp,resend_otpiverify_otpdla jednego kontaktu w krótkim czasie, udane logowania klienta tuż po takiej serii, zmiany danych kontaktowych w profilu. Brak blokady w WAF nie oznacza braku ataku. - Po aktualizacji zleć w kontrolowanym środowisku test limitu z równoległymi żądaniami. Pojedynczy sekwencyjny test niczego tu nie dowodzi. Przy podejrzeniu przejęcia konta unieważnij sesje klienta i postępuj według procedury obsługi naruszeń danych.
Powiązane na blogu
- GitHub Enterprise Server CVE-2026-19118 — wyścig między walidacją a użyciem
- ZITADEL — podatności tożsamości wymagają innego priorytetu niż sortowanie po CVSS
- WordPress CVE-2026-87902 — exploit half-life krótszy niż SLA
- rclone auth-proxy — kiedy funkcja wygląda na granicę bezpieczeństwa, ale nią nie jest
Źródła
- CVE.org — CVE-2026-110653 (CNA Wordfence, CVSS 3.1 9.1, CWE-287, zakres do 5.7.4, oś czasu, zgłaszający) — cve.org
- NVD — CVE-2026-110653 (status Deferred, SSVC CISA) — nvd.nist.gov
- Wordfence Intelligence — wpis podatności (odnośnik z rekordu CVE) — wordfence.com
- WordPress.org — LatePoint (wersja 5.7.5 z 16.10.2026, changelog, liczba instalacji) — wordpress.org
- WordPress.org SVN — tagi LatePoint (lib/helpers/otp_helper.php w 5.7.4 i 5.7.5, lib/models/otp_model.php w 5.7.5) — plugins.svn.wordpress.org
- MITRE — CWE-362: Concurrent Execution using Shared Resource with Improper Synchronization — cwe.mitre.org
- CISA Known Exploited Vulnerabilities Catalog (brak CVE-2026-110653 w wersji 2026.10.21) — cisa.gov
Nota redakcyjna: wpis powstał na podstawie szkicu VMRT Radar z 19.10.2026 i został zweryfikowany ze źródłami pierwotnymi 20 października 2026 (ok. 19:10 CEST): rekord CVE.org (CNA Wordfence, CVSS 3.1 9.1, CWE-287, zakres do 5.7.4, oś czasu, zgłaszający), NVD API 2.0 (status „Deferred”, SSVC CISA), API wtyczek WordPress.org (wersja, data, changelog, liczba instalacji), pliki w SVN WordPress.org dla tagów 5.7.4 i 5.7.5, EPSS i katalog CISA KEV 2026.10.16. Strona Wordfence Intelligence nie była dostępna z mojego środowiska. Względem szkicu potwierdziłem poprawkę bezpośrednio w kodzie 5.7.5 (szkic oznaczał ją jako niepotwierdzoną przez changelog), dodałem wektor CVSS, CWE, oś czasu i analizę ryzyka resztkowego. Ponownie 22 października 2026 (ok. 22:40 CEST) sprawdziłem katalog CISA KEV (wersja 2026.10.21: nadal brak wpisu), EPSS (bez zmian) oraz wersje w PyPI, GitHub i WordPress.org (bez nowych wydań). Nie potwierdziłem: czy logowanie OTP jest domyślnie włączone, jak klienci LatePoint mapują się na konta WordPress, czy limit generowania kodów jest odporny na współbieżność, jaki jest czas ważności kodu, ani czy błąd był wykorzystywany. Wyliczenie budżetu prób to szacunek autora na podstawie stałych w kodzie. CWE-362 w słowach kluczowych to klasyfikacja autora, rekord podaje CWE-287.