CVEReports
CVEReports

Automated vulnerability intelligence platform. Comprehensive reports for high-severity CVEs generated by AI.

Product

  • Home
  • Sitemap
  • RSS Feed

Company

  • About
  • Contact
  • Privacy Policy
  • Terms of Service

© 2026 CVEReports. All rights reserved.

Made with love by Amit Schendel & Alon Barad



GHSA-2JX3-65F3-XR8R

GHSA-2JX3-65F3-XR8R: Dynamic Property Injection (Mass Assignment) in spomky-labs/otphp

Amit Schendel
Amit Schendel
Senior Security Researcher

Jun 19, 2026·7 min read·3 visits

Executive Summary (TL;DR)

Unauthenticated remote attackers can deliver crafted OTP provisioning URIs to overwrite internal properties of the `otphp` library, causing denial of service, validation bypasses, or immediate application crashes.

A critical mass-assignment (property injection) vulnerability exists in the PHP One-Time Password (OTP) library spomky-labs/otphp within the Factory::loadFromProvisioningUri method. When an application loads an OTP provisioning URI (such as a QR code configuration link), a hostile URI can inject query parameters that dynamically overwrite internal, private, or read-only object properties of the OTP instance. This behavior leads to application state corruption, validation bypasses, or uncaught TypeErrors that crash the executing application process.

Vulnerability Overview

The PHP One-Time Password (OTP) library spomky-labs/otphp is a widely utilized package in the PHP ecosystem for generating and validating Google Authenticator, Microsoft Authenticator, and standard TOTP/HOTP values. The core attack surface is exposed via the Factory::loadFromProvisioningUri() interface. This method is routinely used by applications to programmatically import multi-factor authentication (MFA) settings from QR codes or configuration URLs provided directly by users or third-party authentication managers.

Historically, this method accepted any syntactically valid otpauth:// URI and parsed its query parameters into an instance of the OTP object. However, versions of the package prior to 11.4.3 contain an improper input validation vulnerability classified under CWE-915: Improperly Controlled Modification of Dynamically-Determined Object Attributes. By supplying query parameter keys that map to internal class properties, an attacker can hijack the deserialization process and execute a target-state injection attack.

This dynamic injection leads to multiple severe conditions including absolute internal state corruption, unexpected TypeErrors that crash the PHP thread, and security validation bypasses. Because many application workflows consume these provisioning URIs unauthenticated during user registration or recovery stages, this vulnerability presents an accessible vector for denial of service (DoS) and application state manipulation. No specialized privileges or complex user actions are required to trigger these states.

Root Cause Analysis

To understand the mechanics of the property injection, we must examine the parameter processing logic in src/OTP.php. When a provisioning URI is loaded, query parameters are parsed into name-value pairs and passed to OTP::setParameter($parameter, $value). The legacy implementation checked whether the parameter matched a defined class property using PHP's native property_exists() function.

If property_exists($this, $parameter) returned true, the library executed a dynamic property assignment: $this->{$parameter} = $value. Under standard object-oriented programming paradigms, private or protected variables are isolated from external direct manipulation. However, the use of property_exists against a user-controlled string bypassed these scoping rules, allowing an attacker to overwrite internal object fields that were never meant to be publicly mutable.

Two principal injection vectors arise from this dynamic assignment. First, an attacker can specify parameters[foo]=bar. Since the class contains a protected $parameters array, the code executes $this->parameters = ['foo' => 'bar']. This completely obliterates the existing state map that stores critical variables such as period, digits, digest, and secret. Subsequent calls to verify credentials or retrieve parameters trigger ParameterNotFoundException errors, resulting in a denial-of-service condition.

Second, an attacker can inject typed properties, such as issuer_included_as_parameter=notabool. Since the internal property $issuer_included_as_parameter is explicitly typed as a boolean in PHP, assigning a string to it triggers a native PHP TypeError. In vulnerable versions, this assignment occurs outside the limited exception-handling try-catch block in Factory, meaning the fatal error escapes, aborts execution, and immediately terminates the server thread.

Code-Level Analysis and Patch Verification

The security vulnerability was addressed in version 11.4.3 through three primary modifications. The first and most critical fix restricts which properties can be dynamically assigned from URI query parameters. Rather than executing a generic property_exists() check, the patched src/OTP.php now enforces a strict whitelist containing only the public label and issuer properties.

// Legacy vulnerable dynamic assignment in src/OTP.php
if (property_exists($this, $parameter)) {
    $this->{$parameter} = $value;
} else {
    $this->parameters[$parameter] = $value;
}
// Patched dynamic assignment in src/OTP.php (v11.4.3)
if (in_array($parameter, ['label', 'issuer'], true)) {
    $this->{$parameter} = $value;
} else {
    $this->parameters[$parameter] = $value;
}

Additionally, the patch modifies src/Factory.php to handle any unexpected anomalies that occur during object creation or population. The loadFromProvisioningUri method now traps all exceptions implementing the PHP global Throwable interface, packaging them into a uniform, safely handled InvalidProvisioningUriException. This prevents any TypeErrors from leaking out to crash the parent process.

