From CI/CD to Cloud Data: How Shai Hulud Persistence Leads to Redshift Breach
Affected Platforms: Amazon Web Services (AWS)Impacted Users: Any organizationImpact: Stolen clo 2026-6-26 13:0:0 Author: feeds.fortinet.com(查看原文) 阅读量:5 收藏

Affected Platforms: Amazon Web Services (AWS)
Impacted Users: Any organization
Impact: Stolen cloud credentials result in significant financial losses and data breaches of sensitive information.
Severity Level: High

Organizations with modern CI/CD pipelines face threats from the Shai Hulud supply chain campaign, a software worm attributed to TeamPCP that has been targeting npm and PyPI packages since late 2025. Named after the giant sandworms in Dune, Shai Hulud injects malicious packages that execute during installs or CI jobs, harvesting build credentials to move into cloud infrastructure.

Organizations running modern CI/CD are learning a critical lesson from the Shai Hulud supply chain campaign: a poisoned build dependency doesn't stop at the pipeline—it becomes a bridge into the production cloud.

In May 2026, FortiCNAPP helped customers identify AWS estates affected by the worm. By mid-May, investigators found evidence of ongoing access to a Jenkins runner that matched Shai Hulud’s credential-harvesting pattern. They showed that an operator had exploited the Jenkins role over the internet, escalated privileges to full cloud admin, changed database network controls, and extracted data from Amazon Redshift. They also staged activities with object storage and email.

For leaders, the key takeaway is operational: pipeline identity equals production identity. Practitioners have a rich evidence trail if they know where to look. This blog details CloudTrail and host signals, showing how FortiCNAPP detected an intrusion with the alert “Potentially Compromised Keys,” helping analysts reconstruct host and cloud timelines.

Shai Hulud and the Cloud Threat

There was a sharp increase in detected instances of the Shai Hulud worm during the “Mini Shai Hulud” wave in April–May 2026. Previously observed incidents described a repeatable model:

  • Malicious package versions execute during installations or CI jobs.
  • Payloads harvest credentials from the build environment, including package registry tokens, GitHub tokens, AWS credentials, Kubernetes secrets, SSH keys, and more.
  • Stolen credentials enable the autonomous republication of additional poisoned packages.
  • Recent variants have abused trusted OIDC publishing in GitHub Actions, producing artifacts that appear legitimately attested even though the runner itself was attacker-influenced.

CI/CD runners become a vector for quickly expanding the cloud attack surface. Jenkins, GitHub Actions, and self-hosted agents routinely hold instance profiles, long-lived secrets, and network access to data tiers—exactly what Shai Hulud was built to collect.

Timeline

PhaseApproximate DateWhat Happened
Supply chain exposureEarly May 2026Environment affected by aggressive Shai Hulud activity
Suspected CI persistenceThrough mid-May 2026Possible residual access on Jenkins / build infrastructure
Cloud intrusion & exfiltrationMid-MayExternal use of Jenkins instance role; IAM escalation; RDS/Redshift targeting and data exfiltration

Table 1. Linking Shai Hulud to the Jenkins runner

Although there's no definitive proof that the worm infection caused the cloud compromise, the suspected link is strongly supported.

  • Temporal alignment: Supply chain compromise in early May precedes cloud abuse by about two weeks, consistent with typical persistence-and-staging patterns.
  • Initial cloud identity: The first malicious CloudTrail activity used the Jenkins EC2 instance role, the same credential class that Shai Hulud families are documented to steal from CI hosts.
  • Known worm capabilities: TeamPCP tooling is designed to harvest cloud and CI secrets and reuse them outside the victim network; external use of instance credentials aligns with that playbook.

Host-level forensics and analysis to trace the complete attack path, from a specific malicious package artifact to the mid-May session, remain an ongoing area of investigation.

Figure 1, below, provides an overview of the attack and identity compromise that enable lateral movement within cloud infrastructure:

The following stages describe the cloud-based activities that took place after credential theft and were identified by FortiCNAPP Alerting:

  1. Initial cloud access: Instance credentials were retrieved via instance metadata on the compromised EC2. The role was then invoked from external IP addresses, not from the instance.
  2. Privilege escalation: Attacker-created IAM user named cloudops-monitor given administrator privileges and access keys.
  3. Infrastructure manipulation: Attacker security groups, stage EC2 instances, and modify ModifyDBCluster or ModifyCluster to attach these groups to Aurora and Redshift.
  4. Credential and data access: Enumeration of Mass Secrets Manager; access to targeted warehouse secrets; executing Redshift Data API queries.
  5. Exfiltration: Inline policies exfil-s3-write and exfil-s3-full; usage of AssumeRole with the session name exfil; and executing SSM SendCommand on a managed instance.
  6. Secondary staging: Amazon SES quota and identity verification (corporate addresses redacted below).
