ci/cd dangers

during a recent assessment of some ci/cd pipelines at work, i ran into a scenario where custom github actions were implemented to perform infrastructure as code (iac) scans against commits in the repo.

                                   PASS ┌─────────┐    ┌────────┐  
                                 ┌──────│  MERGE  ├───►│ DEPLOY │  
  ┌──────────┐     ┌──────────┐  │      └─────────┘    └────────┘  
  │PR OPENED ├────►│IAC SCANS ├──┤                                 
  └──────────┘     └──────────┘  │      ┌─────────┐                
                                 └──────│  BLOCK  │                
                                   FAIL └─────────┘                
figure 1. high level pipeline overview

the pipeline was pretty straightforward and just did some basic checks against the pull request to ensure that code standards are met and basic security issues (e.g. hardcoded creds) were not present. the pipeline used snyk/actions to perform an initial scan, then followed up with a custom action which was basically just a wrapper around wizcli.

a look at wizcli

wizcli is a tool to help devsecops teams integrate security into deployment pipelines and enforce best practices. while it has a few more use cases beyond iac scans, that is out of scope for this post. wiz allows you to define custom scan profiles with various thresholds for findings, which can influence whether a scan passes or fails. templates are defined in the web console, then specified using a cli arg in wizcli. the action code looked something like this, where the policy var is pulled from a repo variable and passed to the action, then scanCmd is passed to a shell with node's child_process() function.

let scanCmd = "./wizcli iac scan";

if (policy) {
    scanCmd += ` --policy "${policy}"`
}

scope creep !!!

github has three (3) scope levels for variables used in actions. this is very useful as it allows organizations to manage configurations for development teams at the enterprise and organization levels. while useful, a common oversight when establishing ci/cd guardrails is that repository variables are only editable by repository owners.

note: in environments i have seen, the repository owner role is typically held by members of the devsecops team, not members of the development team. this leads to a false assumption that the repository variables are adequately protected from unauthorized changes

github repo variables policy variable
figure 2: github repository variables

profit

now going back to this... "${policy}" the final command would be:

./wizcli iac scan --policy "IaC Scan Policy"

we can clearly see how this is a problem. anyone with control of this variable can compromise the action to exfiltrate secrets, access additional resources, or even move laterally to on-prem or cloud environments. in reality, anything is possible it truly just depends on the pipeline configuration.

a good starting point is to enumerate the environment vars in the action runner like so: exfil secrets

figure 3: data exfil example

opsec considerations

le fin

while not super complex and potentially very niche, i hope this helps in any future assessments. keep an eye out for those overlooked configurations :)