//Azure DevOps - Security Testing Guide (Ado-STG)/Pipeline Injection

Testing for Code Injection


ID: AdoSTG-PI-01, AdoSTG-PI-02, AdoSTG-PI-03, AdoSTG-PI-04
If you need help assessing the security of your Azure DevOps or organization, we offer a wide range of security services. So, always feel free to reach out at: Sales@enterprisesoftproducts.com
NOTICE: The Azure DevOps - Security Testing Guide (Ado-STG) is currently in BETA; this means that there might be substantial changes and some parts may contain errors or not be fully implemented.

1 - Summary

This section describes how a tester can check if it's possible to abuse variables, parameters, macros, and templates in pipelines to gain code execution.

The main difference between variables and parameters is evaluation.

Evaluation is syntax-dependent. Variables defined with macro syntax ('$(var)') evaluate at runtime before a task runs and are used in scripts and tasks. Variables defined with runtime expressions ('$[variables.var]') evaluate before a job or stage runs and are used in conditions or dynamic variable assignment.

2 - Test Objectives

3 - How to Test

3.1 - Code injection

To test for this, examine the pipeline definition for compile-time template expressions ${{ }}. If these are in the form of parameters, then they might be user-controlled and can be used for code injection in the pipeline.

3.2 - Variable from PR context

Check if variables can come from a PR context; for this to be the case, "variables." need to be used in the pipeline, and these must not be system-safe variables, i.e., not user-controlled. A list of these is presented below:

0"Build_BuildId", 
1"Build_BuildNumber", 
2"Build_Repository_Name", 
3"Build_Repository_Uri", 
4"Build_SourceVersion", 
5"Build_DefinitionName", 
6"System_TeamProject", 
7"System_CollectionUri", 
8"System_DefinitionId", 
9"System_TeamProjectId", 
10"System_CollectionId", 
11"System_HostType", 
12"System_JobId", 
13"System_PlanId", 
14"System_StageId", 
15"System_PhaseId", 
16"System_TimelineId", 
17"System_TaskInstanceId", 
18"System_JobDisplayName", 
19"System_StageDisplayName", 
20"Agent_BuildDirectory", 
21"Agent_Id", 
22"Agent_MachineName", 
23"Agent_Name", 
24"Agent_OS", 
25"Agent_OSArchitecture", 
26"Agent_TempDirectory", 
27"Agent_ToolsDirectory", 
28"Agent_WorkFolder", 
29"Agent_JobName", 
30"Pipeline_Workspace", 

3.3 - Parameter interpolation in runtime expressions

This issue contains parameters and variables that are used together with runtime expressions $[ ]; if this is the case and the runtime user expression is user-controlled, then the user can inject untrusted code into the pipeline.

To check for this, look for runtime expressions $[ ] and "parameters." or "variables."

3.4 - Macro references an injectable context

To check for this issue:

0"Build.BuildId", 
1"Build.BuildNumber", 
2"Build.SourceVersion", 
3"Build.SourceVersionMessage", 
4"Build.Repository.Name", 
5"Build.Repository.Uri", 
6"Build.DefinitionName", 
7"Build.Reason", 
8"System.TeamProject", 
9"System.CollectionUri", 
10"System.DefinitionId", 
11"System.TeamProjectId", 
12"System.CollectionId", 
13"System.HostType", 
14"System.JobId", 
15"System.PlanId", 
16"System.StageId", 
17"System.PhaseId", 
18"System.TimelineId", 
19"System.TaskInstanceId", 
20"System.JobDisplayName", 
21"System.StageDisplayName", 
22"Agent.BuildDirectory", 
23"Agent.Id", 
24"Agent.MachineName", 
25"Agent.Name", 
26"Agent.OS", 
27"Agent.OSArchitecture", 
28"Agent.TempDirectory", 
29"Agent.ToolsDirectory", 
30"Agent.WorkFolder", 
31"Agent.JobName", 
32"Pipeline.Workspace" 

4 - Exploitaiton

This section shows how this issue can be exploited.

4.1 - Privileges

The minimum privileges to execute this attack, given a vulnerable build definition was found, are:

Edit queue build configuration and Queue builds

4.2 - Abuse

For standard code injection, the user can add the command to be injected into the vulnerable variable.

A vulnerable .yaml file might look like this:

0trigger: 
1- main 
2pool: 
3 vmImage: ubuntu-latest 
4steps: 
5- script: | 
6 echo $(InjectionPoint) 
7 displayName: 'Use variable to perform code injection' 

Using a standard code injection such as && whoami produces the following:

0Condition evaluation 
1Starting: Use variable to perform code injection 
2============================================================================== 
3Task         : Command line 
4Description  : Run a command line script using Bash on Linux and macOS and cmd.exe on Windows 
5Version      : 2.279.0 
6Author       : Microsoft Corporation 
7Help         : https://docs.microsoft.com/azure/devops/pipelines/tasks/utility/command-line 
8============================================================================== 
9[Host] Selected Node version: Node24 (Strategy: Node24Strategy) 
10Generating script. 
11Script contents: 
12echo  && whoami 
13========================== Starting Command Output =========================== 
14/usr/bin/bash --noprofile --norc /home/vsts/work/_temp/268acd86-4744-46ab-8373-471b0118bae6.sh 
15vsts 
16Finishing: Use a variable to perform code injection 

5 - Remediation

5.1 - Code injection

Use an environment variable instead of direct parameter interpolation in scripts. Set the parameter as an env var and reference it safely.

5.2 - Variable from PR context

Avoid using variables directly in scripts, as they may come from PR context. Use environment variables with validation instead.

5.3 - Parameter interpolation in runtime expressions

Runtime expressions with parameters or variables can be exploited. Use environment variables with validation instead of runtime expressions in scripts.

5.4 - Macro references an injectable context

Macro expression references an injectable variable. Use an environment variable instead of direct macro interpolation in scripts.

6 - References