Skip to main content
SAP Pentest Playbook
Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Toggle Dark/Light/Auto mode Back to homepage

Build & Deploy Pipeline Tampering

Description

Every app that runs on SAP BTP got there through a build-and-deploy path: a developer laptop or CI runner takes source, runs mbt build / npm install / a Kyma image build, produces an .mtar or container image, and pushes it into the subaccount with cf deploy / kubectl / Cloud Transport Management. That path is an attack surface in its own right, and the tester-facing question is not “was there an incident” but where along this path can I inject, and who is allowed to?

Two properties make the path attractive:

  1. The runner holds BTP deploy credentials at the exact moment untrusted code executes. CI runners and developer machines that build MTAs routinely hold CF org/space credentials, Cloud Management service keys, transport service keys, and HANA/XSUAA/Destination bindings in environment variables or config files - right when npm install runs an install hook, or when a build script the attacker influenced executes.
  2. The artifacts are unsigned and unattested. .mtar archives, mta.yaml, lockfiles, and (by default) Kyma images carry no signature the platform verifies at deploy time. Whoever can write the artifact, or influence what goes into it, controls what runs in the subaccount.

This page is the offensive/tester view of that surface. For the durable-persistence view inside Integration Suite (Script Collections, CTMS transport injection into a downstream tenant), see Integration Suite Persistence.

Risk

A single injection point in the build path - for example a merged PR, a poisoned lockfile, a compromised CI service key, an unsigned artifact swap - converts into code execution on infrastructure that holds live BTP deployment authority, and from there into the running subaccount. Because the same MTA/CAP toolchain is used across a customer’s whole BTP estate, and because provider-subaccount apps in multitenant SaaS-on-BTP push to every subscriber, one tampered build can have a blast radius far larger than the one repo it started in.

Options

Map where deploy credentials sit - enumerate before injecting

The reachable credential is the finding. Look for:

# In CI config / runner environment:
grep -rE "CF_(USERNAME|PASSWORD)|cf api|btp login|BTP_.*_SECRET" .github/ .gitlab-ci.yml Jenkinsfile azure-pipelines.yml
# On a developer/CI machine with an active session - plaintext OAuth refresh tokens:
cat ~/.cf/config.json                 # CF refresh token, no password needed
cat ~/.btp/config.json                # btp CLI cached credentials

A live cf login / btp login session on any machine the payload can read yields a working BTP session with no password - see the same caching caveat in Service Key Abuse.

Install-hook execution - the classic injection primitive

preinstall / postinstall scripts in package.json execute on npm install, which runs during mbt build, every CI pipeline, and every developer-machine install:

{ "scripts": { "preinstall": "node setup.mjs" } }

Anyone who can land a change to package.json, a lockfile, or a transitive dependency the app pulls in executes code inside a runner that holds BTP deploy credentials. Test: can you get a lockfile or dependency change merged, or into the runner, without effective review? A protected main that still builds untrusted branches, or an auto-merge bot, is the opening.

Mitigating this on the migration side (and worth noting as a defensive check): npm install --ignore-scripts prevents hook execution.

mta.yaml / .mtar artifact tampering

MTA archives are plain zip files and are not signed or attested on cf deploy:

unzip -l app.mtar                     # inspect module/resource layout
# modify a module's payload or mta.yaml, repackage, drop back into the artifact store

Write access to the artifact store, the CI artifact cache, or the transport queue between build and deploy lets an attacker swap what actually deploys, with no platform-side check that the artifact matches what was built.

npm OIDC Trusted Publishing scope - a directly testable config finding

Where a target publishes its own npm packages via GitHub Actions OIDC Trusted Publishing, check the trust rule’s scope. A rule that trusts any workflow in the repository rather than a specific protected-branch release workflow means a push to a non-main branch can obtain a valid publish token.

Dependency confusion against internal scopes

Where the build pulls internal packages (@company/..., sometimes @sap-adjacent private mirrors), check registry precedence on the runner: if the public registry is queried for a scope that should resolve only to an internal registry, a public package of the same name at a higher version can be pulled instead. Inspect .npmrc scope-to-registry mappings and whether registry fallback reaches the public registry for internal scopes.

Kyma image supply chain

Kyma pulls workload images from a customer-configured registry. Admission-time image signature verification is not enabled by default - there is no built-in gate that the image matches a signed provenance. An attacker who can push to the registry the cluster pulls from, or influence the image tag a Function/Deployment resolves, runs their image in the mesh. See Kyma / Kubernetes for what a mesh workload foothold then reaches.

Provider → subscriber blast radius (multitenant SaaS-on-BTP)

In a multitenant SaaS built on BTP, the provider subaccount owns the application code that every subscriber tenant consumes. Tampering with the provider’s build - or its deploy pipeline - propagates into every subscriber on the next update, with no per-subscriber review. Where the engagement scope includes a SaaS provider, the provider’s CI/CD is the highest-leverage target on the whole estate.

Cloud Transport Management (CTMS) injection

Content packages move between BTP tenants via SAP Cloud Transport Management, and a compromised transport-automation service key can inject a malicious artifact directly into a downstream (potentially production) tenant, bypassing manual review by design. The Integration-Suite-specific mechanics (WorkspacePackagesTransport role, .../transportmodule/Transport endpoint) are documented in Integration Suite Persistence; the same pattern applies to any content type CTMS moves.

Mitigation

  • Treat CI/CD runners that build MTAs as holding Tier-0 BTP deploy credentials; scope those credentials to the minimum org/space/subaccount, prefer short-lived over standing keys, and never leave them readable to build steps that execute third-party code.
  • Pin exact dependency versions and use lockfiles; run npm install --ignore-scripts where hook execution is not genuinely required, and gate lockfile/dependency changes behind review.
  • Scope npm OIDC Trusted Publishers to a specific protected-branch release workflow only - never “any workflow in this repository.”
  • Configure internal package scopes to resolve only against the internal registry; disable public-registry fallback for those scopes.
  • Sign and verify build artifacts and container images; enable admission-time signature verification for Kyma workloads.
  • Put a review/approval gate inside the CTMS pipeline itself, not only at the source tenant, so an injected artifact cannot reach production purely through automation.
  • For SaaS-on-BTP, apply the strictest change control to the provider subaccount’s build and deploy path - its blast radius is every subscriber.
  • Scrub ~/.cf/config.json and btp CLI caches from CI artifacts, shared images, and machine backups; treat them as credential material.

Detection and Monitoring

  • CI/CD audit logs for dependency/lockfile changes, and for build steps that make unexpected outbound network connections during install (install-hook exfil signature).
  • Artifact-store and transport-queue write events from identities other than the expected build pipeline.
  • npm/registry publish events from non-release workflows or non-main branches.
  • Kyma image-pull events resolving to unexpected registries or unsigned digests.
  • Cloud IAM audit trails (CF, and any bound hyperscaler) for activity immediately following npm install / build events on runners.
  • All subject to the BTP Audit Log’s coverage and retention limits - see BTP Audit Log Blind Spots.

References