CVE-2026-33944 SQL Injection w Dolibarr ERP/CRM — Third Party Update
SQL Injection w Dolibarr ERP/CRM v23.0.0 — parametr localtax1_value trafia bezpośrednio do zapytania SQL UPDATE bez sanityzacji. Pełna eksfiltracja bazy danych, w tym haszy haseł administratora. CVSS 3.1: 8.8 (HIGH).
CVE-2026-33944 — SQL Injection w Dolibarr ERP/CRM via localtax1_value parameter
Responsible Disclosure: Podatność została zgłoszona do zespołu Dolibarr poprzez GitHub Security Advisory i zaakceptowana przez producenta. Poprawka została publicznie wydana przed publikacją niniejszego artykułu. W momencie publikacji GitHub Security Advisory GHSA-v5fq-cf5m-vwv7 pozostaje nieopublikowane, pomimo dostępnej poprawki. Researcher: Grzegorz Tworek (Sec4check). Jeśli korzystasz z Dolibarr, upewnij się, że Twoja instancja została zaktualizowana.
Podsumowanie
Podczas niezależnych badań bezpieczeństwa zidentyfikowałem podatność typu SQL Injection w funkcji aktualizacji kontrahentów (Third Party) w Dolibarr ERP/CRM. Podatność dotyczy najnowszej wersji v23.0.0 oraz prawdopodobnie wszystkich wcześniejszych wersji. Reprodukcja została przeprowadzona na czystej, domyślnej instalacji Dolibarr (oficjalny obraz Docker) z bazą MariaDB 10.11.
Istota problemu: parametr lt1 (local tax 1 value) jest przekazywany z danych wejściowych użytkownika bezpośrednio do zapytania SQL UPDATE w pliku societe/class/societe.class.php na linii 1672 — bez żadnego escape'owania, rzutowania typów czy parametryzacji zapytania.
Co istotne, sąsiednie pola w tym samym zapytaniu SQL (np. localtax1_assuj) są poprawnie rzutowane na (int) lub escapowane przez $db->escape(), co wskazuje, że deweloperzy byli świadomi ryzyka injection, ale nie zastosowali tej samej ochrony do pól z wartościami podatku.
| Typ | CWE-89: SQL Injection |
|---|---|
| Produkt | Dolibarr ERP/CRM |
| Testowana wersja | v23.0.0 (najnowsza stabilna, marzec 2026) |
| Platforma | Docker (oficjalny obraz dolibarr/dolibarr), MariaDB 10.11 |
| Komponent | societe/card.php + societe/class/societe.class.php |
| Wektor ataku | Sieć (uwierzytelniony POST request) |
| Interakcja użytkownika | Brak (atakujący exploituje bezpośrednio) |
| CVSS 3.1 | 8.8 HIGH |
| Wektor CVSS | AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H |
Analiza techniczna
Przyczyna źródłowa (Root Cause)
Podatność znajduje się w pliku societe/class/societe.class.php. Przepływ danych wygląda następująco:
1. Wejście (societe/card.php, linia 1135):
2. Sanityzacja (societe.class.php, linia 1528) — niewystarczająca:
3. Sink SQL (societe.class.php, linia 1672) — bezpośrednia konkatenacja:
Filtr wejściowy alpha usuwa tagi HTML i podwójne cudzysłowy, ale nie usuwa apostrofów, nawiasów, przecinków, spacji ani słów kluczowych SQL. Ponieważ injection point znajduje się w numerycznym kontekście SQL (wartość nie jest otoczona cudzysłowami), atakujący nie potrzebuje żadnych znaków, które filtr usuwa.
Kluczowa obserwacja: WAF Dolibarr (waf.inc.php) sprawdza wzorce SQL injection wyłącznie w parametrach GET, nie POST. Parametr lt1 przesyłany przez POST omija WAF całkowicie. Ta sama podatność istnieje dla localtax2_value (parametr lt2) na linii 1682.
Kroki reprodukcji
- Zainstaluj Dolibarr v23.0.0 (testowane z oficjalnym obrazem Docker, MariaDB 10.11)
- Zaloguj się użytkownikiem z uprawnieniem „Kontrahenci: Tworzenie/Edycja" (standardowy użytkownik, nie admin)
- Utwórz lub otwórz dowolny rekord kontrahenta (Third Party)
- Kliknij „Edytuj" aby otworzyć formularz aktualizacji
- Przechwyć request POST (np. Burp Suite) i dodaj do body następujące parametry:
localtax1assuj_value=1
lt1=1,note_private=(SELECT pass_crypted FROM llx_user WHERE rowid=1) - Wyślij formularz (serwer odpowiada HTTP 302 Found — aktualizacja powiodła się)
- Przejdź do zakładki „Uwagi" kontrahenta — hash MD5 hasła administratora jest teraz widoczny w polu „Uwaga (prywatna)"
Weryfikacja: echo -n "admin123" | md5sum → 0192023a7bbd73250516f069df18b500 (identyczny z wyekstrahowanym hashem).
PoC (curl)
Dowody (Evidence)
Środowisko testowe: Dolibarr v23.0.0, oficjalny obraz Docker, MariaDB 10.11, macOS host.
Przed exploitacją — zakładka Uwagi (puste pole)
Zakładka Uwagi przed exploitacją — pole „Uwaga (prywatna)" jest puste. Dolibarr v23.0.0, konfiguracja domyślna.
Burp Suite Repeater — nagłówki żądania POST i odpowiedź 302
Burp Suite Repeater — pełny request POST do /societe/card.php z odpowiedzią HTTP/1.1 302 Found potwierdzającą pomyślną aktualizację.
Burp Suite Repeater — payload w body żądania
Body żądania z wstrzykniętymi parametrami: localtax1assuj_value=1 (linia 181) oraz lt1=1,note_private=(SELECT pass_crypted FROM llx_user WHERE rowid=1) (linia 187). Subquery SQL jest przekazywane bez sanityzacji.
Po exploitacji — hash hasła administratora w polu Uwaga (prywatna)
Zakładka Uwagi po exploitacji — „Uwaga (prywatna)" zawiera 0192023a7bbd73250516f069df18b500, czyli hash MD5 hasła administratora („admin123"), wyekstrahowany z llx_user.pass_crypted poprzez SQL injection.
Dodatkowe dowody arytmetyczne
Ewaluacja arytmetyczna: Wysłanie lt1=3+4 powoduje zapisanie w bazie localtax1_value=7.0000. Wyrażenie 3+4 zostało obliczone przez silnik SQL, a nie zapisane jako literalny string — jednoznaczny dowód na SQL injection.
Ocena wpływu (Impact Assessment)
Atakujący z podstawowym uprawnieniem „Kontrahenci: Tworzenie/Edycja" (standardowy użytkownik, nie administrator) może w pełni skompromitować instancję Dolibarr:
Pełny odczyt bazy danych
Wyciągnięcie dowolnych danych z bazy: hasze haseł (pass_crypted), klucze API (api_key), dane klientów i dostawców, faktury, szczegóły kont bankowych, wewnętrzne notatki.
Modyfikacja danych
Wstrzyknięcie dodatkowych klauzul SET do zapytania UPDATE umożliwia modyfikację dowolnych rekordów w bazie danych — jak zademonstrowano z polem note_private.
Eskalacja uprawnień
Wyciągnięcie i złamanie hashy haseł administratora. Dolibarr domyślnie używa niesolonego MD5, co czyni łamanie trywialne (rainbow tables, hashcat).
Potencjalne RCE
Po eskalacji do admina — dostęp do modułu Website (PHP injection) lub ModuleBuilder (zapis plików PHP). Potencjalnie również przez funkcje MariaDB (INTO OUTFILE, UDF).
CVSS 3.1 — szczegółowa punktacja
| Metryka | Wartość |
|---|---|
| Attack Vector | Network — exploitowane przez HTTP POST |
| Attack Complexity | Low — brak specjalnych warunków |
| Privileges Required | Low — standardowy użytkownik z uprawnieniem edycji kontrahentów |
| User Interaction | None — atakujący exploituje endpoint bezpośrednio |
| Scope | Unchanged — wpływ ograniczony do aplikacji Dolibarr |
| Confidentiality | High — pełny odczyt bazy danych (hasła, dane biznesowe) |
| Integrity | High — możliwość modyfikacji dowolnego rekordu przez injection |
| Availability | High — możliwość DROP tabel lub uszkodzenia danych krytycznych |
CVSS 3.1 Score: 8.8 (HIGH)
Vector: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
Rekomendowana remediacja
Wymagane poprawki:
- Rzutowanie typów na wejściu (societe.class.php, linia 1528):
$this->localtax1_value = (float) price2num(trim($this->localtax1_value)); - Rzutowanie typów w zapytaniu SQL (societe.class.php, linia 1672):
$sql .= ",localtax1_value =".((float) $this->localtax1_value); - Analogiczne poprawki dla
localtax2_value(linie 1529 i 1682)
Dodatkowe rekomendacje: rozszerzenie WAF o kontrolę wzorców SQLi w parametrach POST, zmiana algorytmu hashowania haseł z niesolonego MD5 na bcrypt/argon2 w celu zmniejszenia wpływu przyszłych podatności umożliwiających eksfiltrację danych.
Oś czasu (Disclosure Timeline)
| Data | Zdarzenie |
|---|---|
| 2026-03-20 | Odkrycie podatności podczas niezależnych badań bezpieczeństwa |
| 2026-03-20 | Zgłoszenie poprzez GitHub Security Advisory (GHSA-v5fq-cf5m-vwv7) |
| 2026-03-23 | Producent zaakceptował zgłoszenie |
| 2026-03-25 | GitHub przydzielił CVE-2026-33944 |
| 2026-05-26 | Producent opublikował poprawkę w Dolibarr 22.0.5 (release notes zawierają GHSA-v5fq-cf5m-vwv7) |
| 2026-06-16 | Publikacja artykułu po wydaniu poprawki |
| 2026-06-16 | GitHub Security Advisory nadal pozostaje nieopublikowane |
Wnioski
Ta podatność jest klasycznym przykładem niespójnej sanityzacji danych — jednego z najczęstszych wzorców prowadzących do SQL injection. Deweloperzy zastosowali poprawne rzutowanie (int) dla pola localtax1_assuj w tym samym bloku kodu, ale pominęli pola z wartościami podatku, prawdopodobnie ze względu na założenie, że filtr alpha zapewnia wystarczającą ochronę.
Przypadek ten ilustruje kilka kluczowych zasad:
- Obrona w głąb (defense-in-depth) — WAF sprawdzający tylko GET nie jest wystarczający; każda warstwa musi zakładać, że poprzednia zawiodła
- Parametryzacja zapytań — konkatenacja stringów w zapytaniach SQL nigdy nie jest bezpieczna, niezależnie od filtrów wejściowych
- Spójność mechanizmów obronnych — jeśli rzutujesz typ w jednym polu, musisz to zrobić we wszystkich polach tego samego kontekstu
- Audyt kodu „wokół" poprawek — znalezienie jednego bezpiecznego pola powinno skłonić do sprawdzenia wszystkich sąsiednich pól
Kluczowy takeaway: Filtry wejściowe (jak alpha) nie są substytucją dla parametryzacji zapytań SQL. W numerycznym kontekście SQL (bez cudzysłowów) atakujący nie potrzebuje żadnych „specjalnych" znaków — wystarczą litery, nawiasy i przecinki, które większość filtrów przepuszcza.
Szukasz podatności w swojej aplikacji?
Jako niezależny badacz bezpieczeństwa i pentester, pomagam organizacjom identyfikować i eliminować podatności, zanim zrobią to atakujący. Oferuję profesjonalne testy penetracyjne aplikacji webowych zgodne z OWASP ASVS.
Umów bezpłatną konsultację