I clicked on the message and lost all my passwords, 2FA tokens, and three months of correspondence.

Webmail phishing attacks typically follow a familiar pattern: attachment, link, macro, downloader. In the new campaign against a Ukrainian government agency, the attackers dispensed with all of these elements. The malicious code was hidden directly in the HTML body of the email, and it was launched the moment the victim opened the message in a vulnerable version of Zimbra.
The attack was discovered by Seqrite Labs specialists. The recipient of the fateful email was an employee of the State Hydrographic Agency, which is responsible for navigational, maritime, and hydrographic support for shipping and is considered a critical infrastructure agency.
The bait was mundane in appearance, making it particularly dangerous. The email, written in Ukrainian, was disguised as a phishing scam requesting an internship. The sender posed as a fourth-year student at the National Academy of Internal Affairs and politely asked if the recipient had any suitable contacts or job openings. An apology was added at the end in case the message had reached the wrong recipient. This ploy has long been known in phishing: a harmless tone lowers suspicion and makes the email appear like a regular work correspondence.
At first glance, the email appeared unremarkable. Researchers found no malicious attachments, external links, or macro-enabled Office documents. The entire attack chain was contained within the HTML markup of a single message. The code was hidden in a hidden div with the display:none style and consisted of a large fragment of Base64-encoded JavaScript. Additionally, filtering was bypassed using the @import CSS directive embedded in tag and attribute names. To simple verification tools, this fragment appeared as broken markup, but the browser ultimately assembled it into an executable script.
Technically, the attack relied on an XSS vulnerability in Zimbra Collaboration Suite. The issue stemmed from insufficient HTML sanitization, including when handling specially crafted @import constructs and other script injection vectors. The vulnerability had already been patched in ZCS 10.0.18 and 10.1.13 back in November 2025, but the attack demonstrated that not all installations had been updated in time. Importantly, the vulnerability required the victim to open the email in the classic Zimbra web interface, where the malicious markup was executed within the context of an active session.
After opening the email, the script ran in the browser almost unnoticed by the user. The first stage was a JavaScript loader. It checked whether the malicious module was already embedded in the page, then decoded the payload using atob(), XORed the key twichcba5e, and loaded the final code into the top document of the page. This technique gave the malicious module access to cookies, localStorage, and same-origin permissions as the webmail itself. Simply put, the script gained almost the same capabilities as the victim's open Zimbra session.
The second stage operated as a full-fledged browser stealer. The code ran directly in the browser's memory and collected credentials, SOAP session tokens, two-factor authentication backup codes, email inbox contents, attachments, cookies, and other sensitive information. A separate 12-character identifier was created for each victim, which was used in all requests to the command-and-control server. The researchers specified the domain zimbrasoft[.]com[.]ua as the command-and-control center. If any stage failed, the malicious code sent a message to the server with the stage name, error text, and a traceback, allowing the operator to immediately identify where the chain had failed.
Of particular interest is the exploitation of Zimbra's internal API. The malicious code used SOAP requests to /service/soap/, the default interface of the mail system itself. To validate the requests, it substituted a stolen cross-site request forgery protection token, which Zimbra stores in plaintext in localStorage in the classic interface. This made the requests appear as normal user activity within the webmail system. If one of the SOAP requests was rejected, the wrapper simply returned null and allowed the remaining operations to continue in parallel.
Data was exported via two channels simultaneously: HTTPS and DNS. For DNS transfer, values were encoded using RFC 4648 Base32, broken into 60-character chunks, and converted into subdomain names. This method is useful for attackers on networks where regular web traffic is more strictly filtered, while DNS queries are more easily processed. Larger objects, such as full configuration dumps, were serialized as binary data and sent via HTTPS to the /v/d path with the X-Filename header. Short messages, beacons, and service events were sent via the /v/p path. As a result, the same fragment could be sent via two paths simultaneously: DNS was helpful on restricted networks, while HTTPS preserved the full data if the channel was available.
The malicious code launched nine parallel tasks simultaneously via Promise.all. This approach was necessary to collect the maximum amount of data, even if the user quickly closed the tab. One function sent a startup beacon to the command-and-control server. Another collected email addresses and account data, first attempting to extract them from embedded page variables and then requesting them via GetIdentitiesRequest. A separate module fingerprinted the client environment and, via GetInfoRequest, requested a full set of server and account parameters, including the Zimbra version, mailbox quota, settings, and node name. The resulting data was saved as a JSON file with analytics.
One of the most dangerous features extracted backup two-factor authentication codes via GetScratchCodesRequest. These codes are intended for emergency login when the user doesn't have the second factor at hand. If an attacker obtains them, changing the password isn't always enough: some security barriers can be bypassed later. Each code was sent separately via DNS.
Another module created an application password via the CreateAppSpecificPasswordRequest. Zimbra allows for the generation of unique passwords for external clients and services. In the analyzed sample, a new permanent password was created under the name ZimbraWeb. The problem is that such a secret often survives a regular master password change. For an attacker, this is a convenient way to gain a foothold in the mailbox for a long time and access it later via IMAP or API, even if the victim has already noticed something is wrong.
The malicious code also collected data about connected mobile devices via GetDeviceStatusRequest from the urn:zimbraSync namespace. The response included device IDs, types, sync status, and other useful details. For the attackers, this wasn't just technical information, but a victim profile and a foundation for further attacks on the mobile network. Furthermore, via GetOAuthConsumersRequest, the script downloaded a list of third-party OAuth applications with access to the mailbox. This gave the operator a picture of the external services associated with the account and helped them understand which platforms could be used for further exploitation.
A separate technique targeted not Zimbra, but the browser. Two hidden form fields with the attributes autocomplete="username" and autocomplete="current-password" were injected into the page undetected. The code then waited five seconds for the browser manager to autofill the values. Afterward, the content was read, sent to the server, and the injected elements were deleted. This step doesn't even require a Zimbra token, as the attack operates at the browser level and relies on password managers' habit of automatically filling in saved data.
Another module silently enabled IMAP via ModifyPrefsRequest, setting the zimbraPrefImapEnabled setting to TRUE. The rationale here was extremely pragmatic: after creating an application password, the attacker needed a remote protocol that could be used to read email in a regular client and continue monitoring the mailbox without a web interface. Enabling IMAP transformed a one-time compromise into persistent access .
The heaviest and perhaps most valuable part of the attack involved downloading 90 days of archived correspondence. The sendArchives module cycled through days 0 through 89 and downloaded all unsolicited email not marked as spam for each day via Zimbra's built-in export point /home/~/?fmt=tgz. Each daily archive was immediately sent to the server. For modern browsers, streaming was used via ReadableStream without buffering in memory. For older browsers, a buffered mode with a 500 MB limit was used. Control keys of the form zd_comp_YYYY-MM-DD were stored in localStorage to prevent downloaded days from being re-downloaded when the tab was reopened. A 24-hour timeout was set for each day, meaning the tab could remain open and download archives for a very long time until the user closed it.
The researchers write that the command-and-control server domain was created on January 20, 2026, just shortly before the mailing. At least two generated domains have already been detected in the infrastructure: js-l1wt597cimk[.]i[.]zimbrasoft[.]com[.]ua and js-26tik3egye4[.]i[.]zimbrasoft[.]com[.]ua. The email arrived on January 22, 2026, and according to the headers, it was sent through infrastructure associated with the National Academy of Internal Affairs. The researchers speculate that the sender's account had likely already been compromised. An additional caveat: at the time of initial detection, the sample had no hits on VirusTotal.
The report specifically mentions previously documented operations by APT28, APT29, and TA473 against Zimbra and other webmail platforms in Eastern Europe. The GhostMail attack is most closely related to APT28 activity. Seqrite Labs points to overlaps with Operation RoundPress and the SpyPress.ZIMBRA malicious logic, which also used SOAP requests to the Zimbra API and email dumping. This discovery has already been reported to CERT-UA.
The GhostMail story clearly demonstrates how the very logic of email attacks is changing. Attackers no longer always need an executable file on disk, a macro in a document, or a separate downloader. If webmail is vulnerable, the entire range of actions can be performed directly in the browser, within an active user session, using the platform's built-in capabilities against itself. This leads to very specific conclusions for defenders: update Zimbra without delay, strictly filter HTML content in webmail, and closely monitor anomalous SOAP requests, unexpected IMAP activations, the creation of application passwords, and the mass export of email archives.

