VoidLink: Exploitation and Kernel-Level Implant Deployment
An adversary has exploited a Java-based Apache Dubbo service to deploy a ZigLang-based VoidLink implant and maintained stealth using an unsigned kernel-level rootkit.
Based on research by Cisco Talos 2026-09-20 11 steps · 3 queries T1190 T1204.002 T1574.002
Brief
Why this hunt
Cisco Talos published an analysis of VoidLink, a sophisticated framework targeting Linux environments. The report, Cisco Talos — VoidLink, details how adversaries exploit Java-based services like Apache Dubbo to deploy custom implants. These attackers use ZigLang-based binaries and unsigned kernel modules to maintain persistence and evade standard monitoring. A simple detection rule for these behaviors often triggers too many alerts in complex server environments. This hunt provides a structured way to find these indicators by focusing on the relationship between vulnerable software and anomalous behavior.
How the hunt flows
The hunt begins with a scoping phase. The first query searches the software inventory to identify every host running Apache Dubbo. This middleware is a known entry point for the UAT-9921 group. By identifying these hosts first, the hunt avoids running expensive forensic queries across the entire fleet. If the hunt finds no instances of Dubbo, it stops to save resources.
Once the hunt identifies target servers, it starts a parallel forensic fan-out. The first branch looks for rare child processes spawned by Java. The query focuses on binaries executed from temporary or shared memory paths like /tmp or /dev/shm. Because legitimate Java applications rarely spawn unknown binaries from these locations, these events suggest successful remote code execution. The hunt uses a prevalence filter to ensure that only binaries appearing on a few hosts reach the analyst.
The second branch of the fan-out examines kernel module activity. It searches for unsigned modules loaded from temporary or hidden directories. VoidLink uses these modules to hide its presence and its command-and-control traffic. Legitimate modules typically reside in standard library paths and carry valid signatures. Finding an unsigned module in a world-writable path is a high-confidence indicator of rootkit activity.
In the final phase, an agent synthesizes these results. It correlates the presence of the vulnerable software with any identified process anomalies or kernel modules. This host-based context allows the hunt to produce a specific verdict. If a host running Dubbo also shows a rare Java child process and an unsigned kernel module, the hunt recommends immediate isolation.
What this hunt cannot see
This hunt has three primary blind spots. First, it relies on accurate software inventory. If a server does not report its installed packages, the scoping lead will fail, and the hunt will not examine that host. Second, the hunt focuses on traditional Linux Kernel Modules (LKM). If the adversary uses an eBPF-based rootkit, it may not appear in the module activity logs used here. Finally, the behavioral queries only protect managed systems. Any unmanaged or shadow IT systems running Dubbo remain invisible to this hunt.
In this series
Steps
-
Lead: Identify Apache Dubbo software
Query · scopingIdentify the subset of the fleet running Apache Dubbo, which UAT-9921 targets for remote code execution.
reads hb_software_inventorysqlSELECT device_hostname, package_name, package_version, vendor_name FROM hb_software_inventory WHERE LOWER(package_name) LIKE '%dubbo%'What a hit looks like. Hosts running Dubbo. If none are found, the likelihood of this specific exploit chain is low, though manual deployments may still exist.
-
Evaluate software lead
Agent triageDetermine if any host runs the vulnerable software before starting expensive telemetry queries.
-
Gate: Proceed on software match
DecisionAvoid running deep forensic queries across the entire estate if no target software is identified.
-
Rare child processes spawned by Java
Query · detection candidateDetect the transition from Java serialization exploitation to execution of an implant, focusing on rare binaries in temporary directories.
reads hb_process_activitysqlSELECT LOWER(process_path) AS binary_path, parent_process_name, COUNT(DISTINCT device_hostname) AS host_count FROM hb_process_activity WHERE LOWER(parent_process_name) LIKE '%java%' AND (LOWER(process_path) LIKE '%/tmp/%' OR LOWER(process_path) LIKE '%/dev/shm/%' OR LOWER(process_path) LIKE '%/var/tmp/%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY 1, 2 HAVING host_count <= {{rare_threshold}}What a hit looks like. Rare binaries spawned by Java in world-writable paths. A small host count indicates a targeted deployment rather than legitimate maintenance.
-
Unsigned kernel modules in suspicious paths
Query · baselineIdentify potential LKM rootkits deployed by VoidLink to hide its presence and the C2 server.
reads hb_module_activitysqlSELECT device_hostname, module_path, module_name, module_signed, time FROM hb_module_activity WHERE module_signed = 0 AND (LOWER(module_path) LIKE '%/tmp/%' OR LOWER(module_path) LIKE '%/.%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')What a hit looks like. Unsigned modules loaded from temporary or hidden paths. Legitimate kernel modules are typically signed and live in /lib/modules/.
-
Synthesize VoidLink evidence
Agent triageCombine the software context with behavioural markers to determine if a host is compromised.
-
Route on verdict
DecisionTrigger isolation for high-confidence threats and review for suspicious anomalies.
-
Isolate host
Response actionContain the threat and prevent further lateral movement or data exfiltration.
-
Analyst review
Analyst taskPerform manual verification of the triage findings and look for additional VoidLink markers.
-
Close out
Analyst taskDocument the hunt results and record any tuning notes.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Apache Dubbo Java Serialization Exploitation T1190 |
Yes | lead-dubbo-inventory, suspicious-java-children |
| VoidLink Implant Execution T1204.002 · T1574.002 |
Yes | suspicious-java-children |
| Persistence and Privilege Escalation T1574.002 |
Yes | unsigned-kernel-modules |
| Internal Network Reconnaissance T1090.003 |
Out of scope | Belongs to another part of the 'VoidLink' series. |
| Cloud and Container Asset Discovery | Out of scope | Belongs to another part of the 'VoidLink' series. |
| Peer-to-Peer Mesh C2 T1071 · T1090.003 · T1041 |
Out of scope | Belongs to another part of the 'VoidLink' series. |
Blind spots
- Needs hb_software_inventory for all Linux servers. A host without inventory reporting might be a target but will cause the lead query to return silence, skipping forensic analysis. It would answer whether Apache Dubbo is present on servers that do not report inventory.
- Needs eBPF probe registration telemetry. VoidLink supports eBPF rootkits which may not appear as a module file on disk or a traditional .ko load event in hb_module_activity. It would answer whether an eBPF rootkit is active without a traditional LKM load.
- Needs Endpoint agent on high-value servers. The behavioural queries only cover systems where telemetry is collected; unmanaged servers exploited via Dubbo remain invisible. It would answer whether VoidLink activity occurs on unmanaged or shadow IT systems.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine for process and module activity. |
rare_threshold | number | 5 | The maximum number of hosts a process path can appear on to be considered rare. |
scope_hosts | list[host] | — | List of hostnames from the software inventory lead to focus behavioral queries. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
Source
---
analysis: A simple rule for Dubbo exploitation or rootkit loading would generate excessive
noise in large Linux environments. This hunt uses a gated logic to only perform
forensic analysis on hosts with the target software, applies prevalence baseline
counting to filter common maintenance scripts, and uses an agent to weigh multiple
independent indicators before recommending isolation.
blind_spots:
- id: no-inventory-telemetry
question: whether Apache Dubbo is present on servers that do not report inventory
requires: hb_software_inventory for all Linux servers
risk: A host without inventory reporting might be a target but will cause the lead
query to return silence, skipping forensic analysis.
stage: initial-access-dubbo-exploit
- id: ebpf-rootkit-stealth
question: whether an eBPF rootkit is active without a traditional LKM load
requires: eBPF probe registration telemetry
risk: VoidLink supports eBPF rootkits which may not appear as a module file on disk
or a traditional .ko load event in hb_module_activity.
stage: rootkit-and-privilege-escalation
- id: no-agent-coverage
question: whether VoidLink activity occurs on unmanaged or shadow IT systems
requires: Endpoint agent on high-value servers
risk: The behavioural queries only cover systems where telemetry is collected; unmanaged
servers exploited via Dubbo remain invisible.
coverage:
- stage: initial-access-dubbo-exploit
status: covered
steps:
- lead-dubbo-inventory
- suspicious-java-children
- stage: implant-execution-and-sideloading
status: covered
steps:
- suspicious-java-children
- stage: rootkit-and-privilege-escalation
status: covered
steps:
- unsigned-kernel-modules
- reason: Belongs to another part of the 'VoidLink' series.
stage: internal-scanning-recon
status: out_of_scope
- reason: Belongs to another part of the 'VoidLink' series.
stage: cloud-api-discovery
status: out_of_scope
- reason: Belongs to another part of the 'VoidLink' series.
stage: mesh-c2-communications
status: out_of_scope
guardrails:
claims: no_unsupported
evidence: citation_required
missing_data: not_benign
telemetry: untrusted
hunt:
applicability: campaign-specific
handoff: promote-to-detection
justification: VoidLink is a sophisticated, defense-contractor grade framework targeting
Linux environments. Identifying its deployment through initial exploit patterns
and kernel persistence is essential for protecting server infrastructure from
persistent compromise.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary has exploited a Java-based Apache Dubbo service to deploy
a ZigLang-based VoidLink implant and maintained stealth using an unsigned kernel-level
rootkit.
labels:
- hunt
- attack.t1190
- attack.t1204.002
- attack.t1574.002
name: 'VoidLink: Exploitation and Kernel-Level Implant Deployment'
parameters:
lookback_days:
default: '14'
description: Days of history to examine for process and module activity.
from:
kind: manual
observed: '2024-01-01'
ref: VoidLink analysis lookback
type: number
rare_threshold:
default: '5'
description: The maximum number of hosts a process path can appear on to be considered
rare.
type: number
scope_hosts:
default: []
description: List of hostnames from the software inventory lead to focus behavioral
queries.
type: list[host]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://blog.talosintelligence.com/voidlink/
gates:
- dry-run
- lint
model: hb_google/gemini-3-flash-preview
rationale: Focus on Linux servers running Java middleware, particularly those with
internet exposure. The hunt starts with a wide software scan and then uses specific
behavioural triggers to narrow the volume.
references:
- name: "Cisco Talos \u2014 VoidLink"
url: https://blog.talosintelligence.com/voidlink/
related:
- hunt: voidlink-internal-recon-and-lateral-movement
reason: This hunt focuses on initial deployment; a follow-on hunt is needed for
internal scanning using FSCAN and mesh-C2 traffic.
relation: follows
scenario:
stages:
- name: Apache Dubbo Java Serialization Exploitation
observables:
- Apache Dubbo project
- Java serialization vulnerabilities
- Pre-obtained credentials
slug: initial-access-dubbo-exploit
tactic: initial-access
techniques:
- T1190
- name: VoidLink Implant Execution
observables:
- ZigLang-based implant binary
- DLL sideloading on Windows
- C-based plugins loaded via ELF linker
- VoidLink compile-on-demand framework
slug: implant-execution-and-sideloading
tactic: execution
techniques:
- T1204.002
- T1574.002
- name: Persistence and Privilege Escalation
observables:
- eBPF rootkit
- Loadable Kernel Module (LKM) rootkit
- Container privilege escalation
- Docker/Kubernetes sandbox escape
slug: rootkit-and-privilege-escalation
tactic: persistence
techniques:
- T1574.002
- name: Internal Network Reconnaissance
observables:
- FSCAN
- SOCKS server on compromised hosts
- Scanning of entire Class C networks
- Internal and external network scanning
slug: internal-scanning-recon
tactic: discovery
techniques:
- T1090.003
- name: Cloud and Container Asset Discovery
observables:
- Kubernetes API interactions
- Docker API interactions
- Cloud-aware gathering of environment info
slug: cloud-api-discovery
tactic: discovery
- name: Peer-to-Peer Mesh C2
observables:
- GoLang backend
- Mesh Peer-to-Peer (P2P) routing
- Dead-letter queue routing
- Encrypted/obfuscated exfiltration traffic
slug: mesh-c2-communications
tactic: command-and-control
techniques:
- T1071
- T1090.003
- T1041
summary: UAT-9921 utilizes the VoidLink modular framework, targeting Linux and Windows
systems by exploiting Java serialization vulnerabilities in Apache Dubbo or using
stolen credentials. The ZigLang-based implant deploys plugins for eBPF-based rootkits
and container escapes, while conducting internal reconnaissance with FSCAN and
establishing a peer-to-peer mesh command-and-control network.
series:
index: 1
slug: voidlink
title: VoidLink
total: 2
severity: high
targets:
analyst:
name: Tier-2 analyst
role: analyst
endpoint:
category: endpoint
name: Endpoint telemetry (hb_ surfaces)
telemetry:
- endpoint
hunter:
agent: true
name: Hunt agent
tlp: clear
type: investigation
---
# VoidLink: Exploitation and Kernel-Level Implant Deployment
This hunt targets the initial stages of a VoidLink intrusion. It identifies servers running Apache Dubbo, which is a reported entry point for UAT-9921. If the software is present, the hunt triggers a gated forensic fan-out to find suspicious child processes spawning from Java and unsigned kernel modules that indicate rootkit activity. An agent then synthesizes the inventory context with the behavioural findings to identify compromised hosts.
## lead-dubbo-inventory
<!-- Lead: Identify Apache Dubbo software -->
Identify the subset of the fleet running Apache Dubbo, which UAT-9921 targets for remote code execution.
```sqlite target=endpoint role=scoping
~~~yaml
expected: Hosts running Dubbo. If none are found, the likelihood of this specific
exploit chain is low, though manual deployments may still exist.
reads:
- device_hostname
- package_name
- package_version
- vendor_name
silence: not_evidence_of_absence
source: hb_software_inventory
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, package_name, package_version, vendor_name FROM hb_software_inventory WHERE LOWER(package_name) LIKE '%dubbo%'
```
## evaluate-scoping
<!-- Evaluate software lead -->
```agent target=hunter
cite: required
context:
- lead-dubbo-inventory
max_iterations: 3
objective: Confirm the presence of Apache Dubbo and identify target servers for forensic
analysis.
success_criteria: A list of hostnames to populate the scope_hosts parameter.
tools:
- endpoint
```
## gate-to-forensics
<!-- Gate: Proceed on software match -->
if: `evaluate-scoping.rows > 0`
then: → forensic-fan-out
else: → close-out
## forensic-fan-out
<!-- Forensic Fan-Out -->
parallel:
- → suspicious-java-children
- → unsigned-kernel-modules
join: → triage-synthesis
## suspicious-java-children
<!-- Rare child processes spawned by Java -->
Detect the transition from Java serialization exploitation to execution of an implant, focusing on rare binaries in temporary directories.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts, rare_threshold=rare_threshold)
~~~yaml
baseline:
compare: new_this_window
window: '{{lookback_days}}d'
expected: Rare binaries spawned by Java in world-writable paths. A small host count
indicates a targeted deployment rather than legitimate maintenance.
prevalence:
by: device_hostname
key:
- process_path
rare_below: 5
reads:
- device_hostname
- process_path
- parent_process_name
- time
silence: evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT LOWER(process_path) AS binary_path, parent_process_name, COUNT(DISTINCT device_hostname) AS host_count FROM hb_process_activity WHERE LOWER(parent_process_name) LIKE '%java%' AND (LOWER(process_path) LIKE '%/tmp/%' OR LOWER(process_path) LIKE '%/dev/shm/%' OR LOWER(process_path) LIKE '%/var/tmp/%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days') GROUP BY 1, 2 HAVING host_count <= {{rare_threshold}}
```
## unsigned-kernel-modules
<!-- Unsigned kernel modules in suspicious paths -->
Identify potential LKM rootkits deployed by VoidLink to hide its presence and the C2 server.
```sqlite target=endpoint role=baseline params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Unsigned modules loaded from temporary or hidden paths. Legitimate kernel
modules are typically signed and live in /lib/modules/.
prevalence:
by: device_hostname
key:
- module_name
rare_below: 3
reads:
- device_hostname
- module_path
- module_name
- module_signed
- time
silence: not_evidence_of_absence
source: hb_module_activity
verified: dry-run
verified_at: '2026-09-20'
~~~
SELECT device_hostname, module_path, module_name, module_signed, time FROM hb_module_activity WHERE module_signed = 0 AND (LOWER(module_path) LIKE '%/tmp/%' OR LOWER(module_path) LIKE '%/.%') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) AND time >= datetime('now', '-{{lookback_days}} days')
```
## triage-synthesis
<!-- Synthesize VoidLink evidence -->
```agent target=hunter
cite: required
context:
- evaluate-scoping
- suspicious-java-children
- unsigned-kernel-modules
max_iterations: 6
objective: Determine if any host showing Dubbo software also exhibits rare Java child
processes or suspicious kernel modules.
success_criteria: A per-host verdict of malicious | suspicious | benign, citing specific
binary paths and module names.
tools:
- endpoint
```
## route-on-verdict
<!-- Route on verdict -->
if~: "the triage verdict is malicious for at least one host running Dubbo" (confidence: high, judge=hunter)
then: → isolate-host
indeterminate: → analyst-review
unavailable: → analyst-review (blind_spot: no-agent-coverage)
else: → close-out
## isolate-host
<!-- Isolate host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host immediately. Preserve any artifacts in temporary directories and perform a memory dump to capture the rootkit state.
```
→ analyst-review
## analyst-review
<!-- Analyst review -->
```manual target=analyst
Review the cited child processes and unsigned modules. Confirm if the rare binaries use ZigLang or C as reported. Investigate the command line of the Java parent to identify the serialization exploit attempt.
```
→ end
## close-out
<!-- Close out -->
```manual target=analyst
Record the hosts scanned and the absence of matching behaviour. If many false positives occurred from legitimate Java child processes, update the rare_threshold or add exclusions for known maintenance scripts.
```
→ end
Run it
Take this hunt into your environment.
Open it in Huntbase to run every step against your own connections, with Scout weighing the evidence and your analysts in command. Or take the open hunt.md file anywhere that reads the format.
Machine-drafted by huntbase-hunt-generation using hb_google/gemini-3-flash-preview, gated by dry-run, lint, then reviewed by a person.