<figure><img src="image_1.png" /></figure>

HAL
open science

# Generating causal logs to explain attacks

Viet-Huy Ha, Vincent Gauthier, Eric Totel

► To cite this version:

Viet-Huy Ha, Vincent Gauthier, Eric Totel. Generating causal logs to explain attacks. 20th International Conference on Risks and Security of Internet and Systems (CRISIS), Oct 2025, Gatineau, Canada. hal-05494614

HAL Id: hal-05494614

https://hal.science/hal-05494614v1

Submitted on 5 Feb 2026

HAL is a multi-disciplinary open access
archive for the deposit and dissemination of sci-
entific research documents, whether they are pub-
lished or not. The documents may come from
teaching and research institutions in France or
abroad, or from public or private research centers.

L'archive ouverte pluridisciplinaire HAL, est
destinée au dépôt et à la diffusion de documents
scientifiques de niveau recherche, publiés ou non,
émanant des établissements d'enseignement et de
recherche français ou étrangers, des laboratoires
publics ou privés.

<figure><img src="image_2.png" /></figure>

Distributed under a Creative Commons CC BY 4.0 - Attribution - International License
# Generating Causal Logs to explain attacks

Viet-Huy HA¹[0009-0002-7172-6442], Vincent Gauthier¹[0000-0003-2252-7332], and
Eric Totel¹[0009-0009-2774-007X]

SAMOVAR, Télécom SudParis, Institut Polytechnique de Paris, 91120 Palaiseau,
France

**Abstract.** The growing complexity and volume of cyber threats demand more efficient methods for intrusion detection and incident analysis. Traditional Security Information and Event Management (SIEM) systems often struggle with multi-step attacks, as they lack explicit causal links among individual events. To address this limitation, we propose a causal log generation framework that leverages extended Berkeley Packet Filter (eBPF) technology to intercept system calls in real time. By assigning vector timestamps to relevant events, our method dynamically constructs causal graphs, enabling precise reconstruction of an attack's progression across system boundaries. We evaluate our approach against the real-world attack scenario, such as Command and Control (C&C) backdoor activities, demonstrating that causal logs greatly improve traceability and forensic insight compared to conventional SIEM-based correlation. We further discuss the scalability challenges of large-scale deployments and how causal logging may transform threat hunting and incident response.

**Keywords:** intrusion detection · attack explanation · causal logs · causal-ity graphs

## 1 Introduction

Intrusion detection and threat analysis are usually restricted by the inherent complexity of large-scale systems and the sophistication of modern cyberattacks. Security Information and Event Management (SIEM) solutions aim to address these challenges by gathering data from diverse sources (system logs, network traffic, application events, and more) and applying various correlation rules or anomaly detection algorithms. Although SIEMs play a critical role in centralizing and organizing security data, correlating multi-step attacks remains a significant hurdle. Typical logs record discrete events without capturing the deeper, causal relationships that connect them. As a result, reconstructing an incident (or even detecting one in progress) can be error-prone and time-consuming.

Recent advances in kernel instrumentation, notably eBPF (extended Berkeley Packet Filter), have paved the way for finer-grained visibility into system activities. By intercepting system calls and associating them with corresponding processes, files, and network sockets, eBPF provides an opportunity to generate more nuanced logging data. Instead of simply recording an event, a causal log
explicitly encodes how one event leads to another via a series of causal chains, thus creating a clearer narrative of how an attacker moves through the system.

In this paper, we introduce a new framework for generating and analyzing causal logs in real time. Specifically, we describe how eBPF can be used to monitor relevant system calls in a lightweight manner, how vector timestamps can capture the partial ordering of concurrent events, and how these enhanced logs can be translated into causality graphs. We then demonstrate the efficacy of our approach by applying it to the common attack scenario: Command and Control (C&C) backdoor compromises. Through this example, we show how capturing causal links facilitates more accurate attack explanation and assists security analysts in piecing together even highly stealthy, multi-step intrusions. Finally, we discuss the broader benefits and practical considerations surrounding large-scale deployments of causal logging.

# 2 State of the art

## 2.1 Causal Logs

Causal logs have been referenced in various studies [3][1], but there is no widely accepted definition of the term. Most existing methodologies retrospectively infer causality from conventional logs, analyzing past system activities to reconstruct event sequences. This retrospective approach suffers from inherent limitations, such as insufficient granularity, noise, and difficulty distinguishing genuine causal relationships from coincidental correlations. Thus, threat detection and forensic investigations remain inefficient, as analysts must manually infer attack pathways from disparate log entries.

