Appearance
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 scriptConfiguration 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_DATAenvironment 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
- Test Environment - Deploy with
enabled: false - Limited Scope - Start with specific patterns
- Monitoring - Watch logs for unexpected behavior
- Gradual Expansion - Slowly expand scope
- Production - Deploy fully tested reactions only