MITRE TacticRepresentative TechniquesWhat We Observed
Initial AccessT1195.002 (Supply chain compromise)Prior Shai Hulud exposure; suspected Jenkins persistence
Valid AccountsT1078.004 (Cloud accounts)Jenkins instance role used from external IPs
Privilege EscalationT1098 (Account manipulation)IAM user cloudops-monitor with admin policy
DiscoveryT1580, T1526 (Cloud infrastructure / IAM)S3, VPC, RDS, Redshift, Secrets enumeration
PersistenceT1578 (Modify cloud compute infra)Security groups, EC2, cluster SG attachment
Credential AccessT1552.005 (Cloud instance metadata), T1555IMDS curl on runner; Secrets Manager; GetClusterCredentials
CollectionT1530 (Data from cloud storage)Redshift Data API ExecuteStatement (high volume)
ExfiltrationT1537 (Transfer to cloud account)S3 policy staging; AssumeRole sessions named exfil*
ImpactT1496 (Resource hijacking)SES identity verification staging

Table 2. Mapping MITRE tactics to observed malware behavior

Bridging Host and Cloud: Port 8080 and Instance Metadata

The earliest high-confidence cloud signal was the use of the Jenkins instance profile outside AWS, detected by FortiCNAPP as external instance-credential abuse.

On the Jenkins host, investigators observed access to the instance metadata service (IMDS) consistent with credential theft:

curl -s --connect-timeout 3 hxxp://169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/

curl -s hxxp://169[.]254[.]169[.]254/latest/meta-data/iam/security-credentials/jenkins

While that explained how the role credentials could be removed from the instance, the more challenging question is who managed them afterward?

A key piece of corroboration is network telemetry from the build environment, showing an unusual outbound connection to 89[.]22[.]231[.]63 on port 8080 during the investigation period. This same IP address was later observed as the source of the cloudops-monitor identity and of subsequent privileged escalation activities. The reuse of this IP tightly links host activity on the runner to operator infrastructure in the cloud logs, and the correlation detected by FortiCNAPP proved to be a much stronger indicator than any single detection signal.

Two external IPs bookend the CloudTrail story:

IPRole in incident
185[.]204[.]1[.]225First observed abuse of Jenkins role (recon, IAM escalation)
89[.]22[.]231[.]63Primary operator IP after escalation; host outbound :8080

Stage 1: Jenkins role abuse from the internet

After obtaining temporary instance credentials from the compromised EC2, initial cloud authentication was detected through GetCallerIdentity and ListBuckets using the Jenkins assumed-role session. FortiCNAPP flagged the credentials being used from a non-instance IP, which should trigger immediate containment measures like disabling or restricting the instance profile and isolating the runner.

User-agent strings in these calls form two groups (see Indicators): one linked to a Fedora/Qubes AWS CLI build from the first IP, and another associated with an Ubuntu 26 AWS CLI build that accounts for most of the later activity. This pattern suggests either multiple operators or a change in the toolchain during the intrusion.

Stage 2: IAM privilege escalation via cloudops-monitor

While still authenticated as Jenkins, the actor:

  • Enumerated policies on the Jenkins role.
  • Created IAM user cloudops-monitor and attached AWS managed AdministratorAccess.
  • Issued active access keys.

FortiCNAPP mapped CreateUser, AttachUserPolicy, and CreateAccessKey to Privilege Escalation (TA0004) with T1078 and T1098.

After 06:35 UTC, the bulk of activity runs as cloudops-monitor from 89[.]22[.]231[.]63.

