critical Threat analysis

Megalodon GitHub Actions Secret Exfiltration Campaign

Megalodon added malicious GitHub Actions workflows to thousands of public repositories to collect environment variables, cloud credentials, source-control secrets, and runner tokens.

#supply-chain#github-actions#ci-cd#credential-theft#workflow-injection
On this page 0% read

    Executive Summary

    StepSecurity reported Megalodon as a mass GitHub Actions secret-exfiltration campaign affecting 5,561 public repositories, with SafeDep publishing a dataset of 5,718 malicious commits. The campaign inserted disguised workflow files into repositories so GitHub Actions would execute attacker-controlled secret collection logic StepSecurity.

    The payload collected environment variables, process environments, cloud credentials, SSH keys, Docker configuration, npm tokens, Kubernetes configs, Vault tokens, Terraform credentials, OIDC tokens, and source-code secrets before posting a compressed archive to 216[.]126[.]225[.]129:8443. Use the workflow-history, runner-egress, and downstream identity audit recipes below to determine which repositories executed the payload and which credential classes were exposed StepSecurity.

    Key Facts

    threat_type: "malicious GitHub Actions workflow injection"
    ecosystem: "GitHub Actions"
    registry: "GitHub repositories"
    affected_packages:
      - "not package-specific; repository workflow compromise"
    malicious_versions: []
    known_good_versions: []
    fixed_or_safe_versions: []
    execution_trigger: "GitHub Actions workflow execution after malicious workflow file is committed"
    primary_impact: "mass CI/CD secret collection and exfiltration"
    campaign_context: "May 2026 CI/CD supply-chain wave focused on direct runner execution and credential theft."
    confidence: "medium"
    canonical_source: "https://www.stepsecurity.io/blog/megalodon-mass-github-actions-secret-exfiltration-across-5-500-public-repositories"
    last_verified: "2026-05-24"

    Source Confidence & Evidence Mapping

    • confirmed: StepSecurity reports 5,561 affected repositories and 5,718 malicious commits in a SafeDep-published dataset StepSecurity.
    • confirmed: The campaign used malicious workflow files with names such as SysDiag and Optimize-Build to trigger GitHub Actions execution StepSecurity.
    • confirmed: The payload collected multiple classes of secrets and exfiltrated to 216[.]126[.]225[.]129:8443 StepSecurity.
    • unclear: The dataset was not independently fetched in this local pass, so per-repository remediation status remains a collection gap.

    Impact Determination

    ClassificationCriteriaRequired evidenceRequired actionClosure condition
    Confirmed compromiseRepository history contains the reported malicious workflow, commit hash, C2 endpoint, or payload content and the workflow executed.Commit object, workflow file, Actions run metadata, runner logs, and network telemetry.Remove the workflow, isolate affected runners, and rotate every credential class available to the run.Malicious commits are reverted or removed, workflows are disabled, and downstream audits are clean.
    Presumed exposedA matching workflow exists and may have run with secrets, even if exfiltration telemetry is missing.Workflow path, commit timestamp, runner assignment, permissions, and secret availability.Rotate GitHub, cloud, package, container, SSH, Kubernetes, Vault, and Terraform credentials reachable from the job.Credential owners confirm revocation of old material and no suspicious downstream writes are found.
    Potentially exposedRepository search finds suspicious workflow names, bot authors, archive creation, or the C2 IP but execution state is incomplete.Code search hits, git log output, workflow run list, and runner telemetry availability.Freeze suspicious workflow paths and collect missing run evidence before narrowing rotation.Each hit is dispositioned as confirmed, presumed, or not exposed.
    Not exposedNo matching workflow names, C2 endpoint, malicious hashes, or suspicious workflow additions exist in repository history.Repository code search, git history search, and Actions run export.Record the clean search and keep workflow ownership controls active.Search artifacts are preserved with the date, repository list, and query terms.
    UnknownRepository history, Actions logs, or the public dataset comparison is unavailable.Named evidence gaps and the repository population not yet searched.Keep the repository in the investigation queue and apply conservative credential rotation for high-value projects.Dataset comparison and workflow history are complete or the risk owner accepts the gap.

    Minimum Evidence To Collect

    minimum_evidence:
      - "Repository search results for `.github/workflows/SysDiag.yml`, `.github/workflows/Optimize-Build.yml`, reported hashes, and `216[.]126[.]225[.]129`."
      - "Git commit metadata for any workflow additions and the author identity used."
      - "GitHub Actions run metadata for malicious or suspicious workflows."
      - "Runner process, archive creation, and egress telemetry."
      - "Inventory of secrets and OIDC permissions available to each matching workflow run."

    Timeline

    • 2026-05-22 StepSecurity publishes Megalodon public analysis, citing 5,561 affected repositories and 5,718 malicious commits StepSecurity.
    • 2026-05-24 This local feed split creates a standalone Megalodon article instead of grouping it into a weekly roundup.

    What Happened

    Megalodon did not need to compromise a package registry. It targeted repository automation directly by adding workflow files disguised as normal CI optimization or diagnostics. Once the workflow ran, the runner exposed a high-value environment: repository tokens, cloud credentials, deployment secrets, and process-level secrets.

    StepSecurity’s writeup emphasizes the directness of the attack. A workflow file committed to a repository can run trusted automation without any application-code dependency update, which makes repository history and workflow governance critical evidence sources StepSecurity.

    Technical Analysis

    Initial Access

    The public report focuses on malicious commits and affected repository count; it does not prove one universal initial access mechanism for every repository. Review commit authorship, branch protection, token scopes, and whether malicious workflow commits bypassed normal review.

    Package or Artifact Tampering

    The artifact is the GitHub Actions workflow file itself, not a package release. Reported workflow names include SysDiag and Optimize-Build, which are plausible enough to blend into routine automation StepSecurity.

    Execution Trigger

    Execution occurs when GitHub Actions runs the malicious workflow. Trigger conditions depend on the committed workflow, but the important defender point is that the malicious code executes inside the repository’s trusted CI context.

    Payload Behavior

    The payload collects environment variables, process environments, cloud credentials, SSH keys, Docker configuration, npm tokens, Kubernetes configs, Vault tokens, Terraform credentials, OIDC tokens, and source-code secrets. It then compresses and posts collected data to the C2 endpoint StepSecurity.

    Exfiltration / C2

    The reported exfiltration endpoint is 216[.]126[.]225[.]129:8443/collect. Any runner egress to that host and port should be treated as a high-confidence incident StepSecurity.

    Propagation

    Megalodon propagated operationally through many repository commits rather than through a self-replicating package payload. StepSecurity reports thousands of affected repositories and malicious commits, making source-control search and dataset comparison the primary scoping methods.

    Obfuscation or Evasion

    The campaign used benign-looking workflow names and CI-maintenance framing. This is effective because many repositories accept workflow changes as routine build hygiene unless workflow file review is explicitly protected.

    Workflow Injection and Exfiltration Path

    The following architectural flowchart details the Megalodon attack lifecycle, illustrating how a workflow injection event triggers automated secret harvesting inside the CI/CD runner and the subsequent egress path to the C2 server:

    graph TD
        classDef attacker fill:#f96,stroke:#333,stroke-width:2px;
        classDef github fill:#9cf,stroke:#333,stroke-width:2px;
        classDef runner fill:#fcf,stroke:#333,stroke-width:2px;
        classDef c2 fill:#ff9,stroke:#333,stroke-width:2px;
    
        Attacker[1. Attacker]:::attacker
        GitRepo[2. Target GitHub Repo]:::github
        GHActions[3. GitHub Actions CI/CD Engine]:::github
        Runner[4. CI/CD Runner Environment]:::runner
        Exfil[5. Exfiltration C2 <br/> 216.126.225.129:8443]:::c2
    
        Attacker -- "Injects Malicious Commit <br/> (e.g. SysDiag / Optimize-Build)" --> GitRepo
        GitRepo -- "Triggers Workflow Event <br/> (e.g. pull_request_target / push)" --> GHActions
        GHActions -- "Spawns Runner with Secrets" --> Runner
    
        subgraph Runner Context
            Runner -- "1. Harvest Env Vars" --> Secrets[Credentials & Tokens]
            Runner -- "2. Harvest SSH Keys" --> SSH[~/.ssh/*]
            Runner -- "3. Harvest Cloud Keys" --> Cloud[AWS, Azure, GCP]
            Runner -- "4. Harvest API Keys" --> API[NPM, Terraform, Vault]
            Secrets & SSH & Cloud & API --> Archive[Compressed Secrets Archive]
        end
    
        Runner -- "Exfiltrates Archive (POST)" --> Exfil

    Affected Assets and Blast Radius

    affected_assets:
      ecosystems:
        - "GitHub Actions"
        - "GitHub repositories"
      packages: []
      versions:
        - "5,718 malicious commits reported by StepSecurity/SafeDep"
      repositories:
        - "5,561 public repositories reported by StepSecurity"
      ci_cd_systems:
        - "GitHub Actions"
      container_images: []
      developer_tools:
        - "GitHub Actions"
        - "repository workflow automation"
    credentials_at_risk:
      - "GitHub tokens"
      - "GitHub Actions secrets"
      - "OIDC tokens"
      - "AWS credentials"
      - "Azure credentials"
      - "GCP credentials"
      - "SSH private keys"
      - "Docker registry credentials"
      - "npm tokens"
      - "Kubernetes configs"
      - "Vault tokens"
      - "Terraform credentials"
    not_currently_known_to_affect:
      - "Private repositories not represented in the public dataset, unless local audit finds matching commits or workflow files."

    Indicators of Compromise

    package_versions: []
    files:
      - ".github/workflows/SysDiag.yml"
      - ".github/workflows/Optimize-Build.yml"
    hashes:
      - "1c9e803c80cc7fed000022d4c94f4b5bc2e90062"
      - "7f6120bb10c870b9fde146961a18e5bf0b3d4401"
      - "acac5a9854650c4ae2883c4740bf87d34120c038"
    domains: []
    urls:
      - "hxxps://216[.]126[.]225[.]129:8443/collect"
    ips:
      - "216[.]126[.]225[.]129"
    process_patterns:
      - "workflow collects environment variables and credential files"
    network_patterns:
      - "HTTPS POST to 216[.]126[.]225[.]129:8443/collect"
    provenance_signals:
      - "workflow files named SysDiag or Optimize-Build added by unexpected commits"
      - "commit authors such as [email protected] or [email protected]"

    Detection and Hunting

    Script: local repository and exported telemetry scope

    #!/usr/bin/env python3
    import os
    import sys
    import json
    import subprocess
    from pathlib import Path
    
    ROOT = sys.argv[1] if len(sys.argv) > 1 else "."
    LOG_ROOT = os.environ.get("LOG_ROOT", "")
    OUT = Path(os.environ.get("OUT", "hp-megalodon-github-actions-secret-exfiltration-scope"))
    SINCE = "2026-05-24T00:00:00Z"
    UNTIL = "2026-05-24T23:59:59Z"
    
    PACKAGES = [
    ]
    VERSIONS = [
    ]
    FILES = [
      ".github/workflows/SysDiag.yml",
      ".github/workflows/Optimize-Build.yml",
    ]
    DOMAINS = [
    ]
    URLS = [
      "https://216.126.225.129:8443/collect",
    ]
    IPS = [
      "216.126.225.129",
    ]
    HASHES = [
      "1c9e803c80cc7fed000022d4c94f4b5bc2e90062",
      "7f6120bb10c870b9fde146961a18e5bf0b3d4401",
      "acac5a9854650c4ae2883c4740bf87d34120c038",
    ]
    PROCESS_PATTERNS = [
      "workflow collects environment variables and credential files",
    ]
    NETWORK_PATTERNS = [
      "HTTPS POST to 216.126.225.129:8443/collect",
    ]
    
    # Positive signal: repository, lockfile, artifact, process, or network telemetry contains one of the exact incident selectors above.
    # Escalation: any match tied to a production build, CI run, deployed asset, or secret-bearing host moves the asset to presumed exposed.
    
    OUT.mkdir(parents=True, exist_ok=True)
    indicators_file = OUT / "indicators.txt"
    
    # Collect unique indicators
    indicators = set()
    for group in [PACKAGES, VERSIONS, FILES, DOMAINS, URLS, IPS, HASHES, PROCESS_PATTERNS, NETWORK_PATTERNS]:
        for val in group:
            if val:
                indicators.add(val)
    
    with open(indicators_file, "w") as f:
        for ind in sorted(indicators):
            f.write(ind + "\n")
    
    print(f"[+] Written unique selectors to {indicators_file}")
    
    # Walk local directory
    print(f"[+] Scanning directory: {ROOT} for selectors...")
    matches = []
    exclude_dirs = {"node_modules", "vendor", "dist", ".git"}
    for root, dirs, filenames in os.walk(ROOT):
        dirs[:] = [d for d in dirs if d not in exclude_dirs]
        for filename in filenames:
            filepath = Path(root) / filename
            try:
                content = filepath.read_text(errors="ignore")
                for ind in indicators:
                    if ind in content:
                        matches.append(f"{filepath}: found '{ind}'")
            except Exception:
                pass
    
    if matches:
        (OUT / "repository-indicator-matches.txt").write_text("\n".join(matches) + "\n")
        print(f"[!] Found {len(matches)} matches in codebase!")
    
    # Optional Log Scanning
    if LOG_ROOT and os.path.exists(LOG_ROOT):
        print(f"[+] Scanning telemetry log directory: {LOG_ROOT}...")
        log_matches = []
        for root, _, filenames in os.walk(LOG_ROOT):
            for filename in filenames:
                filepath = Path(root) / filename
                try:
                    content = filepath.read_text(errors="ignore")
                    for ind in indicators:
                        if ind in content:
                            log_matches.append(f"{filepath}: found '{ind}'")
                except Exception:
                    pass
        if log_matches:
            (OUT / "exported-telemetry-indicator-matches.txt").write_text("\n".join(log_matches) + "\n")
            print(f"[!] Found {len(log_matches)} matches in logs!")
    
        if PACKAGES:
            registry_dir = OUT / "registry"
            registry_dir.mkdir(exist_ok=True)
    
    print(f"[+] Wrote scope artifacts under {OUT}")

    Downstream Abuse Audits

    Script: GitHub organization run, release, secret, and workflow audit

    #!/usr/bin/env python3
    import os
    import sys
    import json
    import subprocess
    from pathlib import Path
    
    if "ORG" not in os.environ:
        print("ERROR: Set ORG environment variable to the GitHub organization to audit", file=sys.stderr)
        sys.exit(1)
    
    ORG = os.environ["ORG"]
    SINCE = "2026-05-24T00:00:00Z"
    UNTIL = "2026-05-24T23:59:59Z"
    OUT = Path(os.environ.get("OUT", "hp-megalodon-github-actions-secret-exfiltration-github-audit"))
    
    SELECTORS = [
      ".github/workflows/SysDiag.yml",
      ".github/workflows/Optimize-Build.yml",
      "https://216.126.225.129:8443/collect",
      "216.126.225.129",
      "1c9e803c80cc7fed000022d4c94f4b5bc2e90062",
      "7f6120bb10c870b9fde146961a18e5bf0b3d4401",
      "acac5a9854650c4ae2883c4740bf87d34120c038",
    ]
    
    # Positive signal: a workflow run, release, secret, key, package, or workflow change overlaps the exposure window and references an incident selector.
    # Remediation trigger: unauthorized post-exposure write activity or a secret-bearing run matching an incident selector requires token revocation and downstream cloud/registry review.
    
    OUT.mkdir(parents=True, exist_ok=True)
    (OUT / "runs").mkdir(exist_ok=True)
    (OUT / "logs").mkdir(exist_ok=True)
    (OUT / "repos").mkdir(exist_ok=True)
    
    # 1. Write incident-selectors file
    selectors_file = OUT / "incident-selectors.txt"
    with open(selectors_file, "w") as sf:
        for s in SELECTORS:
            if s:
                sf.write(s + "\n")
    
    # 2. Get list of repos
    print(f"[+] Fetching repositories for organization: {ORG}")
    repo_res = subprocess.run(["gh", "repo", "list", ORG, "--limit", "1000", "--json", "nameWithOwner"], capture_output=True, text=True)
    if repo_res.returncode != 0:
        print(f"[-] Failed to fetch repos: {repo_res.stderr}", file=sys.stderr)
        sys.exit(1)
    
    repos = [r["nameWithOwner"] for r in json.loads(repo_res.stdout)]
    
    for repo in repos:
        safe_repo = repo.replace("/", "__")
        print(f"[+] Auditing repository: {repo}")
    
        # Check runs in the window
        runs_res = subprocess.run([
            "gh", "api", f"/repos/{repo}/actions/runs",
            "-f", "per_page=100",
            "-f", f"created=>={SINCE}",
            "--paginate"
        ], capture_output=True, text=True)
    
        if runs_res.returncode == 0:
            try:
                all_runs = json.loads(runs_res.stdout).get("workflow_runs", [])
                filtered_runs = [r for r in all_runs if r["created_at"] <= UNTIL]
    
                if filtered_runs:
                    with open(OUT / "runs" / f"{safe_repo}-runs.jsonl", "w") as rf:
                        for run in filtered_runs:
                            rf.write(json.dumps(run) + "\n")
    
                            # Fetch log dynamically
                            run_id = str(run["id"])
                            log_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--log"], capture_output=True, text=True)
                            if log_res.returncode == 0:
                                (OUT / "logs" / f"{safe_repo}-{run_id}.log").write_text(log_res.stdout)
    
                            # Fetch details
                            view_res = subprocess.run(["gh", "run", "view", run_id, "--repo", repo, "--json", "databaseId,workflowName,headSha,event,createdAt,jobs"], capture_output=True, text=True)
                            if view_res.returncode == 0:
                                (OUT / "runs" / f"{safe_repo}-{run_id}.json").write_text(view_res.stdout)
            except Exception as e:
                print(f"[-] Error parsing runs for {repo}: {e}")
    
        # Check releases in window
        subprocess.run(["gh", "api", f"/repos/{repo}/releases", "-f", "per_page=100", "--paginate"], capture_output=True)
        # Check repo secrets updated in window
        subprocess.run(["gh", "api", f"/repos/{repo}/actions/secrets", "-f", "per_page=100", "--paginate"], capture_output=True)
        # Check deploy keys
        subprocess.run(["gh", "api", f"/repos/{repo}/keys", "-f", "per_page=100", "--paginate"], capture_output=True)
    
    # Scan output directory for any indicator selector matches
    print("[+] Scanning gathered telemetry for indicator matches...")
    subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(selectors_file), str(OUT)], capture_output=False)
    
    print(f"[+] Wrote GitHub audit artifacts under {OUT}")

    Script: cloud OIDC and deployment credential follow-on audit

    #!/usr/bin/env python3
    import os
    import json
    import subprocess
    from pathlib import Path
    
    SINCE = "2026-05-24T00:00:00Z"
    UNTIL = "2026-05-24T23:59:59Z"
    OUT = Path(os.environ.get("OUT", "hp-megalodon-github-actions-secret-exfiltration-cloud-audit"))
    AWS_REGIONS = os.environ.get("AWS_REGIONS", "us-east-1").split(",")
    
    # Positive signal: token exchange or privileged write activity occurs in the exposure window from GitHub, CI/CD, package registry, or deployment automation identity.
    # Remediation trigger: unexpected write, deploy, IAM, secret, or registry activity tied to an exposed CI/CD path requires trust-policy disablement and credential rotation.
    
    OUT.mkdir(parents=True, exist_ok=True)
    
    # 1. AWS CloudTrail Audit
    print("[+] Querying AWS CloudTrail for Web Identity token exchanges...")
    aws_events = []
    for region in AWS_REGIONS:
        res = subprocess.run([
            "aws", "cloudtrail", "lookup-events",
            "--region", region,
            "--start-time", SINCE,
            "--end-time", UNTIL,
            "--lookup-attributes", "AttributeKey=EventName,AttributeValue=AssumeRoleWithWebIdentity",
            "--output", "json"
        ], capture_output=True, text=True)
    
        if res.returncode == 0:
            try:
                events = json.loads(res.stdout).get("Events", [])
                for event in events:
                    ct = json.loads(event.get("CloudTrailEvent", "{}"))
                    ct["region"] = region
                    aws_events.append(ct)
            except Exception as e:
                print(f"[-] Error parsing AWS CloudTrail events: {e}")
    
    if aws_events:
        with open(OUT / "aws-assume-role-with-web-identity.jsonl", "w") as f:
            for ev in aws_events:
                f.write(json.dumps(ev) + "\n")
    
        # Audit follow-on events for returned access keys
        for ev in aws_events:
            access_key = ev.get("responseElements", {}).get("credentials", {}).get("accessKeyId")
            region = ev.get("region", "us-east-1")
            if access_key:
                print(f"[+] Enumerating AWS events for AccessKey: {access_key}")
                f_res = subprocess.run([
                    "aws", "cloudtrail", "lookup-events",
                    "--region", region,
                    "--start-time", SINCE,
                    "--end-time", UNTIL,
                    "--lookup-attributes", f"AttributeKey=AccessKeyId,AttributeValue={access_key}",
                    "--output", "json"
                ], capture_output=True, text=True)
                if f_res.returncode == 0:
                    try:
                        f_events = json.loads(f_res.stdout).get("Events", [])
                        with open(OUT / "aws-follow-on-api-calls.jsonl", "a") as ff:
                            for fe in f_events:
                                ff.write(fe.get("CloudTrailEvent", "{}") + "\n")
                    except Exception as e:
                        print(f"[-] Error writing follow-on events: {e}")
    
    # 2. Azure Activity Log Audit
    print("[+] Querying Azure activity logs...")
    az_res = subprocess.run([
        "az", "monitor", "activity-log", "list",
        "--start-time", SINCE,
        "--end-time", UNTIL,
        "--query", "[?contains(operationName.value, 'write') || contains(operationName.value, 'delete') || contains(operationName.value, 'Microsoft.ManagedIdentity')]",
        "-o", "json"
    ], capture_output=True, text=True)
    
    if az_res.returncode == 0:
        (OUT / "azure-write-delete-activity.json").write_text(az_res.stdout)
    
    # 3. GCP Logging Audit
    print("[+] Querying GCP Cloud Logging...")
    gcp_filter = f'timestamp>="{SINCE}" AND timestamp<="{UNTIL}" AND (protoPayload.methodName="google.sts.v1.SecurityTokenService.ExchangeToken" OR protoPayload.methodName:"GenerateAccessToken" OR protoPayload.methodName:"CreateServiceAccountKey" OR protoPayload.methodName:"SetIamPolicy")'
    gcp_res = subprocess.run([
        "gcloud", "logging", "read", gcp_filter,
        "--format", "json"
    ], capture_output=True, text=True)
    
    if gcp_res.returncode == 0:
        (OUT / "gcp-token-and-iam-activity.json").write_text(gcp_res.stdout)
    
    print(f"[+] Wrote cloud audit artifacts under {OUT}")

    Script: registry metadata and artifact audit

    #!/usr/bin/env python3
    import os
    import json
    import subprocess
    from pathlib import Path
    
    SINCE = "2026-05-24T00:00:00Z"
    OUT = Path(os.environ.get("OUT", "hp-megalodon-github-actions-secret-exfiltration-registry-audit"))
    PACKAGES = [
    ]
    VERSIONS = [
    ]
    
    # Positive signal: workflow files or extensions reference the affected action/extension names or versions.
    # Remediation trigger: exposed secrets or OIDC federation policies must be immediately rotated.
    
    OUT.mkdir(parents=True, exist_ok=True)
    
    with open(OUT / "affected-versions.txt", "w") as av:
        for version in VERSIONS:
            if version:
                av.write(version + "\n")
    
        # 1. Search local workspace files for the affected actions/extensions
        print("[+] Scanning workspace workflows for selectors...")
        for file in Path(".").glob(".github/workflows/**/*.yml"):
            subprocess.run(["rg", "-n", "--hidden", "--fixed-strings", "-f", str(OUT / "affected-versions.txt"), str(file)])
    
        # 2. HOW TO ROTATE EXPOSED GITHUB ACTIONS SECRETS:
        # Overwrite compromised secrets with newly generated credentials:
        # subprocess.run(["gh", "secret", "set", "COMPROMISED_SECRET_NAME", "--body", "my-new-secret-value", "--repo", "my-org/my-repo"])
        # For organization-level secrets:
        # subprocess.run(["gh", "secret", "set", "COMPROMISED_SECRET_NAME", "--org", "my-org", "--visibility", "private"])
        # Revoke compromised OIDC federated trust credentials in AWS/GCP and redeploy the IAM trust policy:
        # subprocess.run(["aws", "iam", "update-assume-role-policy", "--role-name", "my-role-name", "--policy-document", "file://new-clean-trust-policy.json"])
    
    print(f"[+] Wrote registry audit artifacts under {OUT}")

    Sources

    1. StepSecurity: Megalodon: Mass GitHub Actions Secret Exfiltration Across 5,500+ Public Repositories - Role: PRIMARY_RESEARCH - Impact: Documents affected repository and commit counts, workflow names, payload collection scope, C2 IP, and hunting pivots.

    IOC Clipboard

    7 IOCs
    Defang IOCs
    url https://216.126.225.129:8443/collect hxxps://216[.]126[.]225[.]129:8443/collect
    ip 216.126.225.129 216[.]126[.]225[.]129
    hash 1c9e803c80cc7fed000022d4c94f4b5bc2e90062 1c9e803c80cc7fed000022d4c94f4b5bc2e90062
    hash 7f6120bb10c870b9fde146961a18e5bf0b3d4401 7f6120bb10c870b9fde146961a18e5bf0b3d4401
    hash acac5a9854650c4ae2883c4740bf87d34120c038 acac5a9854650c4ae2883c4740bf87d34120c038
    file .github/workflows/SysDiag.yml .github/workflows/SysDiag.yml
    file .github/workflows/Optimize-Build.yml .github/workflows/Optimize-Build.yml