CVE-2026-55485
Piccolo Admin: Privilege escalation - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Piccolo Admin is an admin interface and content management system for Python, built on top of Piccolo. Prior to 1.14.0, piccolo_admin/endpoints.py uses superuser_validators to block PUT, PATCH, DELETE, and POST requests by non-superusers but permits GET requests to configured user and session tables, while piccolo_api/session_auth/tables.py exposes SessionsBase.token because the token column is not secret. In deployments that add the Sessions and User tables to create_admin, a non-superuser administrator can call GET /api/tables/sessions/, obtain another user's live session token, replay it as the Cookie id value to impersonate a superuser, and permanently set superuser to true on the attacker's own row. This issue is fixed in version 1.14.0.
Attack profile
How easy it is to exploit
- Attack vector
- Network
- Complexity
- Low
- Privileges needed
- Low
- User interaction
- None
What an attacker can affect
- Confidentiality
- High
- Integrity
- High
- Availability
- High
Exploit likelihood
What each source reports
CVE List Score used
8.8
Piccolo Admin: Privilege escalation - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Piccolo Admin is an admin interface and content management system for Python, built on top of Piccolo. Prior to 1.14.0, piccolo_admin/endpoints.py uses superuser_validators to block PUT, PATCH, DELETE, and POST requests by non-superusers but permits GET requests to configured user and session tables, while piccolo_api/session_auth/tables.py exposes SessionsBase.token because the token column is not secret. In deployments that add the Sessions and User tables to create_admin, a non-superuser administrator can call GET /api/tables/sessions/, obtain another user's live session token, replay it as the Cookie id value to impersonate a superuser, and permanently set superuser to true on the attacker's own row. This issue is fixed in version 1.14.0.
- Record
CVE-2026-55485- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-200, CWE-269, CWE-863
- Updated
- 2026-11-02
EUVDEUVD-2026-67858
8.8
Piccolo Admin is an admin interface and content management system for Python, built on top of Piccolo. Prior to 1.14.0, piccolo_admin/endpoints.py uses superuser_validators to block PUT, PATCH, DELETE, and POST requests by non-superusers but permits GET requests to configured user and session tables, while piccolo_api/session_auth/tables.py exposes SessionsBase.token because the token column is not secret. In deployments that add the Sessions and User tables to create_admin, a non-superuser administrator can call GET /api/tables/sessions/, obtain another user's live session token, replay it as the Cookie id value to impersonate a superuser, and permanently set superuser to true on the attacker's own row. This issue is fixed in version 1.14.0.
- Record
EUVD-2026-67858- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
GitHub AdvisoriesGHSA-2gh4-jmwq-rr8w
8.8
piccolo-admin has a privilege escalation issue - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Summary
piccolo_admin uses a helper called superuser_validators to gate access to the user and session tables for non-superusers. The helper rejects PUT, PATCH, DELETE, and POST, but does not reject GET.
The sessions table stores live session tokens in plaintext, and the token column is not marked secret=True, so it is included in every GET response. Any non-superuser admin can therefore list every other user's live session token with one request, replay the token as their own Cookie: id=…, impersonate that user (including the superuser), and then permanently self-promote by writing superuser = true on their own row.
The chain is reachable on a realistic, documented configuration: a deployer adds the Sessions (and User) tables to create_admin([...]) so superusers have a UI to monitor and revoke sessions.
Affected component
- File:
piccolo_admin/endpoints.py - Function:
superuser_validators(around line 419)
def superuser_validators(piccolo_crud: PiccoloCRUD, request: Request):
user: BaseUser = request.user.user
if not user.superuser:
if request.method.upper() in ["PUT", "PATCH", "DELETE", "POST"]:
raise HTTPException(
detail="Only superusers can perform these actions.",
status_code=405,
)
The method check is a deny-list instead of an allow-list; GET is absent. Compounding the issue, SessionsBase.token in piccolo_api/session_auth/tables.py is a Varchar without secret=True, so the default exclude_secrets=True in PiccoloCRUD does not strip it.
Preconditions
- Network reachability to the admin.
- Valid credentials for a non-superuser admin (
admin=True, superuser=False— the default role created byBaseUser.create_user(admin=True)). - The deployment includes the
Sessionstable (and typically theUsertable) increate_admin([...])— the documented pattern for "active sessions" management UIs.
Steps to reproduce
-
Log in as the non-superuser admin (
john / john123). Open the Piccolo User table and confirm john'sSUPERUSERcolumn is ✗. (See Screenshot 1.) <img width="3024" height="1430" alt="01-john-piccolo_user-list" src="https://github.com/user-attachments/assets/31a6f81e-7d12-434a-ac99-ff64e15511f9" /> -
Attempt the target write directly. Send the following request:
PATCH /api/tables/piccolo_user/2/ HTTP/1.1 Host: target:8001 Content-Type: application/json Cookie: id=<john's session>; csrftoken=<token> X-CSRFToken: <token> {"superuser": true}The server returns:
HTTP/1.1 405 {"detail":"Only superusers can perform these actions."}The same response is shown both in the dashboard banner (Screenshot 2) and in Burp Repeater (Screenshot 3). This establishes the privilege boundary that the bug will break. <img width="3024" height="2158" alt="02-john-save-blocked-405" src="https://github.com/user-attachments/assets/81f4b0b6-fe58-43da-b63e-6161e5911fc0" /> <img width="1213" height="713" alt="03-john-save-blocked-405" src="https://github.com/user-attachments/assets/2eb33b00-a209-4e92-b18b-1f6520dde702" />
-
Leak the credential. As the same john user, request:
GET /api/tables/sessions/ HTTP/1.1 Host: target:8001 Cookie: id=<john's session>; csrftoken=<token>Response:
200 OKcontaining every active session in plaintext, e.g.{"rows":[ {"token":"jeb1d-IXIC0BWTOV6G-ApTksrbvdBDkZV9KN4taN2nE","user_id":1, ...}, {"token":"...","user_id":2, ...}, ... ]}Copy the
tokenvalue of any row whoseuser_idmatches the superuser. That string IS the live session cookie of that user. (Screenshot 4.) <img width="1512" height="850" alt="04-john-sees-all-session-tokens" src="https://github.com/user-attachments/assets/3303231f-ed87-4b32-8cab-7aa480315529" /> -
**Replay the…
- Record
GHSA-2gh4-jmwq-rr8w- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-200, CWE-269, CWE-863
- Updated
- 2026-11-02
NVD
8.8
Piccolo Admin is an admin interface and content management system for Python, built on top of Piccolo. Prior to 1.14.0, piccolo_admin/endpoints.py uses superuser_validators to block PUT, PATCH, DELETE, and POST requests by non-superusers but permits GET requests to configured user and session tables, while piccolo_api/session_auth/tables.py exposes SessionsBase.token because the token column is not secret. In deployments that add the Sessions and User tables to create_admin, a non-superuser administrator can call GET /api/tables/sessions/, obtain another user's live session token, replay it as the Cookie id value to impersonate a superuser, and permanently set superuser to true on the attacker's own row. This issue is fixed in version 1.14.0.
- Record
CVE-2026-55485- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-200, CWE-269, CWE-863
- Updated
- 2026-11-02
OSV
8.8
Piccolo Admin: Privilege escalation - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Piccolo Admin is an admin interface and content management system for Python, built on top of Piccolo. Prior to 1.14.0, piccolo_admin/endpoints.py uses superuser_validators to block PUT, PATCH, DELETE, and POST requests by non-superusers but permits GET requests to configured user and session tables, while piccolo_api/session_auth/tables.py exposes SessionsBase.token because the token column is not secret. In deployments that add the Sessions and User tables to create_admin, a non-superuser administrator can call GET /api/tables/sessions/, obtain another user's live session token, replay it as the Cookie id value to impersonate a superuser, and permanently set superuser to true on the attacker's own row. This issue is fixed in version 1.14.0.
- Record
CVE-2026-55485- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-200, CWE-269, CWE-863
- Updated
- 2026-11-02
OSVGHSA-2gh4-jmwq-rr8w
8.8
piccolo-admin has a privilege escalation issue - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Summary
piccolo_admin uses a helper called superuser_validators to gate access to the user and session tables for non-superusers. The helper rejects PUT, PATCH, DELETE, and POST, but does not reject GET.
The sessions table stores live session tokens in plaintext, and the token column is not marked secret=True, so it is included in every GET response. Any non-superuser admin can therefore list every other user's live session token with one request, replay the token as their own Cookie: id=…, impersonate that user (including the superuser), and then permanently self-promote by writing superuser = true on their own row.
The chain is reachable on a realistic, documented configuration: a deployer adds the Sessions (and User) tables to create_admin([...]) so superusers have a UI to monitor and revoke sessions.
Affected component
- File:
piccolo_admin/endpoints.py - Function:
superuser_validators(around line 419)
def superuser_validators(piccolo_crud: PiccoloCRUD, request: Request):
user: BaseUser = request.user.user
if not user.superuser:
if request.method.upper() in ["PUT", "PATCH", "DELETE", "POST"]:
raise HTTPException(
detail="Only superusers can perform these actions.",
status_code=405,
)
The method check is a deny-list instead of an allow-list; GET is absent. Compounding the issue, SessionsBase.token in piccolo_api/session_auth/tables.py is a Varchar without secret=True, so the default exclude_secrets=True in PiccoloCRUD does not strip it.
Preconditions
- Network reachability to the admin.
- Valid credentials for a non-superuser admin (
admin=True, superuser=False— the default role created byBaseUser.create_user(admin=True)). - The deployment includes the
Sessionstable (and typically theUsertable) increate_admin([...])— the documented pattern for "active sessions" management UIs.
Steps to reproduce
-
Log in as the non-superuser admin (
john / john123). Open the Piccolo User table and confirm john'sSUPERUSERcolumn is ✗. (See Screenshot 1.) <img width="3024" height="1430" alt="01-john-piccolo_user-list" src="https://github.com/user-attachments/assets/31a6f81e-7d12-434a-ac99-ff64e15511f9" /> -
Attempt the target write directly. Send the following request:
PATCH /api/tables/piccolo_user/2/ HTTP/1.1 Host: target:8001 Content-Type: application/json Cookie: id=<john's session>; csrftoken=<token> X-CSRFToken: <token> {"superuser": true}The server returns:
HTTP/1.1 405 {"detail":"Only superusers can perform these actions."}The same response is shown both in the dashboard banner (Screenshot 2) and in Burp Repeater (Screenshot 3). This establishes the privilege boundary that the bug will break. <img width="3024" height="2158" alt="02-john-save-blocked-405" src="https://github.com/user-attachments/assets/81f4b0b6-fe58-43da-b63e-6161e5911fc0" /> <img width="1213" height="713" alt="03-john-save-blocked-405" src="https://github.com/user-attachments/assets/2eb33b00-a209-4e92-b18b-1f6520dde702" />
-
Leak the credential. As the same john user, request:
GET /api/tables/sessions/ HTTP/1.1 Host: target:8001 Cookie: id=<john's session>; csrftoken=<token>Response:
200 OKcontaining every active session in plaintext, e.g.{"rows":[ {"token":"jeb1d-IXIC0BWTOV6G-ApTksrbvdBDkZV9KN4taN2nE","user_id":1, ...}, {"token":"...","user_id":2, ...}, ... ]}Copy the
tokenvalue of any row whoseuser_idmatches the superuser. That string IS the live session cookie of that user. (Screenshot 4.) <img width="1512" height="850" alt="04-john-sees-all-session-tokens" src="https://github.com/user-attachments/assets/3303231f-ed87-4b32-8cab-7aa480315529" /> -
**Replay the…
- Record
GHSA-2gh4-jmwq-rr8w- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Weakness
- CWE-200, CWE-269, CWE-863
- Updated
- 2026-11-02
OSVPYSEC-2026-3882
8.8
piccolo-admin has a privilege escalation issue - admin to superuser via session-token disclosure in GET /api/tables/sessions/.
Summary
piccolo_admin uses a helper called superuser_validators to gate access to the user and session tables for non-superusers. The helper rejects PUT, PATCH, DELETE, and POST, but does not reject GET.
The sessions table stores live session tokens in plaintext, and the token column is not marked secret=True, so it is included in every GET response. Any non-superuser admin can therefore list every other user's live session token with one request, replay the token as their own Cookie: id=…, impersonate that user (including the superuser), and then permanently self-promote by writing superuser = true on their own row.
The chain is reachable on a realistic, documented configuration: a deployer adds the Sessions (and User) tables to create_admin([...]) so superusers have a UI to monitor and revoke sessions.
Affected component
- File:
piccolo_admin/endpoints.py - Function:
superuser_validators(around line 419)
def superuser_validators(piccolo_crud: PiccoloCRUD, request: Request):
user: BaseUser = request.user.user
if not user.superuser:
if request.method.upper() in ["PUT", "PATCH", "DELETE", "POST"]:
raise HTTPException(
detail="Only superusers can perform these actions.",
status_code=405,
)
The method check is a deny-list instead of an allow-list; GET is absent. Compounding the issue, SessionsBase.token in piccolo_api/session_auth/tables.py is a Varchar without secret=True, so the default exclude_secrets=True in PiccoloCRUD does not strip it.
Preconditions
- Network reachability to the admin.
- Valid credentials for a non-superuser admin (
admin=True, superuser=False— the default role created byBaseUser.create_user(admin=True)). - The deployment includes the
Sessionstable (and typically theUsertable) increate_admin([...])— the documented pattern for "active sessions" management UIs.
Steps to reproduce
-
Log in as the non-superuser admin (
john / john123). Open the Piccolo User table and confirm john'sSUPERUSERcolumn is ✗. (See Screenshot 1.) <img width="3024" height="1430" alt="01-john-piccolo_user-list" src="https://github.com/user-attachments/assets/31a6f81e-7d12-434a-ac99-ff64e15511f9" /> -
Attempt the target write directly. Send the following request:
PATCH /api/tables/piccolo_user/2/ HTTP/1.1 Host: target:8001 Content-Type: application/json Cookie: id=<john's session>; csrftoken=<token> X-CSRFToken: <token> {"superuser": true}The server returns:
HTTP/1.1 405 {"detail":"Only superusers can perform these actions."}The same response is shown both in the dashboard banner (Screenshot 2) and in Burp Repeater (Screenshot 3). This establishes the privilege boundary that the bug will break. <img width="3024" height="2158" alt="02-john-save-blocked-405" src="https://github.com/user-attachments/assets/81f4b0b6-fe58-43da-b63e-6161e5911fc0" /> <img width="1213" height="713" alt="03-john-save-blocked-405" src="https://github.com/user-attachments/assets/2eb33b00-a209-4e92-b18b-1f6520dde702" />
-
Leak the credential. As the same john user, request:
GET /api/tables/sessions/ HTTP/1.1 Host: target:8001 Cookie: id=<john's session>; csrftoken=<token>Response:
200 OKcontaining every active session in plaintext, e.g.{"rows":[ {"token":"jeb1d-IXIC0BWTOV6G-ApTksrbvdBDkZV9KN4taN2nE","user_id":1, ...}, {"token":"...","user_id":2, ...}, ... ]}Copy the
tokenvalue of any row whoseuser_idmatches the superuser. That string IS the live session cookie of that user. (Screenshot 4.) <img width="1512" height="850" alt="04-john-sees-all-session-tokens" src="https://github.com/user-attachments/assets/3303231f-ed87-4b32-8cab-7aa480315529" /> -
**Replay the…
- Record
PYSEC-2026-3882- CVSS 3.1
- 8.8 High
CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H - Updated
- 2026-11-02
Affected products
Affected packages
| Package | Affected | Fixed |
|---|---|---|
piccolo-adminPyPI |
through 1.13.0 | 1.14.0 |
piccolo-adminPyPI |
before 1.14.0, plus listed versions 0.1.00.1.10.1.20.1.30.1.4 Show 45 moreShow less0.10.00.10.10.10.20.10.30.10.40.10.50.10.60.10.70.10.80.10.90.11.00.11.10.11.100.11.110.11.120.11.130.11.20.11.30.11.40.11.50.11.60.11.70.11.80.11.90.12.00.12.10.13.00.13.10.13.20.14.00.15.00.15.10.15.20.16.00.16.10.17.00.18.00.18.10.18.20.19.00.19.10.19.20.19.30.19.40.19.5 |
1.14.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-55485 is a high vulnerability (CVSS 8.8) in Piccolo-orm Piccolo Admin, published August 28, 2026. No source lists public exploit code. EPSS puts the chance of exploitation in the next 30 days at 0.56%, the 47th percentile. Fixed in piccolo-admin 1.14.0.
References
github.com/piccolo-orm/piccolo_admin/security/advisories/GHSA-2gh4-jmwq-rr8w
github.com/piccolo-orm/piccolo_api/pull/331
github.com/piccolo-orm/piccolo_admin/commit/96ddae12baf12288056cbb0cda6f9e8d7e22c86d
github.com/piccolo-orm/piccolo_api/commit/520ec2567ae1d2cc417c8c0d1ad0ddc05549a8a4
github.com/piccolo-orm/piccolo_admin/releases/tag/1.14.0
github.com/piccolo-orm/piccolo_api/releases/tag/1.10.0
Left out here: 102 versions and the rest of 3 source descriptions. The JSON record has them.