YARA Rules
Rule 1: Nezha Core Malware Detection
yara
rule Nezha_Malware_Core
{
meta:
description = "Detects Nezha RAT/Botnet core components"
author = "SOC Threat Intelligence"
date = "2024-12-25"
severity = "critical"
malware_family = "Nezha"
target_platform = "Linux, Windows"
reference = "Chinese C2 Infrastructure Campaign Dec 2024"
strings:
// Chinese language strings in Simplified Chinese
$chinese1 = "nezha" ascii wide nocase
$chinese2 = {E5 93 AA E5 90 92} // 哪吒 (Nezha in UTF-8)
$chinese3 = "配置文件" wide // Configuration file
$chinese4 = "命令执行" wide // Command execution
$chinese5 = "连接服务器" wide // Connect to server
$chinese6 = "心跳包" wide // Heartbeat packet
// Network communication patterns
$net1 = "ws://" ascii
$net2 = "wss://" ascii
$net3 = "/api/v1/ws/agent" ascii
$net4 = "User-Agent: nezha-agent" ascii
$net5 = "X-Nezha-Token" ascii
// Installation and persistence
$install1 = "/tmp/.nezha" ascii
$install2 = "/var/tmp/nezha" ascii
$install3 = "nezha.service" ascii
$install4 = "nezha-agent" ascii
$install5 = "/usr/local/bin/nezha" ascii
// C2 communication
$c2_1 = "heartbeat" ascii
$c2_2 = "execute_command" ascii
$c2_3 = "report_status" ascii
$c2_4 = "agent_secret" ascii
// Crypto operations
$crypto1 = "AES-256-GCM" ascii
$crypto2 = "cipher.NewGCM" ascii
$crypto3 = "crypto/aes" ascii
condition:
uint32(0) == 0x464c457f or uint16(0) == 0x5a4d and
(
(2 of ($chinese*) and 2 of ($net*)) or
(3 of ($install*) and any of ($c2_*)) or
(any of ($chinese*) and 2 of ($install*) and 1 of ($crypto*))
)
}
Rule 2: Nezha Installation Scripts
yara
rule Nezha_Installation_Scripts
{
meta:
description = "Detects Nezha installation and deployment scripts"
author = "SOC Threat Intelligence"
severity = "high"
malware_family = "Nezha"
strings:
// Script patterns
$script1 = "#!/bin/bash" ascii
$script2 = "#!/bin/sh" ascii
// Installation commands
$install1 = "wget" ascii
$install2 = "curl -fsSL" ascii
$install3 = "chmod +x" ascii
$install4 = "systemctl enable" ascii
$install5 = "systemctl start" ascii
// Nezha specific
$nezha1 = "nezha-agent" ascii
$nezha2 = "nezha.service" ascii
$nezha3 = "/opt/nezha" ascii
$nezha4 = "NEZHA_" ascii
// Chinese comments in scripts
$comment1 = "# 安装" wide ascii // Install
$comment2 = "# 配置" wide ascii // Configuration
$comment3 = "# 启动" wide ascii // Start
$comment4 = "# 守护进程" wide ascii // Daemon
// Alibaba Cloud indicators
$cloud1 = "aliyuncs.com" ascii
$cloud2 = "alibabacloud.com" ascii
$cloud3 = "oss-ap-northeast-1" ascii
// Service configuration
$service1 = "[Unit]" ascii
$service2 = "ExecStart=" ascii
$service3 = "Restart=always" ascii
$service4 = "RestartSec=" ascii
condition:
(any of ($script*)) and
(
(2 of ($install*) and 2 of ($nezha*)) or
(any of ($nezha*) and any of ($comment*)) or
(2 of ($nezha*) and any of ($cloud*)) or
(2 of ($service*) and any of ($nezha*))
)
}
Rule 3: Nezha Network Communication Module
yara
rule Nezha_Network_Communication
{
meta:
description = "Detects Nezha C2 communication components"
author = "SOC Threat Intelligence"
severity = "critical"
strings:
// WebSocket communication
$ws1 = "gorilla/websocket" ascii
$ws2 = "websocket.Upgrader" ascii
$ws3 = "websocket.Dial" ascii
$ws4 = "Sec-WebSocket-Key" ascii
// Protocol strings
$proto1 = "agent_hello" ascii
$proto2 = "server_hello" ascii
$proto3 = "ping_pong" ascii
$proto4 = "command_request" ascii
$proto5 = "command_response" ascii
// JSON structures
$json1 = "\"agent_id\":" ascii
$json2 = "\"secret\":" ascii
$json3 = "\"timestamp\":" ascii
$json4 = "\"command\":" ascii
$json5 = "\"result\":" ascii
// Alibaba Cloud endpoints
$cloud1 = ".aliyuncs.com" ascii
$cloud2 = "oss-ap-northeast" ascii
$cloud3 = "47.74." ascii
$cloud4 = "47.245." ascii
$cloud5 = "161.117." ascii
// Chinese error messages
$error1 = "连接失败" wide // Connection failed
$error2 = "认证失败" wide // Authentication failed
$error3 = "超时" wide // Timeout
condition:
(
(2 of ($ws*) and 2 of ($proto*)) or
(3 of ($json*) and any of ($cloud*)) or
(any of ($proto*) and any of ($cloud*) and any of ($error*))
)
}
Rule 4: Nezha Persistence Mechanisms
yara
rule Nezha_Persistence
{
meta:
description = "Detects Nezha persistence techniques"
author = "SOC Threat Intelligence"
severity = "high"
strings:
// Systemd service
$systemd1 = "[Unit]" ascii
$systemd2 = "Description=Nezha" ascii nocase
$systemd3 = "[Service]" ascii
$systemd4 = "Type=simple" ascii
$systemd5 = "ExecStart=/usr/local/bin/nezha-agent" ascii
$systemd6 = "Restart=always" ascii
$systemd7 = "RestartSec=5" ascii
// Cron job patterns
$cron1 = "*/5 * * * *" ascii
$cron2 = "nezha-agent" ascii
$cron3 = "/tmp/.nezha" ascii
// Init script
$init1 = "#!/bin/sh" ascii
$init2 = "# chkconfig:" ascii
$init3 = "start-stop-daemon" ascii
// Binary locations
$path1 = "/usr/local/bin/nezha" ascii
$path2 = "/opt/nezha/agent" ascii
$path3 = "/var/lib/nezha" ascii
$path4 = "$HOME/.nezha" ascii
// Chinese descriptions
$desc1 = "监控代理" wide // Monitoring agent
$desc2 = "守护进程" wide // Daemon process
$desc3 = "自动启动" wide // Auto start
condition:
(
(4 of ($systemd*)) or
(2 of ($cron*) and any of ($path*)) or
(2 of ($init*) and any of ($path*)) or
(2 of ($path*) and any of ($desc*))
)
}
Rule 5: Nezha Anti-Analysis and Obfuscation
yara
rule Nezha_Anti_Analysis
{
meta:
description = "Detects anti-analysis techniques in Nezha malware"
author = "SOC Threat Intelligence"
severity = "medium"
strings:
// VM/Sandbox detection
$vm1 = "/proc/self/cgroup" ascii
$vm2 = "docker" ascii
$vm3 = "lxc" ascii
$vm4 = "/sys/class/dmi" ascii
$vm5 = "vmware" nocase ascii
$vm6 = "virtualbox" nocase ascii
// Debugger detection
$debug1 = "ptrace" ascii
$debug2 = "TracerPid" ascii
$debug3 = "/proc/self/status" ascii
// Sleep/timing checks
$timing1 = "time.Sleep" ascii
$timing2 = "time.Now" ascii
$timing3 = "time.Since" ascii
// Process checks
$proc1 = "ps aux" ascii
$proc2 = "/proc/" ascii
$proc3 = "wireshark" ascii
$proc4 = "tcpdump" ascii
$proc5 = "strace" ascii
// String obfuscation
$obf1 = "base64.StdEncoding.DecodeString" ascii
$obf2 = "hex.DecodeString" ascii
$obf3 = "strings.Replace" ascii
// Environment checks
$env1 = "os.Getenv" ascii
$env2 = "HOSTNAME" ascii
$env3 = "USER" ascii
condition:
(
(2 of ($vm*) and any of ($debug*)) or
(2 of ($timing*) and 2 of ($proc*)) or
(2 of ($obf*) and any of ($env*))
)
}
Rule 6: Nezha Credential Harvesting Module
yara
rule Nezha_Credential_Theft
{
meta:
description = "Detects credential harvesting components"
author = "SOC Threat Intelligence"
severity = "critical"
strings:
// SSH key theft
$ssh1 = ".ssh/id_rsa" ascii
$ssh2 = ".ssh/authorized_keys" ascii
$ssh3 = "known_hosts" ascii
// Password files
$pass1 = "/etc/shadow" ascii
$pass2 = "/etc/passwd" ascii
$pass3 = ".mysql_history" ascii
$pass4 = ".bash_history" ascii
// Browser credential locations
$browser1 = "Login Data" ascii
$browser2 = "Cookies" ascii
$browser3 = ".mozilla/firefox" ascii
$browser4 = "Google/Chrome" ascii
// Config files with credentials
$config1 = "database.yml" ascii
$config2 = ".env" ascii
$config3 = "config.json" ascii
$config4 = "credentials" ascii
// Regex patterns for credentials
$regex1 = "password\\s*[:=]" nocase ascii
$regex2 = "api[_-]?key" nocase ascii
$regex3 = "secret[_-]?key" nocase ascii
// Chinese strings
$cn1 = "密码" wide // Password
$cn2 = "凭据" wide // Credentials
$cn3 = "密钥" wide // Secret key
condition:
(
(2 of ($ssh*)) or
(2 of ($pass*)) or
(2 of ($browser*)) or
(2 of ($config*) and any of ($regex*)) or
(any of ($cn*) and 2 of them)
)
}
Sigma Rules
Sigma Rule 1: Nezha Network Communication to Alibaba Cloud
yaml
title: Nezha Malware Communication to Alibaba Cloud Japan Infrastructure
id: a7c9e3f1-4b8d-4f2a-9e5c-7d6b8a2f9e3c
status: experimental
description: Detects network connections to Alibaba Cloud infrastructure in Japan associated with Nezha C2 servers
author: SOC Threat Intelligence Team
date: 2024/12/25
modified: 2024/12/25
tags:
- attack.command_and_control
- attack.t1071.001
- attack.t1573
logsource:
category: firewall
product: any
detection:
selection_alibaba_ip:
destination.ip|startswith:
- '47.74.'
- '47.245.'
- '161.117.'
- '47.91.'
- '47.243.'
selection_alibaba_domain:
destination.domain|contains:
- '.aliyuncs.com'
- '.alibabacloud.com'
- 'oss-ap-northeast-1'
- 'ecs.ap-northeast-1'
selection_websocket:
destination.port:
- 443
- 8080
- 8443
- 5555
protocol: 'tcp'
selection_suspicious_pattern:
destination.url|contains:
- '/api/v1/ws/agent'
- '/ws/agent'
- '/agent/connect'
- '/nezha'
condition: (selection_alibaba_ip or selection_alibaba_domain) and selection_websocket or selection_suspicious_pattern
fields:
- source.ip
- destination.ip
- destination.domain
- destination.port
- destination.url
- bytes.sent
- bytes.received
- timestamp
falsepositives:
- Legitimate use of Alibaba Cloud services
- Organizations with business operations in Japan using Alibaba infrastructure
level: high
Sigma Rule 2: Nezha Installation and Persistence
yaml
title: Nezha Malware Installation and Persistence Mechanism
id: b8d4f2e7-5c9a-4e3b-8f6d-9a1c7b3e5f4a
status: experimental
description: Detects installation activities and persistence mechanisms associated with Nezha malware
author: SOC Threat Intelligence Team
date: 2024/12/25
tags:
- attack.persistence
- attack.t1543.002
- attack.t1053.003
- attack.execution
- attack.t1059.004
logsource:
category: process_creation
product: linux
detection:
selection_download:
CommandLine|contains:
- 'wget'
- 'curl -fsSL'
- 'curl -sL'
CommandLine|contains:
- 'nezha'
- '.aliyuncs.com'
- 'oss-ap-northeast'
selection_installation:
CommandLine|contains:
- 'chmod +x'
- 'mv'
- 'cp'
CommandLine|contains:
- 'nezha-agent'
- '/usr/local/bin/nezha'
- '/opt/nezha'
- '/tmp/.nezha'
selection_persistence_systemd:
CommandLine|contains:
- 'systemctl enable'
- 'systemctl start'
- 'systemctl daemon-reload'
CommandLine|contains:
- 'nezha'
selection_persistence_cron:
CommandLine|contains:
- 'crontab -e'
- 'echo'
CommandLine|contains:
- 'nezha-agent'
- '*/5 * * * *'
selection_service_creation:
Image|endswith:
- '/systemctl'
- '/service'
CommandLine|contains: 'nezha.service'
timeframe: 5m
condition: 1 of selection_*
fields:
- user.name
- process.command_line
- process.parent.name
- process.working_directory
- host.name
falsepositives:
- Legitimate system monitoring agent installations
- Authorized deployment of Nezha monitoring software
level: high
Sigma Rule 3: Nezha Suspicious Script Execution
yaml
title: Nezha Installation Script with Chinese Language Elements
id: c9e5f3a2-6d8b-4f7e-9a3c-8b1d7f4e2a9b
status: experimental
description: Detects execution of scripts containing Chinese language elements associated with Nezha malware installation
author: SOC Threat Intelligence Team
date: 2024/12/25
tags:
- attack.execution
- attack.t1059.004
- attack.defense_evasion
- attack.t1027
logsource:
category: process_creation
product: linux
detection:
selection_shell:
Image|endswith:
- '/bash'
- '/sh'
- '/dash'
selection_script_location:
CommandLine|contains:
- '/tmp/'
- '/var/tmp/'
- '/dev/shm/'
selection_chinese_comments:
CommandLine|contains:
- '安装' # Install
- '配置' # Configuration
- '启动' # Start
- '哪吒' # Nezha
- '守护' # Daemon
selection_nezha_specific:
CommandLine|contains:
- 'nezha-agent'
- 'nezha.service'
- 'NEZHA_'
selection_download_execute:
CommandLine|contains:
- 'wget'
- 'curl'
CommandLine|contains:
- '| bash'
- '| sh'
condition: selection_shell and (selection_script_location or selection_chinese_comments or (selection_nezha_specific and selection_download_execute))
fields:
- process.command_line
- process.parent.command_line
- user.name
- process.working_directory
falsepositives:
- Legitimate scripts with Chinese documentation
- System administrators in Chinese-speaking regions
level: high
Sigma Rule 4: Nezha WebSocket Connection Pattern
yaml
title: Nezha WebSocket C2 Communication Pattern
id: d7f4e8a3-7c9b-4e2d-9f5a-8c6d7b3e4f2a
status: experimental
description: Detects WebSocket connections consistent with Nezha C2 communication patterns
author: SOC Threat Intelligence Team
date: 2024/12/25
tags:
- attack.command_and_control
- attack.t1071.001
- attack.t1573.002
logsource:
category: proxy
product: any
detection:
selection_websocket:
http.request.method: 'GET'
http.request.headers|contains:
- 'Upgrade: websocket'
- 'Connection: Upgrade'
selection_nezha_pattern:
url.path|contains:
- '/api/v1/ws/agent'
- '/ws/agent'
- '/agent/ws'
selection_user_agent:
http.user_agent|contains:
- 'nezha-agent'
- 'Go-http-client'
- 'websocket'
selection_headers:
http.request.headers|contains:
- 'X-Nezha-Token'
- 'X-Agent-Secret'
- 'X-Agent-ID'
selection_destination:
destination.domain|contains:
- 'aliyuncs.com'
- 'alibabacloud.com'
destination.geo.country: 'JP'
condition: selection_websocket and (selection_nezha_pattern or selection_headers or (selection_user_agent and selection_destination))
fields:
- source.ip
- destination.ip
- destination.domain
- url.full
- http.user_agent
- http.request.headers
falsepositives:
- Legitimate WebSocket applications
- Monitoring and management tools
level: high
Sigma Rule 5: Nezha Credential Harvesting Activity
yaml
title: Nezha Credential and Configuration File Access
id: e8f5a4b2-8d9c-4f3e-9b6a-7c5d8e4f3a2b
status: experimental
description: Detects file access patterns consistent with Nezha credential harvesting operations
author: SOC Threat Intelligence Team
date: 2024/12/25
tags:
- attack.credential_access
- attack.t1552.001
- attack.t1552.004
- attack.collection
- attack.t1005
logsource:
category: file_access
product: linux
detection:
selection_ssh_keys:
file.path|contains:
- '/.ssh/id_rsa'
- '/.ssh/id_dsa'
- '/.ssh/id_ecdsa'
- '/.ssh/authorized_keys'
- '/.ssh/known_hosts'
selection_shadow:
file.path:
- '/etc/shadow'
- '/etc/gshadow'
selection_history:
file.path|endswith:
- '.bash_history'
- '.zsh_history'
- '.mysql_history'
- '.psql_history'
selection_config:
file.path|contains:
- 'database.yml'
- 'credentials'
- '.env'
- 'config.json'
file.path|contains:
- 'password'
- 'secret'
- 'api_key'
selection_process:
process.name|contains:
- 'nezha'
- 'agent'
process.executable|contains:
- '/tmp/'
- '/var/tmp/'
- '.nezha'
timeframe: 10m
condition: 1 of selection_ssh_keys or selection_shadow or (2 of selection_history or selection_config) and selection_process
fields:
- file.path
- process.name
- process.command_line
- user.name
- host.name
falsepositives:
- Legitimate system administration activities
- Backup operations
- Configuration management tools
level: high
Sigma Rule 6: Nezha Process Injection and Memory Manipulation
yaml
title: Nezha Process Injection and Memory Manipulation
id: f9a6b5c3-7e8d-4f2a-9c5b-8d7e6f3a4b2c
status: experimental
description: Detects process injection techniques used by Nezha malware
author: SOC Threat Intelligence Team
date: 2024/12/25
tags:
- attack.privilege_escalation
- attack.t1055
- attack.defense_evasion
- attack.t1055.001
logsource:
category: process_creation
product: linux
detection:
selection_ptrace:
CommandLine|contains:
- 'ptrace'
- 'PTRACE_ATTACH'
- 'PTRACE_POKETEXT'
selection_proc_mem:
file.path|contains:
- '/proc/'
- '/mem'
selection_ld_preload:
CommandLine|contains:
- 'LD_PRELOAD'
- 'LD_LIBRARY_PATH'
selection_suspicious_parent:
process.parent.name|contains:
- 'nezha'
- 'agent'
process.parent.executable|contains:
- '/tmp/'
- '/var/tmp/'
selection_memory_access:
syscall:
- 'ptrace'
- 'process_vm_readv'
- 'process_vm_writev'
condition: (selection_ptrace or selection_proc_mem or selection_ld_preload) and selection_suspicious_parent or selection_memory_access
fields:
- process.name
- process.parent.name
- process.command_line
- syscall
- user.name
falsepositives:
- Debuggers and development tools
- System monitoring software
level: medium
```
## Snort/Suricata Rules
### Rule 1: Nezha WebSocket C2 Communication
```
alert tcp $HOME_NET any -> $EXTERNAL_NET [443,8080,8443,5555] (
msg:"MALWARE Nezha WebSocket C2 Communication";
flow:established,to_server;
content:"GET"; http_method;
content:"Upgrade|3a| websocket"; http_header;
content:"/api/v1/ws/agent"; http_uri;
content:"User-Agent|3a|"; http_header;
pcre:"/User-Agent\x3a\s+(nezha-agent|Go-http-client)/i";
threshold:type limit, track by_src, count 1, seconds 300;
classtype:trojan-activity;
sid:6000001;
rev:1;
metadata:
malware_family nezha,
target multi_platform,
severity critical;
)
```
### Rule 2: Nezha Heartbeat Pattern
```
alert tcp $HOME_NET any -> $EXTERNAL_NET any (
msg:"MALWARE Nezha Heartbeat Communication Pattern";
flow:established,to_server;
content:"|00 00 00|"; depth:3;
content:"heartbeat"; distance:0;
content:"agent_id"; distance:0;
content:"timestamp"; distance:0;
dsize:<512;
threshold:type both, track by_src, count 3, seconds 300;
classtype:trojan-activity;
sid:6000002;
rev:1;
metadata:
malware_family nezha,
c2_pattern heartbeat;
)
```
### Rule 3: Nezha to Alibaba Cloud Infrastructure
```
alert tcp $HOME_NET any -> [47.74.0.0/16,47.245.0.0/16,161.117.0.0/16] 443 (
msg:"MALWARE Nezha Connection to Alibaba Cloud Japan C2";
flow:established,to_server;
ssl_state:client_hello;
content:"|16 03|"; depth:2;
threshold:type threshold, track by_src, count 5, seconds 60;
classtype:trojan-activity;
sid:6000003;
rev:1;
metadata:
malware_family nezha,
c2_location alibaba_cloud_japan,
severity high;
)
```
### Rule 4: Nezha Installation Script Download
```
alert http $EXTERNAL_NET any -> $HOME_NET any (
msg:"MALWARE Nezha Installation Script Download";
flow:established,to_client;
file_data;
content:"#!/bin/"; depth:8;
content:"nezha-agent"; distance:0;
content:"systemctl"; distance:0;
content:"aliyuncs.com"; distance:0;
classtype:trojan-activity;
sid:6000004;
rev:1;
metadata:
malware_family nezha,
delivery_method script;
)
```
### Rule 5: Nezha DNS Tunneling
```
alert dns $HOME_NET any -> any 53 (
msg:"MALWARE Nezha Potential DNS Tunneling";
content:"|01 00 00 01|";
content:"|00 00 10 00 01|";
pcre:"/[a-z0-9]{32,}/i";
byte_extract:1,12,query_len;
byte_test:1,>,50,12;
threshold:type both, track by_src, count 10, seconds 60;
classtype:policy-violation;
sid:6000005;
rev:1;
metadata:
malware_family nezha,
exfiltration_method dns_tunnel;
)
```
### Rule 6: Nezha Command Execution Response
```
alert tcp $HOME_NET any -> $EXTERNAL_NET any (
msg:"MALWARE Nezha Command Execution Response";
flow:established,to_server;
content:"command_response"; offset:0; depth:100;
content:"\"status\""; distance:0;
content:"\"output\""; distance:0;
content:"\"error\""; distance:0;
dsize:>512;
classtype:trojan-activity;
sid:6000006;
rev:1;
metadata:
malware_family nezha,
activity command_execution;
)
```
### Rule 7: Nezha Agent Registration
```
alert tcp $HOME_NET any -> $EXTERNAL_NET [443,8080] (
msg:"MALWARE Nezha Agent Registration Attempt";
flow:established,to_server;
content:"POST"; http_method;
content:"agent_hello"; http_client_body;
content:"secret"; http_client_body;
content:"version"; http_client_body;
content:"hostname"; http_client_body;
classtype:trojan-activity;
sid:6000007;
rev:1;
metadata:
malware_family nezha,
activity agent_registration;
)
```
### Rule 8: Nezha Encrypted Data Exfiltration
```
alert tcp $HOME_NET any -> $EXTERNAL_NET 443 (
msg:"MALWARE Nezha Large Encrypted Data Exfiltration";
flow:established,to_server;
dsize:>10240;
content:!"Host|3a| www.google.com"; http_header;
content:!"Host|3a| www.microsoft.com"; http_header;
byte_test:4,>,1048576,0,relative;
threshold:type threshold, track by_src, count 5, seconds 300;
classtype:policy-violation;
sid:6000008;
rev:1;
metadata:
malware_family nezha,
activity data_exfiltration;
)
Splunk Detection Queries
Query 1: Nezha Alibaba Cloud C2 Communication
spl
index=firewall OR index=proxy
(dest_ip="47.74.*" OR dest_ip="47.245.*" OR dest_ip="161.117.*"
OR dest_domain="*.aliyuncs.com" OR dest_domain="*.alibabacloud.com"
OR dest_domain="*oss-ap-northeast-1*")
dest_port IN (443, 8080, 8443, 5555)
| eval is_japan_cloud=if(match(dest_domain, "ap-northeast-1") OR match(dest_ip, "^(47\.74\.|47\.245\.|161\.117\.)"), 1, 0)
| eval suspicious_path=if(match(url, "(/ws/agent|/api/v1/ws|/nezha)"), 1, 0)
| where is_japan_cloud=1 OR suspicious_path=1
| stats count,
dc(dest_ip) as unique_destinations,
sum(bytes_out) as total_bytes_sent,
values(dest_domain) as domains,
values(url) as urls,
values(user_agent) as user_agents,
earliest(_time) as first_seen,
latest(_time) as last_seen
by src_ip, dest_port
| eval duration_hours=round((last_seen-first_seen)/3600, 2)
| eval data_sent_mb=round(total_bytes_sent/1048576, 2)
| where count > 5 OR data_sent_mb > 10
| eval severity=case(
count > 50 AND data_sent_mb > 100, "critical",
count > 20 OR data_sent_mb > 50, "high",
"medium")
| eval threat_family="Nezha"
| eval c2_location="Alibaba Cloud Japan"
| table first_seen, src_ip, dest_port, domains, count, unique_destinations,
data_sent_mb, duration_hours, severity, threat_family
| sort -severity, -count
Query 2: Nezha Installation Detection
spl
index=linux sourcetype=linux_audit OR sourcetype=bash_history OR sourcetype=syslog
(
(CommandLine="*wget*" OR CommandLine="*curl*")
(CommandLine="*nezha*" OR CommandLine="*aliyuncs.com*" OR CommandLine="*oss-ap-northeast*")
)
OR (CommandLine="*chmod +x*" CommandLine="*nezha*")
OR (CommandLine="*systemctl*" CommandLine="*nezha*")
OR (CommandLine="*crontab*" CommandLine="*nezha*")
| rex field=CommandLine "(?<download_url>https?://[^\s]+)"
| eval action=case(
match(CommandLine, "wget|curl"), "Download",
match(CommandLine, "chmod"), "Execute_Permission",
match(CommandLine, "systemctl enable|systemctl start"), "Persistence_Systemd",
match(CommandLine, "crontab"), "Persistence_Cron",
true(), "Other")
| transaction host maxspan=10m
| where mvcount(action) >= 2
| stats count,
values(CommandLine) as commands,
values(action) as actions,
values(download_url) as download_urls,
values(User) as users,
min(_time) as start_time,
max(_time) as end_time
by host
| eval installation_duration_min=round((end_time-start_time)/60, 2)
| eval threat_indicators=mvappend(
if(match(commands, "aliyuncs\.com"), "Alibaba Cloud Source", null()),
if(match(commands, "systemctl.*nezha"), "Systemd Persistence", null()),
if(match(commands, "crontab.*nezha"), "Cron Persistence", null()),
if(installation_duration_min < 5, "Rapid Installation", null()))
| table start_time, host, users, actions, installation_duration_min,
download_urls, threat_indicators
| eval severity="high"
| eval malware_family="Nezha"
| sort -start_time
Query 3: Nezha WebSocket Communication Pattern
spl
index=proxy OR index=web
http_method="GET"
(uri_path="*/ws/agent*" OR uri_path="*/api/v1/ws*" OR uri_path="*nezha*")
http_header="*Upgrade: websocket*"
| eval is_alibaba=if(match(dest_domain, "(aliyuncs\.com|alibabacloud\.com)"), 1, 0)
| eval is_japan=if(match(dest_domain, "ap-northeast"), 1, 0)
| eval suspicious_ua=if(match(user_agent, "(nezha-agent|Go-http-client)"), 1, 0)
| eval has_custom_headers=if(match(http_header, "(X-Nezha-Token|X-Agent-Secret|X-Agent-ID)"), 1, 0)
| where is_alibaba=1 OR is_japan=1 OR suspicious_ua=1 OR has_custom_headers=1
| stats count as connection_count,
dc(src_ip) as unique_sources,
values(dest_domain) as c2_domains,
values(user_agent) as user_agents,
values(uri_path) as paths,
sum(bytes_in) as total_received,
sum(bytes_out) as total_sent,
earliest(_time) as first_connection,
latest(_time) as last_connection
by src_ip, dest_ip
| eval session_duration_hours=round((last_connection-first_connection)/3600, 2)
| eval total_data_mb=round((total_received+total_sent)/1048576, 2)
| eval websocket_score=0
| eval websocket_score=websocket_score + if(is_alibaba=1, 30, 0)
| eval websocket_score=websocket_score + if(is_japan=1, 20, 0)
| eval websocket_score=websocket_score + if(suspicious_ua=1, 25, 0)
| eval websocket_score=websocket_score + if(has_custom_headers=1, 25, 0)
| where websocket_score >= 40
| eval severity=case(
websocket_score >= 70, "critical",
websocket_score >= 50, "high",
"medium")
| table first_connection, src_ip, dest_ip, c2_domains, connection_count,
session_duration_hours, total_data_mb, websocket_score, severity
| eval threat_type="Nezha WebSocket C2"
| sort -websocket_score
Query 4: Nezha Credential Harvesting Activity
spl
index=linux sourcetype=linux_audit type=PATH
(name="*/.ssh/id_rsa*" OR name="*/.ssh/authorized_keys*"
OR name="*/etc/shadow*" OR name="*/.bash_history*"
OR name="*/.mysql_history*" OR name="*/database.yml*"
OR name="*/.env*" OR name="*/credentials*")
| rex field=exe "(?<process_path>\/[^\s]+)"
| eval suspicious_location=if(match(process_path, "(\/tmp\/|\/var\/tmp\/|\.nezha)"), 1, 0)
| eval file_category=case(
match(name, "\.ssh"), "SSH_Keys",
match(name, "shadow"), "System_Passwords",
match(name, "history"), "Command_History",
match(name, "(database|\.env|credentials)"), "Config_Credentials",
true(), "Other")
| stats count as access_count,
dc(name) as unique_files,
values(name) as accessed_files,
values(file_category) as file_categories,
values(exe) as processes,
values(comm) as commands,
earliest(_time) as first_access,
latest(_time) as last_access
by host, uid, auid
| eval access_duration_min=round((last_access-first_access)/60, 2)
| where unique_files >= 3 OR suspicious_location=1
| eval risk_score=0
| eval risk_score=risk_score + (unique_files * 10)
| eval risk_score=risk_score + if(suspicious_location=1, 30, 0)
| eval risk_score=risk_score + if(mvcount(file_categories) >= 2, 20, 0)
| where risk_score >= 40
| join host [
search index=firewall dest_domain="*aliyuncs.com*" OR dest_domain="*alibabacloud*"
| stats count by src_ip
| rename src_ip as host
]
| eval severity=case(
risk_score >= 70, "critical",
risk_score >= 50, "high",
"medium")
| table first_access, host, uid, commands, file_categories, unique_files,
access_duration_min, risk_score, severity
| eval attack_type="Credential Harvesting"
| eval suspected_malware="Nezha"
| sort -risk_score
Query 5: Nezha Persistence Mechanism Detection
spl
index=linux (sourcetype=linux_audit OR sourcetype=syslog)
(
(type=SERVICE_START name="*nezha*")
OR (CommandLine="*systemctl enable*" CommandLine="*nezha*")
OR (CommandLine="*systemctl daemon-reload*" file="*nezha.service*")
OR (type=CRON CommandLine="*nezha*")
OR (file="/etc/systemd/system/*nezha*.service" OR file="/lib/systemd/system/*nezha*.service")
)
| eval persistence_type=case(
match(_raw, "systemctl enable"), "Systemd_Enable",
match(_raw, "SERVICE_START"), "Service_Started",
match(_raw, "daemon-reload"), "Service_Reload",
match(_raw, "CRON"), "Cron_Job",
match(file, "\.service$"), "Service_File_Created",
true(), "Other")
| stats count,
values(persistence_type) as persistence_methods,
values(CommandLine) as commands,
values(file) as files,
values(exe) as executables,
min(_time) as first_seen,
max(_time) as last_seen
by host, User
| eval persistence_count=mvcount(persistence_methods)
| where persistence_count >= 2 OR match(commands, "nezha")
| eval time_range_hours=round((last_seen-first_seen)/3600, 2)
| join type=left host [
search index=linux sourcetype=bash_history
(CommandLine="*wget*" OR CommandLine="*curl*")
(CommandLine="*aliyuncs.com*" OR CommandLine="*nezha*")
| stats earliest(_time) as download_time by host
]
| eval installation_to_persistence_min=if(isnotnull(download_time),
round((first_seen-download_time)/60, 2), null())
| eval rapid_persistence=if(installation_to_persistence_min < 10 AND installation_to_persistence_min > 0, 1, 0)
| eval severity=case(
rapid_persistence=1 AND persistence_count >= 2, "critical",
persistence_count >= 2, "high",
"medium")
| table first_seen, host, User, persistence_methods, commands, files,
persistence_count, installation_to_persistence_min, severity
| eval threat_family="Nezha"
| eval alert_type="Persistence Mechanism"
| sort -severity, -persistence_count
Query 6: Nezha Lateral Movement Detection
spl
(index=linux sourcetype=linux_secure OR index=auth)
(
(action=success user=* src_ip=* dest_ip=*)
OR (EventCode IN (4624, 4625) Logon_Type=3)
OR (sshd session opened OR sshd Accepted publickey)
)
| eval auth_method=case(
match(_raw, "publickey"), "SSH_Key",
match(_raw, "password"), "Password",
match(_raw, "keyboard-interactive"), "Interactive",
true(), "Unknown")
| stats count as auth_attempts,
dc(dest_ip) as unique_destinations,
dc(user) as unique_users,
values(dest_ip) as target_hosts,
values(user) as users,
values(auth_method) as methods,
earliest(_time) as first_attempt,
latest(_time) as last_attempt
by src_ip
| eval timespan_minutes=round((last_attempt-first_attempt)/60, 2)
| where unique_destinations >= 3 OR (auth_attempts >= 10 AND timespan_minutes < 30)
| join src_ip [
search index=firewall dest_domain="*aliyuncs.com*" OR dest_domain="*alibabacloud*"
| stats count by src_ip
]
| join type=left src_ip [
search index=linux sourcetype=bash_history CommandLine="*nezha*"
| stats count as nezha_commands by src_ip
]
| eval lateral_movement_score=0
| eval lateral_movement_score=lateral_movement_score + (unique_destinations * 15)
| eval lateral_movement_score=lateral_movement_score + if(timespan_minutes < 30, 20, 0)
| eval lateral_movement_score=lateral_movement_score + if(match(methods, "SSH_Key"), 25, 0)
| eval lateral_movement_score=lateral_movement_score + if(nezha_commands > 0, 40, 0)
| where lateral_movement_score >= 50
| eval severity=case(
lateral_movement_score >= 80, "critical",
lateral_movement_score >= 60, "high",
"medium")
| table first_attempt, src_ip, unique_destinations, target_hosts, users,
auth_attempts, timespan_minutes, methods, lateral_movement_score, severity
| eval attack_stage="Lateral Movement"
| eval suspected_threat="Nezha"
| sort -lateral_movement_score
Microsoft Sentinel KQL Queries
KQL Query 1: Nezha Alibaba Cloud C2 Communication
kql
// Nezha C2 Communication to Alibaba Cloud Japan Infrastructure
let AlibabaIPRanges = dynamic([
"47.74.0.0/16", "47.245.0.0/16", "161.117.0.0/16",
"47.91.0.0/16", "47.243.0.0/16"
]);
let AlibabaDomains = dynamic([
"aliyuncs.com", "alibabacloud.com", "oss-ap-northeast-1"
]);
let SuspiciousPorts = dynamic([443, 8080, 8443, 5555]);
let TimeWindow = 24h;
CommonSecurityLog
| where TimeGenerated > ago(TimeWindow)
| where DeviceVendor in ("Palo Alto Networks", "Fortinet", "Check Point") or DeviceProduct == "Firewall"
| where DestinationPort in (SuspiciousPorts)
| where (ipv4_is_in_any_range(DestinationIP, AlibabaIPRanges)
or DestinationHostName has_any (AlibabaDomains))
| extend IsWebSocket = iff(RequestContext has "websocket" or RequestContext has "Upgrade:", true, false)
| extend SuspiciousPath = iff(RequestURL has_any ("/ws/agent", "/api/v1/ws", "/nezha"), true, false)
| summarize
ConnectionCount = count(),
TotalBytesSent = sum(SentBytes),
TotalBytesReceived = sum(ReceivedBytes),
UniqueDestinations = dcount(DestinationIP),
DestinationIPs = make_set(DestinationIP),
DestinationDomains = make_set(DestinationHostName),
URLs = make_set(RequestURL),
FirstSeen = min(TimeGenerated),
LastSeen = max(TimeGenerated),
WebSocketConnections = countif(IsWebSocket),
SuspiciousPathCount = countif(SuspiciousPath)
by SourceIP, SourceHostName, DestinationPort
| extend
DurationHours = datetime_diff('hour', LastSeen, FirstSeen),
TotalDataMB = (TotalBytesSent + TotalBytesReceived) / 1048576.0,
C2Score = 0
| extend C2Score = C2Score + iff(ConnectionCount > 50, 30,
iff(ConnectionCount > 20, 20, iff(ConnectionCount > 10, 10, 0)))
| extend C2Score = C2Score + iff(TotalDataMB > 100, 25,
iff(TotalDataMB > 50, 15, iff(TotalDataMB > 10, 5, 0)))
| extend C2Score = C2Score + iff(WebSocketConnections > 0, 20, 0)
| extend C2Score = C2Score + iff(SuspiciousPathCount > 0, 25, 0)
| where C2Score >= 30
| extend
Severity = case(
C2Score >= 70, "Critical",
C2Score >= 50, "High",
"Medium"),
ThreatFamily = "Nezha",
C2Location = "Alibaba Cloud Japan",
ThreatType = "Command and Control"
| project
TimeGenerated = LastSeen,
SourceIP,
SourceHostName,
DestinationIPs,
DestinationDomains,
URLs,
ConnectionCount,
TotalDataMB,
DurationHours,
WebSocketConnections,
C2Score,
Severity,
ThreatFamily,
C2Location
| sort by C2Score desc, ConnectionCount desc
KQL Query 2: Nezha Installation and Persistence
kql
// Nezha Installation Process and Persistence Detection
let TimeFrame = 1h;
let NezhaKeywords = dynamic(["nezha", "nezha-agent", "nezha.service"]);
let AlibabaKeywords = dynamic(["aliyuncs.com", "alibabacloud.com", "oss-ap-northeast"]);
let InstallCommands =
Syslog
| where TimeGenerated > ago(TimeFrame)
| where Facility == "authpriv" or Facility == "user"
| where SyslogMessage has_any (NezhaKeywords)
or SyslogMessage has_any (AlibabaKeywords)
| where SyslogMessage has_any ("wget", "curl", "chmod", "mv", "cp")
| extend
CommandLine = extract(@"(?:sudo\s+)?(.+)", 1, SyslogMessage),
IsDownload = iff(SyslogMessage has_any ("wget", "curl"), true, false),
IsExecution = iff(SyslogMessage has "chmod +x", true, false),
IsMovement = iff(SyslogMessage has_any ("mv", "cp"), true, false)
| project TimeGenerated, Computer, ProcessName, CommandLine, IsDownload, IsExecution, IsMovement;
let PersistenceActions =
Syslog
| where TimeGenerated > ago(TimeFrame)
| where SyslogMessage has_any (NezhaKeywords)
| where SyslogMessage has_any ("systemctl", "crontab", "service", "chkconfig")
| extend
CommandLine = extract(@"(?:sudo\s+)?(.+)", 1, SyslogMessage),
PersistenceType = case(
SyslogMessage has "systemctl enable", "Systemd_Enable",
SyslogMessage has "systemctl start", "Systemd_Start",
SyslogMessage has "daemon-reload", "Service_Reload",
SyslogMessage has "crontab", "Cron_Job",
"Other")
| project TimeGenerated, Computer, ProcessName, CommandLine, PersistenceType;
InstallCommands
| join kind=inner (PersistenceActions) on Computer
| summarize
InstallCommands = make_set(CommandLine),
PersistenceTypes = make_set(PersistenceType),
InstallStart = min(TimeGenerated),
PersistenceEnd = max(TimeGenerated1),
DownloadCount = countif(IsDownload),
ExecutionCount = countif(IsExecution),
MovementCount = countif(IsMovement)
by Computer
| extend
InstallationDurationMinutes = datetime_diff('minute', PersistenceEnd, InstallStart),
TotalActions = DownloadCount + ExecutionCount + MovementCount,
RapidInstallation = iff(InstallationDurationMinutes < 10, true, false)
| where TotalActions >= 2
| extend
RiskScore = 0,
RiskScore = RiskScore + iff(RapidInstallation, 30, 0),
RiskScore = RiskScore + (TotalActions * 10),
RiskScore = RiskScore + (array_length(PersistenceTypes) * 15)
| extend
Severity = case(
RiskScore >= 70, "Critical",
RiskScore >= 50, "High",
"Medium"),
ThreatFamily = "Nezha",
AttackStage = "Installation + Persistence"
| project
InstallStart,
Computer,
InstallCommands,
PersistenceTypes,
InstallationDurationMinutes,
TotalActions,
RapidInstallation,
RiskScore,
Severity,
ThreatFamily
| sort by RiskScore desc
KQL Query 3: Nezha WebSocket Communication Pattern
kql
// Nezha WebSocket C2 Communication Detection
let TimeWindow = 6h;
let WebSocketPaths = dynamic(["/ws/agent", "/api/v1/ws", "/agent/ws", "/nezha"]);
let NezhaUserAgents = dynamic(["nezha-agent", "Go-http-client", "websocket"]);
let NezhaHeaders = dynamic(["X-Nezha-Token", "X-Agent-Secret", "X-Agent-ID"]);
let AlibabaDomains = dynamic(["aliyuncs.com", "alibabacloud.com"]);
W3CIISLog
| where TimeGenerated > ago(TimeWindow)
| where csMethod == "GET"
| where csUriStem has_any (WebSocketPaths)
or csUserAgent has_any (NezhaUserAgents)
or csReferer has_any (AlibabaDomains)
| extend
IsWebSocket = iff(csUriStem has "Upgrade" or csUserAgent has "websocket", true, false),
HasNezhaPath = iff(csUriStem has_any (WebSocketPaths), true, false),
HasNezhaUA = iff(csUserAgent has_any (NezhaUserAgents), true, false),
HasAlibabaRef = iff(csReferer has_any (AlibabaDomains) or sIP has_any (AlibabaDomains), true, false)
| summarize
RequestCount = count(),
UniqueURIs = dcount(csUriStem),
URIs = make_set(csUriStem),
UserAgents = make_set(csUserAgent),
TotalBytesSent = sum(csbytes),
TotalBytesReceived = sum(scbytes),
FirstRequest = min(TimeGenerated),
LastRequest = max(TimeGenerated),
WebSocketCount = countif(IsWebSocket),
NezhaPathCount = countif(HasNezhaPath),
NezhaUACount = countif(HasNezhaUA),
AlibabaCount = countif(HasAlibabaRef)
by cIP, sIP, sComputerName
| extend
SessionDurationHours = datetime_diff('hour', LastRequest, FirstRequest),
TotalDataMB = (TotalBytesSent + TotalBytesReceived) / 1048576.0,
WebSocketScore = 0
| extend WebSocketScore = WebSocketScore + iff(WebSocketCount > 0, 25, 0)
| extend WebSocketScore = WebSocketScore + iff(NezhaPathCount > 0, 30, 0)
| extend WebSocketScore = WebSocketScore + iff(NezhaUACount > 0, 25, 0)
| extend WebSocketScore = WebSocketScore + iff(AlibabaCount > 0, 20, 0)
| where WebSocketScore >= 40
| extend
Severity = case(
WebSocketScore >= 70, "Critical",
WebSocketScore >= 50, "High",
"Medium"),
ThreatType = "Nezha WebSocket C2",
ThreatFamily = "Nezha"
| project
FirstRequest,
ClientIP = cIP,
ServerIP = sIP,
ServerName = sComputerName,
RequestCount,
URIs,
UserAgents,
SessionDurationHours,
TotalDataMB,
WebSocketScore,
Severity,
ThreatType
| sort by WebSocketScore desc
KQL Query 4: Nezha Credential Harvesting
kql
// Nezha Credential and Sensitive File Access
let TimeFrame = 12h;
let SensitiveFiles = dynamic([
".ssh/id_rsa", ".ssh/id_dsa", ".ssh/id_ecdsa", ".ssh/authorized_keys",
"/etc/shadow", "/etc/gshadow", ".bash_history", ".mysql_history",
"database.yml", ".env", "config.json", "credentials"
]);
let SuspiciousLocations = dynamic(["/tmp/", "/var/tmp/", ".nezha"]);
Syslog
| where TimeGenerated > ago(TimeFrame)
| where Facility == "authpriv" or SyslogMessage has_any ("open", "read", "cat", "cp")
| where SyslogMessage has_any (SensitiveFiles)
| extend
AccessedFile = extract(@"([\/\w\.\-]+(?:\.ssh|shadow|history|yml|env|json|credentials)[\/\w\.\-]*)", 1, SyslogMessage),
ProcessPath = extract(@"(?:sudo\s+)?(\S+)", 1, ProcessName),
IsSuspiciousLocation = iff(ProcessName has_any (SuspiciousLocations), true, false)
| extend
FileCategory = case(
AccessedFile has ".ssh", "SSH_Keys",
AccessedFile has "shadow", "System_Passwords",
AccessedFile has "history", "Command_History",
AccessedFile has_any ("database", ".env", "credentials", "config"), "Config_Credentials",
"Other")
| summarize
AccessCount = count(),
UniqueFiles = dcount(AccessedFile),
AccessedFiles = make_set(AccessedFile),
FileCategories = make_set(FileCategory),
Processes = make_set(ProcessPath),
FirstAccess = min(TimeGenerated),
LastAccess = max(TimeGenerated),
SuspiciousLocCount = countif(IsSuspiciousLocation)
by Computer, ProcessName
| extend
AccessDurationMinutes = datetime_diff('minute', LastAccess, FirstAccess),
MultiCategoryAccess = iff(array_length(FileCategories) >= 2, true, false)
| where UniqueFiles >= 3 or SuspiciousLocCount > 0
| join kind=leftouter (
CommonSecurityLog
| where TimeGenerated > ago(TimeFrame)
| where DestinationHostName has_any ("aliyuncs.com", "alibabacloud.com")
| distinct SourceIP
| extend Computer = SourceIP
) on Computer
| extend HasC2Connection = iff(isnotnull(SourceIP), true, false)
| extend
RiskScore = 0,
RiskScore = RiskScore + (UniqueFiles * 10),
RiskScore = RiskScore + iff(SuspiciousLocCount > 0, 30, 0),
RiskScore = RiskScore + iff(MultiCategoryAccess, 20, 0),
RiskScore = RiskScore + iff(HasC2Connection, 40, 0)
| where RiskScore >= 40
| extend
Severity = case(
RiskScore >= 80, "Critical",
RiskScore >= 60, "High",
"Medium"),
AttackType = "Credential Harvesting",
SuspectedMalware = "Nezha"
| project
FirstAccess,
Computer,
ProcessName,
FileCategories,
UniqueFiles,
AccessedFiles,
AccessDurationMinutes,
HasC2Connection,
RiskScore,
Severity,
SuspectedMalware
| sort by RiskScore desc
KQL Query 5: Nezha Lateral Movement
kql
// Nezha Lateral Movement Detection
let TimeFrame = 6h;
let NezhaIndicators =
Syslog
| where TimeGenerated > ago(TimeFrame)
| where SyslogMessage has "nezha"
| distinct Computer
| extend HasNezhaActivity = true;
SecurityEvent
| where TimeGenerated > ago(TimeFrame)
| where EventID in (4624, 4625, 4648, 4768, 4769)
| where LogonType in (3, 10)
| extend
SourceIP = iff(IpAddress == "-", "0.0.0.0", IpAddress),
AuthResult = iff(EventID == 4625, "Failed", "Success")
| summarize
TotalAttempts = count(),
SuccessCount = countif(AuthResult == "Success"),
FailureCount = countif(AuthResult == "Failed"),
UniqueDestinations = dcount(Computer),
UniqueAccounts = dcount(TargetUserName),
DestinationHosts = make_set(Computer),
Accounts = make_set(TargetUserName),
FirstAttempt = min(TimeGenerated),
LastAttempt = max(TimeGenerated)
by SourceIP, LogonType
| extend TimeSpanMinutes = datetime_diff('minute', LastAttempt, FirstAttempt)
| where UniqueDestinations >= 3 or (TotalAttempts >= 10 and TimeSpanMinutes < 30)
| join kind=leftouter (NezhaIndicators) on $left.SourceIP == $right.Computer
| join kind=leftouter (
CommonSecurityLog
| where TimeGenerated > ago(TimeFrame)
| where DestinationHostName has_any ("aliyuncs.com", "alibabacloud.com")
| distinct SourceIP
) on SourceIP
| extend
HasC2Connection = iff(isnotnull(SourceIP1), true, false),
LateralMovementScore = 0
| extend LateralMovementScore = LateralMovementScore + (UniqueDestinations * 15)
| extend LateralMovementScore = LateralMovementScore + iff(TimeSpanMinutes < 30, 20, 0)
| extend LateralMovementScore = LateralMovementScore + iff(LogonType == 10, 10, 0)
| extend LateralMovementScore = LateralMovementScore + iff(HasNezhaActivity, 40, 0)
| extend LateralMovementScore = LateralMovementScore + iff(HasC2Connection, 35, 0)
| where LateralMovementScore >= 50
| extend
Severity = case(
LateralMovementScore >= 90, "Critical",
LateralMovementScore >= 70, "High",
"Medium"),
AttackStage = "Lateral Movement",
SuspectedThreat = "Nezha"
| project
FirstAttempt,
SourceIP,
UniqueDestinations,
DestinationHosts,
Accounts,
TotalAttempts,
SuccessCount,
FailureCount,
TimeSpanMinutes,
HasNezhaActivity,
HasC2Connection,
LateralMovementScore,
Severity,
SuspectedThreat
| sort by LateralMovementScore desc
KQL Query 6: Nezha Complete Attack Chain
kql
// Nezha Complete Attack Chain Analysis
let TimeWindow = 24h;
let NezhaKeywords = dynamic(["nezha", "nezha-agent"]);
let AlibabaIndicators = dynamic(["aliyuncs.com", "alibabacloud.com", "47.74.", "47.245.", "161.117."]);
// Stage 1: Initial Access and Installation
let Installation =
Syslog
| where TimeGenerated > ago(TimeWindow)
| where SyslogMessage has_any (NezhaKeywords) or SyslogMessage has_any (AlibabaIndicators)
| where SyslogMessage has_any ("wget", "curl", "chmod")
| summarize InstallTime = min(TimeGenerated) by Computer
| extend Stage1_Installation = true;
// Stage 2: Persistence
let Persistence =
Syslog
| where TimeGenerated > ago(TimeWindow)
| where SyslogMessage has_any (NezhaKeywords)
| where SyslogMessage has_any ("systemctl", "crontab", "service")
| summarize PersistenceTime = min(TimeGenerated) by Computer
| extend Stage2_Persistence = true;
// Stage 3: C2 Communication
let C2Communication =
CommonSecurityLog
| where TimeGenerated > ago(TimeWindow)
| where DestinationHostName has_any (AlibabaIndicators)
or ipv4_is_in_any_range(DestinationIP, dynamic(["47.74.0.0/16", "47.245.0.0/16", "161.117.0.0/16"]))
| summarize
C2StartTime = min(TimeGenerated),
C2Connections = count(),
C2DataSent = sum(SentBytes)
by SourceIP
| extend
Stage3_C2 = true,
Computer = SourceIP;
// Stage 4: Credential Access
let CredentialAccess =
Syslog
| where TimeGenerated > ago(TimeWindow)
| where SyslogMessage has_any (".ssh", "shadow", "history", "credentials")
| summarize
CredAccessTime = min(TimeGenerated),
SensitiveFileAccess = count()
by Computer
| extend Stage4_CredentialAccess = true;
// Stage 5: Lateral Movement
let LateralMovement =
SecurityEvent
| where TimeGenerated > ago(TimeWindow)
| where EventID in (4624, 4648)
| where LogonType == 3
| summarize
LateralMoveTime = min(TimeGenerated),
UniqueTargets = dcount(Computer)
by IpAddress
| extend
Stage5_LateralMovement = true,
Computer = IpAddress;
// Combine all stages
Installation
| join kind=fullouter (Persistence) on Computer
| join kind=fullouter (C2Communication) on Computer
| join kind=fullouter (CredentialAccess) on Computer
| join kind=fullouter (LateralMovement) on Computer
| extend Computer = coalesce(Computer, Computer1, Computer2, Computer3, Computer4)
| summarize arg_min(InstallTime, *) by Computer
| extend
StagesCompleted =
iff(Stage1_Installation, 1, 0) +
iff(Stage2_Persistence, 1, 0) +
iff(Stage3_C2, 1, 0) +
iff(Stage4_CredentialAccess, 1, 0) +
iff(Stage5_LateralMovement, 1, 0)
| extend
AttackTimeline = pack_array(
iff(Stage1_Installation, strcat("Installation: ", InstallTime), ""),
iff(Stage2_Persistence, strcat("Persistence: ", PersistenceTime), ""),
iff(Stage3_C2, strcat("C2: ", C2StartTime), ""),
iff(Stage4_CredentialAccess, strcat("Cred Access: ", CredAccessTime), ""),
iff(Stage5_LateralMovement, strcat("Lateral Move: ", LateralMoveTime), "")
)
| where StagesCompleted >= 2
| extend
TotalDurationHours = datetime_diff('hour',
max_of(PersistenceTime, C2StartTime, CredAccessTime, LateralMoveTime),
InstallTime),
Severity = case(
StagesCompleted >= 4, "Critical",
StagesCompleted >= 3, "High",
"Medium"),
ThreatFamily = "Nezha",
CompromiseLevel = case(
StagesCompleted >= 4, "Full Compromise",
StagesCompleted == 3, "Significant Compromise",
"Initial Compromise")
| project
Computer,
FirstActivity = InstallTime,
StagesCompleted,
CompromiseLevel,
TotalDurationHours,
AttackTimeline,
C2Connections,
C2DataSentMB = C2DataSent / 1048576.0,
SensitiveFileAccess,
UniqueTargets,
Severity,
ThreatFamily
| sort by StagesCompleted desc, Severity desc
These detection rules provide comprehensive coverage across multiple security platforms and techniques to identify Nezha malware throughout its attack lifecycle from initial installation through C2 communication, persistence, credential theft, and lateral movement.
