Safeguard
Supply Chain Security•

Flutter package on pub.dev shipped XCSSET malware in its example app

Aikido found XCSSET macOS malware in the example folder of universal_file_viewer 0.1.5 on pub.dev. Depending on the package is not the risk; building its example is.

Safeguard Security Team
3 min read

On 8 September 2026 Aikido Security reported a compromised package on pub.dev, the Dart and Flutter registry. The package is universal_file_viewer version 0.1.5, which had about 500 downloads. Aikido says it is the first compromised package it has detected on pub.dev, and that the malware is a variant of XCSSET, a macOS worm.

What happened

According to Aikido (researcher Ilyas Makari), the package traces back to a compromised GitHub repository. The lib/ folder is clean. The infected files are in example/, which ships in the published tarball but is not compiled into apps that depend on the package.

So the risk is not flutter pub add. It is a developer who clones or unpacks the package and builds the example app locally. Aikido says the maintainer's own infected machine spread the worm into the published files, and that this was not a targeted attack.

The infected build configuration, per Aikido:

  • an Android Gradle preBuild hook in build.gradle.kts
  • Xcode PBXBuildRule entries in project.pbxproj for iOS and macOS
  • pre-commit hooks under .git/hooks, used to spread to other projects

Aikido describes staged loading with AES-256-CBC, in-memory execution through osascript, and theft of browser credentials and cookies, Telegram sessions, Notes, Reminders and Calendar data, clipboard contents and files in home directory folders. Its listed domains are 5yotmxcc54l9xda.ru, qdgs232i-q.ru and ejntin6hkjt7gj2.ru.

Confirmed, reported and unknown

Reported by one researcher, with indicators published: the package, version, infected files, behavior and domains. I found no registry statement in what I fetched.

Unknown: whether pub.dev has removed the package, whether other versions are affected, how many people were infected, the maintainer and repository identity, and who runs the campaign. Aikido says a GitHub search shows other maintainers still pushing infected projects, with no count.

What to do now

  1. Search for universal_file_viewer in pubspec.lock. If present, it is safe to say a dependency alone did not run the malware, but check whether anyone built the example.
  2. On any Mac that built an example/ project from this package, treat the machine as compromised. Isolate it, then reimage.
  3. Rotate browser saved passwords, session tokens, Git and cloud credentials used on that Mac.
  4. In your own repositories, search for the artifacts Aikido lists:
grep -rn "printf xAxd | tr -d A" --include=build.gradle.kts .
grep -rln 'sh -c "${A3EA261}"' --include=project.pbxproj .
find . -path "*/.git/hooks/pre-commit" -exec grep -l "base64" {} +
  1. Review recent commits for build files you did not change.

How this could have been avoided

  • Treat example/, test/ and tool/ folders from third party packages as untrusted code. Open them in a disposable VM, not on a developer Mac with browser sessions.
  • Build and review pre-commit hooks from a version controlled template. A .git/hooks/pre-commit file with nested base64 decoding is easy to flag.
  • Add a check in CI that fails when a PBXBuildRule or Gradle preBuild hook contains shell commands that decode strings.
  • Keep browser sessions and cloud credentials off the machine used to run unfamiliar sample apps.
  • Publishers should verify what is in the tarball before release by running dart pub publish --dry-run and reading the file list.

Quick check

Does adding universal_file_viewer as a dependency run the malware?

Per Aikido, no. The infected files are in the example folder, which is not compiled for dependents. Building the example is the risk.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.