Unlike conventional logs, an explicitly causal log would inherently record causal relationships at event occurrence time, significantly enhancing accuracy and reducing the manual effort needed during forensic analyses. Systematically embedding causality into logs provides an improved basis for swiftly reconstructing multi-step attack scenarios.

## 2.2 Causality

In distributed systems, causality denotes how events affect each other, guiding the logical order of operations critical for maintaining consistency, debugging, and enhancing security [2]. Distributed causality is traditionally divided into:

- *Internal Causality*: Sequential dependencies within a single process.

- *External Causality*: Dependencies across processes mediated through communication (e.g., message passing or shared resources).

Lamport's Logical Clocks [2] provide a foundational approach to capturing causality, utilizing monotonically increasing scalar timestamps. Despite their effectiveness in sequencing events, scalar clocks do not fully capture concurrent events across different processes.
Advanced frameworks by D'Ausbourg [6] and Xosanavongsa [5] further refine causal dependency models by incorporating object states and contextual factors. These models effectively handle heterogeneous systems involving complex interactions among diverse objects and contexts, essential for accurately mapping multi-stage security incidents.

# 3 Xosanavongsa's model

<figure><img src="image_3.png" /><figcaption>Fig. 1. Xosanavongsa's causal dependency frameworks [5]</figcaption></figure>

Addressing limitations of traditional causal frameworks, Xosanavongsa's model integrates Lamport's "happened-before" relation and D'Ausbourg's object-based dependencies, enriched by runtime context [4] [5]. This contextual approach is particularly beneficial for tracing intricate, multi-object interactions in security breaches.

## 3.1 Contextual Action and Sessions

A contextual action $(a, (o, t))$ is an operation performed by an object $o$ at a specific state $(o, t)$. Objects are categorized as either active (processes, interfaces) or passive (files, pipes), clearly pinpointing each action's context. Sessions group sequences of causally linked actions within an object, formally defined as consecutive contextual actions without causal overlaps:

$$ \text{Session}_n(o) = \{(a_i, (o, t_i)/(o, t_i) \rightarrow (o, t_{i+1}))\} $$

This session-based grouping aids in isolating coherent causal sequences within objects, crucial for detailed attack reconstruction.

## 3.2 Contextual Action Causal Dependency

Xosanavongsa introduces a contextual causal dependency operator $(\rightarrow)$, extending Lamport and D'Ausbourg frameworks to comprehensively cover both internal (within-object) and external (cross-object) interactions:
- Same-object dependency occurs within continuous sessions, following chronological order.

- Cross-object dependency emerges through information flow or explicit message exchanges between distinct objects or processes.

By explicitly distinguishing between session-based dependencies and cross-object interactions, Xosanavongsa's framework accurately captures both local and global causalities.

## 3.3 Contextual Events

A contextual event $(e, o, t_e)$ pairs an observable logged event with its object and timestamp. Observations from contextual actions generate sets of logged events that facilitate establishing event-level causal dependencies. Events are causally dependent if their underlying contextual actions share defined causal relationships:

$$ (e_1, o_1, t_{e_1}) \rightarrow (e_2, o_2, t_{e_2}) \text{ if and only if } (a_1, (o_1, t_1)) \mapsto (a_2, (o_2, t_2)) $$

Thus, the event-level causal dependency directly inherits the causal structure from contextual actions, ensuring comprehensive causality capture within the generated logs. This approach precisely reflects the true system dynamics, significantly enhancing the accuracy of forensic analysis and threat detection.

# 4 Generating causal logs

## 4.1 Computing causality with vector clocks

Capturing accurate causal relationships among events in a distributed system is challenging due to the lack of a global clock and the possibility of concurrent operations. Early work by Lamport introduced logical (scalar) clocks, which assign monotonically increasing integers to events while preserving a “happened-before” ordering [2] (Figure 2). However, Lamport’s scalar clocks do not fully capture concurrency: if two events occur in parallel in different processes, scalar timestamps alone can erroneously place one before the other.

**Definitions and Update Rules** Let $p_i \in \mathbf{P}$ be a set of processes. We denote the vector clock for process $p_i \in \mathbf{P}$ as $V_{p_i}$ with size $|\mathbf{P}|$. For instance, the entry $k$ in the vector $V_{p_i}$ tracks $p_i$'s current knowledge logical time about the process $p_k$. The following rules describe how vector clocks are updated:

