Login Followed By Tool Transfer

high Correlation T1078T1105 Initial Access, Command and Control

A session logs in and transfers a tool within the same five-minute window: the automated dropper chain of IoT and Linux botnets. Commands only exist after a login in Cowrie, so grouping by session gives the order without an ordered correlation (which the Splunk backend doesn't support). Buckets are fixed, so a chain that straddles a bucket boundary is missed; widen the timespan to trade precision for recall.

Tested

Fires on 4 attack events in match.jsonl (2 groups) and stays silent on 5 events in nomatch.jsonl. See the test events.

False positives

Sigma

title: Cowrie Successful Login
name: cowrie_login_success
id: e2755e10-e7f4-4ea1-b0be-1627e577ccbe
status: experimental
description: Base rule for the login-then-transfer correlation.
author: Estul Lachin
date: 2026-11-08
logsource:
  product: cowrie
detection:
  selection:
    eventid: cowrie.login.success
  condition: selection
level: informational
---
title: Cowrie Tool Transfer Command
name: cowrie_tool_transfer
id: eb6b0dea-d89b-4072-8fff-dc734e9aec17
status: experimental
description: Base rule for the login-then-transfer correlation. Download tools, /dev/tcp, or a hex-echo write.
author: Estul Lachin
date: 2026-11-08
logsource:
  product: cowrie
detection:
  command:
    eventid: cowrie.command.input
    input|re: '\b(wget|curl|tftp|ftpget|scp)\b|/dev/tcp/|echo\s+-[a-z]*e[a-z]*\s+["'']?(\\x[0-9a-fA-F]{2}){4}'
  download_event:
    eventid:
      - cowrie.session.file_download
      - cowrie.session.file_download.failed
  condition: command or download_event
level: informational
---
title: Login Followed By Tool Transfer
id: e0fd1b5d-fea0-406c-8f3e-2a527feea7b0
status: experimental
description: |
  A session logs in and transfers a tool within the same five-minute window: the automated dropper
  chain of IoT and Linux botnets. Commands only exist after a login in Cowrie, so grouping by session
  gives the order without an ordered correlation (which the Splunk backend doesn't support). Buckets
  are fixed, so a chain that straddles a bucket boundary is missed; widen the timespan to trade
  precision for recall.
references:
  - https://attack.mitre.org/techniques/T1078/
  - https://attack.mitre.org/techniques/T1105/
author: Estul Lachin
date: 2026-11-08
tags:
  - attack.initial-access
  - attack.t1078
  - attack.command-and-control
  - attack.t1105
correlation:
  type: temporal
  rules:
    - cowrie_login_success
    - cowrie_tool_transfer
  group-by:
    - session
  timespan: 5m
falsepositives:
  - Administrators who log in and immediately download software
level: high

rules/login_then_tool_transfer.yml

Splunk SPL

``` Login Followed By Tool Transfer. Generated from rules/login_then_tool_transfer.yml by tools/build.py; do not edit. ```
| multisearch
[ search index=honeypot sourcetype=cowrie:json eventid="cowrie.login.success" | eval event_type="cowrie_login_success" ]
[ search index=honeypot sourcetype=cowrie:json 
| rex field=input "(?<inputMatch>\\b(wget|curl|tftp|ftpget|scp)\\b|/dev/tcp/|echo\\s+-[a-z]*e[a-z]*\\s+[\"']?(\\\\x[0-9a-fA-F]{2}){4})"
| eval inputCondition=if(isnotnull(inputMatch), "true", "false")
| search (eventid="cowrie.command.input" inputCondition="true") OR eventid IN ("cowrie.session.file_download", "cowrie.session.file_download.failed") | eval event_type="cowrie_tool_transfer" ]
| bin _time span=5m
| stats dc(event_type) as event_type_count by _time session
| search event_type_count >= 2

Verified in Splunk against the test events. dist/spl/login_then_tool_transfer.spl

Microsoft Sentinel KQL

// Login Followed By Tool Transfer. Generated from rules/login_then_tool_transfer.yml by tools/build.py; do not edit.
union
    (Cowrie_CL | where eventid =~ "cowrie.login.success" | extend event_type = "cowrie_login_success"),
    (Cowrie_CL | where (eventid =~ "cowrie.command.input" and input matches regex "\\b(wget|curl|tftp|ftpget|scp)\\b|/dev/tcp/|echo\\s+-[a-z]*e[a-z]*\\s+[\"']?(\\\\x[0-9a-fA-F]{2}){4}") or (eventid in~ ("cowrie.session.file_download", "cowrie.session.file_download.failed")) | extend event_type = "cowrie_tool_transfer")
| summarize event_type_count = dcount(event_type) by bin(TimeGenerated, 5m), session
| where event_type_count >= 2

For a custom table Cowrie_CL with Cowrie's field names. Converted, not yet run in Sentinel. dist/kql/login_then_tool_transfer.kql