Abusing GitLab CI Runners as a Command-and-Control Framework
Detection stack
- AIDR
- Alert
- ETL
- Query
Summary
The article describes a proof of concept named GitRunner C2 that turns legitimate, digitally signed GitLab CI runner binaries into a command-and-control framework. By installing the runners as Windows services, an attacker can run arbitrary PowerShell commands and exfiltrate data through GitLab’s built-in infrastructure. Because the traffic uses outbound HTTPS to gitlab.com, the activity can blend in with normal developer communications.
Investigation
The researcher demonstrated the complete attack chain, starting with enrollment through an elevated PowerShell one-liner and ending with interactive command execution. The investigation relied on Sysmon and PowerShell Operational logs to record process creation, registry changes, and executed commands through Script Block Logging. The study showed that the runner’s legitimate behavior can help evade traditional EDR and network-based detection.
Mitigation
Defenders should watch for newly installed Windows services with unusual names or services launched from non-standard file paths. Enabling PowerShell Script Block Logging is essential for visibility into commands executed through the runner. Organizations should also limit who can install services and monitor for suspicious outbound HTTPS traffic to GitLab from endpoints that are not used for development work.
Response
If this activity is detected, isolate the affected endpoint immediately to stop further command-and-control communication. Revoke any GitLab Personal Access Tokens and runner registration tokens tied to the compromised environment. Investigators should then review PowerShell logs and Windows Event Logs to determine the scope of executed commands and any resulting data exfiltration.
"flowchart TD step_ingress["T1105 u2013 Ingress Tool Transfer: Download of legitimate gitlab-runner-windows-amd64.exe via PowerShell from S3"] step_persistence["T1543.003 u2013 Create or Modify System Process: Windows Service: Installing GitLab runner as a native Windows service for persistence"] rules_for_persistence("<b>Rule Name</b>: Suspicious Service Binary Path (via system)<br/><b>Rule ID</b>: 780e6396-d9f4-42b2-8d73-89918e2dab16") step_trust_subversion["T1553.002 u2013 Subvert Trust Controls: Code Signing: Using the digitally signed GitLab binary to evade detection"] step_execution["T1059.003 u2013 Command and Scripting Interpreter: Windows Command Shell: Executing command payloads via CI variables using shell executor"] step_c2["T1071 u2013 Application Layer Protocol: Using HTTPS (port 443) to GitLab infrastructure as a C2 relay"] step_exfiltration["T1567.001 u2013 Exfiltration Over Web Service: Exfiltration to Code Repository: Moving files via GitLab Artifacts and Generic Package Registry"] step_ingress –>|leads_to| step_persistence step_persistence –>|enables| step_trust_subversion step_persistence -.->|detected_by| rules_for_persistence step_trust_subversion –>|then| step_execution step_execution –>|uses| step_c2 step_c2 –>|leads_to| step_exfiltration "
Attack Flow
Detections
The Possibility of Execution Through Hidden PowerShell Command Lines (via cmdline)
View
Possible GitRunner C2 Execution (via powershell)
View
Possible Scheduled Task Creation (via powershell)
View
Possible Scheduled Task Creation Using Powershell (via powershell)
View
Service Image Operations by Rare Process (via registry_event)
View
Suspicious Service Binary Path (via system)
View
PowerShell Script Block Logging Indicating Potential GitLab Runner Abuse [Windows Powershell]
View
PowerShell GitLab Runner Service Evasion [Windows Sysmon]
View
Simulation Execution
Prerequisite: The Telemetry & Baseline Pre-flight Check must have passed.
Rationale: This section details the precise execution of the adversary technique (TTP) designed to trigger the detection rule. The commands and narrative MUST directly reflect the TTPs identified and aim to generate the exact telemetry expected by the detection logic. Abstract or unrelated examples will lead to misdiagnosis.
-
Attack Narrative & Commands: An adversary has gained initial access and intends to establish a persistent Command and Control (C2) channel. Instead of using known malware, they download the legitimate
gitlab-runner-windows-amd64.exeto blend in with DevOps activity. They use PowerShell to drop the file into a temporary directory and then register it as a Windows Service named “gitlab-runner”. This method aims to exploit the trusted nature of CI/CD tools to bypass traditional security scrutiny. -
Regression Test Script:
# Simulation of GitLab Runner Installation for Detection Validation $ErrorActionPreference = "Stop" # 1. Define paths and names to match the detection logic exactly $TargetDir = "C:WindowsTemp" $BinaryName = "gitlab-runner-windows-amd64.exe" $BinaryPath = Join-Path $TargetDir $BinaryName $ServiceName = "gitlab-runner" Write-Host "[+] Starting Simulation: Installing $BinaryName" # 2. Simulate File Creation (Sysmon Event ID 11) # In a real attack, this would be a web download. Here we create a dummy file with the specific name. New-Item -Path $BinaryPath -ItemType File -Force | Out-Null Write-Host "[+] Created file: $BinaryPath (Triggers Event ID 11)" # 3. Simulate Registry Service Installation (Sysmon Event ID 13) # Creating the service path in the registry to trigger the ImagePath detection. $RegPath = "HKLM:SYSTEMCurrentControlSetServices$ServiceName" New-Item -Path $RegPath -Force | Out-Null Set-ItemProperty -Path $RegPath -Name "ImagePath" -Value $BinaryPath Write-Host "[+] Created registry key and ImagePath: $RegPath (Triggers Event ID 13)" Write-Host "[+] Simulation Complete. Check SIEM for alerts." -
Cleanup Commands:
# Cleanup simulation artifacts $TargetDir = "C:WindowsTemp" $BinaryName = "gitlab-runner-windows-amd64.exe" $BinaryPath = Join-Path $TargetDir $BinaryName $ServiceName = "gitlab-runner" Remove-Item -Path $BinaryPath -Force -ErrorAction SilentlyContinue Remove-Item -Path "HKLM:SYSTEMCurrentControlSetServices$ServiceName" -Recurse -Force -ErrorAction SilentlyContinue Write-Host "[+] Cleanup complete."