Webmail phishing attacks typically follow a familiar pattern: attachment, link, macro, downloader. In the new campaign against a Ukrainian government agency, the attackers dispensed with all of these elements. The malicious code was hidden directly in the HTML body of the email, and it was launched the moment the victim opened the message in a vulnerable version of Zimbra.
The attack was discovered by Seqrite Labs specialists. The recipient of the fateful email was an employee of the State Hydrographic Agency, which is responsible for navigational, maritime, and hydrographic support for shipping and is considered a critical infrastructure agency.
The bait was mundane in appearance, making it particularly dangerous. The email, written in Ukrainian, was disguised as a phishing scam requesting an internship. The sender posed as a fourth-year student at the National Academy of Internal Affairs and politely asked if the recipient had any suitable contacts or job openings. An apology was added at the end in case the message had reached the wrong recipient. This ploy has long been known in phishing: a harmless tone lowers suspicion and makes the email appear like a regular work correspondence.
At first glance, the email appeared unremarkable. Researchers found no malicious attachments, external links, or macro-enabled Office documents. The entire attack chain was contained within the HTML markup of a single message. The code was hidden in a hidden div with the display:none style and consisted of a large fragment of Base64-encoded JavaScript. Additionally, filtering was bypassed using the @import CSS directive embedded in tag and attribute names. To simple verification tools, this fragment appeared as broken markup, but the browser ultimately assembled it into an executable script.
Technically, the attack relied on an XSS vulnerability in Zimbra Collaboration Suite. The issue stemmed from insufficient HTML sanitization, including when handling specially crafted @import constructs and other script injection vectors. The vulnerability had already been patched in ZCS 10.0.18 and 10.1.13 back in November 2025, but the attack demonstrated that not all installations had been updated in time. Importantly, the vulnerability required the victim to open the email in the classic Zimbra web interface, where the malicious markup was executed within the context of an active session.
After opening the email, the script ran in the browser almost unnoticed by the user. The first stage was a JavaScript loader. It checked whether the malicious module was already embedded in the page, then decoded the payload using atob(), XORed the key twichcba5e, and loaded the final code into the top document of the page. This technique gave the malicious module access to cookies, localStorage, and same-origin permissions as the webmail itself. Simply put, the script gained almost the same capabilities as the victim's open Zimbra session.
The second stage operated as a full-fledged browser stealer. The code ran directly in the browser's memory and collected credentials, SOAP session tokens, two-factor authentication backup codes, email inbox contents, attachments, cookies, and other sensitive information. A separate 12-character identifier was created for each victim, which was used in all requests to the command-and-control server. The researchers specified the domain zimbrasoft[.]com[.]ua as the command-and-control center. If any stage failed, the malicious code sent a message to the server with the stage name, error text, and a traceback, allowing the operator to immediately identify where the chain had failed.
Of particular interest is the exploitation of Zimbra's internal API. The malicious code used SOAP requests to /service/soap/, the default interface of the mail system itself. To validate the requests, it substituted a stolen cross-site request forgery protection token, which Zimbra stores in plaintext in localStorage in the classic interface. This made the requests appear as normal user activity within the webmail system. If one of the SOAP requests was rejected, the wrapper simply returned null and allowed the remaining operations to continue in parallel.
Data was exported via two channels simultaneously: HTTPS and DNS. For DNS transfer, values were encoded using RFC 4648 Base32, broken into 60-character chunks, and converted into subdomain names. This method is useful for attackers on networks where regular web traffic is more strictly filtered, while DNS queries are more easily processed. Larger objects, such as full configuration dumps, were serialized as binary data and sent via HTTPS to the /v/d path with the X-Filename header. Short messages, beacons, and service events were sent via the /v/p path. As a result, the same fragment could be sent via two paths simultaneously: DNS was helpful on restricted networks, while HTTPS preserved the full data if the channel was available.
The malicious code launched nine parallel tasks simultaneously via Promise.all. This approach was necessary to collect the maximum amount of data, even if the user quickly closed the tab. One function sent a startup beacon to the command-and-control server. Another collected email addresses and account data, first attempting to extract them from embedded page variables and then requesting them via GetIdentitiesRequest. A separate module fingerprinted the client environment and, via GetInfoRequest, requested a full set of server and account parameters, including the Zimbra version, mailbox quota, settings, and node name. The resulting data was saved as a JSON file with analytics.
One of the most dangerous features extracted backup two-factor authentication codes via GetScratchCodesRequest. These codes are intended for emergency login when the user doesn't have the second factor at hand. If an attacker obtains them, changing the password isn't always enough: some security barriers can be bypassed later. Each code was sent separately via DNS.
Another module created an application password via the CreateAppSpecificPasswordRequest. Zimbra allows for the generation of unique passwords for external clients and services. In the analyzed sample, a new permanent password was created under the name ZimbraWeb. The problem is that such a secret often survives a regular master password change. For an attacker, this is a convenient way to gain a foothold in the mailbox for a long time and access it later via IMAP or API, even if the victim has already noticed something is wrong.
The malicious code also collected data about connected mobile devices via GetDeviceStatusRequest from the urn:zimbraSync namespace. The response included device IDs, types, sync status, and other useful details. For the attackers, this wasn't just technical information, but a victim profile and a foundation for further attacks on the mobile network. Furthermore, via GetOAuthConsumersRequest, the script downloaded a list of third-party OAuth applications with access to the mailbox. This gave the operator a picture of the external services associated with the account and helped them understand which platforms could be used for further exploitation.
A separate technique targeted not Zimbra, but the browser. Two hidden form fields with the attributes autocomplete="username" and autocomplete="current-password" were injected into the page undetected. The code then waited five seconds for the browser manager to autofill the values. Afterward, the content was read, sent to the server, and the injected elements were deleted. This step doesn't even require a Zimbra token, as the attack operates at the browser level and relies on password managers' habit of automatically filling in saved data.
Another module silently enabled IMAP via ModifyPrefsRequest, setting the zimbraPrefImapEnabled setting to TRUE. The rationale here was extremely pragmatic: after creating an application password, the attacker needed a remote protocol that could be used to read email in a regular client and continue monitoring the mailbox without a web interface. Enabling IMAP transformed a one-time compromise into persistent access .
The heaviest and perhaps most valuable part of the attack involved downloading 90 days of archived correspondence. The sendArchives module cycled through days 0 through 89 and downloaded all unsolicited email not marked as spam for each day via Zimbra's built-in export point /home/~/?fmt=tgz. Each daily archive was immediately sent to the server. For modern browsers, streaming was used via ReadableStream without buffering in memory. For older browsers, a buffered mode with a 500 MB limit was used. Control keys of the form zd_comp_YYYY-MM-DD were stored in localStorage to prevent downloaded days from being re-downloaded when the tab was reopened. A 24-hour timeout was set for each day, meaning the tab could remain open and download archives for a very long time until the user closed it.
The researchers write that the command-and-control server domain was created on January 20, 2026, just shortly before the mailing. At least two generated domains have already been detected in the infrastructure: js-l1wt597cimk[.]i[.]zimbrasoft[.]com[.]ua and js-26tik3egye4[.]i[.]zimbrasoft[.]com[.]ua. The email arrived on January 22, 2026, and according to the headers, it was sent through infrastructure associated with the National Academy of Internal Affairs. The researchers speculate that the sender's account had likely already been compromised. An additional caveat: at the time of initial detection, the sample had no hits on VirusTotal.
The report specifically mentions previously documented operations by APT28, APT29, and TA473 against Zimbra and other webmail platforms in Eastern Europe. The GhostMail attack is most closely related to APT28 activity. Seqrite Labs points to overlaps with Operation RoundPress and the SpyPress.ZIMBRA malicious logic, which also used SOAP requests to the Zimbra API and email dumping. This discovery has already been reported to CERT-UA.
The GhostMail story clearly demonstrates how the very logic of email attacks is changing. Attackers no longer always need an executable file on disk, a macro in a document, or a separate downloader. If webmail is vulnerable, the entire range of actions can be performed directly in the browser, within an active user session, using the platform's built-in capabilities against itself. This leads to very specific conclusions for defenders: update Zimbra without delay, strictly filter HTML content in webmail, and closely monitor anomalous SOAP requests, unexpected IMAP activations, the creation of application passwords, and the mass export of email archives.