On July 4, 2017, Ukrainian cyber police, backed by a Europol team, seized servers at the Kyiv offices of Intellect Service, the 20-person company that publishes the M.E.Doc accounting program. The raid was the public end of an investigation that had begun eight days earlier when NotPetya began destroying enterprise networks across Ukraine and, through Ukrainian subsidiaries, across the world. The investigation's first-order finding, confirmed publicly by Colonel Serhiy Demedyuk of the Ukrainian Cyber Police on July 5, was that M.E.Doc's update server had been compromised for at least six weeks before NotPetya's June 27 detonation.
This post is about the forensic detail of that compromise. NotPetya's blast radius has been documented elsewhere, including in another post in this series. Here I want to focus on the origin: how did a small Ukrainian software vendor's update infrastructure become the delivery vehicle for what the White House called the most destructive cyberattack in history?
Intellect Service, in context
Intellect Service (Linkos Group) was founded in 1997. By 2017, M.E.Doc was effectively mandatory for Ukrainian businesses that filed electronic tax returns, which covered an estimated 80 percent of Ukrainian enterprises. International branches of multinational companies operating in Ukraine also ran it, which is how Maersk, Merck, Mondelez, Reckitt Benckiser, and TNT Express ended up hit.
The company had around 20 employees. Its infrastructure was, by its own post-incident admissions, small and loosely managed. A single IT administrator handled the update servers. Source code and release binaries lived on infrastructure that was not air-gapped from the business network. The build process was manual and lightly audited.
None of this is unusual for a small enterprise software vendor. That is the uncomfortable part.
The three poisoned updates
Cisco Talos and ESET published technical analyses in July 2017 confirming that at least three M.E.Doc updates before June 27 had been tampered with:
- April 14, 2017. Update contained a module (
ZvitPublishedObjects.dll) with a backdoor capability for arbitrary code execution. This appears to have been the reconnaissance and staging phase. - May 15, 2017. A second update with an updated backdoor, refined capabilities, credential gathering.
- June 22, 2017. A third update, delivered five days before NotPetya, containing the NotPetya loader staged to activate on a delayed trigger.
Each of these updates was delivered through ezvit.exe, M.E.Doc's auto-update component. They were signed by the Intellect Service certificate. They were served from the legitimate upd.me-doc.com.ua infrastructure. From the perspective of the M.E.Doc client software, they were indistinguishable from normal updates.
The backdoor itself was embedded in ZvitPublishedObjects.dll, a module of M.E.Doc that handled report generation. ESET's analysis showed that the attackers had modified specific methods inside the DLL to:
- Read a configuration blob from a specific registry key,
HKCU\SOFTWARE\WC. - If that blob contained a C2 instruction, fetch a payload over HTTPS and execute it.
- Use the unique company EDRPOU tax identifier (Ukrainian business tax ID) as a fingerprint, allowing the attackers to selectively target specific companies by their tax ID.
That EDRPOU targeting is the most revealing detail. The attackers were not just distributing NotPetya broadly. They had the ability to identify specific Ukrainian companies by their government-issued business tax identifier. That is a precision targeting capability, disguised as blunt-force ransomware.
How the compromise happened
The Ukrainian Cyber Police investigation, combined with ESET and Talos analyses, produced the following reconstruction:
Initial access, timing. At some point in late 2016 or early 2017, attackers gained access to Intellect Service's internal network. The specific initial vector has not been published in public reporting. The most likely paths, based on Intellect Service's post-incident admissions, were phishing against employees with administrative access and reuse of a weak service account password that had access to multiple production systems.
Lateral movement. From internal network access, the attackers moved to the update server infrastructure. That infrastructure was not segmented from the corporate network, an architectural weakness confirmed by the Ukrainian Cyber Police statement on July 5.
Persistence. The attackers gained access to both the source code repository for M.E.Doc and the build/signing pipeline. They modified ZvitPublishedObjects.dll at the source level, meaning the malicious code went through whatever minimal build process existed at Intellect Service, was signed by the automatic signing system, and was distributed through the update server as a normal release.
The six-week dwell. The April 14 and May 15 updates were reconnaissance and capability-building. The backdoor allowed the attackers to enumerate infected machines, identify high-value targets via EDRPOU tax ID, and stage additional capabilities. During this dwell period, the attackers were building a map of which Ukrainian enterprises ran M.E.Doc and, through it, had reachable internal networks.
The detonation. The June 22 update carried NotPetya. Five days later, on June 27 at 10:30 UTC, NotPetya activated and began its worming propagation using EternalBlue, EternalRomance, and Mimikatz-harvested credentials.
The signing problem, specifically
Intellect Service's code-signing certificate was issued by a Ukrainian CA. It was used to sign all M.E.Doc updates. The signing process, per post-incident disclosure, was automated: whatever binary the build system produced, the signing system signed. There was no multi-party review, no reproducible-build check, no separate signer with distinct credentials.
That pattern, the build system and the signing system sharing a trust boundary, is what made the compromise end-to-end. Once the attackers had access to the build system, the signing happened automatically, and the update distribution happened automatically, and the client validation happened automatically, and the payload ran on 80 percent of Ukrainian enterprises.
Every link in that chain was legitimate from a cryptographic perspective. The chain was compromised because the root of it, the small vendor's internal network, was compromised.
What Intellect Service did after
In August 2017, Intellect Service issued a detailed post-incident statement. The remediation steps included:
- Rebuilding all infrastructure from scratch, on segmented networks.
- Moving the update signing key to a hardware security module with manual approval for each release.
- Introducing two-person review for all production code commits.
- Engaging an external security firm for ongoing audit.
Mykola Lynnyk, Intellect Service's CEO, publicly apologized for the breach. He also pushed back on claims that the company had been negligent, pointing out that the intrusion was attributed to a state-level actor and that few 20-person companies are equipped to resist dedicated state-grade adversaries.
Both things are true. Intellect Service's infrastructure was inadequate. Intellect Service was also attacked by an adversary that has compromised significantly larger organizations.
The structural lesson
The hardest lesson from M.E.Doc is not about Intellect Service specifically. It is about the shape of risk when an industry depends on a small vendor for a mandatory function.
Ukrainian tax law effectively required using M.E.Doc (or a similar certified product, of which there were very few). That requirement concentrated risk in Intellect Service. Intellect Service was a small company with limited security budget. The adversary knew this and targeted accordingly.
The same pattern exists in every market where regulatory, contractual, or integration lock-in creates de-facto monopolies held by small vendors. Healthcare billing software. Industrial control HMI vendors. Shipping-industry-specific EDI systems. Tax-prep tools in many jurisdictions. Any of them could be an M.E.Doc in waiting.
The defensive response is not to demand small vendors adopt FAANG-grade security posture (that is not going to happen economically). It is to treat vendor update channels as untrusted by default and to build detection into the client side of the relationship. If an M.E.Doc update introduces new outbound network behavior, or adds new API calls, or writes to new registry keys, the enterprise running M.E.Doc should know before the update deploys, not after.
What the incident changed at the protocol level
Ukraine's national CERT (CERT-UA) post-NotPetya began publishing certified update hashes for commonly used business software. Enterprises could subscribe to a feed of expected hashes and compare to what their endpoints actually received. Adoption was uneven, but the concept took hold.
International agencies followed in more targeted ways. The U.S. CISA's ICS-CERT and equivalent bodies began publishing more detailed supply chain advisories, including for small foreign vendors whose software was present in U.S. critical infrastructure.
How Safeguard Helps
M.E.Doc exposed the risk of trusting vendor updates by default. Safeguard instruments that trust by generating per-update SBOMs and comparing them against baselines, so a new module like ZvitPublishedObjects.dll with altered behavior shows up as a diff rather than as an invisible update. Reachability analysis tells you which of your systems actually execute vendor code paths with network or credential access, prioritizing where a compromised update would hurt most. Griffin AI reviews vendor update changes for anomalies like unexpected outbound callbacks or registry-based triggers, TPRM scores small vendors on build and signing posture so you know which suppliers deserve deeper review, and policy gates let you hold vendor releases in staging until attestation clears before they reach production.