%GoCortexIO Snippets

Cortex XSIAM

| 5 minutes to implement | ~4 min read

#Scenario

Seasons don't fear the reaper, but your Linux endpoints definitely should when RingReaper weaponizes io_uring to silently sever its system call hooks.

markdown
> Make sure you're comfortable with the results in your own environment before using this more widely.

# Snippet - Seasons don't fear the reaper, but your Linux endpoints definitely should when RingReaper weaponises io_uring to silently sever its system call hooks.

## Scenario

This XQL detection rule hunts for evasive Linux post-exploitation activity by correlating three distinct events within a single causality chain:  

### Native API Abuse: 
Activity from an `io_uring SQPOLL` kernel thread.  
### Anomalous Recon: 
Execution of discovery commands (like `whoami` or `id`) operating without a parent shell.  
### Sensitive Data Access: 
Reads of critical system files (such as `/etc/shadow` or SSH keys).  

In short, it flags when a suspicious kernel thread bypasses normal shell execution to quietly map out the system and steal credentials.
xql
// Make sure you're comfortable with the results in your own environment before using this more widely.
/ =============================================================================
// GoCortex | RingReaper / io_uring Abuse (T1106) - generic behavioural hunt
// -----------------------------------------------------------------------------
// Detects the TECHNIQUE
// via co-occurrence under one causality chain on one Linux host:
//   A) io_uring SQPOLL kernel thread          iou-sqp-<pid>        (Tier 1)
//   B) recon binaries with NO shell ancestor  whoami/id/hostname/uname (Tier 2)
//   C) sensitive-file reads                    passwd/shadow/net/tcp/utmp/ssh keys
// Run as: XQL search / scheduled hunt | Range: last 7-14 days
// ATT&CK: T1106 Native API; T1033/T1082/T1087 discovery; T1003.008/T1005/T1552.004
// =============================================================================

dataset = xdr_data

// --- 1. Scope: Linux only (io_uring is Linux), process + file telemetry --------
| filter agent_os_type = ENUM.AGENT_OS_LINUX
| filter event_type = ENUM.PROCESS or event_type = ENUM.FILE

// --- 2. Normalise names for matching -----------------------------------------
| alter
    proc   = lowercase(action_process_image_name),
    parent = lowercase(actor_process_image_name),
    cause  = lowercase(causality_actor_process_image_name)

// --- 3. Generic behavioural signals ------------------------------------------
| alter
    // A) io_uring SQPOLL kernel thread (privileged ring poller = strong signal)
    is_iouring_thread = if(action_process_image_name contains "iou-sqp-"
                        or actor_process_image_name  contains "iou-sqp-", 1, 0),
    // recon utilities
    is_recon_bin = if(proc in ("whoami", "id", "hostname", "uname"), 1, 0),
    // is a shell anywhere in the immediate lineage?
    parent_is_shell = if(parent in ("sh","bash","dash","zsh","ash","ksh","fish","busybox")
                      or cause  in ("sh","bash","dash","zsh","ash","ksh","fish","busybox"), 1, 0),
    // C) sensitive files RingReaper harvests via the ring
    is_sensitive_file = if(action_file_path in ("/etc/passwd","/etc/shadow",
                              "/proc/net/tcp","/proc/net/tcp6","/run/utmp","/var/run/utmp")
                        or action_file_path contains "/.ssh/id_", 1, 0)

// B) recon spawned programmatically (fork+execve) with no shell in lineage
| alter is_shellless_recon = if(is_recon_bin = 1 and parent_is_shell = 0, 1, 0)

// --- 4. Keep only events that carry one of the three signals -------------------
| filter is_iouring_thread = 1 or is_shellless_recon = 1 or is_sensitive_file = 1

// --- 5. Correlate per host + causality chain (ties the ring thread, the recon --
//        children and the file reads back to the SAME originating process) ------
| comp
    sum(is_iouring_thread)   as iouring_events,
    sum(is_shellless_recon)  as shellless_recon_events,
    sum(is_sensitive_file)   as sensitive_file_events,
    count_distinct(if(is_shellless_recon = 1, proc, null))            as distinct_recon_bins,
    count_distinct(if(is_sensitive_file = 1, action_file_path, null)) as distinct_sensitive_files,
    values(if(is_iouring_thread  = 1, action_process_image_name, null))     as iouring_threads,
    values(if(is_shellless_recon = 1, action_process_image_name, null))     as recon_procs,
    values(if(is_sensitive_file  = 1, action_file_path, null))              as sensitive_files,
    values(action_process_image_command_line)          as action_cmdlines,
    values(causality_actor_process_command_line)       as causality_cmdlines,
    min(_time) as first_seen,
    max(_time) as last_seen
  by agent_hostname, causality_actor_process_image_name

// --- 6. Require the io_uring OR shell-less-recon anchor (drop file-only noise) --
| filter iouring_events > 0 or shellless_recon_events > 0

// --- 7. Fidelity score: the technique = signals TOGETHER, not in isolation -----
| alter tier_count = add(add(
      if(iouring_events > 0, 1, 0),
      if(shellless_recon_events > 0, 1, 0)),
      if(sensitive_file_events > 0, 1, 0))
| alter risk_score = add(add(add(
      if(iouring_events > 0 and shellless_recon_events > 0, 55, 0),
      if(iouring_events > 0, 25, 0)),
      if(distinct_recon_bins >= 2, 10, 0)),
      if(sensitive_file_events > 0, 10, 0))

// --- 8. Triage output ---------------------------------------------------------
| fields
    risk_score, tier_count,
    agent_hostname, causality_actor_process_image_name,
    iouring_events, iouring_threads,
    shellless_recon_events, distinct_recon_bins, recon_procs,
    sensitive_file_events, distinct_sensitive_files, sensitive_files,
    action_cmdlines, causality_cmdlines,
    first_seen, last_seen
| sort desc risk_score, desc tier_count, desc iouring_events