{"id":342,"date":"2026-03-21T08:28:33","date_gmt":"2026-03-21T08:28:33","guid":{"rendered":"https:\/\/thedigitalfortress.us\/?p=342"},"modified":"2026-03-21T08:28:33","modified_gmt":"2026-03-21T08:28:33","slug":"trivy-supply-chain-attack-triggers-self-spreading-canisterworm-across-47-npm-packages","status":"publish","type":"post","link":"https:\/\/thedigitalfortress.us\/?p=342","title":{"rendered":"Trivy Supply Chain Attack Triggers Self-Spreading CanisterWorm Across 47 npm Packages"},"content":{"rendered":"<div>\n<p><span class=\"p-author\"><i class=\"icon-font icon-user\">\ue804<\/i><span class=\"author\">Ravie Lakshmanan<\/span><i class=\"icon-font icon-calendar\">\ue802<\/i><span class=\"author\">Mar 21, 2026<\/span><\/span><span class=\"p-tags\">Malware \/ Threat Intelligence<\/span><\/p>\n<\/div>\n<div id=\"articlebody\">\n<div class=\"separator\" style=\"clear: both;\"><a href=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEgJqn31IC9aCQ9LMLCLRXgpwsa1gvtzXlYk20-1yRmCMYVM_MwGHedfSgbKl24yaeTx4fqRc4-vscge-d3P6sN8sErQBVGD0kgxMGzV-mDCI1wGFh87BB8me019zcennhvA6xyMHLnH9IKZ-txSWs9OwL5cGbg0X8sx_KZ2tj5A5awErRRRMbdSrw_cXs6a\/s1700-e365\/npm-malware.jpg\" style=\"clear: left; display: block; float: left;  text-align: center;\"><\/a><\/div>\n<p>The threat actors behind the supply chain attack targeting the popular Trivy scanner are suspected to be conducting follow-on attacks that have led to the compromise of a large number of npm packages with a previously undocumented self-propagating worm dubbed <strong>CanisterWorm<\/strong>.<\/p>\n<p>The name is a reference to the fact that the malware uses an <a href=\"https:\/\/docs.internetcomputer.org\/building-apps\/essentials\/canisters\" rel=\"noopener\" target=\"_blank\">ICP canister<\/a>, which refers to tamperproof smart contracts on the Internet Computer blockchain, as a <a href=\"https:\/\/attack.mitre.org\/techniques\/T1102\/001\/\" rel=\"noopener\" target=\"_blank\">dead drop resolver<\/a>. The development marks the first publicly documented abuse of an ICP canister for the explicit purpose of fetching the command-and-control (C2) server, Aikido Security researcher Charlie Eriksen <a href=\"https:\/\/www.aikido.dev\/blog\/teampcp-deploys-worm-npm-trivy-compromise\" rel=\"noopener\" target=\"_blank\">said<\/a>.<\/p>\n<p>The list of affected packages is below &#8211;<\/p>\n<ul>\n<li>28 packages in the @EmilGroup scope<\/li>\n<li>16 packages in the @opengov scope<\/li>\n<li>@teale.io\/eslint-config<\/li>\n<li>@airtm\/uuid-base32<\/li>\n<li>@pypestream\/floating-ui-dom<\/li>\n<\/ul>\n<p>The development comes within a day after threat actors leveraged a compromised credential to publish malicious trivy, trivy-action, and setup-trivy releases containing a credential stealer. A cloud-focused <a href=\"https:\/\/cyble.com\/threat-actor-profiles\/teampcp\/\" rel=\"noopener\" target=\"_blank\">cybercriminal operation<\/a> known as TeamPCP is suspected to be behind the attacks.<\/p>\n<div class=\"dog_two clear\">\n<div class=\"cf\"><a href=\"https:\/\/thehackernews.uk\/not-fast-enough-d\" rel=\"nofollow noopener sponsored\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"lazyload\" alt=\"Cybersecurity\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEhlXM830ruQd2xT6M7JNeNRjaFa1onD12WjSCHihTFMTzbyfT9h-irPmXy_h3E1HGSs6sdv7FTmnyNVTM5kmSb7BuUtZe8gKoTQt99P1sSzRcqqXpOJP6eoAOhR3DGb6qHx9kOZ_HBZUMmVnsnd0DM7QfUp81bgzTvvgLww6oqB-EhnDfWXH5pWCYhAsyLs\/s728-e100\/tl-d.jpg\" width=\"729\" height=\"91\"\/><\/a><\/div>\n<\/div>\n<p>The infection chain involving the npm packages involves leveraging a postinstall hook to execute a loader, which then drops a Python backdoor that&#8217;s responsible for contacting the ICP canister dead drop to retrieve a URL pointing to the next-stage payload. The fact that the dead drop infrastructure is decentralized makes it resilient and resistant to takedown efforts.<\/p>\n<p><a name=\"more\"\/><\/p>\n<p>\u00abThe canister controller can swap the URL at any time, pushing new binaries to all infected hosts without touching the implant,\u00bb Eriksen said.<\/p>\n<p>Persistence is established by means of a systemd user service, which is configured to automatically start the Python backdoor after a 5-second delay if it gets terminated for some reason by using the \u00ab<a href=\"https:\/\/www.freedesktop.org\/software\/systemd\/man\/latest\/systemd.service.html\" rel=\"noopener\" target=\"_blank\">Restart=always<\/a>\u00bb directive. The systemd service masquerades as PostgreSQL tooling (\u00abpgmon\u00bb) in an attempt to fly under the radar.<\/p>\n<p>The backdoor, as mentioned before, phones the <a href=\"https:\/\/dashboard.internetcomputer.org\/canister\/tdtqy-oyaaa-aaaae-af2dq-cai\" rel=\"noopener\" target=\"_blank\">ICP canister<\/a> with a spoofed browser User-Agent every 50 minutes to fetch the URL in plaintext. The URL is subsequently parsed to fetch and run the executable.<\/p>\n<p>\u00abIf the URL contains youtube[.]com, the script skips it,\u00bb Eriksen explained. \u00abThis is the canister&#8217;s dormant state. The attacker arms the implant by pointing the canister at a real binary, and disarms it by switching back to a YouTube link. If the attacker updates the canister to point to a new URL, every infected machine picks up the new binary on its next poll. The old binary keeps running in the background since the script never kills previous processes.\u00bb<\/p>\n<p>It&#8217;s worth noting that a similar youtube[.]com-based kill switch has also been flagged by Wiz in connection with the trojanized Trivy binary (version 0.69.4), which reaches out to the same ICP canister via another Python dropper (\u00absysmon.py\u00bb). As of writing, the URL returned by the C2 is a <a href=\"https:\/\/en.wikipedia.org\/wiki\/Rickrolling\" rel=\"noopener\" target=\"_blank\">rickroll YouTube video<\/a>.<\/p>\n<p>The Hacker News found that the ICP canister <a href=\"https:\/\/dashboard.internetcomputer.org\/canister\/tdtqy-oyaaa-aaaae-af2dq-cai\" rel=\"noopener\" target=\"_blank\">supports<\/a> three methods \u2013 get_latest_link, http_request, update_link \u2013 the last of which allows the threat actor to modify the behavior at any time to serve an actual payload.<\/p>\n<p>In tandem, the packages come with a \u00abdeploy.js\u00bb file that the attacker runs manually to spread the malicious payload to every package a stolen npm token provides access to in a programmatic fashion. The worm, assessed to be vibe-coded using an artificial intelligence (AI) tool, makes no attempt to conceal its functionality.<\/p>\n<p>\u00abThis isn&#8217;t triggered by npm install,\u00bb Aikido said. \u00abIt&#8217;s a standalone tool the attacker runs with stolen tokens to maximize blast radius.\u00bb<\/p>\n<p>To make matters worse, a subsequent iteration of CanisterWorm detected in \u00ab@teale.io\/eslint-config\u00bb versions 1.8.11 and 1.8.12 has been found to self-propagate on its own without the need for manual intervention.<\/p>\n<div class=\"dog_two clear\">\n<div class=\"cf\"><a href=\"https:\/\/thehackernews.uk\/cyber-comm-guide-d\" rel=\"nofollow noopener sponsored\" target=\"_blank\"><img loading=\"lazy\" decoding=\"async\" class=\"lazyload\" alt=\"Cybersecurity\" src=\"https:\/\/blogger.googleusercontent.com\/img\/b\/R29vZ2xl\/AVvXsEigDbfWwE4P_DsjfBRxgecgosqTRr8-2j328LrzdUBWrWmWeDUTI7OhXc-zXveYOjBc7GStGz5WnpXsJGaLCuoryIXbL7NxRyaWzIJGO1TBpd48NkYzNqTMj9zWMzgfvqh20RxsdMll45TFiMzXja0pAd7roFjMnzsRYBGHOWSLnyKN-oMKyCLoYcjmb5hm\/s728-e100\/ciso-d.jpg\" width=\"729\" height=\"91\"\/><\/a><\/div>\n<\/div>\n<p>Unlike \u00abdeploy.js,\u00bb which was a self-contained script the attacker had to execute with the pilfered npm tokens to push a malicious version of the npm packages to the registry, the new variant incorporates this functionality in \u00abindex.js\u00bb within a findNpmTokens() function that&#8217;s run during the postinstall phase to collect npm authentication tokens from the victim&#8217;s machine.<\/p>\n<p>The main difference here is that the postinstall script, after installing the persistent backdoor, attempts to locate every npm token from the developer&#8217;s environment and spawns the worm right away with those tokens by launching \u00abdeploy.js\u00bb as a fully detached background process.<\/p>\n<p>Interestingly, the threat actor is said to have swapped out the ICP backdoor payload for a dummy test string (\u00abhello123\u00bb), likely to ensure that the entire attack chain is working as intended before adding the malware.<\/p>\n<p>\u00abThis is the point where the attack goes from &#8216;compromised account publishes malware&#8217; to &#8216;malware compromises more accounts and publishes itself,'\u00bb Eriksen said. \u00abEvery developer or CI pipeline that installs this package and has an npm token accessible becomes an unwitting propagation vector. Their packages get infected, their downstream users install those, and if any of them have tokens, the cycle repeats.\u00bb<\/p>\n<p><em>(This is a developing story. Please check back for more details.)<\/em><\/p>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\ue804Ravie Lakshmanan\ue802Mar 21, 2026Malware \/ Threat Intelligence The threat actors behind the supply chain attack targeting the popular Trivy scanner are suspected to be conducting follow-on attacks that have led&hellip;<\/p>\n","protected":false},"author":1,"featured_media":343,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[220,806,219,39,35,805,218,798,800],"class_list":["post-342","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-uncategorized","tag-attack","tag-canisterworm","tag-chain","tag-npm","tag-packages","tag-selfspreading","tag-supply","tag-triggers","tag-trivy"],"_links":{"self":[{"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/posts\/342","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=342"}],"version-history":[{"count":0,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/posts\/342\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=\/wp\/v2\/media\/343"}],"wp:attachment":[{"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=342"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=342"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/thedigitalfortress.us\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=342"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}