CWE-208
AllowedObservable Timing Discrepancy
Abstraction: Base · Status: Incomplete
Two separate operations in a product require different amounts of time to complete, in a way that is observable to an actor and reveals security-relevant information about the state of the product, such as whether a particular operation was successful or not.
392 vulnerabilities reference this CWE, most recent first.
GHSA-PFWQ-9X97-JV4Q
Vulnerability from github – Published: 2026-08-13 09:31 – Updated: 2026-08-13 09:31Padding oracle attack vulnerability in Oberon microsystem AG’s ocrypto library in all versions since 3.0.0 and prior to 4.0.1 allows an attacker to recover plaintexts via timing measurements of RSA PKCS#1 v1.5 decrypt operations.
{
"affected": [],
"aliases": [
"CVE-2026-16458"
],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-13T09:17:12Z",
"severity": "MODERATE"
},
"details": "Padding oracle attack vulnerability in Oberon microsystem AG\u2019s ocrypto library in all versions since 3.0.0 and prior to 4.0.1 allows an attacker to recover plaintexts via timing measurements of RSA PKCS#1 v1.5 decrypt operations.",
"id": "GHSA-pfwq-9x97-jv4q",
"modified": "2026-08-13T09:31:10Z",
"published": "2026-08-13T09:31:10Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-16458"
},
{
"type": "WEB",
"url": "https://www.oberon.ch/security-advisories/cve-2026-16458"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:H/AT:P/PR:N/UI:N/VC:H/VI:N/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-PJV4-3C63-699F
Vulnerability from github – Published: 2026-05-06 22:32 – Updated: 2026-05-14 20:42Summary
A server-side authentication bypass in azureauthextension allows any party who holds a single valid Azure access token for any scope the collector's configured identity can mint for to authenticate to any OpenTelemetry receiver that uses auth: azure_auth. The extension's Authenticate method does not validate incoming bearer tokens as JWTs. Instead, it calls its own configured credential to obtain an access token and compares the client's token to the result with string equality — and the scope for that server-side token request is taken from the client-supplied Host header. As a result, a token minted for any Azure resource the service principal has ever been issued a token for (ARM, Graph, Key Vault, Storage, etc.) will authenticate to the collector if the attacker picks a matching Host. Tokens are replayable for the full issued lifetime (commonly several hours for managed identity tokens).
Severity: High (CVSS 8.1). See "Threat model" below for the preconditions that inform that score.
Root cause
The extension implements both extensionauth.HTTPClient (outbound: "attach my identity to requests I send") and extensionauth.Server (inbound: "validate a credential someone presented to me"). Those two interfaces look symmetric but are not: holding a credential to present says nothing about the ability to validate a credential someone else presents. The outbound path only requires credential.GetToken(); the inbound path requires JWT signature verification against the issuer's JWKS, issuer/audience/exp/nbf checks, and an algorithm allowlist — none of which the extension does.
PR #39178 ("Implement extensionauth.HTTPClient and extensionauth.Server interface functions") added the Server path in v0.124.0 by reusing the same credential object and comparing strings. That server-side path is present in every release through v0.150.0. The outbound HTTPClient path (used by Azure exporters) is unaffected.
Details
Vulnerable code — extension/azureauthextension/extension.go:208–235:
func (a *authenticator) Authenticate(ctx context.Context, headers map[string][]string) (context.Context, error) {
auth, err := getHeaderValue("Authorization", headers)
if err != nil { return ctx, err }
host, err := getHeaderValue("Host", headers)
if err != nil { return ctx, err }
authFormat := strings.Split(auth, " ")
if len(authFormat) != 2 { /* ... */ }
if authFormat[0] != "Bearer" { /* ... */ }
token, err := a.getTokenForHost(ctx, host) // asks the collector's own identity
if err != nil { return ctx, err }
if authFormat[1] != token { // string comparison, not JWT validation
return ctx, errors.New("unauthorized: invalid token")
}
return ctx, nil
}
And getTokenForHost at extension.go:187–206:
options := policy.TokenRequestOptions{
Scopes: []string{
fmt.Sprintf("https://%s/.default", host), // client-supplied Host chooses scope
},
}
Two independent problems compose here:
1. No JWT validation. Real Entra ID bearer validation requires verifying the JWT signature against the tenant JWKS and checking iss, aud, exp, nbf, plus an algorithm allowlist. The extension does none of this. The "expected" value is a token the server mints from its own credential, not a signature to verify. Any party that already holds a valid token for the collector's identity — a co-tenant pod that shares the managed identity, any peer authenticated with the same service principal, any component that retained an Authorization: header — can replay it directly.
2. Attacker-controlled audience. The scope used to mint the "expected" token comes from the client-supplied Host header: https://<Host>/.default. The azcore credential returns a consistent token per (identity, scope) pair within the cache window, so an attacker can pick any scope the SP has been issued a token for and match it by setting Host accordingly. This is the sharper of the two flaws: it means a token leaked from an unrelated Azure integration — ARM, Graph, Key Vault, a different Storage account — authenticates to the collector.
The correct primitive is a real JWT validator — e.g. github.com/coreos/go-oidc/v3 pointed at the tenant's discovery endpoint, with audience and issuer pinned server-side from configuration, never derived from request headers.
Proof of concept
Both variants assume a collector running with azureauthextension v0.124.0–v0.150.0, configured with any credential mode and referenced from a receiver's auth: block:
extensions:
azure_auth:
managed_identity:
client_id: ${CLIENT_ID}
receivers:
otlp:
protocols:
http:
endpoint: 0.0.0.0:4318
auth:
authenticator: azure_auth
service:
extensions: [azure_auth]
pipelines:
traces:
receivers: [otlp]
exporters: [debug]
Variant A — Replay (same scope)
The attacker controls a workload that shares the collector's managed identity (common in AKS when multiple pods bind the same UAMI). Both workloads query IMDS for https://management.azure.com/.default and receive the same cached token. The attacker replays:
POST /v1/traces HTTP/1.1
Host: management.azure.com
Authorization: Bearer eyJ... # token minted for management.azure.com
Content-Type: application/json
{"resourceSpans":[...]}
Authenticate calls getTokenForHost(ctx, "management.azure.com"), receives the identical cached token, and the string comparison passes.
Variant B — Scope confusion (the stronger case)
The attacker holds a token for the SP issued for a different Azure resource — say Key Vault, obtained from an entirely unrelated integration. The collector was never intended to accept Key Vault tokens. The attacker sets Host to match:
POST /v1/traces HTTP/1.1
Host: vault.azure.net
Authorization: Bearer eyJ... # token minted for vault.azure.net
Content-Type: application/json
{"resourceSpans":[...]}
Authenticate calls getTokenForHost(ctx, "vault.azure.net"). The collector's credential mints (or returns cached) a token for https://vault.azure.net/.default — the same token the attacker holds, because both come from the same SP issued for the same scope by the same IdP. Comparison passes. The collector accepts telemetry gated on "proof of identity to Key Vault."
In a correct implementation, the JWT's aud would be pinned server-side to a value unrelated to Host, and Variant B would fail regardless of what the attacker put in the Host header.
A small Go reproducer can be built around the extension's own test harness: the existing TestAuthenticate in extension_test.go is effectively a demonstration of the broken behavior — it passes when the client-supplied token equals the server-side token for the given Host, which is exactly what an attacker arranges.
Impact
Vulnerability class: Improper Authentication (CWE-287), with contributing CWE-347 (Improper Verification of Cryptographic Signature — no JWT validation), CWE-294 (Authentication Bypass by Capture-replay — tokens replayable for full TTL), and CWE-290 (Authentication Bypass by Spoofing — client Host header chooses the expected scope).
Threat model / precondition. The attacker needs to already hold (or be able to obtain) a valid Azure access token issued to the collector's SP for any scope. In practice this is satisfied by: (a) controlling another workload that binds the same managed identity, (b) compromising any peer authenticated with the same SP, or (c) observing an Authorization: header from any prior legitimate request for the SP. This is what drives the 8.1 score — the precondition is non-trivial but is routine in multi-workload Azure environments.
Who is impacted. Any operator of opentelemetry-collector-contrib v0.124.0 through v0.150.0 who configured azureauthextension on a receiver's auth: block. This applies to both HTTP and gRPC receivers — gRPC receivers surface :authority as Host through the collector's header handling, so the same exploit path applies there.
Deployments most at risk:
- Multi-workload Azure environments where the collector shares a managed identity with other workloads (any such workload can authenticate as an arbitrary telemetry source).
- Deployments that forward Authorization: headers through proxies, service meshes, or logging pipelines (one leaked token is enough, and persists for the token TTL — typically several hours for MI tokens, not the 60-minute user-token window).
- Multi-tenant environments where different customers' telemetry converges at a collector protected by this extension.
Consequences. Unauthenticated (from the collector's perspective) ingest of arbitrary traces, metrics, and logs. Downstream effects depend on the collector's exporters and include telemetry-backend poisoning, log injection (masking real attacker activity in SIEMs), metric manipulation to trigger or suppress alerts, cost-amplification against pay-per-datapoint backends, and adversarial traces that corrupt service-graph and incident-triage signals.
Not impacted. The extension's outbound extensionauth.HTTPClient path, used by Azure exporters, is unaffected. Operators who use azureauthextension only on exporters can continue doing so.
Mitigation
Until a patched release is available, remove azure_auth from any receiver auth: blocks. For genuine Entra ID JWT validation on OTLP receivers, use oidcauthextension pointed at the tenant discovery URL, with audience pinned from configuration:
extensions:
oidc:
issuer_url: https://login.microsoftonline.com/<tenant-id>/v2.0
audience: <expected-api-audience>
Resources
- PR introducing the vulnerable server-side path: #39178
- Affected versions: v0.124.0 – v0.150.0
Assisted-by: Opus 4.7
{
"affected": [
{
"package": {
"ecosystem": "Go",
"name": "github.com/open-telemetry/opentelemetry-collector-contrib/extension/azureauthextension"
},
"ranges": [
{
"events": [
{
"introduced": "0.124.0"
},
{
"last_affected": "0.150.0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-42602"
],
"database_specific": {
"cwe_ids": [
"CWE-208",
"CWE-287",
"CWE-290",
"CWE-294",
"CWE-347"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-06T22:32:43Z",
"nvd_published_at": "2026-05-13T21:16:47Z",
"severity": "HIGH"
},
"details": "### Summary\n\nA server-side authentication bypass in `azureauthextension` allows any party who holds a single valid Azure access token for *any scope the collector\u0027s configured identity can mint for* to authenticate to any OpenTelemetry receiver that uses `auth: azure_auth`. The extension\u0027s `Authenticate` method does not validate incoming bearer tokens as JWTs. Instead, it calls its own configured credential to obtain an access token and compares the client\u0027s token to the result with string equality \u2014 and the scope for that server-side token request is taken from the client-supplied `Host` header. As a result, a token minted for any Azure resource the service principal has ever been issued a token for (ARM, Graph, Key Vault, Storage, etc.) will authenticate to the collector if the attacker picks a matching `Host`. Tokens are replayable for the full issued lifetime (commonly several hours for managed identity tokens).\n\nSeverity: High (CVSS 8.1). See \"Threat model\" below for the preconditions that inform that score.\n\n### Root cause\n\nThe extension implements both `extensionauth.HTTPClient` (outbound: \"attach my identity to requests I send\") and `extensionauth.Server` (inbound: \"validate a credential someone presented to me\"). Those two interfaces look symmetric but are not: holding a credential to present says nothing about the ability to validate a credential someone else presents. The outbound path only requires `credential.GetToken()`; the inbound path requires JWT signature verification against the issuer\u0027s JWKS, issuer/audience/exp/nbf checks, and an algorithm allowlist \u2014 none of which the extension does.\n\nPR #39178 (\"Implement extensionauth.HTTPClient and extensionauth.Server interface functions\") added the `Server` path in v0.124.0 by reusing the same credential object and comparing strings. That server-side path is present in every release through v0.150.0. The outbound `HTTPClient` path (used by Azure exporters) is unaffected.\n\n### Details\n\nVulnerable code \u2014 `extension/azureauthextension/extension.go:208\u2013235`:\n\n```go\nfunc (a *authenticator) Authenticate(ctx context.Context, headers map[string][]string) (context.Context, error) {\n auth, err := getHeaderValue(\"Authorization\", headers)\n if err != nil { return ctx, err }\n host, err := getHeaderValue(\"Host\", headers)\n if err != nil { return ctx, err }\n\n authFormat := strings.Split(auth, \" \")\n if len(authFormat) != 2 { /* ... */ }\n if authFormat[0] != \"Bearer\" { /* ... */ }\n\n token, err := a.getTokenForHost(ctx, host) // asks the collector\u0027s own identity\n if err != nil { return ctx, err }\n if authFormat[1] != token { // string comparison, not JWT validation\n return ctx, errors.New(\"unauthorized: invalid token\")\n }\n return ctx, nil\n}\n```\n\nAnd `getTokenForHost` at `extension.go:187\u2013206`:\n\n```go\noptions := policy.TokenRequestOptions{\n Scopes: []string{\n fmt.Sprintf(\"https://%s/.default\", host), // client-supplied Host chooses scope\n },\n}\n```\n\nTwo independent problems compose here:\n\n**1. No JWT validation.** Real Entra ID bearer validation requires verifying the JWT signature against the tenant JWKS and checking `iss`, `aud`, `exp`, `nbf`, plus an algorithm allowlist. The extension does none of this. The \"expected\" value is a token the server mints from its own credential, not a signature to verify. Any party that already holds a valid token for the collector\u0027s identity \u2014 a co-tenant pod that shares the managed identity, any peer authenticated with the same service principal, any component that retained an `Authorization:` header \u2014 can replay it directly.\n\n**2. Attacker-controlled audience.** The scope used to mint the \"expected\" token comes from the client-supplied `Host` header: `https://\u003cHost\u003e/.default`. The `azcore` credential returns a consistent token per (identity, scope) pair within the cache window, so an attacker can pick any scope the SP has been issued a token for and match it by setting `Host` accordingly. This is the sharper of the two flaws: it means a token leaked from an unrelated Azure integration \u2014 ARM, Graph, Key Vault, a different Storage account \u2014 authenticates to the collector.\n\nThe correct primitive is a real JWT validator \u2014 e.g. `github.com/coreos/go-oidc/v3` pointed at the tenant\u0027s discovery endpoint, with audience and issuer pinned *server-side from configuration*, never derived from request headers.\n\n### Proof of concept\n\nBoth variants assume a collector running with `azureauthextension` v0.124.0\u2013v0.150.0, configured with any credential mode and referenced from a receiver\u0027s `auth:` block:\n\n```yaml\nextensions:\n azure_auth:\n managed_identity:\n client_id: ${CLIENT_ID}\n\nreceivers:\n otlp:\n protocols:\n http:\n endpoint: 0.0.0.0:4318\n auth:\n authenticator: azure_auth\n\nservice:\n extensions: [azure_auth]\n pipelines:\n traces:\n receivers: [otlp]\n exporters: [debug]\n```\n\n#### Variant A \u2014 Replay (same scope)\n\nThe attacker controls a workload that shares the collector\u0027s managed identity (common in AKS when multiple pods bind the same UAMI). Both workloads query IMDS for `https://management.azure.com/.default` and receive the same cached token. The attacker replays:\n\n```\nPOST /v1/traces HTTP/1.1\nHost: management.azure.com\nAuthorization: Bearer eyJ... # token minted for management.azure.com\nContent-Type: application/json\n\n{\"resourceSpans\":[...]}\n```\n\n`Authenticate` calls `getTokenForHost(ctx, \"management.azure.com\")`, receives the identical cached token, and the string comparison passes.\n\n#### Variant B \u2014 Scope confusion (the stronger case)\n\nThe attacker holds a token for the SP issued for a *different* Azure resource \u2014 say Key Vault, obtained from an entirely unrelated integration. The collector was never intended to accept Key Vault tokens. The attacker sets `Host` to match:\n\n```\nPOST /v1/traces HTTP/1.1\nHost: vault.azure.net\nAuthorization: Bearer eyJ... # token minted for vault.azure.net\nContent-Type: application/json\n\n{\"resourceSpans\":[...]}\n```\n\n`Authenticate` calls `getTokenForHost(ctx, \"vault.azure.net\")`. The collector\u0027s credential mints (or returns cached) a token for `https://vault.azure.net/.default` \u2014 the same token the attacker holds, because both come from the same SP issued for the same scope by the same IdP. Comparison passes. The collector accepts telemetry gated on \"proof of identity to Key Vault.\"\n\nIn a correct implementation, the JWT\u0027s `aud` would be pinned server-side to a value unrelated to `Host`, and Variant B would fail regardless of what the attacker put in the `Host` header.\n\nA small Go reproducer can be built around the extension\u0027s own test harness: the existing `TestAuthenticate` in `extension_test.go` is effectively a demonstration of the broken behavior \u2014 it passes when the client-supplied token equals the server-side token for the given `Host`, which is exactly what an attacker arranges.\n\n### Impact\n\n**Vulnerability class:** Improper Authentication (CWE-287), with contributing CWE-347 (Improper Verification of Cryptographic Signature \u2014 no JWT validation), CWE-294 (Authentication Bypass by Capture-replay \u2014 tokens replayable for full TTL), and CWE-290 (Authentication Bypass by Spoofing \u2014 client `Host` header chooses the expected scope).\n\n**Threat model / precondition.** The attacker needs to already hold (or be able to obtain) a valid Azure access token issued to the collector\u0027s SP for any scope. In practice this is satisfied by: (a) controlling another workload that binds the same managed identity, (b) compromising any peer authenticated with the same SP, or (c) observing an `Authorization:` header from any prior legitimate request for the SP. This is what drives the 8.1 score \u2014 the precondition is non-trivial but is routine in multi-workload Azure environments.\n\n**Who is impacted.** Any operator of `opentelemetry-collector-contrib` v0.124.0 through v0.150.0 who configured `azureauthextension` on a receiver\u0027s `auth:` block. This applies to both HTTP and gRPC receivers \u2014 gRPC receivers surface `:authority` as `Host` through the collector\u0027s header handling, so the same exploit path applies there.\n\n**Deployments most at risk:**\n- Multi-workload Azure environments where the collector shares a managed identity with other workloads (any such workload can authenticate as an arbitrary telemetry source).\n- Deployments that forward `Authorization:` headers through proxies, service meshes, or logging pipelines (one leaked token is enough, and persists for the token TTL \u2014 typically several hours for MI tokens, not the 60-minute user-token window).\n- Multi-tenant environments where different customers\u0027 telemetry converges at a collector protected by this extension.\n\n**Consequences.** Unauthenticated (from the collector\u0027s perspective) ingest of arbitrary traces, metrics, and logs. Downstream effects depend on the collector\u0027s exporters and include telemetry-backend poisoning, log injection (masking real attacker activity in SIEMs), metric manipulation to trigger or suppress alerts, cost-amplification against pay-per-datapoint backends, and adversarial traces that corrupt service-graph and incident-triage signals.\n\n**Not impacted.** The extension\u0027s outbound `extensionauth.HTTPClient` path, used by Azure exporters, is unaffected. Operators who use `azureauthextension` only on exporters can continue doing so.\n\n### Mitigation\n\nUntil a patched release is available, remove `azure_auth` from any receiver `auth:` blocks. For genuine Entra ID JWT validation on OTLP receivers, use `oidcauthextension` pointed at the tenant discovery URL, with audience pinned from configuration:\n\n```yaml\nextensions:\n oidc:\n issuer_url: https://login.microsoftonline.com/\u003ctenant-id\u003e/v2.0\n audience: \u003cexpected-api-audience\u003e\n```\n\n### Resources\n\n- PR introducing the vulnerable server-side path: [#39178](https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/39178)\n- Affected versions: v0.124.0 \u2013 v0.150.0\n\nAssisted-by: Opus 4.7",
"id": "GHSA-pjv4-3c63-699f",
"modified": "2026-05-14T20:42:40Z",
"published": "2026-05-06T22:32:43Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-telemetry/opentelemetry-collector-contrib/security/advisories/GHSA-pjv4-3c63-699f"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-42602"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-telemetry/opentelemetry-collector-contrib"
}
],
"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:H",
"type": "CVSS_V3"
}
],
"summary": "opentelemetry-collector-contrib\u0027s azureauthextension Authenticate method does not validate bearer tokens, allowing auth bypass via replay"
}
GHSA-PRXM-C7P3-5GV7
Vulnerability from github – Published: 2026-08-26 00:31 – Updated: 2026-08-26 21:31Observable Timing Discrepancy vulnerability in Drupal Token Content Access allows Brute Force. This issue affects Token Content Access versions: from 0.0.0 to 3.1.2.
{
"affected": [],
"aliases": [
"CVE-2026-18259"
],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-08-25T23:16:57Z",
"severity": "HIGH"
},
"details": "Observable Timing Discrepancy vulnerability in Drupal Token Content Access allows Brute Force. This issue affects Token Content Access versions: from 0.0.0 to 3.1.2.",
"id": "GHSA-prxm-c7p3-5gv7",
"modified": "2026-08-26T21:31:40Z",
"published": "2026-08-26T00:31:03Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-18259"
},
{
"type": "WEB",
"url": "https://www.drupal.org/sa-contrib-2026-090"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-PX9V-979X-QMH9
Vulnerability from github – Published: 2026-08-25 03:32 – Updated: 2026-09-17 20:24Duplicate Advisory
This advisory has been withdrawn because it is a duplicate of GHSA-38p6-h87p-r4cg. This link is maintained to preserve external references.
Original Description
Grav CMS before 2.0.16 contains a timing vulnerability in Utils::verifyNonce() that uses non-constant-time string comparison with the === operator instead of hash_equals() for CSRF nonce validation. Attackers can measure response timing differences to recover valid nonce values byte-by-byte through multiple requests, weakening CSRF protection below its intended security margin.
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c 2.0.16"
},
"package": {
"ecosystem": "Packagist",
"name": "getgrav/grav"
},
"ranges": [
{
"events": [
{
"introduced": "0"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-17T20:24:49Z",
"nvd_published_at": "2026-08-25T02:16:45Z",
"severity": "MODERATE"
},
"details": "### Duplicate Advisory\nThis advisory has been withdrawn because it is a duplicate of GHSA-38p6-h87p-r4cg. This link is maintained to preserve external references.\n\n### Original Description\nGrav CMS before 2.0.16 contains a timing vulnerability in Utils::verifyNonce() that uses non-constant-time string comparison with the === operator instead of hash_equals() for CSRF nonce validation. Attackers can measure response timing differences to recover valid nonce values byte-by-byte through multiple requests, weakening CSRF protection below its intended security margin.",
"id": "GHSA-px9v-979x-qmh9",
"modified": "2026-09-17T20:24:49Z",
"published": "2026-08-25T03:32:09Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/getgrav/grav/security/advisories/GHSA-38p6-h87p-r4cg"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-72701"
},
{
"type": "WEB",
"url": "https://www.vulncheck.com/advisories/grav-cms-before-timing-attack-via-verifynonce"
}
],
"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"
},
{
"score": "CVSS:4.0/AV:N/AC:H/AT:P/PR:N/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
],
"summary": "Duplicate Advisory: Grav: Non constant time nonce comparison in Utils::verifyNonce() used for CSRF protection",
"withdrawn": "2026-09-17T20:24:49Z"
}
GHSA-Q4GQ-R6F8-Q6Q9
Vulnerability from github – Published: 2026-09-29 18:31 – Updated: 2026-09-29 18:31Issue summary: A non-constant-time optimized implementation of scalar point multiplication is used for SM2 private key operations on ARM64 and RISC-V platforms.
Impact summary: An attacker able to measure the time taken by, or to observe the cache-line access pattern of SM2 signing or decryption on an affected platform can learn information about the secret scalar.
CWE: CWE-208: Observable Timing Discrepancy
Description: On ARM64 and RISC-V processors, the SM2 curve uses an optimized scalar multiplication implementation whose conditional branches and table look ups are chosen according to the bits of the secret scalar. The execution time and the cache-access pattern therefore depend on the long-term private key (during SM2 decryption) or the per-signature nonce (during SM2 signature generation), forming a timing and cache side-channel.
FIPS Impact: no SM2 is not a FIPS algorithm and the optimized SM2 implementation is not part of the FIPS module.
OpenSSL 4.0, 3.6, 3.5 and 3.4 are vulnerable to this issue on AArch64 and RISC-V.
OpenSSL 3.0, 1.1.1 and 1.0.2 are not affected by this issue.
OpenSSL 4.0 users should upgrade to OpenSSL 4.0.3. OpenSSL 3.6 users should upgrade to OpenSSL 3.6.5. OpenSSL 3.5 users should upgrade to OpenSSL 3.5.9. OpenSSL 3.4 users should upgrade to OpenSSL 3.4.8.
This issue was reported on 2 May 2026 by Abhinav Agarwal. It was independently reported on 6 June 2026 by Feng Xue. The fix was developed by Igor Ustinov.
-- cut (non-publishing metadata for internal use) -- Reported by: Abhinav Agarwal, Feng Xue Fixed by: Igor Ustinov
{
"affected": [],
"aliases": [
"CVE-2026-54875"
],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-09-29T16:17:08Z",
"severity": "LOW"
},
"details": "Issue summary: A non-constant-time optimized implementation of scalar\npoint multiplication is used for SM2 private key operations on ARM64 and\nRISC-V platforms.\n\nImpact summary: An attacker able to measure the time taken by, or to observe\nthe cache-line access pattern of SM2 signing or decryption on an affected\nplatform can learn information about the secret scalar.\n\nCWE: CWE-208: Observable Timing Discrepancy\n\nDescription: On ARM64 and RISC-V processors, the SM2 curve uses an optimized\nscalar multiplication implementation whose conditional branches and table\nlook ups are chosen according to the bits of the secret scalar. The execution\ntime and the cache-access pattern therefore depend on the long-term private\nkey (during SM2 decryption) or the per-signature nonce (during SM2 signature\ngeneration), forming a timing and cache side-channel.\n\nFIPS Impact: no\nSM2 is not a FIPS algorithm and the optimized SM2 implementation is not part\nof the FIPS module.\n\nOpenSSL 4.0, 3.6, 3.5 and 3.4 are vulnerable to this issue on AArch64 and\nRISC-V.\n\nOpenSSL 3.0, 1.1.1 and 1.0.2 are not affected by this issue.\n\nOpenSSL 4.0 users should upgrade to OpenSSL 4.0.3.\nOpenSSL 3.6 users should upgrade to OpenSSL 3.6.5.\nOpenSSL 3.5 users should upgrade to OpenSSL 3.5.9.\nOpenSSL 3.4 users should upgrade to OpenSSL 3.4.8.\n\nThis issue was reported on 2 May 2026 by Abhinav Agarwal.\nIt was independently reported on 6 June 2026 by Feng Xue.\nThe fix was developed by Igor Ustinov.\n\n-- cut (non-publishing metadata for internal use) --\nReported by: Abhinav Agarwal, Feng Xue\nFixed by: Igor Ustinov",
"id": "GHSA-q4gq-r6f8-q6q9",
"modified": "2026-09-29T18:31:44Z",
"published": "2026-09-29T18:31:44Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-54875"
},
{
"type": "WEB",
"url": "https://github.com/openssl/openssl/commit/3f01bbc28f7e08211fcdc797fd43816504f94257"
},
{
"type": "WEB",
"url": "https://github.com/openssl/openssl/commit/469f3e42629f4a0b5631796e20c66c92c138a3e8"
},
{
"type": "WEB",
"url": "https://github.com/openssl/openssl/commit/9794ed473764839275cb701b4850f3c24d929c28"
},
{
"type": "WEB",
"url": "https://github.com/openssl/openssl/commit/dddad955d5ff3e9507619cf4e0f13e9988e2197c"
},
{
"type": "WEB",
"url": "https://openssl-library.org/news/secadv/20260929.txt"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:L/I:N/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-Q65W-FG65-79F4
Vulnerability from github – Published: 2025-03-14 19:55 – Updated: 2025-03-19 15:28Description:
The feldman_vss library contains timing side-channel vulnerabilities in its matrix operations, specifically within the _find_secure_pivot function and potentially other parts of _secure_matrix_solve. These vulnerabilities are due to Python's execution model, which does not guarantee constant-time execution. An attacker with the ability to measure the execution time of these functions (e.g., through repeated calls with carefully crafted inputs) could potentially recover secret information used in the Verifiable Secret Sharing (VSS) scheme.
The _find_secure_pivot function, used during Gaussian elimination in _secure_matrix_solve, attempts to find a non-zero pivot element. However, the conditional statement if matrix[row][col] != 0 and row_random < min_value: has execution time that depends on the value of matrix[row][col]. This timing difference can be exploited by an attacker.
The constant_time_compare function in this file also does not provide a constant-time guarantee.
This advisory formalizes the timing side-channel vulnerabilities already documented in the library's "Known Security Vulnerabilities" section. The Python implementation of matrix operations in the _find_secure_pivot and _secure_matrix_solve functions cannot guarantee constant-time execution, potentially leaking information about secret polynomial coefficients.
An attacker with the ability to make precise timing measurements of these operations could potentially extract secret information through statistical analysis of execution times, though practical exploitation would require significant expertise and controlled execution environments.
Impact:
Successful exploitation of these timing side-channels could allow an attacker to recover secret keys or other sensitive information protected by the VSS scheme. This could lead to a complete compromise of the shared secret.
References:
- File:
feldman_vss.py - Function:
_find_secure_pivot - Function:
_secure_matrix_solve - Function:
constant_time_compare - Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems (1996) - A seminal paper on timing attacks.
- Side-Channel Attacks - Wikipedia article on side-channel attacks.
Remediation:
As acknowledged in the library's documentation, these vulnerabilities cannot be adequately addressed in pure Python. The advisory recommends:
-
SHORT TERM: Consider using this library only in environments where timing measurements by attackers are infeasible.
-
MEDIUM TERM: Implement your own wrappers around critical operations using constant-time libraries in languages like Rust, Go, or C.
-
LONG TERM: Wait for the planned Rust implementation mentioned in the library documentation that will properly address these issues.
Note that the usage of random.Random() identified in the _refresh_shares_additive function is intentional and secure as documented in the "False-Positive Vulnerabilities" section of the code, and should not be considered part of this vulnerability.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "PostQuantum-Feldman-VSS"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"last_affected": "0.8.0b2"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-29780"
],
"database_specific": {
"cwe_ids": [
"CWE-203",
"CWE-208",
"CWE-385"
],
"github_reviewed": true,
"github_reviewed_at": "2025-03-14T19:55:10Z",
"nvd_published_at": "2025-03-14T18:15:32Z",
"severity": "MODERATE"
},
"details": "**Description:**\n\nThe `feldman_vss` library contains timing side-channel vulnerabilities in its matrix operations, specifically within the `_find_secure_pivot` function and potentially other parts of `_secure_matrix_solve`. These vulnerabilities are due to Python\u0027s execution model, which does not guarantee constant-time execution. An attacker with the ability to measure the execution time of these functions (e.g., through repeated calls with carefully crafted inputs) could potentially recover secret information used in the Verifiable Secret Sharing (VSS) scheme.\n\nThe `_find_secure_pivot` function, used during Gaussian elimination in `_secure_matrix_solve`, attempts to find a non-zero pivot element. However, the conditional statement `if matrix[row][col] != 0 and row_random \u003c min_value:` has execution time that depends on the value of `matrix[row][col]`. This timing difference can be exploited by an attacker.\n\nThe `constant_time_compare` function in this file also does not provide a constant-time guarantee.\n\nThis advisory formalizes the timing side-channel vulnerabilities already documented in the library\u0027s \"Known Security Vulnerabilities\" section. The Python implementation of matrix operations in the _find_secure_pivot and _secure_matrix_solve functions cannot guarantee constant-time execution, potentially leaking information about secret polynomial coefficients.\n\nAn attacker with the ability to make precise timing measurements of these operations could potentially extract secret information through statistical analysis of execution times, though practical exploitation would require significant expertise and controlled execution environments.\n\n**Impact:**\n\nSuccessful exploitation of these timing side-channels could allow an attacker to recover secret keys or other sensitive information protected by the VSS scheme. This could lead to a complete compromise of the shared secret.\n\n**References:**\n\n* File: `feldman_vss.py`\n* Function: `_find_secure_pivot`\n* Function: `_secure_matrix_solve`\n* Function: `constant_time_compare`\n* [Timing Attacks on Implementations of Diffie-Hellman, RSA, DSS, and Other Systems (1996)](https://www.rambus.com/wp-content/uploads/2015/08/TimingAttacks.pdf) - A seminal paper on timing attacks.\n* [Side-Channel Attacks](https://en.wikipedia.org/wiki/Side-channel_attack) - Wikipedia article on side-channel attacks.\n\n**Remediation:**\n\nAs acknowledged in the library\u0027s documentation, these vulnerabilities cannot be adequately addressed in pure Python. The advisory recommends:\n\n1. SHORT TERM: Consider using this library only in environments where timing measurements by attackers are infeasible.\n\n2. MEDIUM TERM: Implement your own wrappers around critical operations using constant-time libraries in languages like Rust, Go, or C.\n\n3. LONG TERM: Wait for the planned Rust implementation mentioned in the library documentation that will properly address these issues.\n\nNote that the usage of random.Random() identified in the _refresh_shares_additive function is intentional and secure as documented in the \"False-Positive Vulnerabilities\" section of the code, and should not be considered part of this vulnerability.",
"id": "GHSA-q65w-fg65-79f4",
"modified": "2025-03-19T15:28:08Z",
"published": "2025-03-14T19:55:10Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/DavidOsipov/PostQuantum-Feldman-VSS/security/advisories/GHSA-q65w-fg65-79f4"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-29780"
},
{
"type": "WEB",
"url": "https://en.wikipedia.org/wiki/Side-channel_attack"
},
{
"type": "PACKAGE",
"url": "https://github.com/DavidOsipov/PostQuantum-Feldman-VSS"
},
{
"type": "WEB",
"url": "https://www.rambus.com/wp-content/uploads/2015/08/TimingAttacks.pdf"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:H/AT:P/PR:L/UI:N/VC:H/VI:L/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Post-Quantum Secure Feldman\u0027s Verifiable Secret Sharing has Timing Side-Channels in Matrix Operations"
}
GHSA-Q66M-CMXR-CPCF
Vulnerability from github – Published: 2026-06-26 18:33 – Updated: 2026-06-26 18:33An observable timing discrepancy in the ASP could allow a privileged attacker to perform a brute-force attack against the hash message authentication code, allowing arbitrary message input, potentially leading to a loss of data integrity.
{
"affected": [],
"aliases": [
"CVE-2023-20540"
],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-26T16:16:28Z",
"severity": "LOW"
},
"details": "An observable timing discrepancy in the ASP could allow a privileged attacker to perform a brute-force attack against the hash message authentication code, allowing arbitrary message input, potentially leading to a loss of data integrity.",
"id": "GHSA-q66m-cmxr-cpcf",
"modified": "2026-06-26T18:33:58Z",
"published": "2026-06-26T18:33:58Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-20540"
},
{
"type": "WEB",
"url": "https://www.amd.com/en/resources/product-security/bulletin/amd-sb-4012.html"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:L/AC:H/AT:N/PR:H/UI:N/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X",
"type": "CVSS_V4"
}
]
}
GHSA-Q7PG-9PR4-MRP2
Vulnerability from github – Published: 2025-09-12 21:11 – Updated: 2025-09-12 21:11Summary
HMAC signature comparison is not timing-safe and is vulnerable to timing attacks.
Details
SharedKey::sign() returns a Vec<u8> which has a non-constant-time equality implementation.
Hmac::finalize() returns a constant-time wrapper (CtOutput) which was discarded. Alternatively, Hmac has a constant-time verify() method.
The problem reported here is due to the following lines in SharedKey::sign() of the previous code:
let mut mac = HmacSha256::new_from_slice(key).unwrap();
mac.update(data);
Ok(mac.finalize().into_bytes().to_vec())
and the merged update changes the third line to directly verify with verify_slice.
Impact
Anyone who uses HS256 signature verification is vulnerably to Timing Attack that allows the attacker to forge a signature.
{
"affected": [
{
"package": {
"ecosystem": "crates.io",
"name": "httpsig"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.0.19"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2025-59058"
],
"database_specific": {
"cwe_ids": [
"CWE-208"
],
"github_reviewed": true,
"github_reviewed_at": "2025-09-12T21:11:13Z",
"nvd_published_at": "2025-09-12T14:15:41Z",
"severity": "MODERATE"
},
"details": "### Summary\nHMAC signature comparison is not timing-safe and is vulnerable to timing attacks.\n\n### Details\n`SharedKey::sign()` returns a `Vec\u003cu8\u003e` which has a non-constant-time equality implementation.\n\n`Hmac::finalize()` returns a constant-time wrapper ([`CtOutput`](https://docs.rs/digest/0.10.7/digest/struct.CtOutput.html)) which was discarded. Alternatively, `Hmac` has a constant-time `verify()` method.\n\nThe problem reported here is due to the following lines in `SharedKey::sign()` of the previous code:\n```rust\nlet mut mac = HmacSha256::new_from_slice(key).unwrap();\nmac.update(data);\nOk(mac.finalize().into_bytes().to_vec())\n```\nand the merged update changes the third line to directly verify with `verify_slice`.\n\n### Impact\n\nAnyone who uses HS256 signature verification is vulnerably to Timing Attack that allows the attacker to forge a signature.",
"id": "GHSA-q7pg-9pr4-mrp2",
"modified": "2025-09-12T21:11:13Z",
"published": "2025-09-12T21:11:13Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/junkurihara/httpsig-rs/security/advisories/GHSA-q7pg-9pr4-mrp2"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2025-59058"
},
{
"type": "WEB",
"url": "https://github.com/junkurihara/httpsig-rs/commit/fc095b6ce6043bb808f5d9c4379cf697899cb458"
},
{
"type": "PACKAGE",
"url": "https://github.com/junkurihara/httpsig-rs"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "httpsig-rs: HMAC verification is vulnerable to timing attack"
}
GHSA-Q97C-8QH3-FPC6
Vulnerability from github – Published: 2026-09-08 21:24 – Updated: 2026-09-29 21:15The pure-PHP X25519 scalar multiplication in phpseclib is not constant-time. Field addition and subtraction each perform a data-dependent conditional modular reduction, so the cost of each Montgomery-ladder step is a linear function of that step's reduction count which is a quantity determined by the secret scalar's prefix.
An observer with per-ladder-step resolution recovers the 251-bit clamped private scalar. This is a per-step leak, not an aggregate one: an instrumented code proof-of-concept recovers 20/20 test keys from 32 observed operations, and an observer that counts libgmp calls instead of timing them recovers a key from a single operation.
This is not a low-order-input issue. Recovery works with the RFC 7748 base point u = 9, with no attacker-chosen input at all. Rejecting low-order public values does not close it.
2. Affected component
Confirmed on phpseclib 3.0.56 (338 files under phpseclib/,sha256(sorted(relpath NUL file_sha256 LF)) = cc7250b611f520e809131aab0931503457c44d8cbfb10d535251c6fec5f62a2b).
The code appears unchanged across the 3.0 series wherever Curve25519 is supported, please confirm the affected range.
| file:line | role |
|---|---|
Math/PrimeField/Integer.php:189 |
add() — conditional subtract($modulo) when the sum ≥ p |
Math/PrimeField/Integer.php:207 |
subtract() — conditional add($modulo) when the result is negative |
Crypt/EC/BaseCurves/Montgomery.php:229–234 |
ladder branch on the secret bit, selecting argument order of doubleAndAddPoint |
Crypt/EC/Formats/Keys/MontgomeryPrivate.php:66 |
multiplyPoint(getBasePoint(), dA) — no engine check of any kind |
Crypt/EC/Formats/Keys/PKCS8.php:194–200 |
the same derivation, correctly gated on ext-sodium — the pattern MontgomeryPrivate is missing |
3. Technical description
Operation counts in the ladder are already constant — 10 field multiplications, 4 additions and 4 subtractions per step, 2560 multiplications per 256-step ladder. Operand values are not. Each PrimeField\Integer::add() / subtract() takes a data-dependent branch costing ~0.85–1.0 µs on the GMP engine, against a ~32 µs step period, so per-step cost is α + β·c where c is that step's conditional-reduction count. Measured across 20 keys: R² = 0.91–0.98, β = 838–920 ns.
c depends on the whole scalar prefix, not on the current bit, so per-step thresholding is useless — it saturates at ~93% per bit for u = p−1 and at chance for u = 9, and recovers 0/20 keys either way, because the bit string is a prefix-XOR in which one flipped step inverts the entire tail. Conditioning on the prefix removes the ambiguity: a beam search replays both branches from each candidate ladder state, reads off the exact c for each, and scores against the observation. The victim's public key adjudicates the small residual search.
Two facts bound the problem and are worth stating precisely, because they determine whether a fix is needed at all:
- Aggregate observation is provably useless. The adjacent-bit transition count
T(k)has exact entropyH(T) = 4.0357bits over clamped scalars, so a noiseless transition-count oracle still leaves ~2^247 candidates. The summed reduction countΣcis richer (~6.6–6.9 bits) and still leaves ~2^244. Any measurement that collapses the call to one number is safe. Per-step measurement is not. - The libgmp call counts are exactly determined. Per ladder step,
__gmpz_add = 4 + csub,__gmpz_sub = 4 + cadd,__gmpz_mul = __gmpz_mod = 10. Verified by differencing gdb breakpoint counts against phpseclib's owndoubleAndAddPoint— 27/27 steps exact, extended independently to 64/64 and 38/38 by our two reviewers. An observer that only counts these calls needs no timing, no calibration and no repetition.
Results, 20 keys × 3 sampling seeds, 800 traces per path collected from 800 distinct PHP processes (so the observations are cross-process, as real requests would be):
| observer | path | observations needed | exact 251-bit recovery |
|---|---|---|---|
| timing | key load, u = 9 |
32 | 20/20 keys, 95% CI [83.9%, 100%] |
| timing | ECDH, u = p−1 |
32 | 18/20 keys, 95% CI [69.9%, 96.8%] |
| timing | either | 8 | 18–23% of trials |
| libgmp call counts | either | 1 | 20/20 keys; tolerates 20–30% of per-step counts being wrong |
The model underlying the decoder is validated against the pinned implementation: 254/254 (key, peer) outputs match the real DH::computeSecret, and all four RFC 7748 §6.1 vectors match both phpseclib and the published constants.
Negative controls are clean — wrong public key, shuffled trace, wrong peer value, foreign key: 0/20 in every case. Nothing derived from the private key reaches the decoder; its inputs are the observation vector, the peer value, the victim's public key, and the public clamping constants.
5. Impact
In the instrumented, local model, recovery of the clamped scalar gives a permanent compromise of the X25519 private key. Clamping is applied on every call, so the recovered value is what every past and future operation with that key uses.
6. Restrictions
Required for exploitation:
- A reused / long-lived X25519 private key. Ephemeral X25519 — the normal TLS and SSH case — defeats this outright. phpseclib's own SSH path generates a fresh scalar per exchange and is not affected.
- Knowledge of the victim's public key. It adjudicates the decoder's residual search; without it no candidate can be selected. This is normally public, but it is a precondition, not a convenience.
- The pure-PHP path must actually run. Measured across four extension configurations:
EC::loadFormat('MontgomeryPrivate', $raw32)runs the ladder in every configuration — but the format declaresIS_INVISIBLE(MontgomeryPrivate.php:40), soPublicKeyLoader::loadskips it and nothing inside phpseclib calls it. An application must name the format explicitly.PKCS8/PublicKeyLoader::load/EC::createKeyrun the ladder only when ext-sodium is absent —PKCS8.php:194gates onsodium_crypto_box_publickey_from_secretkey. OpenSSL does not help here.DH::computeSecretruns the ladder only underEC::forceEngine('PHP'), or when bothopenssl_pkey_derive(DH.php:325) andsodium_crypto_scalarmult(EC/PrivateKey.php:75) are unavailable. ext-sodium is bundled and enabled by default in PHP 7.2+, so the reachable configurations are a minority — thoughdisable_functionshardening and--disable-sodiumbuilds do occur, particularly in shared hosting.- An observer with per-ladder-step resolution, i.e. one that can distinguish ~0.9 µs within a ~32 µs step, or count libgmp entry-point calls. In practice that means local co-residency (e.g. a Flush+Reload spy on the shared
libgmp.somapping —__gmpz_add/__gmpz_subare the correct targets;__gmpn_*are not, being size-dispatched internals).
Not demonstrated — stated so it is not found rather than disclosed:
- We did not build a co-resident spy. Per-step observations in the PoC come from a small
hrtime()hook insideMontgomery::multiplyPoint(one file differs from the pinned tree;Math/PrimeField/Integer.php, which carries the leak, is byte-identical). This models an observer with intra-call resolution; it is not itself an attacker capability. We note that the requisite primitives are present on ordinary hardware — on our test hostclflushandrdtscpwork, with 187–312 cycle cached/flushed separation — but building and validating a spy is separate work we have not done. - Perfect step segmentation is assumed. A real observer must recover step boundaries and separate the ladder from the surrounding modular inversion.
- A single host and configuration. All timing figures are from one 2-vCPU shared VM (PHP 8.3.6, GMP 6.3.0). A noisier host needs more observations; recovery holds within roughly 25–30% additional noise beyond this host's residual.
- We have not identified an affected deployed caller.
Accordingly we report this as a hardening issue and demonstrated side channel, not as a completed remote exploit.
8. Suggested remediation
- Constant-time, fixed-width field arithmetic, or delegate to a vetted native provider. This is the actual fix. Removing the ladder's bit branch is not sufficient while
Integer.php:189and:207remain operand-dependent. - Gate
MontgomeryPrivate.php:66the wayPKCS8.php:194–200already is. That is a one-block change and it closes the only entry point that is un-gated in every configuration. Keep the$curve instanceof Curve25519guard —MontgomeryPrivatealso accepts Curve448 keys. - Consider an OpenSSL arm alongside the sodium arm in
PKCS8::loadECDH, or fail closed, so stacks without ext-sodium do not fall through to the ladder. - Separately, and unrelated to this channel: the pure-PHP path returns an all-zero 32-byte shared secret for low-order peer inputs. Rejecting the full canonicalised low-order set and adding a constant-time all-zero check is correct hygiene for contributory behaviour — but it does not mitigate the timing channel, since recovery works with
u = 9.
Contact
George Stergiopoulos Assistant Professor of cybersecurity Athens University of Economics and Business, Greece E: geostergiop@aueb.gr | s: https://www.aueb.gr/en/faculty_page/stergiopoulos-georgios
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "phpseclib/phpseclib"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "3.0.57"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "phpseclib/phpseclib"
},
"ranges": [
{
"events": [
{
"introduced": "4.0.0"
},
{
"fixed": "4.0.1"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-84308"
],
"database_specific": {
"cwe_ids": [
"CWE-208",
"CWE-385"
],
"github_reviewed": true,
"github_reviewed_at": "2026-09-08T21:24:29Z",
"nvd_published_at": "2026-09-01T20:17:24Z",
"severity": "MODERATE"
},
"details": "The pure-PHP X25519 scalar multiplication in phpseclib is not constant-time. Field addition and subtraction each perform a **data-dependent conditional modular reduction**, so the cost of each Montgomery-ladder step is a linear function of that step\u0027s reduction count which is a quantity determined by the secret scalar\u0027s *prefix*.\n\nAn observer with per-ladder-step resolution recovers the 251-bit clamped private scalar. This is a per-step leak, not an aggregate one: an instrumented code proof-of-concept recovers 20/20 test keys from 32 observed operations, and an observer that counts libgmp calls instead of timing them recovers a key from a **single** operation.\n\nThis is **not** a low-order-input issue. Recovery works with the RFC 7748 base point `u = 9`, with no attacker-chosen input at all. Rejecting low-order public values does not close it.\n\n## 2. Affected component\n\nConfirmed on **phpseclib 3.0.56** (338 files under `phpseclib/`,`sha256(sorted(relpath NUL file_sha256 LF)) = cc7250b611f520e809131aab0931503457c44d8cbfb10d535251c6fec5f62a2b`).\nThe code appears unchanged across the 3.0 series wherever Curve25519 is supported, please confirm the affected range.\n\n| file:line | role |\n|---|---|\n| `Math/PrimeField/Integer.php:189` | `add()` \u2014 conditional `subtract($modulo)` when the sum \u2265 p |\n| `Math/PrimeField/Integer.php:207` | `subtract()` \u2014 conditional `add($modulo)` when the result is negative |\n| `Crypt/EC/BaseCurves/Montgomery.php:229\u2013234` | ladder branch on the secret bit, selecting argument order of `doubleAndAddPoint` |\n| `Crypt/EC/Formats/Keys/MontgomeryPrivate.php:66` | `multiplyPoint(getBasePoint(), dA)` \u2014 **no engine check of any kind** |\n| `Crypt/EC/Formats/Keys/PKCS8.php:194\u2013200` | the same derivation, correctly gated on ext-sodium \u2014 the pattern `MontgomeryPrivate` is missing |\n\n## 3. Technical description\n\nOperation *counts* in the ladder are already constant \u2014 10 field multiplications, 4 additions and 4 subtractions per step, 2560 multiplications per 256-step ladder. Operand *values* are not. Each `PrimeField\\Integer::add()` / `subtract()` takes a data-dependent branch costing ~0.85\u20131.0 \u00b5s on the GMP engine, against a ~32 \u00b5s step period, so per-step cost is `\u03b1 + \u03b2\u00b7c` where `c` is that step\u0027s conditional-reduction count. Measured across 20 keys: R\u00b2 = 0.91\u20130.98, \u03b2 = 838\u2013920 ns.\n\n`c` depends on the whole scalar prefix, not on the current bit, so per-step thresholding is useless \u2014 it saturates at ~93% per bit for `u = p\u22121` and at **chance** for `u = 9`, and recovers 0/20 keys either way, because the bit string is a prefix-XOR in which one flipped step inverts the entire tail. Conditioning on the prefix removes the ambiguity: a beam search replays both branches from each candidate ladder state, reads off the exact `c` for each, and scores against the observation. The victim\u0027s public key adjudicates the small residual search.\n\nTwo facts bound the problem and are worth stating precisely, because they determine whether a fix is needed at all:\n\n- **Aggregate observation is provably useless.** The adjacent-bit transition count `T(k)` has exact entropy `H(T) = 4.0357` bits over clamped scalars, so a noiseless transition-count oracle still leaves ~2^247 candidates. The summed reduction count `\u03a3c` is richer (~6.6\u20136.9 bits) and still leaves ~2^244. Any measurement that collapses the call to one number is safe. Per-step measurement is not.\n- **The libgmp call counts are exactly determined.** Per ladder step, `__gmpz_add = 4 + csub`, `__gmpz_sub = 4 + cadd`, `__gmpz_mul = __gmpz_mod = 10`. Verified by differencing gdb breakpoint counts against phpseclib\u0027s own `doubleAndAddPoint` \u2014 27/27 steps exact, extended independently to 64/64 and 38/38 by our two reviewers. An observer that only *counts* these calls needs no timing, no calibration and no repetition.\n\nResults, 20 keys \u00d7 3 sampling seeds, 800 traces per path collected from **800 distinct PHP processes** (so the observations are cross-process, as real requests would be):\n\n| observer | path | observations needed | exact 251-bit recovery |\n|---|---|---|---|\n| timing | key load, `u = 9` | 32 | **20/20 keys**, 95% CI [83.9%, 100%] |\n| timing | ECDH, `u = p\u22121` | 32 | **18/20 keys**, 95% CI [69.9%, 96.8%] |\n| timing | either | 8 | 18\u201323% of trials |\n| libgmp call counts | either | **1** | 20/20 keys; tolerates 20\u201330% of per-step counts being wrong |\n\nThe model underlying the decoder is validated against the pinned implementation: 254/254 (key, peer) outputs match the real `DH::computeSecret`, and all four RFC 7748 \u00a76.1 vectors match both phpseclib and the published constants.\n\nNegative controls are clean \u2014 wrong public key, shuffled trace, wrong peer value, foreign key: 0/20 in every case. Nothing derived from the private key reaches the decoder; its inputs are the observation vector, the peer value, the victim\u0027s public key, and the public clamping constants.\n\n## 5. Impact\n\nIn the instrumented, local model, recovery of the clamped scalar gives a permanent compromise of the X25519 private key. Clamping is applied on every call, so the recovered value is what every past and future operation with that key uses.\n\n## 6. Restrictions\n\n**Required for exploitation:**\n\n1. **A reused / long-lived X25519 private key.** Ephemeral X25519 \u2014 the normal TLS and SSH case \u2014 defeats this outright. phpseclib\u0027s own SSH path generates a fresh scalar per exchange and is not affected.\n2. **Knowledge of the victim\u0027s public key.** It adjudicates the decoder\u0027s residual search; without it no candidate can be selected. This is normally public, but it is a precondition, not a convenience.\n3. **The pure-PHP path must actually run.** Measured across four extension configurations:\n - `EC::loadFormat(\u0027MontgomeryPrivate\u0027, $raw32)` runs the ladder in **every**\n configuration \u2014 but the format declares `IS_INVISIBLE` (`MontgomeryPrivate.php:40`),\n so `PublicKeyLoader::load` skips it and nothing inside phpseclib calls it. An\n application must name the format explicitly.\n - `PKCS8` / `PublicKeyLoader::load` / `EC::createKey` run the ladder **only when ext-sodium is absent** \u2014 `PKCS8.php:194` gates on `sodium_crypto_box_publickey_from_secretkey`. OpenSSL does not help here.\n - `DH::computeSecret` runs the ladder only under `EC::forceEngine(\u0027PHP\u0027)`, or when *both* `openssl_pkey_derive` (`DH.php:325`) and `sodium_crypto_scalarmult` (`EC/PrivateKey.php:75`) are unavailable. ext-sodium is bundled and enabled by default in PHP 7.2+, so the reachable configurations are a minority \u2014 though `disable_functions` hardening and `--disable-sodium` builds do occur, particularly in shared hosting.\n4. **An observer with per-ladder-step resolution**, i.e. one that can distinguish ~0.9 \u00b5s within a ~32 \u00b5s step, or count libgmp entry-point calls. In practice that means local co-residency (e.g. a Flush+Reload spy on the shared `libgmp.so` mapping \u2014 `__gmpz_add` / `__gmpz_sub` are the correct targets; `__gmpn_*` are not, being size-dispatched internals).\n\n**Not demonstrated \u2014 stated so it is not found rather than disclosed:**\n\n- **We did not build a co-resident spy.** Per-step observations in the PoC come from a small `hrtime()` hook inside `Montgomery::multiplyPoint` (one file differs from the pinned tree; `Math/PrimeField/Integer.php`, which carries the leak, is byte-identical). This models an observer with intra-call resolution; it is not itself an attacker capability. We note that the requisite primitives are present on ordinary hardware \u2014 on our test host `clflush` and `rdtscp` work, with 187\u2013312 cycle cached/flushed separation \u2014 but building and validating a spy is separate work we have not done.\n- **Perfect step segmentation is assumed.** A real observer must recover step boundaries and separate the ladder from the surrounding modular inversion.\n- **A single host and configuration.** All timing figures are from one 2-vCPU shared VM (PHP 8.3.6, GMP 6.3.0). A noisier host needs more observations; recovery holds within roughly 25\u201330% additional noise beyond this host\u0027s residual.\n- **We have not identified an affected deployed caller.**\n\nAccordingly we report this as a **hardening issue and demonstrated side channel**, not as a completed remote exploit.\n\n## 8. Suggested remediation\n\n1. **Constant-time, fixed-width field arithmetic, or delegate to a vetted native provider.** This is the actual fix. Removing the ladder\u0027s bit branch is *not* sufficient while `Integer.php:189` and `:207` remain operand-dependent.\n2. **Gate `MontgomeryPrivate.php:66` the way `PKCS8.php:194\u2013200` already is.** That is a one-block change and it closes the only entry point that is un-gated in every configuration. Keep the `$curve instanceof Curve25519` guard \u2014 `MontgomeryPrivate` also accepts Curve448 keys.\n3. **Consider an OpenSSL arm alongside the sodium arm in `PKCS8::loadECDH`**, or fail closed, so stacks without ext-sodium do not fall through to the ladder.\n4. Separately, and unrelated to this channel: the pure-PHP path returns an all-zero 32-byte shared secret for low-order peer inputs. Rejecting the full canonicalised low-order set and adding a constant-time all-zero check is correct hygiene for contributory behaviour \u2014 but it does **not** mitigate the timing channel, since recovery works with `u = 9`.\n\n## Contact\nGeorge Stergiopoulos\nAssistant Professor of cybersecurity\nAthens University of Economics and Business, Greece\nE: geostergiop@aueb.gr | s: https://www.aueb.gr/en/faculty_page/stergiopoulos-georgios",
"id": "GHSA-q97c-8qh3-fpc6",
"modified": "2026-09-29T21:15:45Z",
"published": "2026-09-08T21:24:29Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/phpseclib/phpseclib/security/advisories/GHSA-q97c-8qh3-fpc6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-84308"
},
{
"type": "WEB",
"url": "https://github.com/phpseclib/phpseclib/commit/fb56bc5bb9009b54a6c26b31aeec8ed944f17373"
},
{
"type": "PACKAGE",
"url": "https://github.com/phpseclib/phpseclib"
},
{
"type": "WEB",
"url": "https://github.com/phpseclib/phpseclib/releases/tag/3.0.57"
},
{
"type": "WEB",
"url": "https://github.com/phpseclib/phpseclib/releases/tag/4.0.1"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:L/AC:H/PR:L/UI:N/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "phpseclib \u2014 non-constant-time X25519 scalar multiplication permits full private-key recovery"
}
GHSA-QF78-7RGR-8JF5
Vulnerability from github – Published: 2023-01-01 18:30 – Updated: 2023-01-09 18:30A vulnerability classified as problematic was found in Ziftr primecoin up to 0.8.4rc1. Affected by this vulnerability is the function HTTPAuthorized of the file src/bitcoinrpc.cpp. The manipulation of the argument strUserPass/strRPCUserColonPass leads to observable timing discrepancy. Upgrading to version 0.8.4rc2 is able to address this issue. The name of the patch is cdb3441b5cd2c1bae49fae671dc4a496f7c96322. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-217171.
{
"affected": [],
"aliases": [
"CVE-2013-10006"
],
"database_specific": {
"cwe_ids": [
"CWE-203",
"CWE-208"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-01-01T17:15:00Z",
"severity": "HIGH"
},
"details": "A vulnerability classified as problematic was found in Ziftr primecoin up to 0.8.4rc1. Affected by this vulnerability is the function HTTPAuthorized of the file src/bitcoinrpc.cpp. The manipulation of the argument strUserPass/strRPCUserColonPass leads to observable timing discrepancy. Upgrading to version 0.8.4rc2 is able to address this issue. The name of the patch is cdb3441b5cd2c1bae49fae671dc4a496f7c96322. It is recommended to upgrade the affected component. The associated identifier of this vulnerability is VDB-217171.",
"id": "GHSA-qf78-7rgr-8jf5",
"modified": "2023-01-09T18:30:18Z",
"published": "2023-01-01T18:30:22Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2013-10006"
},
{
"type": "WEB",
"url": "https://github.com/Ziftr/primecoin/commit/cdb3441b5cd2c1bae49fae671dc4a496f7c96322"
},
{
"type": "WEB",
"url": "https://github.com/Ziftr/primecoin/releases/tag/v0.8.4rc2"
},
{
"type": "WEB",
"url": "https://vuldb.com/?ctiid.217171"
},
{
"type": "WEB",
"url": "https://vuldb.com/?id.217171"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
"type": "CVSS_V3"
}
]
}
No mitigation information available for this CWE.
CAPEC-462: Cross-Domain Search Timing
An attacker initiates cross domain HTTP / GET requests and times the server responses. The timing of these responses may leak important information on what is happening on the server. Browser's same origin policy prevents the attacker from directly reading the server responses (in the absence of any other weaknesses), but does not prevent the attacker from timing the responses to requests that the attacker issued cross domain.
CAPEC-541: Application Fingerprinting
An adversary engages in fingerprinting activities to determine the type or version of an application installed on a remote target.
CAPEC-580: System Footprinting
An adversary engages in active probing and exploration activities to determine security information about a remote target system. Often times adversaries will rely on remote applications that can be probed for system configurations.