// Patched Factory.php exception handling mechanism
try {
    $otp = self::createOTP($parsed_url, $clock);
    self::populateOTP($otp, $parsed_url);
} catch (InvalidProvisioningUriException $exception) {
    throw $exception;
} catch (Throwable $throwable) {
    throw new InvalidProvisioningUriException(
        'Not a valid OTP provisioning URI',
        $throwable->getCode(),
        $throwable
    );
}

Finally, the patch introduces static verification for the maximum allowed OTP digits. In src/OTPInterface.php, a constant is declared: public const MAX_DIGITS = 10. Any input value exceeding this threshold (such as digits=50) is rejected. This prevents standard dynamic truncation integer overflows and division-by-zero errors when calculating 10 ** digits during validation operations.

Exploitation and Attack Methodology

An attacker can exploit this vulnerability by submitting a crafted provisioning URI to any endpoint that consumes user-provided configuration strings. This scenario typically occurs when a user is setting up multi-factor authentication (MFA) and is given the option to paste a configuration link or upload a backup QR code containing an otpauth:// URI.

To execute a State Corruption attack, the attacker generates a URI containing a nested array parameter matching the protected property $parameters. When the factory processes the URI otpauth://totp/Alice?secret=JBSWY3DPEHPK3PXP&parameters[foo]=bar, the execution of $this->parameters = ['foo' => 'bar'] strips the object of its initialization configuration. If the application subsequently invokes $otp->getDigits(), the execution fails with an unhandled exception because the required digits property was deleted from the state.

To execute a Thread Crash attack, the attacker injects mismatched types into typed properties, such as issuer_included_as_parameter=notabool. Since this assignment bypasses standard type validation in PHP and is not caught by the factory, a fatal TypeError is thrown. The parent web server or application worker thread terminates immediately upon handling this request.

Impact Assessment

The security impact of GHSA-2JX3-65F3-XR8R is classified as a medium-severity Denial of Service (DoS) and application integrity corruption vulnerability, with a vendor-assigned CVSS v4 score of 5.3. While this vulnerability does not allow remote code execution (RCE) or direct database exfiltration, it breaks the core security assertions of multi-factor authentication systems.

First, the capacity to crash execution threads via uncaught TypeError exceptions allows unauthenticated attackers to systematically disable login endpoints. If an application allows users to register MFA profiles via an open endpoint, sending multiple requests with type-mismatched query variables will exhaust system resources or crash background queue workers processing the imports.

Second, bypassing validation arrays via property injection introduces significant risk of authentication bypass or spoofing. By injecting alternate configurations, attackers can manipulate internal metadata while evading callback-driven parameter checks, leading to discrepancies in identity verification pipelines. This compromises the reliable validation of multi-factor tokens and introduces potential logic flaws in calling applications.

Remediation and Defensive Design

Remediation of this vulnerability requires upgrading spomky-labs/otphp to version 11.4.3 or greater. Upgrading the package completely replaces the vulnerable property check with the strict whitelist constraint and introduces secure error-handling boundaries. This is the only vendor-supported and complete resolution to the flaw.

If an immediate upgrade is impossible, developers must implement an input-sanitization wrapper to intercept and clean the URI before it is processed by the library. This wrapper must parse the URI query parameters and explicitly strip blacklisted keys that map to private or protected object properties, such as parameters, clock, digits, and issuer_included_as_parameter.

function sanitizeProvisioningUri(string $uri): string {
    $parsed = parse_url($uri);
    if (!isset($parsed['query'])) {
        return $uri;
    }
    parse_str($parsed['query'], $queryData);
    $blacklist = ['parameters', 'clock', 'issuer_included_as_parameter', 'digits'];
    foreach ($blacklist as $key) {
        unset($queryData[$key]);
    }
    $parsed['query'] = http_build_query($queryData);
    return ($parsed['scheme'] ?? 'otpauth') . '://' . 
           ($parsed['host'] ?? '') . 
           ($parsed['path'] ?? '') . '?' . 
           $parsed['query'];
}

In addition to sanitization, software developers should treat all external parser libraries as untrusted boundaries. All interactions with external deserializers, factory loaders, or parser classes should be wrapped inside universal try-catch blocks that intercept PHP Throwable instances. This defensive architectural pattern ensures that logic errors, TypeErrors, or structural exceptions generated deep within imported packages cannot propagate upward and terminate the execution of critical system processes.

Official Patches

Spomky-LabsVulnerability Fix Diff / Patch Compare View

Technical Appendix

CVSS Score
5.3/ 10
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:L/VA:L/SC:N/SI:N/SA:N

Affected Systems

Web applications incorporating PHP MFA / TOTP / HOTP functionality based on the spomky-labs/otphp library prior to v11.4.3

Affected Versions Detail

Product
Affected Versions
Fixed Version
spomky-labs/otphp
Spomky-Labs
< 11.4.311.4.3
AttributeDetail
CWE IDCWE-915
Attack VectorNetwork
CVSS v4 Score5.3 (Medium)
Exploit StatusProof of Concept
Affected ComponentFactory::loadFromProvisioningUri
Vulnerability ClassImproperly Controlled Modification of Dynamically-Determined Object Attributes