Stage 3: Network persistence and datastore control-plane abuse

  1. Security groups and staging compute
    The actor set up security groups with harmless-sounding descriptions like “CloudOps monitoring". They enabled SSH access from 0.0.0.0/0 and launched EC2 instances tagged with the IAM username, trying different subnets if initial placement failed. Multiple calls to DescribeSubnets and DescribeRouteTables suggest intentional mapping of the production VPC.
  2. Aurora RDS: ModifyDBCluster
    Against a production Aurora PostgreSQL cluster, CloudTrail shows:
    • Cluster reconnaissance (DescribeDBClusters).
    • ModifyDBCluster with applyImmediately: true, including an attempt to rotate the master password, followed by attempts to attach an attacker-controlled security group to the cluster.

    This is control-plane aggression: the actor did not limit themselves to stealing secrets. They tried to change how the database is accessed and authenticated.
  3. Redshift: ModifyCluster and port 5439
    For Redshift, the actor:
    • Enumerated clusters across non-production and production warehouse targets.
    • Invoked ModifyCluster to attach the same attacker security group used for RDS.
    • Called AuthorizeSecurityGroupIngress on tcp/5439 (Redshift), allowing the staging security group to reach the warehouse security group—opening a network path to the data plane.

FortiCNAPP impossible travel and sensitive infrastructure discovery observations fired on these APIs.

Stage 4: Secrets manager: warehouse credentials on target

The actor executed hundreds of paginated ListSecrets calls, followed by GetSecretValue requests for secret names clearly linked to regional Redshift warehouses and reporting pipelines. These names included patterns like datawarehouse_redshift_* and truedata_reporting_redshift_*.

FortiCNAPP raised “Suspicious Enumeration of Cloud Secrets” and Credential Access (TA0006) on cloudops-monitor.

Secrets Manager provides the quickest access to connection strings for administrators with IAM rights but restricted network access, matching the description of this phase.

Stage 5: Redshift data API: collection and exfiltration

After network and secret access, the threat actor:

  • Repeatedly called GetClusterCredentials against a production warehouse cluster and database.
  • Issued ExecuteStatement via the Redshift Data API from the same external operator IP, with ~90 execute/describe/result cycles in the available log set.

CloudTrail redacts SQL in request parameters, so individual queries are not visible during log-only review. The sustained interactive volume supports assessment of data collection and exfiltration rather than a one-off health check—consistent with leadership’s reporting of a confirmed warehouse breach.

FortiCNAPP Collection (TA0009) and Impact (TA0040) observations align with this phase.

Stage 6: Exfiltration tradecraft—explicit naming in IAM and STS

Inline S3 policies are linked to an existing analytics role. The threat actor employed PutRolePolicy to attach specifically scoped S3 data-lake permissions to a role used for analytics workloads.

Policy namePermissionsIntent (Assessed)
exfil-s3-writePutObject, GetObject, ListBucket on a production data bucketStage writes
exfil-s3-fullAbove plus DeleteObjectFull object lifecycle control

Policy names like exfil-s3-* are rare gifts to threat hunters—and warnings to teams that rely on naming conventions without content inspection.

AssumeRole with session name exfil

A separate emergency IAM user (not the attacker-created monitor account) eventually converts AssumeRole into a highly privileged break-glass role using roleSessionName: exfil after multiple denials. Following this, ListRoles and SendCommand activities were performed under sessions named exfil10 and exfil12.

SSM SendCommand

During those sessions, the actor ran SendCommand with the AWS-RunShellScript document on a managed EC2 instance (parameters are redacted in the logs). Successful invocations show in-VPC execution when external CLI access is limited, which is typical for tasks such as tooling, archiving, or transferring data to egress points.

Stage 7: SES—staging outbound email

Late in the window, cloudops-monitor and associated principals executed GetSendQuota, GetIdentityVerificationAttributes, VerifyEmailIdentity, and ListIdentities. Multiple corporate mailbox addresses were submitted for verification, but they are omitted here per publication policy.

Some SES calls originated from an in-account EC2 instance rather than from the external operator IP—suggesting a shift to execution within the victim VPC while control-plane abuse continued.

SES preparation often precedes phishing, extortion, or damage to domain reputation, so it's important to monitor even if data exfiltration occurred elsewhere.

Detection with FortiCNAPP: “Potentially Compromised Keys”

FortiCNAPP consumed CloudTrail and identity context, producing 1,095 observations within the incident window. The customer-facing incident Potentially Compromised Keys provided the narrative practitioners need:

Observation CategoryCount (approx.)Why it Mattered
Impossible travel202Operator/API geography inconsistent with baseline
MITRE Discovery170S3, RDS, Redshift, IAM recon
Sensitive infrastructure discovery128Cluster and security-group mutations
New API for principal120First-time APIs on compromised identities
Persistence62SG and cluster modifications
Privilege escalation52User creation, inline policies
Outside connections49External IPs on cloud identities
Credential access41Secrets Manager, GetClusterCredentials
External instance credential use20Jenkins role from non-AWS IP