- *Internal Event in* $p_i$ (e.g., a system call within the process): $V_{p_i}[i] \leftarrow V_{p_i}[i] + 1$
This increments the local clock entry whenever an internal event occurs in process $p_i$.

- *Send event occurs in* $p_i$ (e.g., sending data via a socket or pipe):

• Increment the local entry: $V_{p_i}[i] \leftarrow V_{p_i}[i] + 1$
<figure><img src="image_4.png" /><figcaption>Fig. 2. Lamport Vector clock</figcaption></figure>

* Attach the updated vector clock $V_{p_i}$ to the outgoing message.

- $p_i$ receives message from $p_j$ (e.g., reading from a socket, pipe, or file where causal data originated)

* Upon receipt of a vector $V_{p_j}$ from sender $p_j$:

$$V_{p_i}[j] \leftarrow \max(V_{p_i}[j], V_{p_j}[j]);$$

* Then, increment the local clock entry:

$$V_{p_i}[i] \leftarrow V_{p_i}[i] + 1$$

These operations ensure that for any two events a and b in the system, a “happened-before” b (denoted $a \rightarrow b$) implies $V(a) < V(b)$ elementwise (i.e., $V(a) \neq V(b)$ and $V(a)[k] \leq V(b)[k]$ for all k, with at least one strict inequality).

<figure><img src="image_5.png" /><figcaption>Fig. 3. Event-Based Vector Clocks</figcaption></figure>

**Direct-Dependency Optimization** While vector clocks faithfully capture causal ordering, maintaining or merging full vector clocks for every single system event can be expensive. Fowler and Zwaenepoel [7] proposed a direct-dependency technique, wherein each process tracks only the event identifiers on which it directly depends instead of carrying a full vector of size N. This smaller structure is sometimes called a “dependency vector” or “DDD” (direct dependency descriptor). Whenever a process receives an event from another process, it merges just
the relevant direct-dependency information; at the end of execution—or when constructing a global view—these direct dependencies can be expanded back into full vector clocks [9]. This approach often reduces overhead significantly and works especially well if the number of active processes N is large or if system calls are frequent. The mechanism of Direct-Dependency is presented as follows:

1. Dependency Vector: Each process $p_i$ maintains a dependency vector $D_i$ which record the latest event from each process that directly influences $p_i$. Initially, each element of $D_i$ is set to zero.

2. Internal Events: When an event occurs at $p_i$, the corresponding entry in the dependency vector is incremented:

$$D_i[i] := D_i[i] + 1$$

3. Sending Message: When $p_i$ sends a message to $P_j$, it piggybacks the updated value of its dependency vector related to $p_i$ (i.e., $D_i[i]$)

4. Receiving Messages: Upon receiving a message from $p_i$, process $P_j$ updates its dependency vector as follows:

$$D_i[j] = \max(D_i[j], D_j[j])$$

This operation ensures that $P_j$'s dependency vector reflects the most recent events from $p_i$ that directly influence $P_j$.

## 4.2 Generating Vector Clock

Building on the foundational concepts of Lamport vector clocks [2], direct vector clocks [7] and direct dependency [8], we propose an event-centric approach to maintaining vector clock in Figure 3. Rather than simply updating a single vector clock per process, we assign a dedicated vector clock to each security-relevant event (i.e., each captured system call). By doing so, we capture the causal state precisely at the moment the event is triggered, making it much easier to resolve interactions among multiple processes. By assigning individual indexes to events, it becomes easier to track their respective vector timestamps, facilitating efficient updates and queries. This refined approach enhances the precision of causal relationship tracking, improving the system's capability to identify intricate event sequences and detect multi-step attacks with greater accuracy.

**Motivation for Event-Based Vector Clock** Traditional process-level vector clocks increment and merge timestamps whenever a process sends or receives data. While effective, this approach can become less granular when dealing with frequent, concurrent events. In practice, multi-step attacks often hinge on very specific system calls (for instance, a `write()` that plants malicious data, or a `clone()` that spawns a new thread of execution). Maintaining a process-wide vector clock alone risks obscuring these discrete causal “handoffs”.

