Attack ChainAIDerived
Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain
-
AccessIdentify logged-in Horilla manager
-
DeliveryDeliver malicious link/page
-
ExploitVictim's browser sends GET with SameSite=Lax cookie
-
ExecutionEndpoint mutates state, skips CSRF
-
ImpactAttendance approved as victim, audit falsified
Vulnerability Assessment
| Weakness |
CWE
OWASP Top 10:2025
A01:2025 - Broken Access Control
ASVS 5.0
V3 - Web Frontend Security Anti-CSRF tokens, SameSite cookies
MITRE ATT&CK
1 technique across 1 tactic: Initial Access The technique list with ATT&CK links and the kill-chain view is part of the Basic plan and up. |
| Exploitation Conditions | Exploitation requires a victim who is currently authenticated to Horilla as a manager holding the attendance.change_attendance permission and who, while that session is active, is tricked into triggering a top-level request to /attendance/approve-validate-attendance-request/. It works specifically because the pre-2.0.0 endpoint accepts HTTP GET and mutates state (attendance_validated, is_validate_request_approved, approved_by) before attendance.save(), so Django never enforces CSRF, and the SameSite=Lax cookie policy still permits the cookie on top-level GET navigations. Limiting factors: requires active victim session, requires victim user interaction (clicking/visiting attacker content), only affects instances running versions below 2.0.0, and the attacker cannot choose the approving identity - impact is bounded to whatever the victim manager can approve. |
| Impact | This is a genuine but modest-severity CSRF flaw (CWE-352) with a coherent signal set. The vendor CVSS 4.0 vector (AV:N/AC:L/AT:P/PR:N/UI:A/VC:N/VI:H/VA:N) and score of 5.9 (medium) align with the description: network-reachable, unauthenticated attacker (PR:N), but exploitation is gated on an attack requirement (AT:P) and active victim interaction (UI:A) - a logged-in manager holding attendance.change_attendance must be lured into a top-level request while their SameSite=Lax session cookie rides along. The root cause is confirmed in the commit diff: approve_validate_attendance_request performed state changes over HTTP GET before attendance.save(), so Django's CSRF middleware never engaged; the fix adds @require_http_methods(["POST"]) and converts hx-get to hx-post. No EPSS score was provided, there is no CISA KEV listing, and no public exploit code is referenced (the vuldb entry is an advisory, not a POC), so this is not a confirmed-exploited priority. Impact is confined to integrity of attendance records - silently approving requests and falsely attributing approvals in the audit trail - with no confidentiality or availability loss. Note the impact tension: the vendor rates VI:H, but the practical business impact (approving one class of attendance record) reads closer to a low/moderate integrity effect. Realistic priority: LOW-to-MEDIUM - patch in normal cycle; the double gate of victim authentication plus user interaction sharply limits opportunistic mass exploitation. |
| Exploit ScenarioDerived | An attacker crafts a malicious web page or email link pointing at /attendance/approve-validate-attendance-request/<id>/ and gets a logged-in Horilla manager (with attendance.change_attendance) to open it in the same browser. Because the pre-patch endpoint accepted GET and skipped CSRF validation, the manager's SameSite=Lax session cookie is sent on the top-level navigation, silently approving the targeted attendance request under the victim's identity and stamping the audit trail with the victim as approver. No public exploit code is identified, but the technique is trivial classic CSRF once a victim is targeted. |
Get an e-mail when this CVE is exploited, patched or re-scored. Free account.
Alert meAnalysisAIv1 – Sep 26, 2026
Cross-site request forgery in Horilla HR (horilla-hr) versions before 2.0.0 allows an unauthenticated attacker to silently approve pending attendance requests under a logged-in manager's identity, because the /attendance/approve-validate-attendance-request/ endpoint performed state-changing work (setting attendance_validated, is_validate_request_approved, approved_by and related pending-request flags, then calling attendance.save()) over an HTTP GET that Django's CSRF middleware never validates. The CVSS 3.1 assessment rates this AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N, i.e. a low-integrity-impact flaw that requires a manager holding attendance.change_attendance to be authenticated and tricked into visiting attacker-controlled content while that session is active; the attacker cannot choose the approving identity and the impact is bounded to what that victim manager could approve. Vendor-released patch: 2.0.0. No public exploit identified at time of analysis, and the flaw is not confirmed actively exploited (not in CISA KEV).
RemediationAI
Upgrade to Horilla HR 2.0.0, the vendor-released fixed version (release notes at https://github.com/horilla/horilla-hr/releases/tag/2.0.0), which adds @require_http_methods(["POST"]) to the affected views and converts the approving/cancelling UI triggers from GET to POST so Django's CSRF protection applies - see the upstream advisory GHSA-65vj-5p5g-pv5m and fix commit 87a23ecaf2944c15dc5c131f813d2b83fd56eaa0. If upgrading immediately is not possible, apply the same change manually by requiring POST on approve_validate_attendance_request and validate_attendance_request and updating the templates accordingly; as a stopgap, ensure the Horilla session cookie is set with SameSite=Strict (this blocks the cross-site top-level GET from carrying credentials, but it can break legitimate cross-origin/embedded workflows and some SSO redirect flows), and restrict network access to the /attendance/approve-validate-attendance-request/ and /attendance/cancel-validate-attendance-request/ endpoints so they are only reachable from trusted networks or behind an authenticating reverse proxy (trade-off: does not help remote-work deployments). Consider auditing existing attendance records for approvals attributed to managers who did not perform them and tightening the attendance.change_attendance permission grant set, since the attack can only ever produce approvals the victim manager was authorized to make.
Affected VersionsENISA
Affected Packages
Source: OSV (Open Source Vulnerabilities)
Affects your stack
Affects 1 package in euvd
Checking this CVE against your own stacks and servers is part of the Basic plan and up.
Technical ContextAI · show details
Horilla is a Django-based HR/CRM application that uses CSRF middleware to protect unsafe HTTP methods and HTMX for its front-end interactions. In releases before 2.0.0 the view approve_validate_attendance_request (attendance/views/requests.py) mutated attendance state and persisted it with attendance.save() before any method restriction was applied, so it accepted GET requests - a class of defect mapped to CWE-352 (Cross-Site Request Forgery). Django's CsrfViewMiddleware only enforces tokens on POST, PUT, PATCH and DELETE, meaning a GET that changes state is exempt from CSRF protection by design; combined with the default SameSite=Lax session cookie policy, the browser still attaches the manager's session cookie on a top-level cross-site GET navigation, so the forged request is authenticated as the victim and the resulting approval is attributed to the victim in the audit trail. The upstream fix (commit 87a23ecaf2944c15dc5c131f813d2b83fd56eaa0) hardens both layers: the views validate_attendance_request and approve_validate_attendance_request gain the @require_http_methods(["POST"]) decorator, and the affected templates (request_actions.html, group_by.html, individual_view.html, request_lines.html) switch the relevant hx-get/href triggers to hx-post with hx-confirm prompts. HTMX and Django template interactions are therefore central to the affected code path.
Know when Django gets its next vulnerability
- Watch products and get an email when something new is published
- Check this CVE against your own stack and servers
- Exploit and PoC references, threat intelligence from 22 sources
No credit card · 7-day full trial
Severity by source
CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:A/VC:N/VI:H/VA:N/SC:N/SI:N/SA:N
Unauthenticated CSRF over the network (PR:N) needing victim manager interaction (UI:R); integrity-only and limited to attendance-approval state, so C:N/I:L/A:N and no scope change.
Primary rating from Vendor (github).
CVSS VectorVendor: github
Lifecycle Timeline
1DescriptionCVE.org
Horilla is an HR and CRM software. Prior to 2.0.0, approve_validate_attendance_request at /attendance/approve-validate-attendance-request/ changes attendance_validated, is_validate_request_approved, approved_by, and related pending-request state through an HTTP GET before calling attendance.save(), so Django does not require CSRF validation for the action. An unauthenticated attacker can cause a logged-in manager with attendance.change_attendance to make a top-level request that carries the manager's SameSite=Lax session cookie, silently approving attendance with the victim's privileges and attributing the approval to the victim in the audit trail. This issue is fixed in version 2.0.0.
Threat Intelligence Overview
How this decision was reached
The engine reads each signal below and applies a fixed list of rules from top to bottom. The first rule that matches sets the recommended action – like a statement, every line is shown.
What every action level means
SSVC AssessmentENISA
Priority Score
Attack Technique
Cross-Site Request Forgery exploits the automatic credential inclusion behavior of web browsers.
The package python/cpython from 0 and before 3.6.13, from 3.7.0 and before 3.7.10, from 3.8.0 and before 3.8.8, from 3.9
In Django 1.10.x before 1.10.8 and 1.11.x before 1.11.5, HTML autoescaping was disabled in a portion of the template for
An issue was discovered in Django 3.2 before 3.2.14 and 4.0 before 4.0.6. Rated critical severity (CVSS 9.8), this vulne
Django 3.1.x before 3.1.13 and 3.2.x before 3.2.5 allows QuerySet.order_by SQL injection if order_by is untrusted input
Django before 1.11.27, 2.x before 2.2.9, and 3.x before 3.0.1 allows account takeover. Rated critical severity (CVSS 9.8
An authentication/authorization bypass in the djust Python live-view framework (versions prior to 1.0.7) lets unauthenti
Unauthenticated arbitrary Python module import in djust before 1.0.7 lets any client that can reach the WebSocket or SSE
Django 1.11 before 1.11.29, 2.2 before 2.2.11, and 3.0 before 3.0.4 allows SQL Injection if untrusted data is used as a
Cross-site scripting (XSS) vulnerability in the dismissChangeRelatedObjectPopup function in contrib/admin/static/admin/j
Forged content modification in django-page-cms through 2.0.13 lets unauthenticated remote attackers exploit five CSRF-ex
Cross-site request forgery in djust's Server-Sent-Events transport affects all versions prior to 1.0.7 and allows a cros
Unauthenticated remote attackers can read live Django view and session state and invoke server-side handlers through dju
EUVD-2026-87371
NVD
CVE.org
EPSS
Vulnrichment
GitHub
VulDB
Exploit-DB