Skip to content

GHSA-653j-g8j7-gh54 CVE-2026-113710

ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing

Medium npm
Severity
5.9 / 10
Medium · CVSS 3.1
Exploit chance (EPSS)
0.33%
Chance of exploitation in the next 30 days. Higher than 26.16% of scored vulnerabilities.
Known exploited
No
Not in CISA's catalog
Weakness
CWE-307 Improper Restriction of Excessive Authentication Attempts
Fix
4.33.0
Upgrade apostrophe to this version or later

Description

Summary The self-service password-change endpoint verifies the current password but does not use the login module’s failed-attempt counter, throttle, delay, or lockout mechanism. An attacker with a valid authenticated session can submit repeated guesses for the current password without receiving a lockout or throttle response.

Exact source path packages/apostrophe/modules/@apostrophecms/settings/index.js:621-664

The password-change route is: PATCH /api/v1/@apostrophecms/settings/password

The route calls the password verifier directly through: await self.handleProtectedSubform(req, subform, req.body );

The password verification path is: packages/apostrophe/modules/@apostrophecms/settings/index.js:514-542

async handlePasswordProtectedSubform(req, subform, payload) { try { await self.apos.user.verifyPassword(req.user, payload.passwordCurrent); } catch (e) { throw self.apos.error( 'forbidden', 'apostrophe:passwordCurrentError', { path: 'passwordCurrent' } ); } return subform; }

The verifier itself does not increment the login-attempt cache or enforce a delay: packages/apostrophe/modules/@apostrophecms/user/index.js:412-487

By contrast, the login route explicitly calls checkLoginAttempts and addLoginAttempt in: packages/apostrophe/modules/@apostrophecms/login/index.js:831-929

Local PoC The test used a disposable authenticated guest session against:

Request:

PATCH /api/v1/@apostrophecms/settings/password HTTP/1.1
Host: 127.0.0.1:3000
Cookie: <redacted-authenticated-session>
Content-Type: application/json

{
  "password": "new-temporary-password",
  "passwordRepeat": "new-temporary-password",
  "passwordCurrent": "definitely-wrong-current-password"
}

Equivalent command:

for i in 1 2 3 4 5; do
  time curl -i -sS \
    -b /path/to/temporary-session-cookie-jar.txt \
    -X PATCH \
    'http://127.0.0.1:3000/api/v1/@apostrophecms/settings/password' \
    -H 'Content-Type: application/json' \
    --data-raw '{
      "password": "new-temporary-password",
      "passwordRepeat": "new-temporary-password",
      "passwordCurrent": "definitely-wrong-current-password"
    }'
done

Observed response All five attempts returned the same immediate validation response: HTTP/1.1 403 Forbidden Content-Type: application/json; charset=utf-8 { "name": "forbidden", "data": { "path": "passwordCurrent" }, "message": "apostrophe:passwordCurrentError" }

Observed timings were approximately: Attempt 1: 231 ms Attempt 2: 232 ms Attempt 3: 226 ms Attempt 4: 229 ms Attempt 5: 226 ms

No attempt returned a throttle response, lockout message, Retry-After header, or rate-limit status.

seq 1 20 | xargs -P 20 -I{} sh -c
"curl --max-time 15 -sS
-o /tmp/password-attempt-{}.body
-w '%{http_code}\n'
-b /path/to/temporary-session-cookie-jar.txt
-X PATCH
'http://127.0.0.1:3000/api/v1/@apostrophecms/settings/password'
-H 'Content-Type: application/json'
--data-raw '{"password":"new-temporary-password-{}","passwordRepeat":"new-temporary-password-{}","passwordCurrent":"wrong-current-{}"}'"

Result: 20 requests -> HTTP 403 Forbidden 0 requests -> HTTP 429 Too Many Requests 0 requests -> lockout response

The valid password continued to authenticate afterward: POST /api/v1/@apostrophecms/login/login HTTP/1.1 HTTP/1.1 200 OK

Impact An attacker who has obtained or hijacked a valid authenticated session can repeatedly guess the current password through the password-change endpoint. Each guess receives a normal response, and the route does not apply the login throttle or lockout mechanism. If the current password is weak or reused, this can allow the attacker to pass the current-password check and replace the account password. The attacker would then be able to log in after the password change. The attack requires a valid authenticated session and does not work anonymously. Severity Medium, subject to the application’s session-threat model. The prerequisite of an already authenticated session limits exploitability, but successful guessing leads directly to account takeover through password replacement.

Affected versions

npm

PackageVulnerableFixed in
apostrophe
< 4.33.0
4.33.0

How to fix

Move to the fixed version or later with your package manager, update the lockfile, and run your tests. If the package only arrives through another dependency, upgrade that dependency first. If none of its releases pulls a fixed version yet, force the version with your package manager's override.

apostrophe npm → 4.33.0

npm
npm install apostrophe@4.33.0
yarn
yarn add apostrophe@4.33.0
pnpm
pnpm add apostrophe@4.33.0

Severity breakdown

CVSS 3.1 5.9

CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N

Attack vector
Network
Attack complexity
High
Privileges required
High
User interaction
None
Scope
Unchanged
Confidentiality
High
Integrity
High
Availability
None

Weakness

CWE-307: Improper Restriction of Excessive Authentication Attempts

The product does not implement sufficient measures to prevent multiple failed authentication attempts within a short time frame.

How to prevent it
  • Architecture and Design: Common protection mechanisms include: Disconnecting the user after a small number of failed attempts Implementing a timeout Locking out a targeted account Requiring a computational task on the user's part.
  • Architecture and Design: Use a vetted library or framework that does not allow this weakness to occur or provides constructs that make this weakness easier to avoid [REF-1482]. Consider using libraries with authentication capabilities such as OpenSSL or the ESAPI Authenticator. [REF-45]

References

Timeline

  1. Published
  2. Last updated by the source
  3. Latest EPSS score
AdvisorySeverityWhy it's related
GHSA-86wm-68pq-5jwq
ApostropheCMS: Unauthenticated resource exhaustion via pre-auth aposBigUpload processing in @apostrophecms/import-export
High Also affects apostrophe
GHSA-vwwx-px9w-crrc
ApostropheCMS: Missing Authorization on Notification PATCH/DELETE Endpoints
Low Also affects apostrophe
GHSA-vmxh-77cw-65j7
ApostropheCMS: Authenticated Object-Level Authorization Bypass in exist-in-locale Reveals Restricted Localized Draft Identifiers
Medium Also affects apostrophe
GHSA-m2m5-rw3w-cwmj
ApostropheCMS: Client-side Prototype Pollution in apos.http.parseQuery leading to DOM-based XSS against authenticated users
Medium Also affects apostrophe
GHSA-gqh3-7856-rjjg
ApostropheCMS: :_id/locales REST route omits the public-API authorization gate its sibling read routes enforce (unauthenticated doc-existence + locale metadata disclosure)
Medium Also affects apostrophe
GHSA-hhvr-8m24-qqr3
ApostropheCMS: Cross-domain session token in URL confers session takeover
Medium Also affects apostrophe
GHSA-j5rq-xfvr-p969
ApostropheCMS: Authenticated projection-exclusion prototype-member deletion in @apostrophecms/db-connect causes process-wide denial of service
High Also affects apostrophe
GHSA-qhcq-9pm2-c9w9
ApostropheCMS: Unauthenticated CSV import requests leave rejected uploads in the system temporary directory
High Also affects apostrophe
Is this in your code?

MergeHarbor checks every repository's dependencies against this database, ranks what matters by exploitability and reachability, routes it to the owning team and opens the fix pull request.

Check your repos with MergeHarbor