By contrast, event-based vector clocks reflect the system's causal state at the precise moment an event occurs. That distinction is crucial for:
- Fine-Grained Causal Links: If multiple sends occur concurrently, each send event can be assigned a distinct timestamp. Subsequent receive events can then determine the specific send event(s) they are causally dependent on.

- Direct Dependencies: Inspired by the Fowler and Zwaenepoel approach [7], we store only the minimal set of dependency pointers or timestamps needed for each event. This prevents vector-clock inflation and can reduce overhead in systems where the number of processes is large.

**Event-Based Vector Clock Technique** Our method expands on the Direct Dependency model proposed in [7]. The essence of this technique is to record, for each event e, only the vector clock (or partial vector) that pertains to the processes directly influencing e. Formally:

- Initialization:

• Event Set E:
  Define a set of events E with $|E|$ indicating the total number of events.

• Process Identifiers δ:
  Create an array or structure δ of size $|E|$, used to record which process generated each event. In other words, $δ[e]$ identifies the process to which event e belongs.

• Processes Set P:
  Let P be a set of processes, with $|P|$ denoting the total number of processes.

• Process Vector Clocks:
  For each process $p_i \in P$, initialize a vector clock $V_{p_i}$ of length $|P|$. Set all entries of $V_{p_i}$ to 0, including the local entry $V_{p_i}[i]$:
  $$V_{p_i}[k] \leftarrow 0 \text{ for } \forall k \in [1..|P|]$$

• Event Vector Clocks:
  We initialize a vector clock $V_e$ of size $|P|$ for each event in E:
  $$V_e[k] \leftarrow 0 \text{ for } \forall k \in [1..|P|]$$

• Sequence Number K:
  Define a global sequence number $K=0$. Each time a new event is generated, K is incremented. This variable serves both as a running total of all events and as the unique sequence number assigned to each new event.

- Event Creation: Whenever an event e occurs (e.g., a system call in process $p_i$):

• Assign $E_e = K$.

• Increment the local process vector entry $V_{p_i}[i]$ by 1.

• Increment the global event counter K by 1.

• Assign $δ_e = p_i$ to record that process $p_i$ is the owner of event e.

• Calculate vector clock of e:

$$\ast V_e[p_i] = V_{p_i}[i]$$

$$\ast \text{If } V_{p_i}[i] \ge 2, \text{ let } k = V_{p_i}[i] - 1 \text{ (denotes the previous event of e in } p_i), \text{ we have } V_e[p] = V_k[p] \text{ for } \forall p \in P \ (p \ne p_i).$$
- *External Merges*: If this event corresponds to receiving data from event $e'$ in another process $p_j$ (e.g., via `read()`, `recv()`, or a file operation that another process wrote), we update $\mathcal{V}_e$ by $\mathcal{V}_e[j] = \mathcal{V}_{e'}[j]$.

- *Dependency Linking*: Because each event $e$ explicitly stores its vector $\mathcal{V}_e$, it is straightforward to reconstruct a global partial order or to convert partial vectors to a full vector clock if needed.

This event-level logging approach precisely captures causal relationships by assigning a dedicated vector timestamp to each system event at the exact time of its occurrence, rather than maintaining a single clock per process. Consequently, we can directly reconstruct accurate causal dependencies between events, enabling detailed visualization and analysis through causal dependency graphs.

**Constructing the Full Vector Clock**
Once each event $e$ is annotated with its own vector clock $\mathcal{V}_e$, we can construct a process-level or global-level timeline. Techniques such as those described by Baldy et al. [9] allow us to “ inflate ” or expand these direct-dependency vectors into full-fledged vector clocks for all events in a system. In brief:

1. We collect all events $\mathbf{E} = \{e_1, e_2, ..., e_k\}$.

2. For each event $e_i \in \mathbf{E}$ we retrieve partial vector $\mathcal{V}_{e_i}$.

3. We perform incremental merges across events that share direct dependencies, ensuring that every transitive relationship is accounted for.

4. The result is a consistent vector-time assignment that respects Lamport’s happened-before relation while preserving concurrency where no causal link exists.

The main advantage is that we do not carry a large vector for every operation upfront. Instead, we store the smaller direct dependencies at each event. Only if a full system-level view is required (e.g., for a forensic timeline or an offline correlation step) do we synthesize the final vector clocks.

## 4.3 Using ebpf to intercept system calls

