Vulnerability DatabaseGHSA-526c-px8p-72x8

GHSA-526c-px8p-72x8: 
PHP vulnerability analysis and mitigation

Summary

The counter parameter parsed from a provisioning URI is validated only with a lower bound ($value >= 0) and has no upper bound (src/HOTP.php:120-129). HOTP::verify() checks the code against every counter from $counter to $counter + $window (src/HOTP.php:155-157). When $counter + $window reaches PHP_INT_MAX and no counter matches, ++$i takes $i past PHP_INT_MAX and PHP turns it into a float. The loop condition, now evaluated in floating point, still holds, so the float is passed to the int-typed OTP::at() and PHP raises a TypeError. A correct code at counter PHP_INT_MAX fails the same way, because updateCounter($i + 1) receives a float.

Impact

OTPHP\Factory::loadFromProvisioningUri() forwards the attacker-controlled counter query value to setParameter('counter', $value), which casts it to int, so larger decimal values are also loaded as PHP_INT_MAX. A hostile URI such as otpauth://hotp/Alice?secret=JBSWY3DPEHPK3PXP&counter=9223372036854775807 therefore produces an HOTP object on which every verify() call throws a TypeError, whether the code is wrong or correct and whether a window is passed or not. Because TypeError extends Error (not Exception) and does not implement OTPHP\Exception\OTPExceptionInterface, callers that guard verification with catch (\Exception) or catch (OTPExceptionInterface) do not catch it, turning a malformed URI into an unhandled fatal error (denial of service of the verification path). A smaller counter fails the same way as soon as counter + window >= PHP_INT_MAX. Measured on PHP 8.2 and 8.5 with 11.4.3 and 11.5.0, for a wrong code and a window of 10: counter = PHP_INT_MAX - 11 returns false, counter = PHP_INT_MAX - 10 throws TypeError. Releases older than 9.0 fail on the same input as well, by exhausting memory instead of raising a TypeError.

Affected component

Line numbers are those of 11.4.3 and 11.5.0.

  • src/HOTP.php:120-129 — counter parameter callback (no upper bound)
  • src/HOTP.php:155-157 — window loop of verifyOtpWithWindow(): $i <= $counter + $window, then $this->at($i) and $this->updateCounter($i + 1)

Proof of concept

use OTPHP\Factory;
use OTPHP\InternalClock;
$otp = Factory::loadFromProvisioningUri(
    'otpauth://hotp/Alice?secret=JBSWY3DPEHPK3PXP&counter=9223372036854775807',
    new InternalClock()
);
$otp->verify('000000');              // TypeError: OTPHP\OTP::at(): Argument #1 ($input) must be of type int, float given
$otp->verify($otp->at(PHP_INT_MAX)); // TypeError: OTPHP\HOTP::updateCounter(): Argument #1 ($counter) must be of type int, float given

Neither error is caught by catch (\Exception).

Remediation

Upgrade to 11.4.4 or 11.5.1. HOTP::verify() now bounds its window loop without overflow and returns false once the counter is exhausted: the last counter it can accept is PHP_INT_MAX - 1. If you cannot upgrade yet, do not call verify() on an HOTP object built from an untrusted provisioning URI when $counter > PHP_INT_MAX - 1 - $window, where $counter is the counter you pass to verify() ($otp->getCounter() if you pass none) and $window is the window you pass (0 if none). The same releases also reject, with InvalidParameterException, three values that only the application can pass: a window of PHP_INT_MIN and a negative input to at(), both outside the documented types, and a TOTP timestamp so close to PHP_INT_MAX that adding the leeway overflows. None of them can come from a provisioning URI.


Source: NVD

Related PHP vulnerabilities:

CVE ID

Severity

Score

Technologies

Component name

CISA KEV exploit

Has fix

Published date

CVE-2026-122053MEDIUM6.9
  • PHP logoPHP
  • symfony/serializer
NoYesNov 06, 2026
GHSA-526c-px8p-72x8MEDIUM4.3
  • PHP logoPHP
  • spomky-labs/otphp
NoYesNov 07, 2026
CVE-2026-123824LOW2.3
  • PHP logoPHP
  • symfony/config
NoYesNov 06, 2026
GHSA-h38w-h2gf-6qv2LOWN/A
  • PHP logoPHP
  • spomky-labs/otphp
NoYesNov 07, 2026
GHSA-4v5c-4hwq-6f6mMEDIUMN/A
  • PHP logoPHP
  • spomky-labs/otphp
NoYesNov 07, 2026

Free Vulnerability Assessment

Benchmark your Cloud Security Posture

Evaluate your cloud security practices across 9 security domains to benchmark your risk level and identify gaps in your defenses.

Request assessment

Additional Wiz resources

Get a personalized demo

Ready to see Wiz in action?

"Best User Experience I have ever seen, provides full visibility to cloud workloads."
David EstlickCISO
"Wiz provides a single pane of glass to see what is going on in our cloud environments."
Adam FletcherChief Security Officer
"We know that if Wiz identifies something as critical, it actually is."
Greg PoniatowskiHead of Threat and Vulnerability Management