Skip to main content

Horilla HR EUVDEUVD-2026-87371

| CVE-2026-86066 MEDIUM
Cross-Site Request Forgery (CSRF) (CWE-352)
2026-09-25 security-advisories@github.com
5.9
CVSS 4.0 · Vendor: github
View

Attack ChainAIDerived

Hypothetical attack flow derived from CVE metadata – not a confirmed exploit chain

  1. Access
    Identify logged-in Horilla manager
  2. Delivery
    Deliver malicious link/page
  3. Exploit
    Victim's browser sends GET with SameSite=Lax cookie
  4. Execution
    Endpoint mutates state, skips CSRF
  5. Impact
    Attendance 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.

This Month Plan within 30 days. Why: MEDIUM severity (CVSS 4.0-6.9).
Patch available 0.2% exploitation probability (EPSS)

Get an e-mail when this CVE is exploited, patched or re-scored. Free account.

Alert me

AnalysisAIv1 – 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

horilla-hr < 2.0.0

Affected Packages

euvd horilla_hr fix: 2.0.0

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

Vendor (github) PRIMARY 5.9 MEDIUM
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
vuln.today AI 4.3 MEDIUM

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.

3.1 AV:N/AC:L/PR:N/UI:R/S:U/C:N/I:L/A:N
4.0 AV:N/AC:L/AT:P/PR:N/UI:A/VC:N/VI:L/VA:N/SC:N/SI:N/SA:N

Primary rating from Vendor (github).

CVSS VectorVendor: github

Attack Vector
Network
Attack Complexity
Low
Attack Requirements
Present
Privileges Required
None
User Interaction
Active
Vulnerable system: Confidentiality
None
Vulnerable system: Integrity
High
Vulnerable system: Availability
None
Subsequent system: Confidentiality
None
Subsequent system: Integrity
None
Subsequent system: Availability
None

Lifecycle Timeline

1
Patch available
Sep 25, 2026 - 23:19 EUVD

DescriptionCVE.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

EPSS Score
0.2%
Top 91% exploitable
Prevalence
Low
niche open-source HR/CRM app, limited production/enterprise footprint
Data Quality
6 sources
Confidence: High
EUVD NVD CVE.org EPSS Vulnrichment GitHub VulDB

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.

SignalValue
Severity MEDIUM
CISA KEV No
Public POC No
EPSS 0.2%
Patch available Yes
Active threat No
Deciding rule
MEDIUM severity (CVSS 4.0-6.9)
What every action level means
Emergency Immediate action required. Criteria: CRITICAL + in CISA KEV (actively exploited) without patch, OR CRITICAL + EPSS > 30% without patch, OR CRITICAL + active threat signals without patch.
Act Now Prioritize within 24-48h. Criteria: in KEV with patch available, OR CRITICAL + public POC, OR HIGH + EPSS > 10%, OR CRITICAL + active threat, OR any CRITICAL severity.
This Week Schedule remediation this week. Criteria: HIGH severity (CVSS 7.0-8.9). Patch within normal change window.
This Month Plan within 30 days. Criteria: MEDIUM severity (CVSS 4.0-6.9), lower exploitation probability. Include in regular patch cycle.
Monitor Track and reassess. Criteria: LOW severity (CVSS 0.1-3.9), minimal exploitation risk. No immediate action needed.

SSVC AssessmentENISA

SSVC Decision· derived
Scheduled
Exploitation
Poc
System Exposure· derived
Open
Automatable
No
Human Impact· derived
Medium
Technical Impact
Total

Priority Score

30
Low Medium High Critical
KEV: 0
EPSS: 0
CVSS: +30
PoC: 0
Weaponized: 0
Threat intel: 0
Gov advisory: 0

Attack Technique

Cross-Site Request Forgery exploits the automatic credential inclusion behavior of web browsers.

More in Django

View all
CVE-2021-23336 MEDIUM POC
5.9 Feb 15

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

CVE-2017-12794 MEDIUM POC
6.1 Sep 07

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

CVE-2022-34265 CRITICAL POC
9.8 Jul 04

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

CVE-2021-35042 CRITICAL POC
9.8 Jul 02

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

CVE-2019-19844 CRITICAL POC
9.8 Dec 18

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

CVE-2026-61594 CRITICAL POC
9.1 Sep 16

An authentication/authorization bypass in the djust Python live-view framework (versions prior to 1.0.7) lets unauthenti

CVE-2026-61599 HIGH POC
8.8 Sep 16

Unauthenticated arbitrary Python module import in djust before 1.0.7 lets any client that can reach the WebSocket or SSE

CVE-2020-9402 HIGH POC
8.8 Mar 05

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

CVE-2016-6186 MEDIUM POC
6.1 Aug 05

Cross-site scripting (XSS) vulnerability in the dismissChangeRelatedObjectPopup function in contrib/admin/static/admin/j

CVE-2026-93456 HIGH POC
8.4 Sep 18

Forged content modification in django-page-cms through 2.0.13 lets unauthenticated remote attackers exploit five CSRF-ex

CVE-2026-61593 HIGH POC
8.1 Sep 16

Cross-site request forgery in djust's Server-Sent-Events transport affects all versions prior to 1.0.7 and allows a cros

CVE-2026-61590 HIGH POC
7.4 Sep 16

Unauthenticated remote attackers can read live Django view and session state and invoke server-side handlers through dju

This site uses cookies essential for authentication and security. No tracking or analytics cookies are used. Privacy Policy