GitHub Actions automates software development workflows, including building, testing, and deploying applications. However, if attackers compromise a repository, abuse excessive workflow permissions, or introduce malicious changes into workflow files, they may be able to execute unauthorised commands within CI/CD runners.
Malicious workflow modifications can expose repository secrets, steal cloud credentials, tamper with build artefacts, or compromise software release pipelines. Because workflows often have access to deployment tokens and other sensitive resources, a seemingly small change to a YAML file can have consequences beyond the repository itself.
For SOC analysts and security engineers, investigating these incidents requires correlating GitHub audit logs, pull requests, workflow execution history, runner activity, secret usage, and downstream cloud or deployment events.
1. Introduction: When CI/CD Automation Becomes an Attack Vector
Modern software development relies on continuous integration and continuous delivery (CI/CD) to automate testing, compilation, packaging, and deployment. GitHub Actions enables developers to define these processes using workflow files stored in a repository.
These workflows are powerful because they can execute commands, access repository contents, interact with external services, and deploy software to production environments.
However, this automation creates an attack surface.
Imagine a developer reviewing a routine change to a project. Alongside the expected application changes, a workflow file has been modified to execute an additional command during a build. If the change passes review and the workflow has access to sensitive credentials, the modified pipeline could become a mechanism for unauthorised access.
Unlike conventional malware delivered directly to an employee's computer, malicious workflow activity may execute within a trusted development environment. The resulting activity can appear to be legitimate automation unless investigators examine the workflow definition and execution context.
The challenge is not simply detecting malicious code. It is determining whether the pipeline executed code that was unauthorised, unexpected, or inconsistent with the repository's intended behaviour.
2. Understanding GitHub Actions Workflows
GitHub Actions workflows are defined in YAML files, typically stored under:
.github/workflows/
A workflow can be triggered by events such as:
A push to a branch.
A pull request being opened or updated.
A release being published.
A scheduled event.
A manually initiated workflow.
A repository or organisation event.
A workflow consists of jobs, and each job contains steps that execute commands or invoke reusable actions. Jobs may run on GitHub-hosted runners or self-hosted runners managed by an organisation.
For example, a simplified workflow might contain:
name: Build and Test
on:
push:
branches: [main]
permissions:
contents: read
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run tests
run: npm test
This example illustrates a basic build workflow. In production, actions should be reviewed and pinned to verified full commit SHAs where practical, and permissions should be limited to what each workflow requires.
A workflow file is executable configuration. Changes to its triggers, permissions, actions, scripts, or deployment steps can materially alter the security of the software delivery process.
3. How Attackers Abuse Workflow Modifications
Malicious workflow activity can occur through different access paths. Investigators should identify the actual entry point rather than assume that every incident begins with a compromised developer account.
Scenario A — Malicious changes to a workflow file
An attacker who gains write access to a repository may modify a workflow to introduce unexpected commands, invoke an untrusted action, or change the conditions under which a job executes.
The modification may be disguised as a routine build improvement or dependency update.
Investigative focus: Compare the changed workflow with its previous version, identify who introduced the modification, inspect the associated pull request, and determine which workflow runs executed the new configuration.
Scenario B — Abuse of workflow permissions
GitHub Actions can receive a GITHUB_TOKEN with permissions defined by workflow or job configuration.
If a workflow receives more permissions than it requires, malicious code running within that workflow may be able to perform additional repository operations.
Investigative focus: Review the effective permissions, the event that triggered the workflow, the token's available scope, and any repository changes made during or after execution.
Scenario C — Compromised third-party actions
Workflows frequently depend on actions maintained by external developers or organisations. If an action is compromised or its reference can be redirected to malicious code, the workflow may execute unexpected behaviour without an obvious change to the repository's own script.
Investigative focus: Examine action references, recent dependency changes, commit-SHA pinning, maintainer activity, and any unexpected behaviour introduced by an updated action.
Scenario D — Pull request and event-context abuse
Some workflow designs expose privileged tokens or secrets to code originating from less-trusted contexts. Unsafe use of pull request events, untrusted input, or privileged workflow triggers can create opportunities for code execution or credential exposure.
Investigative focus: Determine which event triggered the workflow, whether untrusted code was checked out or executed, and which secrets or permissions were available to that run.
The exact risk depends on the event type, repository settings, token permissions, and workflow design. Not every pull request workflow is inherently unsafe.
Scenario E — Abuse of self-hosted runners
Self-hosted runners may have access to internal networks, cached credentials, persistent workspaces, or resources unavailable to GitHub-hosted runners.
If an attacker executes code on a poorly isolated self-hosted runner, the impact may extend beyond a single workflow job.
Investigative focus: Review runner assignment, execution history, workspace persistence, network access, operating-system logs, and any other jobs or workloads that used the same runner.
4. Anatomy of a Malicious Workflow Incident
Consider a hypothetical organisation whose repository uses GitHub Actions to test and deploy an application.
Stage 1 — Repository access
An attacker gains access to a repository through compromised credentials, excessive permissions, or another security weakness.
At this stage, repository access alone does not establish that a malicious workflow has been introduced.
Stage 2 — Workflow modification
A workflow file is changed to include an unauthorised action, unexpected command, altered trigger, or expanded permissions.
The change may be submitted through a pull request or pushed directly, depending on the repository's controls and the attacker's access.
Stage 3 — Workflow execution
A push, pull request, release, or other event triggers the modified workflow.
The runner executes the workflow steps using the permissions, secrets, and environmental access available to that run.
Stage 4 — Unauthorised access or data exposure
If the workflow can access sensitive credentials or resources, malicious code may attempt to expose secrets, modify repository content, access cloud services, or alter deployment behaviour.
Whether this succeeds depends on the effective permissions, available secrets, network controls, and other protections.
Stage 5 — Downstream impact
Potential consequences include unauthorised releases, modified build artefacts, compromised deployment credentials, or access to other systems.
Investigators must verify which actions actually occurred. A suspicious workflow change does not prove that secrets were stolen or that production systems were compromised.
5. SOC Investigation: Step-by-Step Workflow
A useful investigation connects the configuration change to the workflow execution and any downstream activity.
Step 1 — Identify the affected repository and workflow
Record:
Repository name and owner.
Workflow file path.
Commit SHA associated with the change.
Branch and triggering event.
Workflow run ID and timestamp.
Actor associated with the change or run.
Runner type and job details.
Preserve the original workflow version and the suspicious revision. Avoid immediately deleting the evidence before establishing what changed and whether the workflow executed.
Step 2 — Compare the workflow changes
Use version control history to compare the suspicious workflow with the last known-good version.
Look for:
New or modified run commands.
Added third-party actions.
Changes to action references or versions.
Expanded permissions settings.
Modified workflow triggers.
New environment variables or secret references.
Changes to deployment conditions.
Commands that download and execute external content.
Obfuscated shell or scripting commands.
These are investigation leads, not automatic indicators of compromise. Legitimate workflows may download dependencies, use secrets, or execute scripts. Determine whether each change is authorised and consistent with the repository's purpose.
Step 3 — Review the GitHub audit trail
Examine available organisation and repository audit records for relevant changes and access events.
Look for:
Repository permission changes.
Unexpected collaborator or team access.
Workflow-file modifications.
Branch protection or ruleset changes.
Unusual authentication or token-related activity.
Changes to secrets, variables, or deployment configuration.
Workflow runs initiated by unexpected actors or events.
Available audit events depend on the GitHub plan, event type, retention period, and logging configuration. A missing audit event should not automatically be interpreted as proof that the action did not occur.
Step 4 — Examine workflow execution logs
Review the full job and step logs for the affected workflow run.
Identify which steps executed, their order, the commands invoked, the actions used, and any failures or unexpected output.
Pay particular attention to commands that:
Access environment variables.
Read or package sensitive files.
Make unexpected outbound requests.
Modify repository contents.
Authenticate to external services.
Publish or deploy artefacts.
Treat logs as sensitive evidence because they may contain confidential output or accidentally exposed credentials. Restrict access and avoid reproducing secret values in incident notes.
Step 5 — Determine which secrets and permissions were available
Identify the permissions effective for the affected job and the secrets or credentials it could access.
Review:
GITHUB_TOKEN permissions.
Repository, environment, and organisation secrets.
Cloud federation or OIDC trust policies.
Deployment credentials.
Package registry tokens.
Credentials available on self-hosted runners.
Access to internal services.
A reference to a secret in a workflow does not prove that its value was exposed. Investigators should establish whether the malicious step could access it and whether there is evidence of disclosure or misuse.
Step 6 — Investigate runner activity
For GitHub-hosted runners, examine workflow logs and available run metadata.
For self-hosted runners, additionally investigate host-level evidence such as:
Process execution.
Network connections.
File modifications.
Workspace changes.
New scheduled tasks or services, where relevant.
Access to local credentials or configuration files.
Activity from other jobs that shared the runner.
Determine whether the runner was ephemeral or persistent and whether its environment was reused after the suspicious run.
Step 7 — Hunt for downstream activity
A workflow incident may affect systems outside GitHub.
Review cloud audit logs, package registries, container registries, deployment platforms, and target systems for suspicious operations occurring after the workflow ran.
Depending on the environment, investigate:
Unexpected cloud API calls.
New or modified access credentials.
Unauthorised package or release publication.
Changes to deployment configuration.
Access to production resources.
Unusual activity associated with the workflow's identity.
Correlate timestamps, principal identities, source addresses, resource names, and available request IDs. Do not assume a downstream event was caused by the workflow without supporting evidence.
Step 8 — Contain and recover
If malicious activity is confirmed or strongly suspected:
Disable or suspend the affected workflow where appropriate.
Restrict the compromised account or repository access.
Revert unauthorised workflow changes using a verified clean version.
Revoke or rotate potentially exposed credentials.
Review cloud and deployment access for misuse.
Rebuild affected artefacts from a trusted source when integrity is uncertain.
Recreate or clean compromised runners as appropriate.
Preserve evidence and document the recovery process.
Rotating a secret is not sufficient if malicious workflow code remains active or if an attacker has established another form of access.
6. Evidence and Indicators to Collect
There is no universal indicator of compromise for a malicious GitHub Actions workflow. The strongest evidence comes from correlating changes, execution records, and downstream effects.
Evidence sourceWhat to examineInvestigative valueGit historyWorkflow diffs, commit authors, timestampsEstablishes what changed and whenPull request recordsReviewers, approvals, comments, changed filesProvides context for authorisation and reviewGitHub audit logsRepository access and configuration changesHelps reconstruct account and administrative activityWorkflow run metadataTriggering event, actor, run ID, job statusConnects the change to executionWorkflow logsCommands, actions, errors, and step sequenceShows what the runner attempted to executeToken permissionsEffective GITHUB_TOKEN scope and access controlsDefines possible repository impactSecret configurationSecret availability and access pathsHelps assess potential credential exposureRunner telemetryProcesses, files, connections, host changesIdentifies activity beyond workflow logsCloud and deployment audit logsAPI calls, releases, resource modificationsEstablishes downstream impact
Record all timestamps with their time zone and preserve the original evidence. Workflow logs and audit records may have different retention periods, so collection should begin promptly.
7. MITRE ATT&CK Mapping
Map observed activity to ATT&CK based on evidence rather than assigning techniques solely because a workflow file was modified.
T1195.001 — Compromise Software Dependencies and Development Tools: Potentially relevant when an attacker compromises a development tool or dependency used in the software supply chain.
T1195.002 — Compromise Software Supply Chain: Relevant when the attack compromises a software build or distribution process.
T1059 — Command and Scripting Interpreter: Relevant when malicious workflow steps execute commands or scripts.
T1552 — Unsecured Credentials: Potentially relevant when evidence shows credentials were exposed through insecure storage or handling.
T1078 — Valid Accounts: Relevant when attackers use legitimate accounts to access repositories or connected resources.
T1562.001 — Disable or Modify Tools: Relevant only if evidence shows that security tools or controls were disabled or modified.
These mappings are conditional. The specific technique and sub-technique should match the observed behaviour and the current ATT&CK definitions.
8. Detection Engineering: What Should SOC Teams Monitor?
Detection should cover workflow changes, execution behaviour, permission changes, and downstream access.
Workflow-file modifications
Alert when workflow files under .github/workflows/ are modified, especially when the change introduces new commands, third-party actions, altered triggers, or broader permissions.
Prioritise changes to protected branches or production deployment workflows.
Unexpected permission expansion
Monitor changes that increase GITHUB_TOKEN permissions or introduce access to sensitive environments.
A workflow that previously needed read-only access but suddenly requests write permissions deserves review, particularly if the change was not expected.
Untrusted code in privileged workflows
Review workflows that execute pull-request-controlled code while possessing secrets or elevated permissions. Pay special attention to unsafe combinations of event triggers, checkout behaviour, token availability, and untrusted inputs.
Unpinned third-party actions
Identify actions referenced by mutable tags or branches and assess the risk of unexpected upstream changes. Where practical, pin actions to verified full commit SHAs and establish a process for safely updating them.
Suspicious runner behaviour
Monitor unexpected outbound connections, unusual process execution, and access to sensitive files from CI/CD runners. Self-hosted runners should have host-level monitoring and restricted network access.
Downstream credential use
Correlate workflow runs with cloud identity events, deployment activity, and package publication. Unexpected use of a workflow-associated identity after a suspicious run can indicate that the investigation needs to extend beyond the repository.
9. Hardening GitHub Actions
Apply least-privilege permissions
Set restrictive default workflow permissions and grant write access only to jobs that require it. Prefer job-level permissions over broad workflow-wide access when practical.
Protect workflow changes
Use branch protection or repository rulesets, required reviews, CODEOWNERS for sensitive workflow paths, and restrictions on direct pushes to protected branches.
Review whether the people approving workflow changes have sufficient context to evaluate their security impact.
Pin and review third-party actions
Pin third-party actions to verified full commit SHAs where practical. Assess maintainers, release history, required permissions, and the action's access to secrets before adoption.
Separate untrusted and privileged workflows
Do not expose privileged credentials to untrusted pull-request code. Design workflows so that validation of untrusted changes is separated from release or deployment jobs that require elevated access.
Use environment protection rules and approvals for sensitive deployments.
Use short-lived credentials
Where supported, use GitHub's OpenID Connect (OIDC) integration to obtain short-lived cloud credentials rather than storing long-lived cloud keys in repository secrets.
Restrict cloud trust policies by repository, branch, environment, and other available claims. OIDC reduces long-lived credential exposure but does not make a malicious workflow harmless if that workflow can obtain authorised credentials.
Isolate runners
Prefer ephemeral runners for workloads that process untrusted code. Restrict self-hosted runner network access, remove unnecessary tools and credentials, and avoid sharing persistent environments between jobs with different trust levels.
Monitor and retain logs
Collect GitHub audit records, workflow run metadata, and relevant cloud or runner logs in a location protected from repository-level compromise. Establish retention periods that support investigations and alert on suspicious configuration changes.
10. Practical Incident Assessment
At the end of the investigation, classify the findings according to the evidence:
Unauthorised modification: A workflow change was not approved or was inconsistent with intended behaviour.
Execution confirmed: The modified workflow ran, and its relevant steps executed.
Credential exposure suspected or confirmed: Evidence indicates that secrets or credentials were accessible to malicious code or were disclosed.
Downstream compromise confirmed: Evidence shows unauthorised access, resource modification, or deployment activity beyond the workflow itself.
Scope unresolved: Missing logs or incomplete visibility prevent a reliable conclusion.
These distinctions help prevent two common mistakes: treating every suspicious workflow change as a confirmed breach, and treating a successful workflow run as harmless merely because it completed without errors.
Conclusion
GitHub Actions is a powerful automation platform, but its integration with source code, credentials, build systems, and deployment environments makes workflow security a critical part of software supply chain defence.
Malicious workflow modifications can turn legitimate CI/CD infrastructure into an execution path for unauthorised commands, credential exposure, or downstream compromise. Investigating these incidents requires correlating repository history, audit logs, workflow execution, runner activity, and cloud or deployment telemetry.
For SOC teams, the central lesson is to treat workflow files as security-sensitive code. Enforce least privilege, protect changes to critical workflows, isolate runners, restrict credentials, and monitor what happens after every suspicious execution.
A secure repository is not enough if its automation pipeline can be modified or executed without effective controls.
Further Reading
GitHub Docs — Security hardening for GitHub Actions
GitHub Docs — Using secrets in GitHub Actions
GitHub Docs — Using OpenID Connect with reusable workflows
MITRE ATT&CK — Compromise Software Supply Chain (T1195.002)