The Extended Berkeley Packet Filter (eBPF) is an innovative Linux kernel framework that offers an efficient and adaptable means of monitoring system calls, making it well-suited to capturing the causal links across diverse system events. Through carefully sandboxed execution of custom programs within the kernel, eBPF delivers real-time tracing functionality (without requiring any modifications to kernel source code), thereby enabling in-depth analysis and seamless correlation of events as they unfold.

**Capturing System Calls with eBPF**
System calls act as the fundamental interface between user processes and the kernel, making them pivotal in deciphering how a system behaves. By leveraging eBPF, these calls can be instrumented in real time to collect details such as process identifiers, arguments, and return
values. Armed with this data, it becomes feasible to uncover how individual
events are linked, thereby constructing the causal dependencies that reveal the
underlying interactions and flow of execution within the system.

<figure><img src="image_6.png" /><figcaption>Fig. 4. EBPF Implementation</figcaption></figure>

Figure 4 depicts an eBPF tracing configuration that links user space with the Linux kernel in order to record syscall occurrences. In user space, an application (APP) initiates system calls, which are intercepted by a Python script (*main_capture.py*) that dynamically injects a *trace()*() function into the kernel. Once deployed, the eBPF program attaches itself to specified syscalls, tracking parameters such as call arguments and return values any time those syscalls run. The trace function then writes the gathered information into a kernel ring buffer, which the Python script regularly reads. Ultimately, this data is stored in an output destination, enabling developers to review or analyze syscall events for generating causality graph. By capturing calls at the kernel level, the approach delivers granular insights into application operations (spawned syscalls), all without having to rebuild the kernel or change existing user programs.

**Constructing Causal Dependencies** Prior investigations [2][5][6] have demonstrated that causal dependencies among system calls can be reliably detected and documented in causal logs by examining event interactions. Within a single process, Lamport's "happened-before" relationship [2] serves to determine causal-
<table><caption>**Table 1.** Syscalls list</caption><thead><tr><td>Syscall</td><td>Causality property</td></tr></thead><tbody><tr><td>clone()</td><td>Since the child process is almost identical to the parent, the clone() call can be considered a preceding event, impacting the child's first event.</td></tr><tr><td>pipe2()</td><td>The pipe2() system call creates a communication channel between processes, establishing a causal dependency. Tracking all pipefd instances and linking them with other system calls is crucial for identifying these dependencies.</td></tr><tr><td>read() and write()</td><td>The read() system call establishes causal dependencies in both PIPE and FILE communications. In PIPE communication, it depends on prior write() calls to the same PIPE, where processes using the corresponding "write_fd" are considered preceding events. In FILE communication, read() links to earlier write() calls on the same file, aiding in tracking event sequences and dependencies.</td></tr><tr><td>send() and recv()</td><td>The send() system calls create a causal link by transmitting data from one socket to another, while the recv() system calls establish a corresponding dependency chain by receiving this data, linking the sending and receiving processes.</td></tr><tr><td>copy_file_range()</td><td>The copy_file_range() system call creates a causal dependency between file objects by transferring data directly between file descriptors within the kernel. Since the data copied from fd_in affects the content of fd_out, any process that previously wrote to fd_in or subsequently reads from fd_out establishes a causal relationship with the copy_file_range() call.</td></tr><tr><td>accept()</td><td>The accept() system call creates a new socket file descriptor for an incoming connection. While it doesn't directly establish a causal dependency, it sets up the communication channel that enables subsequent operations (like send(), recv(), read(), write(),... ) to create causal links between processes.</td></tr><tr><td>connect()</td><td>The connect() system call initiates a connection from the client to a server endpoint by binding the client's socket file descriptor. Although this call does not directly establish a causal dependency, it creates the communication channel through which subsequent operations, such as send(), recv(), read(), write(),..., can build causal relationships between processes.</td></tr></tbody></table>

ity. For external events spanning multiple processes, D'Ausbourg's framework [6] and Xosanavongsa's model [5] offer robust methods for constructing causal links. Taken together, these approaches yield a thorough representation of both internal and cross-process causal relationships. Below are illustrative examples:

- *Process Creation*: Calls like fork() and clone() create causal links between parent and child processes.

- *Message Passing*: Communication via pipe2() or socket() generates dependencies between send() (sender) and recv() (receiver).
- *File Operations*: Writing and subsequent reading from a file establish causal chains among processes.

