Skip to content

Configuration

Configure reactions within detection recipes.

Basic Structure

Reactions are configured as part of detection recipes:

yaml
- kind: recipe_identifier
  name: recipe_name
  enabled: true
  version: 1.0
  description: Description of detection
  breed: detection_type
  mechanism: detection_mechanism
  tactic: mitre_tactic
  technique: mitre_technique
  importance: severity_level

  # Detection criteria (varies by type)
  # ...

  # Reactions
  reactions:
    - format: js
      code: |
        function process(data) {
          // JavaScript code
        }
    - format: shell
      code: |
        #!/bin/bash
        # Shell script

Configuration Fields

format

Required. Specifies execution format.

Values:

  • js - JavaScript execution (recommended)
  • shell - Shell script execution

code

Required. Contains the reaction code.

JavaScript:

  • Must contain a process(data) function
  • Receives complete event data as parameter
  • Has access to all helper functions

Shell:

  • Can be any valid shell script
  • Event data in REACTION_DATA environment variable (JSON)

JavaScript Example

yaml
reactions:
  - format: js
    code: |
      function process(data) {
        Info("Event type: " + kind);
        Info("Recipe name: " + name);

        if (data.process) {
          Warn("Process: " + data.process.cmd);
        }

        let result = NetBlockIp();
        if (result === 0) {
          Info("Network blocked successfully");
        }
      }

Shell Example

yaml
reactions:
  - format: shell
    code: |
      #!/bin/bash

      PROCESS_CMD=$(echo "$REACTION_DATA" | jq -r '.process.cmd')
      FILE_PATH=$(echo "$REACTION_DATA" | jq -r '.file.file // "N/A"')

      echo "Process: $PROCESS_CMD"
      echo "File: $FILE_PATH"

      logger "Jibril reaction: $PROCESS_CMD accessed $FILE_PATH"

Detection Type Examples

File Access Reaction

yaml
- kind: passwd_usage
  name: passwd_usage
  enabled: true
  breed: file_access
  mechanism: execution
  tactic: persistence
  technique: account_manipulation
  importance: high

  file_actions:
    - execve
  file_actions_how: any

  bases:
    - dir: /etc
      base: passwd

  reactions:
    - format: js
      code: |
        function process(data) {
          Info("Password file accessed!");
          Info("Process: " + data.process.cmd);
          Info("User ID: " + data.process.uid);

          if (data.process.uid !== 0) {
            Warn("Non-root access - terminating");
            KillCurrent();
          }
        }

Process Execution Reaction

yaml
- kind: net_suspicious_tool_exec
  name: net_suspicious_tool_exec_critical
  enabled: true
  breed: file_access
  mechanism: execution
  tactic: discovery
  technique: network_service_discovery
  importance: critical

  file_actions:
    - execve
  file_actions_how: any

  bases:
    - base: nc

  reactions:
    - format: js
      code: |
        function process(data) {
          Error("REVERSE SHELL DETECTED!");
          Info("Command: " + data.process.cmd);

          if (data.base.background.ancestry) {
            Info("Process ancestry:");
            for (let i = 0; i < data.base.background.ancestry.length; i++) {
              Info("  " + data.base.background.ancestry[i].cmd);
            }
          }

          KillCurrent();
          NetBlockIp();
        }

Network Activity Reaction

yaml
- kind: malicious_network
  name: malicious_network_01
  enabled: true
  breed: remote_domains
  mechanism: network_peers
  tactic: command_and_control
  technique: application_layer_protocol
  importance: critical

  flow_actions:
    - ingress
    - egress
  flow_actions_how: any

  remote_domains_type: suffix
  remote_domains:
    - onion

  reactions:
    - format: js
      code: |
        function process(data) {
          Warn("Tor connection detected");

          if (data.background && data.background.flows) {
            let flows = data.background.flows;
            if (flows.protocols) {
              for (let protocol of flows.protocols) {
                if (protocol.pairs) {
                  for (let pair of protocol.pairs) {
                    if (pair.nodes && pair.nodes.remote) {
                      Info("Remote: " + pair.nodes.remote.address);
                    }
                  }
                }
              }
            }
          }

          let result = NetBlockDomain();
          if (result === 0) {
            Info("Tor domains blocked");
            let count = parseInt(DataGet("tor_blocks") || "0") + 1;
            DataSet("tor_blocks", String(count));
          }
        }

Multiple Reactions

Define multiple reactions per recipe to execute in parallel:

yaml
reactions:
  # Immediate logging
  - format: js
    code: |
      function process(data) {
        Error("CRITICAL: SSH config modified");
        Info("File: " + data.file.file);
      }

  # Network containment
  - format: js
    code: |
      function process(data) {
        NetBlockIp();
        Info("Network access blocked");
      }

  # Evidence collection
  - format: js
    code: |
      function process(data) {
        let dir = CreateTempDir("evidence-*");
        if (dir !== "") {
          WriteFile(dir + "/event.json", JSON.stringify(data, null, 2));
        }
      }

Error Handling

Implement proper error checking:

yaml
reactions:
  - format: js
    code: |
      function process(data) {
        try {
          let result = NetBlockIp();
          if (result !== 0) {
            Error("Failed to block IP: " + Errno());
          }

          let writeResult = WriteFile("/tmp/log.txt", "data");
          if (writeResult !== 0) {
            Error("Failed to write: " + Errno());
          }
        } catch (error) {
          Error("Reaction error: " + error.toString());
        }
      }

Testing

Start with disabled state for testing:

yaml
- kind: test_reaction
  name: my_test_reaction
  enabled: false  # Test first
  version: 1.0
  breed: file_access
  mechanism: file_access
  importance: low

  bases:
    - dir: /tmp/test
      base: trigger.txt
  file_actions:
    - unlink

  reactions:
    - format: js
      code: |
        function process(data) {
          Info("=== TEST REACTION ===");

          DataSet("test", "success");
          let value = DataGet("test");
          Info("Test: " + (value === "success" ? "PASS" : "FAIL"));

          DataDelete("test");
        }

Deployment Strategy

  1. Test Environment - Deploy with enabled: false
  2. Limited Scope - Start with specific patterns
  3. Monitoring - Watch logs for unexpected behavior
  4. Gradual Expansion - Slowly expand scope
  5. Production - Deploy fully tested reactions only