Jun 19, 2026·7 min read·7 visits
Unbounded digits parameter in otphp provisioning URIs triggers a float-to-integer conversion overflow, culminating in an unhandled division-by-zero fatal crash in PHP.
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.
The spomky-labs/otphp library is a widely used PHP implementation for generating and validating One-Time Passwords (OTPs) conforming to HOTP (RFC 4226) and TOTP (RFC 6238) specifications. The core library supports loading provisioning configurations via URIs using the otpauth:// scheme. Security teams frequently expose this parsing capability to external consumers to support user enrollment and authenticator registration flows.\n\nThe vulnerability, identified as GHSA-g7m4-839x-ch6v, stems from improper input validation of the digits parameter extracted from these provisioning URIs. The library fails to enforce an upper-bound sanity check on this parameter, allowing arbitrary integers to pass through. When the library subsequently attempts to generate or verify a passcode, the excessively large value triggers a fatal runtime crash.\n\nThis flaw allows unauthenticated remote attackers to initiate a Denial of Service (DoS) attack. By supplying a specially crafted URI to endpoints that ingest or validate authenticator profiles, the attacker triggers an unhandled DivisionByZeroError in modern PHP execution environments. This error halts the processing thread, bypassing typical catch blocks that target standard exceptions.
The vulnerability is categorized under CWE-1284 (Improper Validation of Specified Quantity in Input) and CWE-369 (Divide By Zero). The architectural flaw resides in how spomky-labs/otphp processes and validates configuration parameters during initialization. Specifically, the callback responsible for verifying the digits configuration parameter only implements a lower-bound check, ensuring that the value is strictly greater than zero.\n\nDuring OTP generation or verification, the library calculates the truncated OTP code using a modulo operation. The divisor is determined by evaluating 10 ** $digits to scale the output to the requested length. On standard 64-bit PHP 8.x runtimes, the maximum representable signed integer is 9223372036854775807 (approximately 9.22 * 10^18).\n\nWhen a value of 40 or higher is supplied to the digits parameter, the exponentiation operation 10 ** 40 evaluates to a floating-point representation because it exceeds the capacity of a standard 64-bit integer. PHP then attempts to cast this float back into an integer to satisfy the modulo (%) operator. Due to float-to-integer conversion overflow behavior in PHP, this cast yields 0 on 64-bit architectures.\n\nmermaid\ngraph LR\n A["Input URI with digits=50"] --> B["Factory::loadFromProvisioningUri()"]\n B --> C["Parameter check: 50 > 0 (Passes)"]\n C --> D["Modulo calculation: $code % (10 ** 50)"]\n D --> E["PHP converts 1.0e50 to Integer (Overflows to 0)"]\n E --> F["Evaluation: $code % 0"]\n F --> G["Fatal: DivisionByZeroError"]\n\n\nThe resulting calculation simplifies to $code % 0. In PHP 8.x, division or modulo by zero raises a fatal DivisionByZeroError object, which inherits from the Error class instead of the standard Exception class. This execution behavior terminates the thread immediately unless explicitly handled by a \Throwable block.
Examining the vulnerable code path clarifies how the input validation mechanism is bypassed. In the OTP.php implementation, the digits parameter is registered with a validation closure that does not enforce an upper boundary.\n\nphp\n// Vulnerable parameter validation in OTP.php\nprotected function filterDigits(int $digits): void\n{\n // Missing upper-bound validation\n if ($digits <= 0) {\n throw new InvalidArgumentException("Digits must be greater than 0");\n }\n}\n\n\nWhen calculating the code value in OTP.php, the library executes the modulo operation using the value configured through the filterDigits function. The calculation proceeds as follows:\n\nphp\n// Vulnerable generation logic in OTP.php\npublic function at(int $timestamp): string\n{\n // ... calculation of binary code ...\n $digits = $this->getDigits();\n // If $digits is 50, (10 ** 50) is cast to int during % operation\n // (int)(10 ** 50) evaluates to 0 in PHP 8.x on 64-bit systems\n $otp = $code % (10 ** $digits);\n return str_pad((string)$otp, $digits, '0', STR_PAD_LEFT);\n}\n\n\nThe patch implemented in version 11.4.3 corrects this validation gap. It introduces a secondary check that enforces standard OTP bounds on the digits parameter, restricting the input to an acceptable range (such as between 1 and 10, or conforming strictly to standard MFA parameters).\n\nphp\n// Patched parameter validation in OTP.php\nprotected function filterDigits(int $digits): void\n{\n // Check both lower and upper bounds to prevent integer cast overflow\n if ($digits <= 0 || $digits > 10) {\n throw new InvalidArgumentException("Digits must be between 1 and 10");\n }\n}\n\n\nThis corrective action successfully resolves the vulnerability. By checking the boundary during instantiation rather than waiting for execution, any attempt to parse an invalid provisioning URI throws a catchable InvalidArgumentException before the modulo operation can occur.
Exploitation of this vulnerability requires minimal attacker effort and does not rely on authentication or specific privilege levels. The attack surface consists of any application endpoint that accepts and parses user-supplied OTP configurations. This pattern is commonly observed in profile administration portals, multi-factor authentication registration endpoints, and administrator tools that import authentication records.\n\nTo execute the attack, the adversary constructs a valid provisioning URI containing a high digits query parameter value. A value of 50 is sufficient to trigger the overflow on standard x86-64 platforms. The attacker then submits this string directly to the target parsing interface, as represented in the payload below:\n\ntext\notpauth://totp/TargetSystem:user@example.com?secret=JBSWY3DPEHPK3PXP&digits=50\n\n\nWhen the system receives the request, it initiates the parser. The parser successfully constructs the OTP instance because the validation check accepts 50 as a positive integer. The crash is deferred until the application attempts to execute an action on the object, such as generating a test code for verification.\n\nOnce the verification routine triggers $otp->at(time()), the runtime environment halts execution. Because the application logic typically captures generic exceptions rather than throwables, the fatal error escalates to the web server or container worker. This terminates the process, causing a direct application-level denial of service.
The potential impact of GHSA-g7m4-839x-ch6v is classified as High (CVSS v4.0 score of 8.7). Because the flaw triggers a fatal engine crash, it can be abused to disable authentication systems or bring down worker processes processing queues containing the malicious URI.\n\nIn many modern PHP configurations (such as FPM or worker queues), a single unhandled fatal error terminates the worker process. While process managers (such as systemd or Kubernetes pods) automatically restart these processes, a persistent stream of malicious requests can exhaust available workers. This results in server-wide denial of service, rendering other unrelated application paths unresponsive.\n\nThe vulnerability does not allow remote code execution (RCE) or information disclosure, as the runtime terminates immediately without exposing memory content. However, the ease of exploitation makes this a significant availability risk. Security teams should prioritize remediating this issue, particularly in applications where MFA setup endpoints are exposed to unauthenticated public registrants.
The primary remediation strategy is upgrading the spomky-labs/otphp dependency to version 11.4.3 or later. This release introduces strict constraints on parameter ranges, ensuring that arbitrary input cannot trigger mathematical overflows. Organizations should update their Composer environments by running composer update spomky-labs/otphp.\n\nIf upgrading is not immediately possible, security teams can implement an input validation filter. This filter parses and validates the digits parameter before handing it over to the library. If the value exceeds standard limits (e.g., greater than 10 or less than 4), the application should reject the payload:\n\nphp\n// Temporary pre-parsing validation logic\n$uri = $_POST['uri'];\n$query = parse_url($uri, PHP_URL_QUERY);\nif ($query !== null) {\n parse_str($query, $params);\n if (isset($params['digits']) && ((int)$params['digits'] < 4 || (int)$params['digits'] > 10)) {\n throw new InvalidArgumentException('Invalid digits parameter.');\n }\n}\n\n\nAdditionally, developers must review error handling structures throughout the application. When invoking methods on untrusted input, code blocks should catch \Throwable instead of \Exception to prevent unhandled fatal engine errors from terminating the thread:\n\nphp\ntry {\n $otp = OTPHP\Factory::loadFromProvisioningUri($uri);\n $otp->verify($code);\n} catch (\Throwable $t) {\n // Log error securely and prevent application crash\n Log::error('OTP Parsing failed: ' . $t->getMessage());\n return false;\n}\n
CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:N/VI:N/VA:H/SC:N/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
spomky-labs/otphp Spomky-Labs | < 11.4.3 | 11.4.3 |
| Attribute | Detail |
|---|---|
| CWE ID | CWE-1284 / CWE-369 |
| Attack Vector | Network (AV:N) |
| CVSS Score | 8.7 |
| EPSS Score | Not Mapped (No CVE) |
| Impact | Denial of Service (Availability: High) |
| Exploit Status | Proof-of-Concept (PoC) Released |
| KEV Status | Not Listed |
The product does not validate or incorrectly validates the input value, allowing values that are outside the expected range.
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.
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.
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.
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.
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.
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.