
Compromised Flutter package on pub.dev contains XCSSET malware
Aikido Security discovered a variant of the XCSSET macOS worm hidden in universal_file_viewer, a Flutter file-preview package on pub.dev (~500 downloads) — the first compromised package found on that registry. The infection was not a targeted supply-chain attack but resulted from the maintainer's machine being infected with XCSSET, which auto-injects malicious build hooks into every Gradle, Xcode, and git project it finds; the poisoned example/ directory (Android Gradle build, iOS/macOS Xcode project files) shipped in the package tarball. The writeup details XCSSET's four-stage infection chain, its android_finder/git_finder/replicator_finder propagation modules, Launchpad-tile persistence, and theft modules targeting Chrome, Safari, Firefox, Telegram, Notes, and clipboard data, plus C2 domain and file-artifact IOCs.
What it is
A variant of the XCSSET macOS worm shipped inside universal_file_viewer version 0.1.5 on pub.dev. The infection is incidental, not targeted — the maintainer's machine was infected and the worm auto-injected build hooks into files that then got published. The important operational fact the summary omits: dependents are not at risk. lib/ is clean; adding the package to pubspec.yaml and building your own app triggers nothing. Only someone who clones the repo and explicitly builds the example/ app locally is exposed.
How it works — the injected hooks (detection strings)
Three build configs in example/ carry independently injected hooks, each obfuscated to defeat string scanners:
example/android/app/build.gradle.kts— apreBuildhook that reconstructsxxdat runtime viaprintf xAxd | tr -d A, decoding a hex blob to acurlto5yotmxcc54l9xda[.]ru/a(tagp=android_kotlin). Fires on every Gradle build, output discarded.example/ios/Runner.xcodeproj/project.pbxprojandexample/macos/.../project.pbxproj— aPBXBuildRulefiring on any.mdfile, runningsh -c "${A3EA261}". iOS variant (printf bdase64 | tr -d d) hitsqdgs232i-q[.]ru; macOS variant (printf xxVd | tr -d V) hitsejntin6hkjt7gj2[.]ru; both tagp=xcode_rule.
Post-infection, the worm re-spreads via android_finder (Gradle preBuild), git_finder (polymorphic .git/hooks/pre-commit doing base64 --decode | base64 --decode), and replicator_finder (Xcode Run Script phase). Persistence swaps the Dock's Launchpad tile for a fake bundle under ~/Library/Caches/vyf[.]lttzw[.]snjgwq/ impersonating com.apple.launchpad.launcher. All theft runs in-memory via osascript; exfil goes to /u as AES-256-CBC-encrypted multipart POST.
Preconditions / Affected
macOS developers who cloned and built the example/ app; anyone whose Gradle/Xcode/git projects were touched by an already-infected machine. Windows/Linux users of the package are unaffected by execution. The page notes this is still ongoing — other maintainers are pushing infected projects unknowingly.
What to do — IOCs to grep
Scan repos and machines for these artifacts:
C2 domains
5yotmxcc54l9xda[.]ru
qdgs232i-q[.]ru
ejntin6hkjt7gj2[.]ruFile artifacts
project.pbxproj → PBXBuildRule with sh -c "${A3EA261}"
build.gradle(.kts) → printf xAxd | tr -d A (reconstructs xxd)
.git/hooks/pre-commit → base64 --decode | base64 --decode OR xxd -p -r | xxd -p -rAlso check the Dock for a Launchpad tile pointing at the fake Caches bundle above, and browser/Telegram data theft indicators (~/Library/ASafari/, replaced Telegram.app, firefox_browser_data.zip).
Caveats
Only ~500 downloads and a narrow trigger (local example build), so blast radius via this package is small — but the worm's self-propagation means an infected developer machine spreads to unrelated Gradle/Xcode/git projects regardless of this package.
Organisations named here
- universal_file_viewer
- XCSSET
Technologies named here
- Flutter
- macOS
- Android
- Telegram
- Chrome
- pub.dev
Related in Supply Chain


