PST-011: Administrator recovery links, demotion and account deletion need no fresh proof, and taking over a member's account is silent #740

Closed
opened 2026-10-10 10:55:50 +00:00 by tiagoagueda · 1 comment
Owner

Found in the security audit of 2026-10-10 (SECURITY-AUDIT-2026-10-10.md), run against main at c30851b from 8 to 10 October 2026, as PST-011. Severity: Low.

The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it.


  • Severity: Low
  • Category: session-hardening
  • Location: src/postulo/core/server_views.py (236-392; 344-392; 344-411)
  • Found by: ownership review of accounts, ownership review of core
  • Related: PST-010 (#739), PST-013 (#742), PST-063 (#792)

Who can do it, and what they need. Anyone with physical or remote access to an administrator's browser session that was left signed in (shared screen, stolen session cookie).

How it works.

  1. The attacker uses an administrator's open session.
  2. POST /server/people/<pk>/recovery/ mints a password-setting link for any account; POST /server/people/<pk>/delete/ with confirm_username deletes an account and its files; POST /server/people/<pk>/administrator/ promotes or demotes.
  3. None of these views carries @reauthentication_required, whereas BackupDownloadView.get and BackupDeleteView.get/post do (views_backups.py:185, 212), so the password or second factor is never asked again.

Impact. A hijacked or unattended administrator session can take over, delete or promote accounts without proving who is at the keyboard, while the project already recognised this risk for backups and gated those actions.

Evidence.

src/postulo/core/views_backups.py:185: @method_decorator(reauthentication_required) on BackupDownloadView.get
src/postulo/core/server_views.py:236-392: PersonAdminView.post, PersonActiveView.post, PersonDeleteView.post and PersonRecoveryView.post carry only StaffRequiredMixin.

Verification (verdict: confirmed; severity after verification: Low). File/lines correct; add PersonUsernameView (server_views.py:414) and PersonActiveView (:261) to the ungated set. Precondition sharpened: the attacker must drive the administrator's own browser session (unattended screen or an exfiltrated Secure/HttpOnly cookie), a scenario the threat model largely places out of scope, which is why this stays low rather than medium. The inconsistency with backups and self-deletion, which the project already gates, is the substance of the finding; the fix is the same @method_decorator(reauthentication_required) on the POST handlers of the People actions, at least recovery and delete.

Suggested remediation. Decorate PersonRecoveryView.post, PersonDeleteView.post and PersonAdminView.post with allauth's reauthentication_required (as views_backups does), and extend tests/security/test_staff_only.py or test_backups_page.py to assert the redirect to reauthentication for an administrator without a recent authentication.

Also covered by this issue.

  • Administrator recovery link is a silent, full takeover of any member account that has no second factor (src/postulo/core/server_views.py (344-392); Informational). The threat model's promise that 'Server settings shows administrators accounts, never anyone's applications or documents' holds for the pages but not for the account: an administrator can become any member in two requests, with no notification to the member and no recent-authentication check, so a …
  • Any administrator can set any other administrator's password through a recovery link (src/postulo/core/server_views.py (344-411); Informational). Horizontal takeover among administrators and vertical over members, which the threat model accepts ('An administrator is still trusted with the instance ... appoint only people you would trust with the database'). …
Found in the security audit of 2026-10-10 (`SECURITY-AUDIT-2026-10-10.md`), run against `main` at `c30851b` from 8 to 10 October 2026, as **PST-011**. Severity: **Low**. The text below is the audit's entry, unchanged apart from links to the other issues it names. The finding was produced by automated review and checked by a second automated pass; re-check it before acting on it. --- - **Severity:** Low - **Category:** session-hardening - **Location:** `src/postulo/core/server_views.py` (236-392; 344-392; 344-411) - **Found by:** ownership review of accounts, ownership review of core - **Related:** PST-010 (#739), PST-013 (#742), PST-063 (#792) **Who can do it, and what they need.** Anyone with physical or remote access to an administrator's browser session that was left signed in (shared screen, stolen session cookie). **How it works.** 1. The attacker uses an administrator's open session. 2. POST /server/people/&lt;pk>/recovery/ mints a password-setting link for any account; POST /server/people/&lt;pk>/delete/ with confirm_username deletes an account and its files; POST /server/people/&lt;pk>/administrator/ promotes or demotes. 3. None of these views carries @reauthentication_required, whereas BackupDownloadView.get and BackupDeleteView.get/post do (views_backups.py:185, 212), so the password or second factor is never asked again. **Impact.** A hijacked or unattended administrator session can take over, delete or promote accounts without proving who is at the keyboard, while the project already recognised this risk for backups and gated those actions. **Evidence.** ```text src/postulo/core/views_backups.py:185: @method_decorator(reauthentication_required) on BackupDownloadView.get src/postulo/core/server_views.py:236-392: PersonAdminView.post, PersonActiveView.post, PersonDeleteView.post and PersonRecoveryView.post carry only StaffRequiredMixin. ``` **Verification** (verdict: confirmed; severity after verification: Low). File/lines correct; add PersonUsernameView (server_views.py:414) and PersonActiveView (:261) to the ungated set. Precondition sharpened: the attacker must drive the administrator's own browser session (unattended screen or an exfiltrated Secure/HttpOnly cookie), a scenario the threat model largely places out of scope, which is why this stays low rather than medium. The inconsistency with backups and self-deletion, which the project already gates, is the substance of the finding; the fix is the same @method_decorator(reauthentication_required) on the POST handlers of the People actions, at least recovery and delete. **Suggested remediation.** Decorate PersonRecoveryView.post, PersonDeleteView.post and PersonAdminView.post with allauth's reauthentication_required (as views_backups does), and extend tests/security/test_staff_only.py or test_backups_page.py to assert the redirect to reauthentication for an administrator without a recent authentication. **Also covered by this issue.** - **Administrator recovery link is a silent, full takeover of any member account that has no second factor** (`src/postulo/core/server_views.py` (344-392); Informational). The threat model's promise that 'Server settings shows administrators accounts, never anyone's applications or documents' holds for the pages but not for the account: an administrator can become any member in two requests, with no notification to the member and no recent-authentication check, so a … - **Any administrator can set any other administrator's password through a recovery link** (`src/postulo/core/server_views.py` (344-411); Informational). Horizontal takeover among administrators and vertical over members, which the threat model accepts ('An administrator is still trusted with the instance ... appoint only people you would trust with the database'). …
tiagoagueda added this to the 0.6.0 milestone 2026-10-10 10:57:24 +00:00
Author
Owner

Fixed in 634ac0d04 on main.

Fixed in 634ac0d04 on main.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Postulo/postulo#740
No description provided.