Table 3. Potentially compromised activities detected

Representative observation text seen in the export:

  • Use of AWS instance credentials from an outside IP — Jenkins role.
  • AWS user was created — cloudops-monitor.
  • AWS APIs indicating Privilege Escalation (TA0004) activity were observed.
  • Suspicious Enumeration of Cloud Secrets Detected.
  • Impossible travel detected in a short time frame.

The platform’s value lies in correlating events under a single alert title: Jenkins role → cloudops-monitor → datastore APIs → exfil session naming, eliminating the need for analysts to manually connect over 1,300 CloudTrail events spanning six hours. Notably, identifying the use of external instance credentials is crucial for linking host-based and cloud-based threats. Mapping these threats from the host to the cloud is a complex, manual process that FortiCNAPP automates effectively.

Recommendations for Defenders

  1. Treat CI/CD runners as tier-0 identity: Use least-privilege instance profiles; no admin paths; no broad Secrets Manager or datastore admin access from build roles.
  2. Alert on external use of instance credentials: This was the earliest cloud signal. Pair it with host egress rules that flag unexpected outbound connections (e.g., operator IPs on non-standard ports).
  3. Guard datastore control planes: Alert on and enforce change control for ModifyDBCluster, ModifyCluster, and security-group ingress on 5432/5439.
  4. Hunt explicit attacker naming: Policy names (exfil-s3-full) and STS sessions (exfil, exfil12) should be included in high-fidelity detection rules.
  5. Run tabletops from package to warehouse: Shai Hulud demonstrates that cloud exfiltration is a realistic outcome of a poisoned pipeline dependency.

Fortinet Protections

The malware described in this report are detected and blocked by FortiGuard Antivirus as:

JS/Agent.B067!tr
JS/Agent.3A86!tr

The FortiGuard AntiVirus service engine is integrated into FortiGate, FortiMail, FortiClient, and FortiEDR. Customers running these products with up-to-date signatures are protected against the malware components described in this report.

The FortiGuard Web Filtering Service blocks the C2 server.

Organizations seeking to strengthen foundational security awareness may also consider completing Fortinet Certified Fundamentals (FCF) training in Cybersecurity.  This module is designed to help end users learn to identify and protect themselves from phishing attacks.

The FortiGuard IP Reputation and Anti-Botnet Security Service proactively blocks infrastructure associated with this campaign by correlating malicious IP intelligence collected from Fortinet’s global sensor network, CERT collaborations, MITRE, trusted industry partners, and other intelligence sources.

If you believe this or any other cybersecurity threat has impacted your organization, contact our Global FortiGuard Incident Response Team for assistance.

Public IOCs

Validate these in your environment before blocking them. Note that user agents evolve.

IP addresses

IPContext
185[.]204[.]1[.]225Initial external abuse of Jenkins instance role
89[.]22[.]231[.]63Primary post-escalation operator IP; host outbound connection on port 8080

IAM account (attacker-created)

TypeValue
IAM usercloudops-monitor

STS role session names

Session nameContext
exfilAssumed break-glass / super-admin role
exfil10, exfil12Sessions used for SSM SendCommand and follow-on activity

IAM inline policy names (attacker-created)

Policy nameContext
exfil-s3-writeS3 write/list to production data bucket
exfil-s3-writeAdds s3:DeleteObject on same bucket

User-agent strings (representative)

Ubuntu operator toolchain (dominant, IP 89[.]22[.]231[.]63):

aws-cli/2.31.35 md/awscrt#1.0.0.dev0 ua/2.1 os/linux#7.0.0-15-generic md/arch#x86_64 lang/python#3.14.4 md/pyimpl#CPython ... md/distrib#ubuntu.26

Boto3/1.40.72 md/Botocore#1.40.72 ... os/linux#7.0.0-15-generic ... lang/python#3.14.4

Fedora/Qubes toolchain (early Jenkins-role abuse, IP 185[.]204[.]1[.]225):

aws-cli/2.34.29 md/awscrt#0.31.2 ua/2.1 os/linux#6.18.15-1.qubes.fc41.x86_64 md/arch#x86_64 lang/python#3.14.3 md/pyimpl#CPython ... md/distrib#fedora.42


文章来源: https://feeds.fortinet.com/~/958459373/0/fortinet/blog/threat-research~From-CICD-to-Cloud-Data-How-Shai-Hulud-Persistence-Leads-to-Redshift-Breach
如有侵权请联系:admin#unsafe.sh