As illustrated in Table 1, we provide an explanation of the causality properties associated with several key system calls that play a significant role in establishing causal relationships.

## 4.4 Result logs

To gather enough information for comprehensive analysis and causal graph construction, we employ two complementary logging formats: Causal logs and Raw logs. By capturing system events in these distinct ways, we ensure that we have both a higher-level overview of event flows and a granular, low-level record of each event's context and parameters.

<figure><img src="image_7.png" /><figcaption>Fig. 5. Raw syscall logs</figcaption></figure>

<figure><img src="image_8.png" /><figcaption>Fig. 6. Causal logs</figcaption></figure>

**Raw syscall logs (Figure 5)** Originating directly from the eBPF tracing function within the kernel, Raw Logs contain a broader set of fields (such as process names, PIDs, timestamps, and possibly more granular details like syscall arguments or return values). By using this data for processing, we obtain causal logs to calculate causal dependencies among the events.
**Causal logs (Figure 6)** These logs are generated by a user-space program that processes event data received from the kernel. Each entry contains columns for ID, PID, SYSCALL, TIME, PREV_NODE, VECTOR_TIME, and UUID, offering a concise yet robust snapshot of system call activity.

- ID provides a unique identifier for each event, making it straightforward to refer to individual actions.

- PID indicates the process where the event occurred.

- SYSCALL labels which system call was invoked (e.g., `write()`, `read()`, or `fork()`).

- TIME captures the timestamp, allowing chronological ordering of events.

- The fields `PREV_NODE` and `VECTOR_TIME` enable us to trace dependencies between events, which is essential for mapping causal relationships among processes and threads. Additionally, we leverage this data to perform backward calculations, reconstructing the complete vector clock and establishing a detailed causality framework.

- UUID serves as a globally unique reference that can correlate the event with additional logs or external data sources.

By focusing on these key fields, Causal Logs provide a streamlined perspective on the most pertinent event details. They are particularly helpful when visually mapping out how different processes interact, since each event can be connected to its predecessor in the causal chain.

## 4.5 Result Graphs

After gathering the causal logs and computing vector timestamps, we merge that information to build the causality-dependency graph. As shown in Figure 7, each system call is plotted as an event (a point) on its process line. Periods in which no direct system call is invoked (such as reading a file without generating an observable event) are denoted $e_0$. An arrow between two events indicates their causal relationship. These relationships can be understood as follows:

- Within Each Process: Internal events are connected based on the order in which they occur, reflecting their inherent sequencing.

- Socket Communication ($P1 \leftrightarrow P3$): Calls to `send()` and `recv()` establish a direct causal link between Process *P1* and Process *P3*.

- File I/O ($P1 \leftrightarrow P2$): An indirect dependency arises between *P1* and *P2* when data is written to and then read from a file (these `write()` and `read()` calls form a causal chain across the *FILE* object).

- Process Creation ($P2 \leftrightarrow P3$): When *P2* creates *P3* (via `clone()`), it introduces a parent-child relationship. *P2*'s `clone()` call precedes *P3*'s first observable event.

This causal graph provides a clear visual representation of how the system's events are chained together through both direct and indirect dependencies. By
<figure><img src="image_9.png" /><figcaption>Fig. 7. Result graph illustration</figcaption></figure>

observing which nodes connect and in what order, it is possible to piece together
the narrative of a complex multi-stage attack. Investigators can, for example,
start at a suspicious event (anomalous write or newly spawned process) and
follow the backward arrows to see how the malicious action was initiated, or
track forward to see its impact on subsequent events. Such a causal view is
particularly valuable when performing forensic analysis, since it reveals not just
what happened, but why it happened (i.e., which specific actions triggered or
enabled each subsequent step).

5 Explaining Attacks

In this section, we demonstrate how causal logs explain real-world cyberattacks
by deconstructing a specific Backdoor / Command and Control (C&C) Attack.
This multi-stage intrusion serves as our case study because its complexity and
prevalence make it an ideal example for showcasing how our framework recon-
structs an attack's progression.

Our analysis begins by defining the Threat Model and Attack Scenario, which
outlines the adversary’s capabilities, the target environment, and the entry point
for exploitation. We then use the causal logs and the corresponding graph gen-
erated by our framework to trace the attack from initial compromise to final
impact.

