Introduction¶
eleVADR is a network security analysis engine designed to assess Operational Technology (OT) systems by transforming raw PCAP traffic into actionable security intelligence.
The overall goal is to support the automated analysis of data to allow anyone operating OT to make informed decisions about their network security posture.
It is currently designed with ease of use in mind by leveraging containerization.
Core Architecture¶
EleVADR is split into a frontend and a backend component.
Backend¶
The backend is a Python application that is used to run Zeek to collect network data and analyze it with numpy, pandas, and generate a JSON report.
The backend collects a PCAP file from the frontend and spawns a Zeek process to collect the network logs. Those logs are then converted to pandas using zat and then custom analytics are written to create report modules.
Zeek Script Integration¶
eleVADR relies on Zeek to parse raw packet captures (PCAP) and
generate structured log files. To support advanced physical asset
discovery and network logical boundary mapping, the backend executes
custom Zeek scripts located in the backend/src/app/data/zeek_scripts/
directory during the analysis pass.
1. MAC Logging Script (mac_logging.zeek)¶
By default, Zeek tracks connections at Layer 3 (IP addresses) and Layer 4 (Ports). However, mapping physical hardware manufacturers and establishing true hardware identities requires Layer 2 MAC addresses.
The mac_logging.zeek script hooks into Zeek’s connection state removal
pipeline and appends Layer 2 source and destination MAC addresses to the
connection metadata.
Log Hook:
connection_state_remove(c: connection)Extended Fields:
orig_l2_addr(String): The hardware MAC address of the initiator (Originator).resp_l2_addr(String): The hardware MAC address of the responder.
These fields are mapped to src_endpoint.mac and dst_endpoint.mac
in the backend Pandas DataFrame, allowing the engine to aggregate
manufacturer profiles via OUI lookup.
2. Known Services Script (known_services.zeek) DEPRECATED¶
The known_services.zeek script tracks active, validated services
communicating on the network. For the purposes of eleVADR, an active service
is defined under the following lifecycle rules:
TCP Connections: Requires a completed TCP handshake (SYN+ACK) or an established state where server payload transfer is recognized. Passive, unacknowledged connection attempts (e.g., ports scanned but not open) are ignored.
UDP Connections: Requires at least one packet from the server side confirming that the target port is actively responding, unless the configuration option
Known::service_udp_requires_responseis explicitly disabled.ICMP Traffic: Excluded from service classification.
Configurable Options¶
service_tracking: Defaults toLOCAL_HOSTS. Limits active service profiling to local subnets to protect memory overhead.service_store_expiry: Defaults to1day. Forces periodic state evaluation to prevent stale connection maps on continuous captures.
PcapParser¶
The parsing is done through PcapParser.
- class PcapParser(file_path_info)[source]¶
Process PCAP files using Zeek and create traffic dataframe.
The data is then passed to Analyzer.
Analyzer¶
The Analyzer is responsible for processing the data and running
analytics on the data.
- class Analyzer(traffic_df, endpoints_df, services_df, file_path_info)[source]¶
Enrich traffic data with analysis and generate endpoint/service dataframes.
- traffic_df_processing()[source]¶
Add IP, conn type, direction, subnet, service info to traffic concurrently.
- Return type:
None
- endpoints_df_processing()[source]¶
Create endpoint DataFrame with device info, IPs, services, OT label.
- Return type:
None
- services_df_processing()[source]¶
Prepare
self.services_dffromself.traffic_df.copy only the service‑related columns,
convert category fields to comma‑separated strings,
deduplicate by
service.namekeeping the row with the most populatedservice.risk_categories.
- Return type:
None
It is also responsible for responding to the frontend to filter the data for unique views.
- class Analyzer(traffic_df, endpoints_df, services_df, file_path_info)[source]
Enrich traffic data with analysis and generate endpoint/service dataframes.
- connection_lines_filtered(*, ip=None, src_ip=None, dst_ip=None, subnet=None, src_subnet=None, dst_subnet=None, manufacturer=None, service_name=None, connection_state=None, direction=None, success=None, is_ot=None, limit=500)[source]
Return connection rows matching the provided filter set.
- Return type:
list[dict[str,object]]
- devices_filtered(*, manufacturer=None, subnet=None, service_name=None, is_ot=None, is_edge=None)[source]
Return device rows matching the provided filters.
- Return type:
list[dict[str,object]]
- services_filtered(*, subnet=None, manufacturer=None, device_ip=None, risk_category=None, service_name=None)[source]
Return service rows matching the provided filters.
- Return type:
list[dict[str,object]]
Structure¶
All of the findings are summarized into a JSON report.
- class Report(analyzer, report_id=None)[source]¶
Main report orchestrator class.
Instantiates module classes and aggregates their data into a unified report structure.
The report is split into different Modules. See Report Modules
for the list of modules that are available.
Frontend¶
The frontend is a React application that is used to interact with the Python backend component. The frontend takes the JSON report that the backend generates and displays it in an interactive format, allowing the user to filter and drill-down into the data.
The frontend is organized into sections that mirror what the backend generates.
Upload Form¶
The upload form is where a user can upload a PCAP to run the analysis and download the associated JSON report. There is also an option to upload a JSON report to view it.
Report¶
The frontend is a React application that is used to interact with the Python backend component. The frontend takes the JSON report that the backend generates and displays it in an interactive format, allowing the user to filter and drill-down into the data.
The frontend is organized into the following sections.
Executive Summary¶
The executive summary is a high-level set of key takeaways from the report.
It can include alerts defined by
any class that inherits from DetectionModule and implements
executive_summary
- class DetectionModule(report_modules)[source]¶
Abstract base class for detections based on report modules.
- abstract property executive_summary: str¶
Return the human-readable finding summary.
That list is currently:
Report Modules¶
The report modules are panels that aim to answer specific
analytic questions. Each module inherits from
ReportModule and implements generate_data
That list is currently:
- class RiskBasisBreakdownModule(analyzer)[source]¶
Breaks down observed services by risk basis (Observed vs Credible).
Observed - directly used in compromises with evidence. Credible - known threat actor TTPs, not necessarily active now.
- class ExposureBreakdownModule(analyzer)[source]¶
Breaks down observed services by environment/exposure classification.
External - internet-facing or reachable from outside the org. Cross-Zone - traverses trust boundaries without broker/inspection. Internal - confined within a protected cell/zone.
- class ConnectionSuccessModule(analyzer)[source]¶
Connection success vs failure breakdown based on Zeek conn.log.
- class ProtocolPostureModule(analyzer)[source]¶
Breaks down observed services by protocol posture.
Inherently Risky - clear-text or long-standing exploitation history. Conditionally Risky - secure if hardened, risky when exposed or tunnelled.
- class SuspiciousOutboundConnectionsModule(analyzer)[source]¶
Suspicious outbound connections from OT devices module.