CVE-2026-45610
WWBN AVideo plugin/LoginControl/set.json.php: 2FA toggle endpoint has no CSRF protection, letting an attacker page silently disable a logged-in victim's 2FA
WWBN AVideo is an open source video platform. In 29.0 and earlier, there is a cross-site request forgery vulnerability on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- Required
What an attacker can affect
- Confidentiality
- None
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.7
WWBN AVideo plugin/LoginControl/set.json.php: 2FA toggle endpoint has no CSRF protection, letting an attacker page silently disable a logged-in victim's 2FA
WWBN AVideo is an open source video platform. In 29.0 and earlier, there is a cross-site request forgery vulnerability on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request.
- Record
CVE-2026-45610- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - Weakness
- CWE-306, CWE-352
- Updated
- 2026-11-02
EUVDEUVD-2026-33309
5.7
WWBN AVideo is an open source video platform. In 29.0 and earlier, there is a cross-site request forgery vulnerability on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request.
- Record
EUVD-2026-33309- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-3mv2-vmwh-rwfx
5.7
AVideo: 2FA toggle endpoint has no CSRF protection, letting an attacker page silently disable a logged-in victim's 2FA
Summary
Type: Cross-site request forgery on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request. The next phishing/credential-stuffing attempt against that account no longer needs the second factor.
File: plugin/LoginControl/set.json.php, lines 1-37.
Root cause: the developer relied on the User::isLogged() check at line 9 as the only auth, then dispatched directly into LoginControl::setUser2FA(User::getId(), $value=='true'). Other AVideo state-changing endpoints in the same codebase (videoUpdateUsage.json.php, videoStatus.json.php, videoRotate.json.php, etc.) call forbidIfIsUntrustedRequest('<name>') to compare Origin/Referer against the AVideo domain; this endpoint simply omits the call. The session cookie carries the user's identity on every cross-origin POST, so any attacker page can speak for the logged-in user on this endpoint.
Affected Code
File: plugin/LoginControl/set.json.php, lines 1-37.
<?php
require_once '../../videos/configuration.php';
_session_write_close();
header('Content-Type: application/json');
$obj = new stdClass();
$obj->error = true;
$obj->msg = "";
if (!User::isLogged()) {
$obj->msg = "Not logged";
die(json_encode($obj));
}
if (empty($_POST['type'])) {
$obj->msg = "Type is empty";
die(json_encode($obj));
}
if (!isset($_POST['value'])) {
$obj->msg = "value is empty";
die(json_encode($obj));
}
$cu = AVideoPlugin::loadPluginIfEnabled('LoginControl');
if (empty($cu)) {
$obj->msg = "Plugin not enabled";
die(json_encode($obj));
}
$obj->error = false;
switch ($_POST['type']) {
case 'set2FA':
LoginControl::setUser2FA(User::getId(), $_POST['value']=="true" ? true : false); // <-- BUG: no CSRF gate, no re-auth
break;
}
die(json_encode($obj));
Why it's wrong: disabling a victim's second factor is exactly the kind of state change the AVideo CSRF helper forbidIfIsUntrustedRequest() exists to protect. Compare with objects/comments_like.json.php:18 (forbidIfIsUntrustedRequest('comments_like')) — comments-likes get CSRF protection, but the 2FA toggle does not. Beyond CSRF, security-sensitive toggles like 2FA-disable conventionally also require either the current 2FA code or a password re-prompt: a malicious browser extension, an XSS that lands in any AVideo subdomain, or a compromised tab can otherwise flip the bit silently. None of those mitigations exist here.
Exploit Chain
- Attacker hosts
https://attacker.example/avideo-2fa-off.htmlcontaining:
State: page is live and indexable.<form id="f" action="https://avideo.example/plugin/LoginControl/set.json.php" method="POST"> <input type="hidden" name="type" value="set2FA"> <input type="hidden" name="value" value="false"> </form> <script>document.getElementById('f').submit();</script> - Attacker delivers the page to a victim who is logged in to
avideo.example(open redirect on a trusted partner, ad campaign, IM phishing link, encyclopedic-looking forum post). The victim's browser opens the page; the form auto-submits to AVideo. State: cross-origin POST hitsset.json.phpwith the victim's session cookie attached (the cookie'sSameSiteattribute is set toLax/Noneby AVideo's defaults so the cross-origin POST succeeds for top-level navigations). set.json.php:9confirmsUser::isLogged()(true, victim's session is valid). Lines 13-19 seetype=set2FA,value=false. Line 30-32 calls…
- Record
GHSA-3mv2-vmwh-rwfx- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - Weakness
- CWE-306, CWE-352
- Updated
- 2026-11-02
NVD
5.7
WWBN AVideo is an open source video platform. In 29.0 and earlier, there is a cross-site request forgery vulnerability on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request.
- Record
CVE-2026-45610- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - CVSS 3.1
- 6.5 Medium
CVSS:3.1/AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:H/A:N - Weakness
- CWE-306, CWE-352
- Updated
- 2026-11-02
OSV
5.7
WWBN AVideo plugin/LoginControl/set.json.php: 2FA toggle endpoint has no CSRF protection, letting an attacker page silently disable a logged-in victim's 2FA
WWBN AVideo is an open source video platform. In 29.0 and earlier, there is a cross-site request forgery vulnerability on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request.
- Record
CVE-2026-45610- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - Weakness
- CWE-306, CWE-352
- Updated
- 2026-11-02
OSVGHSA-3mv2-vmwh-rwfx
5.7
AVideo: 2FA toggle endpoint has no CSRF protection, letting an attacker page silently disable a logged-in victim's 2FA
Summary
Type: Cross-site request forgery on the 2FA toggle. plugin/LoginControl/set.json.php accepts POST type=set2FA value=false, calls LoginControl::setUser2FA(User::getId(), false) on the session-authenticated user, and returns. There is no forbidIfIsUntrustedRequest() call, no isTokenValid() check, no X-CSRF-Token/SameSite enforcement, and no re-authentication step. A cross-origin page that the victim visits while logged into the AVideo dashboard issues the POST via a hidden form (or fetch without credentials:"omit") and disables the victim's 2FA in one request. The next phishing/credential-stuffing attempt against that account no longer needs the second factor.
File: plugin/LoginControl/set.json.php, lines 1-37.
Root cause: the developer relied on the User::isLogged() check at line 9 as the only auth, then dispatched directly into LoginControl::setUser2FA(User::getId(), $value=='true'). Other AVideo state-changing endpoints in the same codebase (videoUpdateUsage.json.php, videoStatus.json.php, videoRotate.json.php, etc.) call forbidIfIsUntrustedRequest('<name>') to compare Origin/Referer against the AVideo domain; this endpoint simply omits the call. The session cookie carries the user's identity on every cross-origin POST, so any attacker page can speak for the logged-in user on this endpoint.
Affected Code
File: plugin/LoginControl/set.json.php, lines 1-37.
<?php
require_once '../../videos/configuration.php';
_session_write_close();
header('Content-Type: application/json');
$obj = new stdClass();
$obj->error = true;
$obj->msg = "";
if (!User::isLogged()) {
$obj->msg = "Not logged";
die(json_encode($obj));
}
if (empty($_POST['type'])) {
$obj->msg = "Type is empty";
die(json_encode($obj));
}
if (!isset($_POST['value'])) {
$obj->msg = "value is empty";
die(json_encode($obj));
}
$cu = AVideoPlugin::loadPluginIfEnabled('LoginControl');
if (empty($cu)) {
$obj->msg = "Plugin not enabled";
die(json_encode($obj));
}
$obj->error = false;
switch ($_POST['type']) {
case 'set2FA':
LoginControl::setUser2FA(User::getId(), $_POST['value']=="true" ? true : false); // <-- BUG: no CSRF gate, no re-auth
break;
}
die(json_encode($obj));
Why it's wrong: disabling a victim's second factor is exactly the kind of state change the AVideo CSRF helper forbidIfIsUntrustedRequest() exists to protect. Compare with objects/comments_like.json.php:18 (forbidIfIsUntrustedRequest('comments_like')) — comments-likes get CSRF protection, but the 2FA toggle does not. Beyond CSRF, security-sensitive toggles like 2FA-disable conventionally also require either the current 2FA code or a password re-prompt: a malicious browser extension, an XSS that lands in any AVideo subdomain, or a compromised tab can otherwise flip the bit silently. None of those mitigations exist here.
Exploit Chain
- Attacker hosts
https://attacker.example/avideo-2fa-off.htmlcontaining:
State: page is live and indexable.<form id="f" action="https://avideo.example/plugin/LoginControl/set.json.php" method="POST"> <input type="hidden" name="type" value="set2FA"> <input type="hidden" name="value" value="false"> </form> <script>document.getElementById('f').submit();</script> - Attacker delivers the page to a victim who is logged in to
avideo.example(open redirect on a trusted partner, ad campaign, IM phishing link, encyclopedic-looking forum post). The victim's browser opens the page; the form auto-submits to AVideo. State: cross-origin POST hitsset.json.phpwith the victim's session cookie attached (the cookie'sSameSiteattribute is set toLax/Noneby AVideo's defaults so the cross-origin POST succeeds for top-level navigations). set.json.php:9confirmsUser::isLogged()(true, victim's session is valid). Lines 13-19 seetype=set2FA,value=false. Line 30-32 calls…
- Record
GHSA-3mv2-vmwh-rwfx- CVSS 3.1
- 5.7 Medium
CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:N/I:H/A:N - Weakness
- CWE-306, CWE-352
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
WWBN/AVideoPackagist |
through 29.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-45610 is a medium vulnerability (CVSS 5.7) in Wwbn Avideo, published May 15, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.11%, the 1st percentile. No source names a fixed version.
References
Left out here: the rest of 2 source descriptions. The JSON record has them.