The subsequent "Attack Explanation" uses causal diagrams to visualize these
event connections. This allows security analysts to pinpoint the critical steps,
such as privilege escalation or lateral movement, that enabled the attacker to
achieve their goals. This level of visibility into multi-process interactions and
complex pivot steps is difficult to achieve using conventional logging methods.

5.1 Backdoor - Command and Control (C&C) attack

Threat Model and Attack Scenarios :

As illustrated in Figure 8, a Command and Control (C&C) attack scenario
presumes that an adversary has already deployed a backdoor (often via malware
infection or a compromised account) on the target host. Once this backdoor
is active, it attempts to establish an outbound connection (e.g., reverse shell,
HTTP-based beacon, or custom TCP protocol) to an attacker-controlled C&C server. This model relies on the fact that most internal networks allow outbound traffic more freely than inbound traffic, making it easier for the attacker to maintain a hidden channel. There are some common objectives of a C&C attacker:

<figure><img src="image_10.png" /><figcaption>Fig. 8. Command and Control (C&C) - Threat Model</figcaption></figure>

- *Maintaining Persistent Access:* The backdoor re-connects or remains dormant until commanded by the attacker.

- *Network Reconnaissance and Lateral Movement:* The attacker uses the compromised host as a foothold to scan the environment, harvest credentials, or pivot to high-value targets.

- *Data Exfiltration or Further Exploits:* Once entrenched, the attacker exfiltrates sensitive data, deploys ransomware, or sets up additional persistence mechanisms.

A typical attack unfolds in stages:

1. *Backdoor Installation:* The attacker drops or installs a malicious agent on the target system (could be part of a phishing campaign, exploit kit, or via stolen credentials).

2. *Outbound Beaconing:* The backdoor attempts periodic communication to the remote C&C server, often disguising traffic as legitimate.

3. *Remote Commands and Lateral Movement:* The attacker issues commands, enumerates the network, and escalates privileges using the compromised system as a launch point.

4. *Data Exfiltration or Final Impact:* Sensitive data is transferred out, or further disruptive actions (like encrypting files or destroying logs) are triggered.

**Explaining the attack** Figure 9 depicts a Backdoor-based C&C attack in which a malicious agent (the “backdoor”) has already been placed on the target system. Two primary command-execution flows appear in parallel, shown with green arrows labeled (1a) to (4a) and yellow arrows labeled (1b) to (4b). A thick red arrow labeled (5) marks the final “exit” command from the attacker that ends the session. Below is a step-by-step explanation of these labeled transitions and what they signify in the causal logs:
<figure><img src="image_11.png" /><figcaption>Fig. 9. Command and Control (C&C) - Causality Graph</figcaption></figure>

- **Arrow (5):**

The attacker issues an `exit` or similar termination command from a remote server (e.g., `162.216.150.126`) to shut down the existing control channel. We can typically see a final `recv()` call in `proc_3882` (the main backdoor process), capturing the termination command. Immediately afterward, the process may invoke system calls like `close()` or `shutdown()` on the socket to end the session.

- **Arrow (4a) and Arrow (4b):**

Prior to exiting, the attacker transmits multiple commands via the C&C channel to direct the compromised host (such as enumerating processes, reading files, or scanning ports). In the logs, each new command is indicated by a `recv()` call in `proc_3882`, which reads data from the attacker's socket. Shortly afterward, a corresponding `send()` may appear if the backdoor acknowledges receipt or prepares a response.

- **Arrow (3a) and Arrow (3b):**

Upon receiving a new command, `proc_3882` clones a child process (e.g., `proc_3883` or `proc_3884`) to execute the attacker's request in isolation. This design allows the main backdoor to remain free to handle further commands without blocking. In the logs, these spawns appear as `clone()` (or `fork()`) calls within `proc_3882`. Immediately following that, the child process sets up any necessary file descriptors or pipes, linking back to its parent so that output can be routed back to the attacker.

- **Arrow (2a) and Arrow (2b):**

The newly spawned child processes carry out the attacker's requests (running binaries, collecting system data, or attempting privilege escalation). Causal
logs often show `execve()` for command execution, `open()` and `read()` for file operations, or `setuid()` if privilege changes are attempted. These calls follow shortly after the child is cloned, forming clear evidence of malicious behavior and highlighting which specific resources are accessed or modified.

### Arrow (1a) and Arrow (1b):

Once the child completes its assigned task, it transmits any output (e.g., command results, file contents) to control server `162.216.150.126` over the established network connection, captured in the logs as a final `send()` to the attacker's IP and port. This chain of events (command in, child execution, output returning) reveals precisely how reconnaissance or exfiltration occurs in real time.

