CVE-2026-96795
Executive Summary
CVE-2026-96795 is a code injection vulnerability (CWE-94) in Horilla, an open-source HR and CRM application, affecting versions prior to 2.0.0. An authenticated user can supply a crafted columns POST parameter to HorillaListView.export_data in horilla_views/generic/cbv/views.py, which is interpolated into dynamically generated Python source and executed via exec(), leading to arbitrary OS command execution under the application process — including root in the shipped Docker image. With a CVSS base score of 8.8 and no public PoC, the primary risk is post-authentication remote code execution against internet-exposed Horilla deployments.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
Technical Analysis
The root cause is unsafe construction and execution of Python code. According to the NVD description, HorillaListView.export_data in horilla_views/generic/cbv/views.py accepts an authenticated user’s columns POST parameter. The handler takes field_tuple[1], interpolates that value into a string named dynamic_fn_str as Python source, and then passes the generated function definition to exec().
The critical nuance is the parser-bypass condition: a crafted input that remains valid under ast.literal_eval can still inject Python syntax into a default argument of the generated function. Because Python evaluates default argument expressions at function definition time — i.e., when the generated source is compiled/executed — injected syntax inside a default argument is evaluated immediately during exec(), prior to any call to the function. This provides an execution primitive during the definition phase rather than at invocation time, meaning exploitation does not require triggering the generated function afterward.
The result is arbitrary OS command execution with the privileges of the Horilla process. In the vendor’s shipped Docker image, that process runs as root, maximizing impact on containerized deployments. On traditional hosts, the effective privilege depends on the service account under which Horilla runs; a misconfigured deployment running as root would carry the same worst-case outcome.
At the vulnerability-class level, this is an eval/exec injection (code injection). The specific vulnerable sink here is exec() operating on attacker-influenced source, with the attacker’s capability gated by an authentication requirement (PR:L) and reachable over the network (AV:N).
Exploitation Scenarios
No public PoC is available; the following chain is inferred from the vulnerability class and the NVD description. Only identifiers appearing in the description (columns parameter, HorillaListView.export_data, horilla_views/generic/cbv/views.py, field_tuple[1], dynamic_fn_str, exec(), ast.literal_eval) are referenced.
Reconnaissance / target identification. An attacker identifies a Horilla instance prior to 2.0.0. Because the vulnerable functionality is a list/export view built on generic class-based views, any authenticated account with access to that view is a viable starting point. Exposed deployments, including those running the shipped Docker image, are the highest-value targets due to root-level process privileges.
Authentication and access. The vulnerability requires an authenticated user (
PR:L). The attacker logs in with any valid low-privilege credential, or uses a compromised account, then reaches the export/list endpoint associated withHorillaListView.export_data.Triggering the flaw class. The attacker submits a crafted
columnsPOST parameter. The handler extractsfield_tuple[1]from that input and interpolates it intodynamic_fn_str, which is then passed toexec(). The attacker’s payload is constructed so the overall structure survivesast.literal_evalvalidation while still embedding Python syntax inside a default argument. Since default arguments are evaluated at definition time, the injected expression executes as soon as the generated function is defined insideexec().Execution method. At the vulnerability-class level, the injected default-argument expression enables arbitrary OS command execution under the Horilla process account. In the shipped Docker image, that is root. No further interaction with the generated function is required because execution occurs during definition.
Post-exploitation impact. Successful exploitation yields command execution with application privileges — high confidentiality, integrity, and availability impact per CVSS (
C:H/I:H/A:H). On containerized deployments as root, the blast radius can extend to the full container, mounted volumes, secrets, and any broadly privileged network or host access the container was granted. On non-containerized hosts, impact scales with the service account’s permissions.
Because no public exploit code or Nuclei template exists, defenders should monitor for the exploit pattern rather than rely on signature coverage of known payloads.
Impact Assessment
CVSS v3.1 base score 8.8 (High): AV:N/AC:L/PR:L/UI:N/S:U/C:H/I:H/A:H. The attack is remotely reachable, requires only low privileges (a valid authenticated session), and needs no user interaction; complexity is low. Confidentiality, integrity, and availability are all rated High. Scope is Unchanged, but in the shipped Docker image the process runs as root, meaning compromise can affect resources well beyond the intended application boundary within the container.
Business impact includes unauthorized access to and exfiltration of HR and CRM data (employee records, personally identifiable information, customer data), integrity compromise of application and underlying filesystem content, and availability loss from command execution. Because Horilla handles HR and customer relationship data, a successful exploit represents a significant privacy and regulatory exposure.
Detection Guidance
Sigma Rule
title: Horilla export_data Code Injection Attempt (CVE-2026-96795)
id: 3c1f9b7a-2d44-4e01-9a0b-7e5c1f2a9d10
status: experimental
description: Detects POST requests to the Horilla list export view with column parameters containing Python code-injection indicators.
references:
- https://nvd.nist.gov/vuln/detail/CVE-2026-96795
author: valtersit.com
date: 2026-01-01
logsource:
category: webserver
detection:
selection_endpoint:
cs-method: POST
cs-uri-stem|contains: 'export'
selection_payload:
cs-uri-query|contains:
- 'columns='
cs-uri-query|re: '(?i)(exec|__import__|os\\.system|subprocess|popen|eval)'
selection_body:
cs-body|re: '(?i)(columns).*(__import__|os\\.system|subprocess|popen|exec\\(|eval\\()'
condition: selection_endpoint and (selection_payload or selection_body)
falsepositives:
- Legitimate export requests containing suspicious substrings in data values.
level: high
tags:
- attack.t1190
- cve.2026.96795
YARA Rule
rule CVE_2026_96795_Horilla_CodeInjection
{
meta:
description = "Detects Horilla code-injection payloads targeting export_data columns parameter"
reference = "https://nvd.nist.gov/vuln/detail/CVE-2026-96795"
author = "valtersit.com"
date = "2026-01-01"
severity = "high"
strings:
$p1 = "columns" nocase
$i1 = "__import__" nocase
$i2 = "os.system" nocase
$i3 = "subprocess" nocase
$i4 = "popen" nocase
$i5 = "exec(" nocase
$i6 = "eval(" nocase
$f1 = "horilla_views" nocase
$f2 = "export_data" nocase
condition:
(uint16(0) == 0x4d5a or uint32(0) == 0x464c457f or true) and
(
($p1 and 1 of ($i1, $i2, $i3, $i4, $i5, $i6)) or
($f1 and $f2 and 1 of ($i1, $i2, $i3, $i4, $i5, $i6))
)
}
Suricata IDS Rule
alert http any any -> any any (msg:"CVE-2026-96795 Horilla export_data Code Injection Attempt"; flow:established,to_server; http.method; content:"POST"; http.uri; content:"export"; http.request_body; content:"columns"; nocase; http.request_body; pcre:"/(__import__|os\\.system|subprocess|popen|exec\\(|eval\\()/i"; classtype:web-application-attack; sid:202696795; rev:1; reference:cve,2026-96795; metadata:affected_product Horilla, attack_target Web_Server, created_at 2026_01_01, cve CVE_2026_96795, mitre_tactic Exploit_Public_Facing_Application;)
Elastic Detection
sequence by source.ip with maxspan=5m
[ network where network.protocol == "http" and http.request.method == "POST" and
http.request.body.text : "*columns*" and
http.request.body.text : ("*__import__*" or "*os.system*" or "*subprocess*" or "*popen*" or "*exec(*" or "*eval(*") ]
[ network where network.protocol == "http" and http.response.status_code == 200 and
url.path : "*export*" ]
Splunk SPL Query
index=web sourcetype=access_combined OR sourcetype=horilla
| eval payload = coalesce(cs_body, cs_uri_query, request_body, http_body)
| where match(lower(payload), "columns") AND match(lower(payload), "(__import__|os\.system|subprocess|popen|exec\(|eval\()")
| stats count min(_time) as first_seen max(_time) as last_seen values(src_ip) as source_ips values(uri_path) as paths by user
| where count > 0
| sort - count
Wazuh OSSEC Rule
<group name="web,horilla,cve-2026-96795">
<rule id="2026967" level="12">
<decoded_as>json</decoded_as>
<field name="url">export</field>
<regex type="pcre2">(?i)(columns).*(__import__|os\.system|subprocess|popen|exec\(|eval\()</regex>
<description>CVE-2026-96795: Possible Horilla export_data Python code injection attempt</description>
<mitre>
<id>T1190</id>
</mitre>
<group>web_attack,</group>
</rule>
</group>
Mitigation & Patch Guidance
Patch confidence: 0/4 (UNPATCHED — no patch evidence from any tracked source). The advisory states the issue is fixed in Horilla 2.0.0; however, no tracked patch source confirms this, so the following should be validated against the vendor before relying on it.
Immediate mitigations
- Restrict network access to Horilla instances. Do not expose the application to the internet; place it behind VPN or an authenticated reverse proxy.
- Enforce least privilege on the Horilla service account. Never run the process as root; in containerized deployments, override the shipped image to run as a non-root user and drop Linux capabilities.
- Limit and audit access to the export/list functionality. Ensure only trusted, necessary accounts can reach the view associated with
HorillaListView.export_data. - Add a WAF rule or reverse-proxy inspection that blocks Python code-injection indicators (
__import__,os.system,subprocess,popen,exec(,eval() in thecolumnsparameter. - Monitor logs from the detection guidance above.
Patch / upgrade steps
- Plan an upgrade to Horilla 2.0.0 or later, validating the fix independently since no tracked patch evidence exists.
- Until then, treat affected deployments as vulnerable.
Long-term hardening
- Eliminate dynamic code generation and
exec()/eval()usage on user-influenced input. Replace string-built source with safe data-driven field mapping and strict allowlisting of permitted column identifiers. - Enforce server-side validation that rejects any
columnsvalue not matching an expected schema. - Apply defense-in-depth controls: container hardening, non-root execution, network segmentation, and secrets isolation.
- Maintain an SBOM and track Horilla releases for the confirmed 2.0.0 fix.
- Eliminate dynamic code generation and
References
- NVD — https://nvd.nist.gov/vuln/detail/CVE-2026-96795
- CWE-94: Improper Control of Generation of Code — https://cwe.mitre.org/data/definitions/94.html
- MITRE ATT&CK T1190 — https://attack.mitre.org/techniques/T1190/
- Horilla project (vendor) — https://github.com/horilla/horilla (returned 404 — verify manually) (verify advisory and 2.0.0 release)
- CISA KEV: Not listed (CVE-2026-96795 is NOT in the Known Exploited Vulnerabilities catalog)
- EPSS: Not yet scored by FIRST.org. No public PoC, exploit code, or Nuclei template identified at time of writing.
What is this? Security researchers use these exact search queries on Shodan.io (a search engine for internet-connected devices) to find vulnerable systems globally. You can copy these dorks to see if your organization's IP space is exposed.
💣 PoC CODE AVAILABLE
Exploit code for this CVE is tracked in our database.
Access poc_code via API Pro.