Jun 20, 2026·6 min read·8 visits
The @jhb.software/payload-cloudinary-plugin fails to validate client-supplied parameters passed to Cloudinary's cryptographic signing helper. Authenticated users can obtain mathematically valid HMAC-SHA1 signatures for any arbitrary payload, creating a signature oracle to overwrite files, bypass visibility rules, or trigger outbound SSRF webhooks.
The @jhb.software/payload-cloudinary-plugin exposes an endpoint that performs unvalidated cryptographic signing of Cloudinary API parameters, allowing authenticated users with minimal privileges to forge valid signatures for arbitrary actions. This flaw allows attackers to overwrite remote storage assets, execute unauthorized file uploads, alter asset visibility parameters, trigger SSRF webhooks, and perform directory traversal within Cloudinary repositories.
The module @jhb.software/payload-cloudinary-plugin integrates Cloudinary cloud storage within Payload CMS architectures. When configured with client-side direct uploads enabled (clientUploads: true), the application registers a dedicated backend route designed to handle request signing. This design optimizes payload delivery by transferring large binary media uploads directly from the client browser to Cloudinary's infrastructure, reducing CPU usage and bandwidth consumption on the primary application server.\n\nBecause direct client-side uploads require authentication, Cloudinary relies on cryptographic signatures generated with a private API secret key. The plugin mounts a server-side route designed to accept upload parameters from clients, compute the corresponding HMAC-SHA1 signature, and return it to the browser. However, the endpoint acts as an unrestricted signing oracle because it lacks input sanitization, key whitelisting, or parameter verification mechanisms.\n\nAny user possessing a valid, low-privilege authentication token for the Payload CMS instance can make requests to this signing endpoint. By supplying arbitrary parameter blocks, the client forces the server to return valid cryptographic signatures. This interaction enables unauthorized asset modification, access-control subversion, and Server-Side Request Forgery within the context of the associated Cloudinary subscription.
The cryptographic security model of Cloudinary requires that upload payloads match a specific signature generated via the API_SECRET token. The signature calculation sorts the designated keys in alphabetical order, concatenates key-value pairs with ampersands, appends the secret token, and executes an SHA-1 hashing algorithm. Because the secret key must remain hidden from client-side runtime environments, the server is tasked with acting as a trusted helper to perform signature creation.\n\nIn vulnerable versions of the plugin, specifically up to version 0.3.4, the implementation in cloudinary/src/getGenerateSignature.ts fails to validate the keys contained in the request. The application registers the HTTP POST endpoint /api/cloudinary-generate-signature and binds it to a handler that reads the JSON payload directly. It parses the object named paramsToSign provided in the HTTP request body and transfers it directly to the native Cloudinary SDK utility helper.\n\nThe vulnerable sink is found at line 55 of getGenerateSignature.ts, where the application executes cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret). Because there is no schema enforcement or restriction on the dictionary keys, the server signs any values submitted by the client. The absence of validation checks means parameter injection is trivially accomplished, breaking the security boundary between client and server.
An analysis of the source code changes between the vulnerable and patched versions reveals the exact mechanism of the flaw and its remediation. In the vulnerable codebase, the handler parses the request body and processes the parameters without checking for malicious inputs or unauthorized keys:\n\ntypescript\n// Vulnerable Implementation in getGenerateSignature.ts\nconst body = await req.json?.()\nconst signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)\n\n\nIn contrast, the patched version introduces a strict validation pattern to protect the signature generation flow. The update restricts input keys, validates the format of fields, and blocks structural traversal attempts:\n\ntypescript\n// Patched Implementation in getGenerateSignature.ts\nconst paramsToSign = body.paramsToSign as Record<string, unknown>\nconst allowedKeys = new Set(['timestamp', 'folder', 'public_id'])\nif (\n !paramsToSign ||\n Object.keys(paramsToSign).some((key) => !allowedKeys.has(key)) ||\n typeof paramsToSign.timestamp !== 'string'\n) {\n throw new Forbidden()\n}\nif (folder && paramsToSign.folder !== folder.replace(/^\/|\/$/g, '')) {\n throw new Forbidden()\n}\nif (\n typeof paramsToSign.public_id === 'string' &&\n (paramsToSign.public_id.includes('..') || paramsToSign.public_id.startsWith('/'))\n) {\n throw new Forbidden()\n}\nconst signature = cloudinary.utils.api_sign_request(paramsToSign, apiSecret)\n\n\nThe patch enforces strict security controls: first, it checks incoming parameters against an allowed whitelist of timestamp, folder, and public_id, rejecting any unexpected options. Second, it requires the timestamp to be a string value. Third, it validates the folder parameter against the plugin config, preventing directory manipulation. Fourth, it blocks path-traversal strings like .. and root-relative prefix symbols / within public_id parameters. This blocks attempts to access directories outside of the configured folder structure.
Exploiting this signature oracle requires an attacker to possess any valid, low-privilege authentication token for the target Payload CMS system. Since the default route-level authorization only validates session existence (!!req.user), an authenticated guest or low-privilege editor can access the endpoint. The attacker must also retrieve the public Cloudinary parameters, such as the api_key and cloud_name, which are exposed to the client by design in index.ts to facilitate uploads.\n\nThe attacker sends a structured HTTP POST request to /api/cloudinary-generate-signature containing their desired target parameters inside the paramsToSign property. To overwrite an existing asset, the attacker generates an exploit payload containing overwrite=true, the targeted file's public_id, and a current unix timestamp. The backend server signs this payload and returns the valid HMAC-SHA1 signature.\n\nWith the generated signature, the attacker bypasses the application server entirely and transmits the forged payload directly to Cloudinary's API. The remote storage provider validates the signature against its own cryptographic keys, accepts the request, and overwrites the specified high-value media file. The automated Python script in this report demonstrates how to perform these operations, verifying that returned signatures match the expected cryptographic format.
The security implications of an unrestricted signature oracle on a Cloudinary account are extensive. Integrity is compromised because an attacker can overwrite arbitrary media files, which can lead to web application defacement, malicious file distribution, or database corruption if media metadata is parsed dynamically. This allows attackers to replace legitimate assets with spoofed or exploit-carrying files.\n\nFurthermore, the vulnerability introduces a network-level Server-Side Request Forgery risk. An attacker can request a signature that includes the notification_url parameter, pointing to a server under their control. When Cloudinary completes an upload transaction, its backend servers will make an outbound HTTP POST request to the attacker's server, leaking internal file-processing data or metadata.\n\nAdditionally, attackers can perform directory traversal attacks and invalidate CDN caches. By combining path-traversal sequences in the folder parameter with the invalidate=true option, an attacker can delete cached media resources from global CDN edge caches. This can increase administrative costs and degrade application performance, leading to a denial of service.
To remediate this vulnerability, system administrators and developers must upgrade @jhb.software/payload-cloudinary-plugin to version 0.4.0 or higher. This update introduces the parameter validation and path-filtering controls necessary to secure the signing endpoint. After updating, verify that configuration values for default storage paths are correctly populated in the plugin's initialization block.\n\nIf an immediate package upgrade is not feasible, client-side uploads can be disabled by setting clientUploads: false in the plugin's configuration options. This forces all media uploads to process directly through the Payload CMS backend, eliminating the risk associated with the signature generation endpoint. While this increases server bandwidth and processing load, it removes the attack surface.\n\nWeb Application Firewalls can also be configured to block malicious requests targeting this vulnerability. Define rules to monitor POST requests directed to /api/cloudinary-generate-signature and block payloads containing unauthorized parameters such as overwrite, notification_url, invalidate, or directory-traversal characters in the paramsToSign block. This provides defense-in-depth while the application is being upgraded.
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L| Product | Affected Versions | Fixed Version |
|---|---|---|
@jhb.software/payload-cloudinary-plugin jhb-software | >= 0.3.0 < 0.4.0 | 0.4.0 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-347 (Improper Verification of Cryptographic Signature) |
| Attack Vector | Network (Unauthenticated or Low-Privilege authenticated API interaction) |
| CVSS Score | 7.1 (High) |
| Impact | Integrity Loss, Server-Side Request Forgery, Directory Traversal, CDN Invalidation |
| Exploit Status | Proof-of-Concept Available |
| KEV Status | Not Listed |
The software does not verify or incorrectly verifies the cryptographic signature for data.
An authentication bypass in the SiYuan personal knowledge management system before version 3.7.0 exposes a dynamic icon rendering endpoint. This endpoint processes client-supplied Go template directives. By submitting a crafted request, an unauthenticated remote attacker can leverage registered database template functions to execute arbitrary read-only SQL queries and exfiltrate workspace contents.
CVE-2026-54069 is a critical authentication bypass vulnerability in the SiYuan Note personal knowledge management system. The flaw is located in the HTTP server's middleware handling API authorization, which unconditionally trusts requests carrying a 'chrome-extension://' scheme in the Origin HTTP header, granting administrative access without validating API tokens.
CVE-2026-54089 is a critical authentication bypass vulnerability in File Browser affecting instances configured with proxy-based authentication. An unauthenticated remote attacker with direct network access can impersonate arbitrary users or register new accounts by spoofing configured HTTP headers.
The malicious Cargo package 'exploration' was uploaded to the crates.io registry. During compilation or package import, the crate executes code designed to establish an outbound TCP/HTTP connection, download an external second-stage binary, and execute the binary locally on the host machine. This creates an unauthenticated remote code execution vector impacting developer environments and continuous integration pipelines.
CVE-2026-54088 is a critical command injection vulnerability in File Browser prior to version 2.63.6. When Hook Authentication is enabled, the application interpolates unsanitized credentials into a shell command, allowing unauthenticated remote code execution.
An authenticated remote code execution vulnerability exists in NotrinosERP (versions up to and including 1.0.0) within the Human Resource Management (HRM) module. Users with employee management permissions can upload arbitrary file types, including PHP scripts, which are written directly to a web-accessible directory. This allows for arbitrary code execution in the context of the web-server user.