MITRE ATT&CK Mapping

T1190Exploit Public-Facing Application
Initial Access
T1565.001Stored Data Manipulation
Impact
T1499Endpoint Denial of Service
Impact
CWE-915
Improperly Controlled Modification of Dynamically-Determined Object Attributes

The product receives input from an upstream component containing name-value pairs, which are dynamically mapped to attributes of an object, allowing an attacker to modify properties that are not intended to be exposed.

References & Sources

  • [1]GitHub Security Advisory GHSA-2jx3-65f3-xr8r
  • [2]FriendsOfPHP Security Advisory for GHSA-2jx3-65f3-xr8r
  • [3]Official Spomky-Labs otphp Repository
  • [4]Vulnerability Fix Diff / Patch Compare View

Attack Flow Diagram

Press enter or space to select a node. You can then use the arrow keys to move the node around. Press delete to remove it and escape to cancel.
Press enter or space to select an edge. You can then press delete to remove it or escape to cancel.

More Reports

•31 minutes ago•CVE-2026-0755
9.8

CVE-2026-0755: Remote Code Execution and Arbitrary File Exfiltration in gemini-mcp-tool

CVE-2026-0755 is a critical vulnerability in gemini-mcp-tool (<= 1.1.5) that allows unauthenticated remote code execution on Windows installations and arbitrary local file exfiltration across all supported operating systems. The flaws exist within the execAsync command runner and the input handling logic of the Model Context Protocol (MCP) server, which fails to securely escape arguments passed to Node.js child processes and does not validate local file references in user-supplied prompt strings.

Alon Barad
Alon Barad
1 views•7 min read
•about 1 hour ago•GHSA-G7M4-839X-CH6V
8.7

GHSA-g7m4-839x-ch6v: Denial of Service via Unbounded Digits Parameter in spomky-labs/otphp

The spomky-labs/otphp library prior to version 11.4.3 is vulnerable to an unhandled DivisionByZeroError crash when parsing provisioning URIs containing a digits parameter value equal to or greater than 40. This allows unauthenticated remote attackers to trigger a Denial of Service by supplying a crafted URI, which causes float-to-integer cast overflow and subsequent division-by-zero fatal error in modern PHP runtimes.

Alon Barad
Alon Barad
2 views•7 min read
•about 3 hours ago•GHSA-6VVH-PXR4-25R7
5.9

GHSA-6vvh-pxr4-25r7: Cryptographic Integrity Degradation in JWT Framework ChaCha20-Poly1305 Key Encryption

An implementation flaw in the experimental Chacha20Poly1305 key-encryption algorithm within the PHP JWT Framework (web-token/jwt-framework) discards the Poly1305 authentication tag during key wrapping and omits it during decryption. This degrades the Authenticated Encryption with Associated Data (AEAD) protection to unauthenticated ChaCha20, allowing an attacker to manipulate the encrypted Content Encryption Key (CEK) without detection.

Amit Schendel
Amit Schendel
4 views•7 min read
•about 3 hours ago•GHSA-3PRJ-6HQW-CM82
8.7

GHSA-3PRJ-6HQW-CM82: CPU Amplification Denial of Service in web-token JWT Library

An uncontrolled resource consumption vulnerability in the PBES2-HS* key wrapping algorithms of the web-token JWT library allows remote, unauthenticated attackers to cause a denial of service (DoS) by sending JWE tokens with unbounded iteration counts.

Amit Schendel
Amit Schendel
4 views•5 min read
•about 5 hours ago•GHSA-JC38-X7X8-2XC8
8.1

GHSA-jc38-x7x8-2xc8: Algorithm Confusion and Header Override Vulnerability in PHP JWT Framework

An algorithm confusion vulnerability exists in the PHP JWT Framework (web-token/jwt-library) where the JWSVerifier and JWEDecrypter components merge integrity-protected and unprotected headers using insecure methods. Under specific conditions, duplicate parameters defined in unprotected headers override those in integrity-protected headers, allowing an attacker to bypass cryptographic signature verification.

Amit Schendel
Amit Schendel
5 views•7 min read
•about 5 hours ago•GHSA-5739-39V2-5754
6.3

GHSA-5739-39V2-5754: Bleichenbacher / Marvin Padding Oracle in PHP JWE Decryption (RSAES-PKCS1-v1_5)

An observable timing discrepancy vulnerability in the web-token/jwt-framework library allows unauthenticated remote attackers to perform a Bleichenbacher / Marvin padding oracle attack against JWE tokens using the RSAES-PKCS1-v1_5 algorithm. By failing to perform constant-time implicit rejection on PKCS#1 v1.5 padding failures, the decryption process leaks structural validation errors via exceptions and early returns, exposing the wrapped Content Encryption Key (CEK) to cryptographic recovery.

Amit Schendel
Amit Schendel
5 views•7 min read