CodeAnt AI Security Research
CVE-2026-71507: Your SEPA Payment File Now Pays the Attacker

Amartya Jha
CEO, CodeAnt AI
TL;DR
CVE-2026-71507 is a broken-object-level-authorization flaw (BOLA, CWE-639) in Dolibarr ERP CRM's Third Parties REST API, scored CVSS 6.5. A key holding only the routine create-third-parties right rewrites the IBAN and BIC of a supplier it cannot even read, and that attacker IBAN becomes the creditor in the next generated SEPA pain.001 payment file, with the amounts, counts, and payee names unchanged. The read route runs the per-object ownership check and the three write routes skip it. It is fixed in Dolibarr 24.0.0.
CVE-2026-71507 Impact: An Attacker Can Rewrite a Supplier's IBAN
A REST caller holding only the routine societe.creer (create or modify third party) right can overwrite the IBAN and BIC of any company in the instance, including companies the same key gets a hard 403 on when it tries to read them.
Those attacker-controlled bank details are not cosmetic. When the Payment By Bank Transfer module turns validated supplier invoices into a SEPA pain.001 file, it pulls each supplier's default bank account as the creditor, so the attacker's IBAN becomes the payee. It is fixed in Dolibarr 24.0.0.
What is CVE-2026-71507?
CVE-2026-71507 is a broken-object-level-authorization flaw (CWE-639, Authorization Bypass Through User-Controlled Key) in Dolibarr ERP CRM's REST API, in the three bank-account routes of htdocs/societe/class/api_thirdparties.class.php, createCompanyBankAccount(), updateCompanyBankAccount() and deleteCompanyBankAccount().
Because the target record is chosen entirely from the caller-supplied path IDs and these routes never verify the caller is allowed to reach that third party, a low-privilege user can create, rewrite or delete the bank details (IBAN and BIC) of companies it cannot view, and the substituted account propagates into generated SEPA payment files.
It is rated CVSS 6.5, vector CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N, an integrity-only impact with a network-reachable, low-privilege precondition. It was fixed in the 24.0.0 release.
The Authorization Check Already Existed, But the Write Routes Skipped It
The interesting part is that Dolibarr already knew how to protect this object. Ten-odd methods up in the very same class, the read route getCompanyBankAccount() does it correctly. At api_thirdparties.class.php:1904-1906 it calls the project's own per-object helper and refuses anyone who is not a commercial contact of the company:
That is why, in the lab, GET /thirdparties/993001/bankaccounts returned 403 for our test user. The three sibling routes that write the same rows, a couple of hundred lines down in the same file, simply never make that call.
The check is not missing from the codebase. It is missing from three functions that live next to the one that has it. That is the shape of the whole cluster. One surface, the web UI, or here the read route, enforces object access, and a second surface writing the identical records forgets to.
The split-brain authorization pillar walks through why that split keeps happening across Dolibarr's REST API.
What the Vulnerable Write Path Actually Checks
updateCompanyBankAccount() begins at line 2042. Its entire authorization story is a coarse right-check and a consistency test:
The hasRight('societe', 'creer') test asks whether you have the modify-companies right at all, never whether you are allowed to touch this company. The $account->socid != $id test at line 2055 only confirms the bank row belongs to the company named in the path. It says nothing about whether the caller may reach either.
Lines 2060-2068 then copy every supplied JSON field onto the object with no allowlist, so iban, bic, default_rib and type are all attacker-writable in one call. createCompanyBankAccount() (line 1985) and deleteCompanyBankAccount() (line 2098) open with the identical right-check and the same missing per-object guard.
There is a second, independent omission. Dolibarr declares a dedicated right for exactly this data, id 130, societe.thirdparty_paymentinformation.write, at modSociete.class.php:202-206, and the web UI enforces it at paymentmodes.php:95. The REST layer never references that right anywhere, which is why CWE-862 (Missing Authorization) rides along as the secondary weakness.
CVE-2026-71507 Proof of Concept: From Unauthorized IBAN Change to SEPA Payment File
Every request below ran against a throwaway Dolibarr in local Docker, on the hardened 25.0.0-alpha develop build (install locked, production mode, HTTPS forced, CSRF at maximum). No public or third-party system was involved, no file was ever handed to a bank, and no money moved.
The attacker principal was a non-admin with exactly societe.lire and societe.creer, and deliberately without right 130 or the societe.client.voir right that would let it see restricted customers. First the control, proving the read boundary is real. GET /api/index.php/thirdparties/993001 and GET .../993001/bankaccounts both returned 403 Forbidden for the three victim suppliers.
Then the baseline. Generating the bank-transfer batch over three validated EUR invoices produced a pain.001 file totaling EUR 6,600 across 3 transactions, whose creditor IBANs were the legitimate FR1420041010050500013M02606, DE89370400440532013000 and BE68539007547034.
Now the write the user should not have been able to make:
200. The same key that was refused read access to this supplier a moment ago was allowed to rewrite its payment destination. Repeating it for the other two swapped in NL91ABNA0417164300 and ES9121000418450200051332.
An administrator's decrypted read-back confirmed the ciphertext in llx_societe_rib now held the attacker IBANs, while the attacker key still got 403 reading the very rows it had just authored.
Regenerating the payment file is the punchline. The new pain.001 was byte-for-byte the same batch, NbOfTxs still 3, CtrlSum still 6600, the same supplier names, the same EndToEndId values, the same invoice references, with only two fields changed per line, the creditor IBAN and its BIC. An approver eyeballing payees and the total would see precisely the batch they expected:
The same missing guard on DELETE also let the attacker remove a supplier's bank record outright (row count 1 to 0), and the 403-vs-200 split on PUT doubles as a one-bit oracle mapping bank-row IDs to companies the caller cannot read.
CVE-2026-71507 Impact: Why an IBAN Change Becomes Payment Fraud
societe.creer is not an exotic privilege. It is the grant you hand a salesperson, a purchasing clerk, or a CRM-sync integration, precisely the kind of account an organization also restricts from viewing sensitive or unrelated customer records.
That combination is the whole problem. The role that edits company data is often the one fenced out of reading it, and here the fence only stands on the read side.
The payoff is silent supplier-payment redirection that survives a normal four-eyes approval, because the amounts, counts and payee names never change. Catching it needs a line-by-line creditor reconciliation against an out-of-band vendor master.
The audit trail does not save you either. The COMPANY_RIB_MODIFY event records the new label but not the previous IBAN, so the original account cannot be reconstructed after the fact.
How Dolibarr Fixed CVE-2026-71507 in 24.0.0
Version 24.0.0 does the obvious thing. It adds the _checkAccessToResource('societe', $id) call the read route already had to all three write routes, so an unauthorized caller now gets the same 403 whether it reads or writes, which also closes the ID-mapping oracle.
The durable lesson is broader than one helper call. When two entry points can write the same rows, they cannot each carry their own private idea of who is allowed. The authorization decision belongs to the object, not the route.
If the UI enforces a dedicated right and the API does not even know that right exists, you do not have one access-control model with a bug. You have two models, and attackers get to pick.
The API Changed the IBAN, and the Payment File Carried It Forward
CVE-2026-71507 shows how a small authorization gap can become a payment-redirection attack. A low-privilege API key changed a supplier's IBAN without being allowed to access the supplier, and Dolibarr carried that attacker-controlled IBAN into the generated SEPA payment file.
CodeAnt AI Security Research found this as part of a broader Dolibarr assessment, where its agentic pentesting platform traced the vulnerable write beyond the API response to the downstream payment artifact. The lesson: a 200 OK is not proof that an API is secure. You need to know what that successful write can actually change.
This research is part of an ongoing effort by CodeAnt AI Security Research to audit widely-used open-source packages for security vulnerabilities. We believe the open-source ecosystem deserves better tools, better auditing, and more support for the maintainers who keep it running. More findings will be published as patches ship and coordinated disclosure timelines are met.
If you are a maintainer and have been contacted by our team, thank you for your work. If you believe your package may be affected by a similar pattern, we’d love to help: securityresearch@codeant.ai
Related reading: This finding is one node in the split-brain authorization pattern, the pillar behind all nine findings. The same missing object check on Third Parties write routes, taken to a full portal takeover, is documented in CVE-2026-71505. Read the two together to see how far one absent guard reaches.
FAQs
Can CVE-2026-71507 redirect payments without changing the invoice amount?
CVE-2026-71507 is a CWE-639 broken-object-level-authorization vulnerability in Dolibarr's Third Parties REST API. A low-privilege API user can modify the IBAN and BIC of a supplier it cannot read. When Dolibarr later generates a SEPA pain.001 payment file, it uses the modified bank details, allowing the payment destination to be redirected without changing the supplier name, invoice amount, or payment total.
What is a BOLA vulnerability, and how does CVE-2026-71507 illustrate CWE-639?
Which Dolibarr versions are affected, and how is CVE-2026-71507 fixed?
How does editing a company's bank details become payment fraud?
Why does the redirected SEPA file pass a four-eyes approval?

Get Pentest Report