By examining each arrow in reverse, from the "exit" command (`Arrow 5`) back through the child processes (`Arrows 2a/2b` and `3a/3b`) and the commands they executed (`Arrows 4a/4b`), analysts can reconstruct the entire life cycle of the C&C attack. This provides a transparent view of how the attacker maintained remote control: receiving commands, spawning helper processes, executing malicious actions, and returning data to the attacker's server. Armed with these causal insights, defenders gain the clarity needed to pinpoint when and how the system was compromised, block the offending IP addresses or processes, and implement better egress filters and host protections to mitigate future incidents.

## 6 Conclusion and Future Work

We presented a novel framework leveraging eBPF to generate causal logs, significantly enhancing the detection and analysis of Command and Control (C&C) backdoor activities. Our method explicitly traces causal relationships between system calls at the kernel level, improving the interpretability and traceability of complex threats compared to traditional SIEM-based techniques.

Despite these advances, several challenges remain. The fine-grained tracing introduces overhead, particularly in resource-limited environments or high-volume systems. Scaling causal logging for distributed and cloud-native architectures also poses significant complexity. Moreover, enriching causal logs by integrating diverse data sources (e.g., network traffic, application logs) could further enhance threat detection capabilities.

Future work will focus on optimizing performance by exploring lightweight tracing and hardware-assisted monitoring to minimize overhead; enhancing scalability through efficient aggregation of causal logs across distributed systems for a comprehensive global perspective; advancing detection via graph-based machine learning, such as Graph Neural Networks, to automatically identify suspicious patterns; and incorporating additional data sources, including network and user activity logs, to achieve a more holistic view and stronger threat correlation.

By addressing these challenges, we aim to refine and extend our causal logging framework into a comprehensive threat analysis solution. Ultimately, the
goal is to combine high-fidelity, causally rich data with scalable analytics and ma-
chine learning techniques, thereby enhancing the effectiveness of security moni-
toring systems and enabling more proactive defenses against sophisticated cyber
threats.

Acknowledgment

This work has been partially supported by the French National Research Agency under the France 2030 label (Superviz ANR-22-PECY-0008). The views reflected herein do not necessarily reflect the opinion of the French government.

References

1. Kobayashi, S., Fukuda, K., & Esaki, H. (2017, May). Mining causes of network events in log data with causal inference. In 2017 IFIP/IEEE Symposium on Integrated Network and Service Management (IM) (pp. 45-53). IEEE.

2. Lamport, L. (2019). Time, clocks, and the ordering of events in a distributed system. In Concurrency: the Works of Leslie Lamport (pp. 179-196).

3. Markakis, M., Youngmann, B., Gao, T., Zhang, Z., Shahout, R., Chen, P. B., ... & Cafarella, M. From Logs to Causal Inference: Diagnosing Large Systems.

4. Xosanavongsa, C., Totel, E., & Bettan, O. (2019, June). Discovering correlations: A formal definition of causal dependency among heterogeneous events. In 2019 IEEE European Symposium on Security and Privacy (EuroS&P) (pp. 340-355). IEEE.

5. Xosanavongsa, C. (2020). Heterogeneous Event Causal Dependency Definition for the Detection and Explanation of Multi-Step Attacks (Doctoral dissertation, CentraleSupélec).

6. D'ausbourg, B. (1994). Implementing secure dependencies over a network by designing a distributed security subsystem. In Computer Security—ESORICS 94: Third European Symposium on Research in Computer Security Brighton, United Kingdom, November 7–9, 1994 Proceedings 3 (pp. 247-266). Springer Berlin Heidelberg.

7. Fowler, J., & Zwaenepoel, W. (1990). Causal distributed breakpoints. In Proceedings of the Tenth International Conference on Distributed Computer Systems.

8. Schwarz, R., & Mattern, F. (1994). Detecting causal relationships in distributed computations: In search of the holy grail. Distributed computing, 7(3), 149-174.

9. Baldy, P., Dicky, H., Medina, R., Morvan, M., & Vilarem, J. F. (1994, May). Efficient reconstruction of the causal relationship in distributed systems. In Canada-France Conference on Parallel and Distributed Computing (pp. 101-113). Berlin, Heidelberg: Springer Berlin Heidelberg.