On 29 March, CrowdStrike reported malicious activity coming out of a legitimate, signed copy of 3CXDesktopApp, built by 3CX and downloaded from 3CX. This walks the chain from that installer to the icon files on GitHub that carried its command and control addresses, and then to the interim finding three weeks later that the vendor had itself been breached through somebody else's signed installer. The argument is older than the incident: a signature proves who built a file and never what it does.
23 April 2023·9 min read·security
The sentence CrowdStrike opened with on the twenty-ninth of March is worth reading twice, because nothing in it is an accusation. It observed unexpected malicious activity emanating from a legitimate, signed binary. Not a forged signature, not an expired certificate, not a lookalike domain serving a fake installer. A real product, built by the company whose name is on it, delivered from that company's own servers.
Everything a defender is told to check, checked out. The publisher was who it claimed to be. The file had not been altered since signing. It came from the vendor rather than a mirror, a torrent or a search advertisement. A user who followed every piece of standard advice with unusual care arrived at exactly the same place as a user who followed none of it.
The affected builds were 3CXDesktopApp 18.12.407 and 18.12.416 on Windows, and 18.11.1213 and later on macOS, which is to say a normal minor version of a softphone that sits in the tray on a receptionist's machine and a support agent's machine and a sales team's laptops. The install base is the point. Nobody deploys a softphone to three people.
So here is the claim the rest of this rests on, and it is older than the incident. A code signature is an assertion about the past, made by a certificate authority about a company's paperwork, and every question you actually have about a running program is about the future. The two are not close enough to substitute, and the industry has spent twenty years treating them as if they were.
A signed installer arrived over the normal update path. Volexity, publishing on 30 March, described a malicious MSI signed by 3CX and downloaded from 3CX's servers. There is no moment in that sentence at which a user, or an installer policy, or a software inventory, has anything to object to. The product updated itself the way it does every month.
The real executable loaded a fake library. The legitimate 3CXDesktopApp.exe loaded a malicious ffmpeg.dll shipped alongside it. Side loading works because a program is trusted to name its own dependencies, and nothing in the platform asks whether the library sitting next to a signed executable has any business being there.
A payload was appended to the end of a file. Symantec noted an encrypted blob appended to d3dcompiler_47.dll, beginning with the hex value FEEDFACE so the loader could find it, and said the file was possibly a trojanized copy of a legitimate library. Unit 42 described the earlier stage decrypting part of it with RC4 and running the result as shellcode.
Instructions were collected from a public code host. The next stage pulled icon files from a GitHub repository, sixteen of them, each carrying base64 data after a dollar sign at the end of an otherwise valid image. Decrypted, that yielded the command and control addresses. Volexity found commits in that repository going back to 7 December 2022, which puts patience ahead of everything else in this story.
And then came an ordinary infostealer. The final stage, which Volexity named ICONICSTEALER, took hostname and operating system version, browser history from Chrome, Edge, Brave and Firefox, and the application's own configuration file. Modest by the standards of the machinery in front of it, which is usually what a first stage of collection looks like.
The thing that infected these machines is the same thing that patches them. An update channel is a standing arrangement in which a vendor may execute code of its choosing on your endpoints, at a time of its choosing, without review, without a diff, and without anybody in your organization being asked. That arrangement is correct. I would not turn it off, and an argument that ends in disabling automatic updates has lost more than it won.
But look at what it does to advice. The instruction every security awareness program gives is to install software only from the vendor. Here that instruction was a routing rule to the compromise. The people who got hurt are the ones who followed policy, kept current, and had automatic updates enabled, which is the population every program is trying to grow.
The scale property follows from the mechanism rather than from anything about this vendor. A single build, signed once, reaches every customer in a few days because reaching every customer quickly is the feature. So the same design decision that shortens the window between a fix and its arrival shortens the window between a compromise and its arrival, and there is no version of the channel that has one property without the other.
Which means the control cannot sit at installation time. You are not going to review vendor updates; nobody has ever done it at a useful rate and the code is not available to review anyway. If the decision to trust is made once, at purchase, then everything after it has to be a judgment about behavior, and behavior is observed at runtime or not at all.
The vendor was compromised the way its customers were. In an interim report published on 20 April, Mandiant traced the intrusion to a trojanized installer for X_TRADER, a trading application, downloaded from the Trading Technologies website. So 3CX became a victim of a supply chain attack before it became the delivery mechanism for one, which Mandiant describes as the first cascading compromise of this shape it has observed.
It was signed again, by a different company. That X_TRADER installer carried a legitimate digital signature from Trading Technologies International, Inc. Two signed installers in one chain, both valid, both correctly identifying their publisher, neither making any claim at all about what the software would do once it ran. The signature was never the weak link, because the signature was never load bearing.
They took the build, not the download. The report describes a backdoor called VEILEDSIGNAL, lateral movement inside the network, and the compromise of both the Windows and macOS build environments. That is why the resulting binary was authentic in every technical sense: it was produced by 3CX's build pipeline and signed by 3CX's key, because the attacker was inside the pipeline holding the key.
Read the hedging, and the point survives it. Mandiant attributes the activity to UNC4736, a cluster it describes as suspected North Korea nexus, with a moderate confidence link to earlier AppleJeus operations. It is an interim report three days old and those qualifiers are its own. None of the argument here depends on the attribution, which is a good test of whether an argument was about the mechanism.
Authenticode makes two claims and both were true throughout. A private key controlled by an entity that a certificate authority vouched for produced this file, and the file has not been modified since. There is no third claim, and there is no field in the format where a third claim could go, because deciding what a program will do is not a thing any procedure can do in advance.
The chain of trust bottoms out in a business relationship. A certificate authority checked a company's registration documents and issued a certificate, and everything downstream inherits the strength of that check. Compromise the build system and the pipeline applies a genuine signature to hostile content, and every verification anybody performs afterward returns true, because every verification is asking a question whose honest answer is yes.
The only lever after the fact is revocation, and it is blunt in a way that guarantees hesitation. Revoking a key invalidates every binary ever signed with it, including all the versions that were fine, on every machine that checks. So the vendor weighs breaking its entire installed base against leaving a window open, takes some time over it, and the window is open for exactly as long as that deliberation lasts.
Which is why publisher-based allowlisting is not the control people believe they have bought. It converts the question should this run into the question who signed this, and the second question had a correct and reassuring answer for every hour of this campaign. An allowlist by publisher is a decision made once, in advance, about a company. The thing you needed to decide was about a process, this afternoon.
Application control rules are written by publisher. Windows application control lets an administrator permit everything signed by a named company, and it is the only form of allowlisting that survives contact with a real fleet, because the alternative is maintaining hashes for every update of every product. The rule is a bet that a publisher's future output resembles its past output.
Endpoint agents quiet down for known signers. Trusting the signer is how detection products keep their false positive rate low enough that anyone leaves them enabled, and it is a reasonable engineering choice. It also means the confidence a defender has in an alert is partly a function of who signed the thing that generated it.
Reputation systems reward a long history. A newly signed binary from an unknown publisher gets a warning; one from a publisher with years of clean history does not. That is exactly backward for this attack, because the attacker inherited the reputation along with the key, and inherited it at full strength.
Gatekeeper runs a check that is not a behavior check. On macOS an application that is signed and notarized runs without an obstacle, and notarization is an automated scan for known malicious content rather than a statement about what the program does. A novel payload inside an authentic build is precisely the case that scan is not designed to answer.
Inventory tools key off publisher and version. Compliance reporting asks whether the installed product is the current supported version from the expected vendor. A trojanized build of the right version from the right vendor answers yes to all three, so the machine appears in the report as one of the well-managed ones.
Behavior did. SentinelOne says it began seeing a spike in behavioral detections of the application on 22 March, a week before anybody published anything, and CrowdStrike's own opening sentence on the twenty-ninth is an anomaly statement rather than a signature match: unexpected activity, from a binary it knew perfectly well. Both are descriptions of software doing something the software had not done before.
The indicators, meanwhile, were built to defeat the list-shaped controls. Among the twenty actor-controlled domains CrowdStrike published were names reading as akamaicontainer.com, msstorageazure.com and pbxcloudeservices.com. An egress policy that decides by whether a hostname looks like infrastructure waves all three through, and a reviewer skimming a firewall log does the same, faster.
And the step that handed out those addresses ran over GitHub, which is reachable from essentially every corporate network in existence and cannot sensibly be blocked. A dead drop on a service you have already decided to allow makes a blocklist of command and control domains close to worthless, because the malware collects a fresh list from a destination that was never on yours.
The timeline is the part I keep returning to. Volexity found commits in that icon repository going back to 7 December 2022, and the first behavioral detections came on 22 March, and the first public analysis on the twenty-ninth. So the capability sat staged for months, and then a further week passed between machines behaving oddly and anyone stating a conclusion. Detection is not an event. It is a signal, and then an argument about the signal.
Write down where each application should talk. A softphone has a short and boring list of destinations: the vendor, an update host, the media path. It is a list an engineer can produce in ten minutes for the six applications that matter, and nothing else in this incident would have been as cheap to notice.
Baseline the application, not the machine. A workstation cannot be default-deny to the internet and pretending otherwise wastes a year. Per-application egress is a different shape and an achievable one, because the deviation you are looking for is a familiar program talking somewhere it has never talked before.
Stop suppressing alerts by vendor. Most tuning work is the quiet removal of noise from software everybody trusts, and that suppression is exactly the hole this walked through. If the reason a detection is muted is the publisher's name, the mute is inherited from a signature check that was never about behavior.
Treat your build system as production. Both build environments were compromised here, and a host that signs is a host that holds the key. Build infrastructure usually has looser access control, older packages and more privileged credentials than anything it produces, on the grounds that it is only internal.
Delay is a control you own. You cannot review vendor updates, but you can hold them for a few days on the handful of applications with the widest install base. That trades a short window of unpatched exposure for the chance that somebody else finds it first, and it is a genuine trade rather than a free one.
I want to narrow the claim, because there is a version of this argument that ends somewhere useless. Signing is worth doing. It stops the tampered mirror, the substituted download, the modified binary sitting on a file share, and those are common attacks that it defeats completely. Nothing here says the certificate is theater. It says the certificate answers a narrower question than the one people ask it, and that the narrowness only becomes visible when a build system falls.
What stays with me is the shape of the trust, not its strength. A signature is a letter of introduction, and this one was genuine, correctly addressed, and written by exactly the person it claimed to be. It said who was at the door. It did not say what they had come to do, and no letter ever has.