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·8 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

•1 day ago•CVE-2026-54068
5.9

CVE-2026-54068: Unauthenticated Server-Side Template Injection and SQLite Exfiltration in SiYuan PKM

An authentication bypass in the SiYuan personal knowledge management system before version 3.7.0 exposes a dynamic icon rendering endpoint. This endpoint processes client-supplied Go template directives. By submitting a crafted request, an unauthenticated remote attacker can leverage registered database template functions to execute arbitrary read-only SQL queries and exfiltrate workspace contents.

Amit Schendel
Amit Schendel
12 views•5 min read
•1 day ago•CVE-2026-54069
9.1

CVE-2026-54069: Authentication Bypass in SiYuan Note via Origin Header Spoofing

CVE-2026-54069 is a critical authentication bypass vulnerability in the SiYuan Note personal knowledge management system. The flaw is located in the HTTP server's middleware handling API authorization, which unconditionally trusts requests carrying a 'chrome-extension://' scheme in the Origin HTTP header, granting administrative access without validating API tokens.

Alon Barad
Alon Barad
10 views•5 min read
•2 days ago•CVE-2026-54089
9.1

CVE-2026-54089: Authentication Bypass by Spoofing in File Browser

CVE-2026-54089 is a critical authentication bypass vulnerability in File Browser affecting instances configured with proxy-based authentication. An unauthenticated remote attacker with direct network access can impersonate arbitrary users or register new accounts by spoofing configured HTTP headers.

Amit Schendel
Amit Schendel
8 views•7 min read
•2 days ago•GHSA-99J7-FHR2-XFJ4
10.0

GHSA-99J7-FHR2-XFJ4: Malicious Remote Code Execution Payload in 'exploration' Cargo Crate

The malicious Cargo package 'exploration' was uploaded to the crates.io registry. During compilation or package import, the crate executes code designed to establish an outbound TCP/HTTP connection, download an external second-stage binary, and execute the binary locally on the host machine. This creates an unauthenticated remote code execution vector impacting developer environments and continuous integration pipelines.

Amit Schendel
Amit Schendel
11 views•6 min read
•2 days ago•CVE-2026-54088
9.3

CVE-2026-54088: Pre-Authentication Remote Code Execution in File Browser Hook Authentication

CVE-2026-54088 is a critical command injection vulnerability in File Browser prior to version 2.63.6. When Hook Authentication is enabled, the application interpolates unsanitized credentials into a shell command, allowing unauthenticated remote code execution.

Alon Barad
Alon Barad
9 views•6 min read
•2 days ago•GHSA-QV4M-M73M-8HJ7
8.8

GHSA-qv4m-m73m-8hj7: Authenticated Arbitrary File Upload leading to Remote Code Execution in NotrinosERP

An authenticated remote code execution vulnerability exists in NotrinosERP (versions up to and including 1.0.0) within the Human Resource Management (HRM) module. Users with employee management permissions can upload arbitrary file types, including PHP scripts, which are written directly to a web-accessible directory. This allows for arbitrary code execution in the context of the web-server user.

Alon Barad
Alon Barad
8 views•6 min read