APT28: Outlook and Print Spooler Exploitation
An adversary is exploiting unpatched Outlook or Print Spooler vulnerabilities to harvest NTLM credentials or escalate privileges, evidenced by outbound SMB connections to external IPs and rare child processes launched by the spooler service.
Based on research by Sekoia 2026-09-28 12 steps · 4 queries T1041 T1190 T1555 T1566
Brief
Why now
Recent reporting from Sekoia, APT28: An Evolution of Tradecraft from X-Agent to LLM Malware, highlights how this adversary has shifted from bespoke malware to the exploitation of N-day vulnerabilities and common administrative tools. This hunt specifically addresses their use of CVE-2023-23397 (Outlook) and CVE-2022-38028 (Print Spooler) to move laterally and escalate privileges within target environments.
How the hunt flows
The hunt starts by identifying endpoints vulnerable to the specific CVEs weaponized by APT28. A lead query checks the vulnerability inventory for unpatched versions of Outlook and the Print Spooler service. This step scopes the hunt, ensuring that subsequent expensive queries only run against systems with a confirmed attack surface.
Once the hunt identifies vulnerable hosts, it gates the process. An analyst or automated agent reviews the findings to decide if the environment is exposed enough to warrant a deep dive. If vulnerable hosts exist, the hunt triggers three parallel behavioral checks across network and process telemetry.
The network phase looks for outbound SMB connections on port 445. When an adversary exploits the Outlook vulnerability, the client often attempts to authenticate against an external server, leaking Net-NTLMv2 hashes. The query filters for outbound traffic to identify these relay attempts.
The process phase baselines the Print Spooler service. It searches for rare child processes of spoolsv.exe, which is the primary indicator of the GooseEgg privilege escalation utility. The query uses a frequency analysis to ignore common printing utilities and highlight anomalies.
The final enrichment phase searches DNS logs for lookups involving spoofed phishing domains, such as ukr.net. This helps link the exploitation activity back to the initial access campaigns observed in recent APT28 operations.
What this hunt cannot see
This hunt relies on an accurate vulnerability inventory. If a host is unmanaged or the vulnerability scanner misses an endpoint, the scoping step will fail to include it. Host-based network logging is also required to see outbound SMB traffic; without port 445 visibility, the NTLM relay activity remains invisible. Finally, privilege escalation on a host without an endpoint agent will leave no process telemetry for the spooler check.
In this series
Steps
-
Identify vulnerable hosts
Query · scopingIdentify endpoints that are vulnerable to the specific CVEs APT28 is known to weaponize.
reads hb_vulnerability_findingsqlSELECT device_uid, cve_uid, severity, affected_package_name, affected_package_version FROM hb_vulnerability_finding WHERE instr(',' || '{{target_cves}}' || ',', ',' || cve_uid || ',') > 0What a hit looks like. A list of device UIDs that remain unpatched. Zero rows prove the estate is patched against these specific flaws.
-
Assess vulnerability lead
Agent triageDetermine if the vulnerability findings warrant a deep dive into behavioural logs.
-
Gate on vulnerability presence
DecisionRoute the hunt to the expensive behavioural queries only if vulnerable hosts are confirmed.
-
Outbound SMB connections from endpoints
Query · enrichmentDetect potential NTLM hash harvesting triggered by Outlook reminder exploitation.
reads hb_network_connectionsqlSELECT device_hostname, process_name, dst_endpoint_ip, dst_endpoint_port, time FROM hb_network_connection WHERE dst_endpoint_port = 445 AND direction = 'outbound' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)What a hit looks like. Network connections to port 445 on external IP addresses; silence proves no unauthenticated SMB traffic left the scope.
-
Rare child processes of Print Spooler
Query · detection candidateDetect the use of GooseEgg by identifying anomalous children of the Spooler service.
reads hb_process_activitysqlSELECT LOWER(process_name) AS proc, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE LOWER(parent_process_name) LIKE '%spoolsv.exe' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) GROUP BY proc HAVING host_count <= 3 ORDER BY host_count ASCWhat a hit looks like. Rare processes launched by spoolsv.exe; fleet-wide printing utilities will be filtered by the HAVING clause.
-
Lookups for ukr.net phishing indicators
Query · enrichmentIdentify activity related to the spoofed webmail portal used in recent campaigns.
reads hb_dns_activitysqlSELECT device_hostname, process_name, query_hostname, time FROM hb_dns_activity WHERE instr(',' || '{{ukr_net_domain}}' || ',', ',' || LOWER(query_hostname) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)What a hit looks like. Any resolution of the targeted phishing domain on an endpoint.
-
Synthesize exploitation evidence
Agent triageCorrelate the vulnerability findings with the network and process indicators to confirm exploitation.
-
Route based on triage verdict
DecisionInitiate response for confirmed compromises or refer ambiguous cases to an analyst.
-
Isolate compromised host
Response actionContain the threat and prevent further lateral movement or credential abuse.
-
Manual analyst review
Analyst taskVerify the findings and perform deeper forensic analysis.
-
Close out hunt
Analyst taskFinalize the hunt and record the state of the estate.
Coverage
Scenario coverage
| Stage | Covered | How, or why not |
|---|---|---|
| Initial Access via Phishing and Vulnerabilities T1566 · T1190 |
Yes | lead-vulnerability-check, ukr-net-phishing-lookups |
| Privilege Escalation via GooseEgg T1190 |
Yes | spooler-privesc-baseline |
| Net-NTLMv2 Hash Harvesting T1555 |
Yes | outbound-smb-relay |
| Infrastructure Hijacking on Edge Devices T1572 |
Out of scope | Belongs to another part of the 'APT28: An Evolution of Tradecraft from X-Agent to LLM Malware' series. |
| LLM-Integrated Data Exfiltration T1041 |
Out of scope | Belongs to another part of the 'APT28: An Evolution of Tradecraft from X-Agent to LLM Malware' series. |
Blind spots
- Needs Comprehensive hb_vulnerability_finding coverage. An unmanaged host could be exploited without being caught in the lead query. It would answer Are there vulnerable hosts not currently reporting to the vulnerability scanner?. Remediation: Audit the overlap between hb_devices and hb_vulnerability_finding to identify missing assets.
- Needs hb_network_connection with port 445 logging. Exfiltration or hash relay attempts using SMB could be missed if host-based network logging is disabled. It would answer Can we see outbound SMB traffic from every network segment?. Remediation: Ensure outbound SMB traffic is logged and alert on external port 445 connections.
- Needs Endpoint agent on all Windows systems. Privilege escalation via GooseEgg on a host without an agent would leave no behavioural trace. It would answer Do we have process telemetry for the entire Windows estate?. Remediation: Deploy agents to all Windows servers and workstations.
Parameters & data
Parameters
| Parameter | Type | Default | What it is |
|---|---|---|---|
lookback_days | number | 14 | Days of history to examine. |
scope_hosts | list[host] | — | Hostnames identified in the scoping step to focus the behavioural search. |
target_cves | list[string] | CVE-2023-23397, CVE-2022-38028 | CVE identifiers targeted by APT28 for initial access and privilege escalation. |
ukr_net_domain | list[domain] | ukr.net | The phishing target domain spoofed in APT28 campaigns. |
Telemetry
| Source | Category | Telemetry |
|---|---|---|
| Endpoint telemetry (hb_ surfaces) | endpoint | endpoint |
| Network telemetry | network | network |
Source
---
analysis: A standard rule fires on a known CVE or a static IP; this hunt correlates
the presence of unpatched vulnerabilities with behavioural anomalies (rare process
children, outbound SMB) across multiple telemetry surfaces, providing context a
single rule cannot provide.
blind_spots:
- id: vulnerability-inventory-gap
owner: Vulnerability Management Team
question: Are there vulnerable hosts not currently reporting to the vulnerability
scanner?
remediation: Audit the overlap between hb_devices and hb_vulnerability_finding to
identify missing assets.
requires: Comprehensive hb_vulnerability_finding coverage
risk: An unmanaged host could be exploited without being caught in the lead query.
stage: initial-access-phishing-and-vulnerability-exploitation
- id: network-outbound-visibility
owner: Network Security Team
question: Can we see outbound SMB traffic from every network segment?
remediation: Ensure outbound SMB traffic is logged and alert on external port 445
connections.
requires: hb_network_connection with port 445 logging
risk: Exfiltration or hash relay attempts using SMB could be missed if host-based
network logging is disabled.
stage: credential-harvesting-ntlm-relay
- id: no-agent-coverage
owner: Endpoint Engineering
question: Do we have process telemetry for the entire Windows estate?
remediation: Deploy agents to all Windows servers and workstations.
requires: Endpoint agent on all Windows systems
risk: Privilege escalation via GooseEgg on a host without an agent would leave no
behavioural trace.
stage: privilege-escalation-gooseegg
coverage:
- stage: initial-access-phishing-and-vulnerability-exploitation
status: covered
steps:
- lead-vulnerability-check
- ukr-net-phishing-lookups
- stage: privilege-escalation-gooseegg
status: covered
steps:
- spooler-privesc-baseline
- stage: credential-harvesting-ntlm-relay
status: covered
steps:
- outbound-smb-relay
- reason: 'Belongs to another part of the ''APT28: An Evolution of Tradecraft from
X-Agent to LLM Malware'' series.'
stage: c2-edge-device-hijacking
status: out_of_scope
- reason: 'Belongs to another part of the ''APT28: An Evolution of Tradecraft from
X-Agent to LLM Malware'' series.'
stage: exfiltration-llm-infostealer
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: APT28 has demonstrated a decade of persistence in weaponizing zero-day
and unpatched vulnerabilities for credential theft and privilege escalation. Identifying
active exploitation through behaviour is required to catch intrusions that occur
between patching cycles.
methodology: model-assisted
trigger: intel-report
hypothesis: An adversary is exploiting unpatched Outlook or Print Spooler vulnerabilities
to harvest NTLM credentials or escalate privileges, evidenced by outbound SMB connections
to external IPs and rare child processes launched by the spooler service.
labels:
- hunt
- attack.t1190
- attack.t1566
- attack.t1555
- attack.t1041
name: 'APT28: Outlook and Print Spooler Exploitation'
parameters:
lookback_days:
default: '14'
description: Days of history to examine.
from:
kind: manual
observed: '2026-06-22'
ref: standard-retention
type: number
scope_hosts:
default: []
description: Hostnames identified in the scoping step to focus the behavioural
search.
from:
kind: manual
observed: '2026-06-22'
ref: scoping-input
type: list[host]
target_cves:
default:
- CVE-2023-23397
- CVE-2022-38028
description: CVE identifiers targeted by APT28 for initial access and privilege
escalation.
from:
kind: article
observed: '2026-06-22'
ref: sekoia-apt28-evolution
type: list[string]
ukr_net_domain:
default:
- ukr.net
description: The phishing target domain spoofed in APT28 campaigns.
from:
kind: article
observed: '2026-06-22'
ref: sekoia-apt28-evolution
type: list[domain]
provenance:
authors:
- name: Huntbase hunt generation
org: huntbase.io
generated:
by: huntbase-hunt-generation
from: https://www.sekoia.com/blog/apt28-an-evolution-of-tradecraft
gates:
- dry-run
- lint
- critic
model: hb_google/gemini-3-flash-preview
rationale: The hunt should prioritize endpoints that are identified as vulnerable
to the target CVEs. If vulnerability scanning coverage is incomplete, widen the
scope to all Windows workstations and servers during the deep-dive phase.
references:
- name: "Sekoia \u2014 APT28: An Evolution of Tradecraft"
url: https://www.sekoia.com/blog/apt28-an-evolution-of-tradecraft
related:
- hunt: apt28-edge-device-hijacking
reason: Edge device hijacking (MooBot/FrostArmada) targets router infrastructure
rather than endpoint software and is handled in a separate hunt.
relation: out-of-scope-alternative
scenario:
stages:
- name: Initial Access via Phishing and Vulnerabilities
observables:
- CVE-2023-23397 (Outlook)
- CVE-2022-38028 (Windows Print Spooler)
- SedKit exploit kit
- Spear phishing emails
- UKR.NET phishing landing pages
slug: initial-access-phishing-and-vulnerability-exploitation
tactic: initial-access
techniques:
- T1566
- T1190
- name: Privilege Escalation via GooseEgg
observables:
- GooseEgg utility
- Windows Print Spooler service exploitation
- SYSTEM-level execution
slug: privilege-escalation-gooseegg
tactic: execution
techniques:
- T1190
- name: Net-NTLMv2 Hash Harvesting
observables:
- Net-NTLMv2 hashes
- Authentication to attacker-controlled SMB shares
- Crafted Outlook reminders
- Spoofed UKR.NET webmail portal
slug: credential-harvesting-ntlm-relay
tactic: credential-access
techniques:
- T1555
- name: Infrastructure Hijacking on Edge Devices
observables:
- MooBot botnet
- FrostArmada campaign
- Ubiquiti EdgeRouters
- MikroTik and TP-Link routers
- Rewritten DNS/DHCP settings pointing to actor-controlled resolvers
- X-Tunnel network pivot
slug: c2-edge-device-hijacking
tactic: command-and-control
techniques:
- T1572
- name: LLM-Integrated Data Exfiltration
observables:
- LLM-integrated infostealer
- Harvesting Office, PDF, and TXT documents
- Commands generated by legitimate AI services
slug: exfiltration-llm-infostealer
tactic: exfiltration
techniques:
- T1041
summary: APT28 (Fancy Bear) has transitioned from a decade-long reliance on a stable
in-house implant suite like X-Agent to a highly fragmented, disposable toolkit
and extensive infrastructure hijacking. The actor currently weaponizes edge devices
such as Ubiquiti and MikroTik routers for proxying traffic and harvesting credentials,
while integrating LLM-driven malware for automated document exfiltration.
series:
index: 1
slug: apt28-an-evolution-of-tradecraft-from-x-agent-to-llm-malware
title: 'APT28: An Evolution of Tradecraft from X-Agent to LLM Malware'
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
network:
category: network
name: Network telemetry
telemetry:
- network
tlp: clear
type: investigation
---
# APT28: Outlook and Print Spooler Exploitation
This hunt targets the endpoint-resident tradecraft of APT28, specifically focusing on the exploitation of CVE-2023-23397 (Outlook) and CVE-2022-38028 (Print Spooler). The hunt begins with a low-cost lead query to identify vulnerable hosts. If found, it triggers an expensive fan-out to search for active indicators: Net-NTLMv2 hash harvesting via forced SMB authentication, the GooseEgg privilege escalation utility, and lookups for known phishing domains. By correlating vulnerability state with behavioural indicators like rare spooler child processes and outbound SMB traffic, the hunt identifies active intrusions that standard signature-based rules may miss.
## lead-vulnerability-check
<!-- Identify vulnerable hosts -->
Identify endpoints that are vulnerable to the specific CVEs APT28 is known to weaponize.
```sqlite target=endpoint role=scoping params=(target_cves=target_cves)
~~~yaml
expected: A list of device UIDs that remain unpatched. Zero rows prove the estate
is patched against these specific flaws.
reads:
- affected_package_name
- affected_package_version
- cve_uid
- device_uid
- severity
silence: evidence_of_absence
source: hb_vulnerability_finding
verified: dry-run
verified_at: '2026-09-28'
~~~
SELECT device_uid, cve_uid, severity, affected_package_name, affected_package_version FROM hb_vulnerability_finding WHERE instr(',' || '{{target_cves}}' || ',', ',' || cve_uid || ',') > 0
```
## agent-lead-assessment
<!-- Assess vulnerability lead -->
```agent target=hunter
cite: required
context:
- lead-vulnerability-check
max_iterations: 3
objective: Review the vulnerability findings and decide if the environment is exposed
enough to continue the hunt for exploitation.
success_criteria: A recommendation to either start the deep dive or close the hunt.
tools:
- endpoint
- network
```
## gate-on-vulnerability
<!-- Gate on vulnerability presence -->
if~: "the lead assessment confirms that at least one host is unpatched for the target CVEs" (confidence: high, judge=hunter)
then: → deep-dive
indeterminate: → task-manual-review
unavailable: → task-manual-review (blind_spot: vulnerability-inventory-gap)
else: → task-close-out
## deep-dive
<!-- Fan-out behavioural analysis -->
parallel:
- → outbound-smb-relay
- → spooler-privesc-baseline
- → ukr-net-phishing-lookups
join: → agent-triage-results
## outbound-smb-relay
<!-- Outbound SMB connections from endpoints -->
Detect potential NTLM hash harvesting triggered by Outlook reminder exploitation.
```sqlite target=network role=enrichment params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
expected: Network connections to port 445 on external IP addresses; silence proves
no unauthenticated SMB traffic left the scope.
reads:
- device_hostname
- direction
- dst_endpoint_ip
- dst_endpoint_port
- process_name
- time
silence: not_evidence_of_absence
source: hb_network_connection
verified: dry-run
verified_at: '2026-09-28'
~~~
SELECT device_hostname, process_name, dst_endpoint_ip, dst_endpoint_port, time FROM hb_network_connection WHERE dst_endpoint_port = 445 AND direction = 'outbound' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```
## spooler-privesc-baseline
<!-- Rare child processes of Print Spooler -->
Detect the use of GooseEgg by identifying anomalous children of the Spooler service.
```sqlite target=endpoint role=detection-candidate params=(lookback_days=lookback_days, scope_hosts=scope_hosts)
~~~yaml
baseline:
compare: first_seen
window: '{{lookback_days}}d'
expected: Rare processes launched by spoolsv.exe; fleet-wide printing utilities will
be filtered by the HAVING clause.
prevalence:
by: device_hostname
key:
- process_name
rare_below: 3
reads:
- device_hostname
- parent_process_name
- process_name
- time
silence: not_evidence_of_absence
source: hb_process_activity
verified: dry-run
verified_at: '2026-09-28'
~~~
SELECT LOWER(process_name) AS proc, COUNT(DISTINCT device_hostname) AS host_count, MIN(time) AS first_seen FROM hb_process_activity WHERE LOWER(parent_process_name) LIKE '%spoolsv.exe' AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0) GROUP BY proc HAVING host_count <= 3 ORDER BY host_count ASC
```
## ukr-net-phishing-lookups
<!-- Lookups for ukr.net phishing indicators -->
Identify activity related to the spoofed webmail portal used in recent campaigns.
```sqlite target=endpoint role=enrichment params=(lookback_days=lookback_days, ukr_net_domain=ukr_net_domain, scope_hosts=scope_hosts)
~~~yaml
expected: Any resolution of the targeted phishing domain on an endpoint.
reads:
- device_hostname
- process_name
- query_hostname
- time
silence: not_evidence_of_absence
source: hb_dns_activity
verified: dry-run
verified_at: '2026-09-28'
~~~
SELECT device_hostname, process_name, query_hostname, time FROM hb_dns_activity WHERE instr(',' || '{{ukr_net_domain}}' || ',', ',' || LOWER(query_hostname) || ',') > 0 AND time >= datetime('now', '-{{lookback_days}} days') AND ('{{scope_hosts}}' = '' OR instr(',' || '{{scope_hosts}}' || ',', ',' || device_hostname || ',') > 0)
```
## agent-triage-results
<!-- Synthesize exploitation evidence -->
```agent target=hunter
cite: required
context:
- agent-lead-assessment
- outbound-smb-relay
- spooler-privesc-baseline
- ukr-net-phishing-lookups
max_iterations: 6
objective: Determine if any host is exhibiting signs of active APT28 exploitation,
such as GooseEgg execution or NTLM relay activity, and weight the risk for each
host.
success_criteria: A per-host verdict of compromised, suspicious, or benign with supporting
citations.
tools:
- endpoint
- network
```
## decision-route-verdict
<!-- Route based on triage verdict -->
if~: "the triage verdict identifies at least one compromised host with active exploitation indicators" (confidence: high, judge=hunter)
then: → action-isolate-host
indeterminate: → task-manual-review
unavailable: → task-manual-review (blind_spot: no-agent-coverage)
else: → task-manual-review
## action-isolate-host
<!-- Isolate compromised host -->
```action target=endpoint
~~~yaml
approval: required
~~~
Isolate the host immediately. Revoke all active sessions for the user account identified in the triage.
```
→ task-manual-review
## task-manual-review
<!-- Manual analyst review -->
```manual target=analyst
Review the cited rows. Inspect the host for the presence of the GooseEgg utility or unusual SMB client activity. Check for Net-NTLMv2 hash relay attempts in identity provider logs.
```
→ task-close-out
## task-close-out
<!-- Close out hunt -->
```manual target=analyst
Document the vulnerable vs patched state of the estate. Recommend a standing rule for rare spoolsv.exe children if successful.
```
→ 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, critic, then reviewed by a person.