Unrestricted File Upload Leading to Cross-Account XSS and Session Hijack in Monica CRM
⚠️ This issue respects the following points: ⚠️
- This is a bug, not a question or a configuration/webserver/proxy issue.
- This issue is not already reported on Github (I've searched it).
- I agree to follow Monica's Code of Conduct.
Bug description
Monica CRM (v4.1.2, latest stable release) contains an unrestricted file upload vulnerability in the document upload endpoint ( POST /api/documents ). The application accepts files of any MIME type and extension with no validation, stores them in a publicly accessible directory served by Apache without authentication, and serves uploaded HTML files with Content-Type: text/html — causing browsers to execute embedded JavaScript in Monica's origin. An attacker with any valid Monica account (no admin rights required) can upload a malicious HTML file disguised as a business document, send the public URL to a logged-in victim, and silently exfiltrate all CRM contact data plus session tokens — enabling full account takeover. Introduced in PR #1912 and present in every release from v2.10.0 through v4.1.2.
Steps to reproduce
Two Monica accounts required — attacker ( [email protected] , any privilege level) and victim ( [email protected] , logged in with active session).
Create malicious payload file — Create Q1-2025-Report.html containing a JavaScript fetch to /api/contacts with credentials:'include' . The file is disguised as a quarterly business report.
Attacker uploads the file — Log in as attacker and upload Q1- 2025-Report.html via POST /api/documents . Monica returns HTTP 201 with "mime_type": "text/html" — no validation error.
Confirm unauthenticated access — Access the public URL /storage/documents/.html without any cookies. Returns HTTP 200 with Content-Type: text/html .
Attacker sends link to victim — URL is on Monica's own trusted domain. Framed as: "Hey, check this Q1 2025 business report."
Victim clicks the link — XSS fires — JavaScript executes in Monica's origin. Victim's session cookie is auto-sent with the fetch call. All CRM contacts and XSRF-TOKEN are returned and displayed.
Impact:
An attacker with any Monica account can silently steal all CRM contact records, session tokens, and private notes from any logged-in user who clicks a link. The captured XSRF-TOKEN and session cookies grant persistent API access to the victim's entire account without knowing their password. The attack requires one click, leaves no trace for the victim, and the malicious URL is hosted on Monica's own trusted domain — making it indistinguishable from a legitimate link. All personal relationship data stored in Monica (contact names, phone numbers, addresses, notes, journal entries) is at risk of exfiltration and the victim's account is fully compromised.
Expected behavior
Recommended Remediation: 1 — Whitelist safe file types in app/Services/Contact/Document/UploadDocument.php : $request->validate([ 'document' => ['required', 'file', 'mimes:pdf,doc,docx,jpg,jpeg,p ]); 2 — Require authentication to access uploaded files — serve files through an authenticated controller route rather than directly from the public filesystem. 3 — Force download for all uploaded files — set Content Disposition: attachment and X-Content-Type-Options: nosniff headers on the /storage/documents/ path to prevent browsers from executing uploaded content.
Environment
app.monicahq.com (monica v4)
Version of Monica
4.1.2
Installation method
None
Web server
None
Database engine version
None
Additional info
No response
Source: monicahq/monica