DEV Community

Amartya Jha for CodeAnt AI

Posted on Originally published at codeant.ai

CVE-2026-71510 · Ask enough yes/no questions and you know everyone's salary

CVSS 6.5 · Authorization oracle → blind disclosure (CWE-863 / CWE-200) · Fixed in Dolibarr 24.0.0

You can't see the salary column, but the search box will happily answer “is this person's salary above 50,000? yes or no.” Ask twenty smart questions and you've read the exact number without ever seeing it.


The attack in six steps

  1. The user list has a search filter: you can narrow it by a column's value.
  2. It's safe from SQL injection: the obvious attack was checked, and it holds.
  3. The unasked question: which columns may you filter on?: the answer was any of them, including salary and the password hash. (this is the break)
  4. Those columns are stripped from the reply: so you never actually see the value.
  5. But "filter salary above X" returns a row, or nothing: that single yes/no is a leak.
  6. Binary-search the answers to the exact number: salaries recovered blind, plus a case-folded image of the hash. → blind disclosure.

There is a whole class of secret you can steal without ever being shown it. You don't need the system to hand you the value. You only need it to answer yes-or-no questions about it, honestly, as many times as you like. Twenty good questions are enough to pin down almost any number.

This is the story of a search box that would answer exactly those questions about columns it was supposed to keep hidden, salaries, and even the stored password verifier, and how a low-privilege account read them precisely, blind, without the system ever displaying a single one.

It is the most subtle finding in the set, because nothing here looks broken. No error, no leak on the screen, no obvious hole. Just a search filter answering a question it should have refused to consider.

This is the kind of web application security issue that can slip past a straightforward vulnerability scan: the search feature is not injectable, the response does not expose the sensitive field, but the application's authorization logic still lets a low-privilege user interrogate it.

The obvious attack, and the one underneath it

The user list has a search filter, so you can narrow the list by a column's value. The reflex is to test it for injection: can you break out of the query and run your own? We checked, and the answer was no. On that front it is safe, and a lot of reviews would stop there satisfied.

The second, quieter question is not about breaking the query at all. It is about authorization: which columns are you even allowed to filter on? The answer turned out to be all of them, including the salary fields and the password verifier that the results are so careful never to display.

This is where security testing has to go beyond checking whether an endpoint returns the expected response. A penetration test also needs to ask whether a low-privilege user can use an otherwise legitimate feature to access information they are not authorized to interrogate.

Turning a filter into a yes/no oracle

Those columns are stripped from what comes back, so you never see a salary in the list. But you can still filter on one. And a filter you can apply to a hidden column is a question the system will answer: ask it to show everyone whose salary is above fifty thousand, and either a matching row comes back or it doesn't. Row or no row. Yes or no.

That single bit is the whole leak. Ask “above 50,000?”: no. “above 25,000?”: yes. “above 37,000?”: and so on, each question halving the range that's left. In a couple of dozen questions you have squeezed the exact figure out of a value the screen never showed you. Point the same technique at the stored password verifier and you can walk it out piece by piece.

The honest part: what it is not

A responsible finding has to say where the danger stops, and we were careful to. The password material you can recover this way is not a usable key to log in with: Dolibarr stores it in a form it will not accept back as a login, and the recovered image is case-folded rather than exact. So this is a serious disclosure, real salaries, real hidden fields, read blind, but it is not, by itself, account takeover. We tested that boundary, confirmed it held, and wrote the disproof into the report so a reviewer could see we had actually checked rather than assumed.

That distinction matters for trust. It is easy to inflate a finding by implying the worst. It is more useful, and more credible, to show exactly how far it goes and exactly where it stops.

Why it's easy to miss, and what closes it

This is the kind of hole endpoint-by-endpoint testing walks straight past. The filter isn't injectable. The response doesn't leak. Nothing throws an error. The only thing wrong is that a question was allowed which never should have been, and you only see it if you ask, of a search feature, not “can I break this,” but “which fields am I permitted to ask about.”

The fix in 24.0.0 restricts which columns the filter will accept, so hidden fields can no longer be interrogated through the back door of a yes/no search. The lesson is to treat a filter as an authorization surface, not just an injection surface: every column you let a caller filter on is a column you are willing to answer questions about, one bit at a time.

This is also why security research needs to look beyond individual endpoints. In this case, the issue was not an obvious injection vulnerability. It was the interaction between filtering, authorization and sensitive fields, which is exactly the kind of behaviour that deeper application security testing can uncover.


The fix. Upgrade to Dolibarr 24.0.0. That closes this finding. Want to know where your own app disagrees with itself? → Start with a free CodeAnt pentest

← Back to all nine findings

Top comments (0)