Skip to main content
Vulnerability Database/CVE-2026-105856

CVE-2026-105856: Payload CMS SQL Injection Vulnerability

CVE-2026-105856 is a SQL injection flaw in Payload CMS that allows attackers with read and create or update access to inject SQL through crafted field paths. This post covers technical details, affected versions, impact, and mitigation steps.

Published:

CVE-2026-105856 Overview

Payload is a free and open source headless content management system written in TypeScript. CVE-2026-105856 is a SQL injection vulnerability [CWE-89] affecting Payload versions prior to 3.90.0 and 4.0.0-canary.34. An authenticated attacker with read and create or update access to a collection containing a json field or a blocks field with blocksAsJSON enabled can inject SQL through a crafted field path and operators. Collections without those fields are not affected, and richText fields are not affected.

Critical Impact

Authenticated users with low-privilege collection access can inject arbitrary SQL into Postgres and SQLite backends, leading to confidentiality and integrity compromise of the underlying database.

Affected Products

  • Payload CMS versions prior to 3.90.0 (SQLite packages)
  • Payload CMS versions 4.0.0-canary builds prior to 4.0.0-canary.34 (SQLite packages)
  • Payload CMS Postgres packages prior to 3.73.0

Discovery Timeline

  • 2026-10-06 - CVE CVE-2026-105856 published to NVD
  • 2026-10-06 - Last updated in NVD database

Technical Details for CVE-2026-105856

Vulnerability Analysis

The vulnerability lives in Payload's Drizzle-based query layer that translates field paths into native JSON traversal expressions for Postgres and SQLite. When a client issues a query against a json field or a blocks field with blocksAsJSON enabled, path segments and operator names are concatenated into the generated SQL. Insufficient validation of these segments allowed an attacker to break out of the intended JSON path context and append attacker-controlled SQL fragments.

Because Payload exposes query APIs to any authenticated user with read plus create or update permissions on an affected collection, exploitation does not require administrative privileges. The impact is scoped to databases backing collections with the vulnerable field types, but on those collections an attacker can read or modify data outside of their authorization scope.

Root Cause

The root cause is unsafe construction of JSON path traversal expressions in the Drizzle adapter. The previous implementation wrapped non-numeric path segments with the sanitizePathSegment helper, which did not fully constrain segment contents when embedded into Postgres ->/->> operators or the equivalent SQLite expressions. Attacker-controlled operators compounded the issue by widening the set of SQL fragments that could be reached.

Attack Vector

An authenticated user crafts a query targeting a collection with a json or blocksAsJSON field. The attacker supplies malicious path segments and operator values that, after string interpolation inside the Drizzle adapter, form valid SQL executed against Postgres or SQLite. No user interaction is required beyond the attacker's own API call.

typescript
// Security patch: packages/drizzle/src/sqlite/createJSONQuery/convertPathToJSONTraversal.ts
// Replaces the previous sanitizePathSegment helper with a stricter parser
// that classifies each segment as either an object key or a numeric index.

-import { sanitizePathSegment } from '../../utilities/sanitizePathSegment.js'
+import { parseJSONPathSegment } from '../../utilities/parseJSONPathSegment.js'

export const convertPathToJSONTraversal = (incomingSegments: string[]): string => {
  const segments = [...incomingSegments]
  segments.shift()

  return segments.reduce((res, segment, i) => {
-    const formattedSegment = Number.isNaN(parseInt(segment))
-      ? `'${sanitizePathSegment(segment)}'`
-      : segment
+    const parsedSegment = parseJSONPathSegment(segment)
+    const formattedSegment =
+      parsedSegment.type === 'objectKey' ? `'${parsedSegment.value}'` : parsedSegment.value
    const isLast = i === segments.length - 1
    return `${res}${isLast ? '->>' : '->'}${formattedSegment}`
  }, '')
}

Source: GitHub Commit 03b78c7

Detection Methods for CVE-2026-105856

Indicators of Compromise

  • Payload API requests whose where clauses target json or blocksAsJSON fields with unusual characters in path segments such as quotes, parentheses, semicolons, or SQL keywords.
  • Database logs showing JSON traversal operators (->, ->>) adjacent to unexpected SQL tokens or sub-selects.
  • Spikes in query latency or errors from collections containing json or blocksAsJSON fields.

Detection Strategies

  • Enable Postgres log_statement = 'all' or equivalent SQLite tracing in non-production to review generated SQL for malformed JSON paths.
  • Instrument application-layer logging around Payload's REST and GraphQL query endpoints to capture attacker-supplied path and operator values.
  • Correlate authenticated session identifiers with anomalous query patterns against affected collections.

Monitoring Recommendations

  • Alert on authenticated users submitting repeated malformed queries or queries that fail with database syntax errors.
  • Monitor outbound data volumes from Payload hosts for unexpected bulk reads following suspicious query activity.
  • Track upgrades across environments to confirm all deployments run patched versions.

How to Mitigate CVE-2026-105856

Immediate Actions Required

  • Upgrade Payload to version 3.90.0 or 4.0.0-canary.34 for deployments using SQLite packages.
  • Upgrade Payload Postgres packages to version 3.73.0 or later.
  • Audit API tokens and user roles with create or update access to collections containing json or blocksAsJSON fields.
  • Review database audit logs for suspicious queries against affected collections prior to patching.

Patch Information

The fix is published in the GitHub Security Advisory GHSA-pj7x-6wpf-pgvp and shipped in Payload Release v3.90.0. The remediation introduces a strict parseJSONPathSegment utility that classifies each path segment as either an object key or a numeric index before interpolation. See GitHub Commit 03b78c7 for the full change set.

Workarounds

  • Temporarily remove or disable json fields and set blocksAsJSON to false on affected collections until patches are deployed.
  • Restrict create and update permissions on affected collections to trusted administrative accounts only.
  • Place a web application firewall rule in front of the Payload API to block queries containing SQL metacharacters in JSON path parameters.
bash
# Upgrade Payload to a patched version
npm install payload@^3.90.0

# For Postgres deployments on the 3.x line
npm install @payloadcms/db-postgres@^3.73.0

# Verify installed version
npm ls payload

Disclaimer: This content was generated using AI. While we strive for accuracy, please verify critical information with official sources.

Experience the Most Advanced Cybersecurity Platform

See how the world’s most intelligent, autonomous cybersecurity platform can protect your organization today and into the future.