DEV Community

Cover image for F5 Code Theft: 7 Urgent Lessons Vendors Must Pass
Pentest Testing Corp
Pentest Testing Corp

Posted on

F5 Code Theft: 7 Urgent Lessons Vendors Must Pass

7 Urgent Lessons from the F5 Breach Source Code Theft


TL;DR (for busy engineering leaders)

Attackers gained long-term access to F5 and exfiltrated BIG-IP source code and vulnerability information. CISA issued an emergency directive for federal agencies to harden and patch F5 products. Disclosures indicate discovery in August 2025 and public notification mid-October 2025. Treat this as a supply-chain incident: assume a motivated adversary may leverage stolen code to build better exploits.

F5 Code Theft: 7 Urgent Lessons Vendors Must Pass


What happened (overview + timeline)

  • Intrusion & dwell time: F5 reported long-term, persistent access by a nation-state–linked actor with downloads of internal files, including source code.
  • Discovery & disclosure: The company discovered the breach on August 9, 2025, and publicly disclosed October 15, 2025; U.S. officials issued an emergency directive to federal agencies the same day.
  • Attribution chatter: Reporting indicates likely state-linked involvement; some outlets cite a China connection, though official attribution is still developing.
  • Why it matters: With BIG-IP source code allegedly exposed, attackers can study implementation details, correlate with unreleased bug info, and weaponize subtle misconfigurations at scale.

Treat the F5 breach source code theft as a watershed vendor risk and supply-chain moment, even when the vendor is a security company.


Use our free scanner to spot web-facing risks fast

Run a quick external triage with our Free Website Vulnerability Scanner to find obvious exposures while your team works the deeper items.


7 urgent vendor/third-party lessons (DEV-first)

1) Segment and escrow critical code

  • Repository segmentation: Split monorepos; isolate sensitive components behind separate review & release boundaries.
  • Code escrow: Place release-critical modules under escrow with monitored access trails and key sharding among two approvers.

GitHub branch protections (example via API):

# Require reviews + signed commits on main branch
curl -X PUT \
  -H "Authorization: token $GH_TOKEN" \
  -H "Accept: application/vnd.github+json" \
  https://api.github.com/repos/org/prod-repo/branches/main/protection \
  -d '{
    "required_pull_request_reviews": {"required_approving_review_count": 2},
    "enforce_admins": true,
    "required_signatures": true,
    "required_status_checks": {"strict": true, "contexts": ["ci/test","sast/scan"]},
    "restrictions": null
  }'
Enter fullscreen mode Exit fullscreen mode

2) Least-privilege, token-scoped vendor access

Use time-boxed, IP-restricted access for vendor support. Prefer OIDC-backed short-lived credentials over PATs.

AWS IAM role for vendors (IP + ExternalId conditions):

# terraform
resource "aws_iam_role" "vendor_support" {
  name = "vendor-support-role"
  assume_role_policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect = "Allow",
      Principal = {"AWS": "arn:aws:iam::123456789012:root"},
      Action = "sts:AssumeRole",
      Condition = {
        StringEquals = {"sts:ExternalId": "ticket-<case-id>"},
        IpAddress   = {"aws:SourceIp": ["203.0.113.0/24","198.51.100.0/24"]}
      }
    }]
  })
}
resource "aws_iam_policy" "vendor_least_priv" {
  name = "vendor-support-lp"
  policy = jsonencode({
    Version = "2012-10-17",
    Statement = [{
      Effect = "Allow",
      Action = ["ec2:Describe*","elasticloadbalancing:Describe*"],
      Resource = "*"
    }]
  })
}
Enter fullscreen mode Exit fullscreen mode

Rotate on a schedule and enforce approvals via change-tickets in GitOps.

3) Monitor for code exfiltration signals

Deploy detective controls that catch unusual repo activity: giant clone sizes, non-developer hours, atypical geos, or sudden access to archival branches.

Audit Git server for anomalous packfiles:

#!/usr/bin/env bash
# flag clones > 2 GB or unusual hours
LOG=/var/log/git-daemon/audit.log
awk '
/packfile/ {
  size=$NF; ts=$1" "$2
  hour=strftime("%H", mktime(gensub(/[-:]/," ","g",$1" "$2" 0")))
  if (size > 2147483648 || hour < 6 || hour > 22) {
    print ts, $0
  }
}' "$LOG" | tee suspicious_git_activity.log
Enter fullscreen mode Exit fullscreen mode

KQL (Microsoft Sentinel) for off-hours mass clones:

GitServerLogs
| where Action in ("clone","fetch") 
| summarize totalBytes=sum(Bytes) by User, bin(TimeGenerated, 1h), SrcIp
| where totalBytes > 5gb and hour(TimeGenerated) in (0..5,23)
Enter fullscreen mode Exit fullscreen mode

4) Source integrity: signed commits + SLSA provenance

Require GPG/SSH-signed commits and verifiable build provenance so attackers can’t slip altered code via stolen tokens.

GitHub Actions: verify SLSA provenance + Sigstore attestations

name: verify-provenance
on: [workflow_call, workflow_dispatch]
jobs:
  slsa-verify:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: slsa-framework/slsa-verifier/actions/verify-artifact@v2
        with:
          artifact_path: dist/app.tar.gz
          provenance_path: dist/app.intoto.jsonl
      - name: Verify signed commits
        run: |
          git log --pretty="%H %G?" | awk '$2!="G"{print "UNVERIFIED:",$1; exit 1}'
Enter fullscreen mode Exit fullscreen mode

5) SBOMs & dependency policy gates

Mandate CycloneDX/SPDX SBOMs on every build and fail releases on high-risk transitive upgrades.

Node + CycloneDX + OSSF Scorecard gate:

npm ci
npx @cyclonedx/cyclonedx-npm --output-file sbom.json
# run OSSF Scorecard
docker run --rm gcr.io/ossf/scorecard:stable --repo=$GITHUB_SERVER_URL/$GITHUB_REPOSITORY --checks Vulnerabilities --format json > scorecard.json
jq '. | select(.Scorecard.Score < 7)' scorecard.json && exit 1 || echo "Score OK"
Enter fullscreen mode Exit fullscreen mode

6) Event-driven detection for vendor roles

Emit logs when a vendor toggles sensitive permissions.

Python: alert on IAM policy drift

import boto3, json, hashlib, os
iam = boto3.client('iam')
baseline = json.load(open('vendor_policy_baseline.json'))
current = iam.get_policy_version(PolicyArn=os.environ['POLICY_ARN'],
                                 VersionId=iam.get_policy(PolicyArn=os.environ['POLICY_ARN'])['Policy']['DefaultVersionId'])
h1 = hashlib.sha256(json.dumps(baseline, sort_keys=True).encode()).hexdigest()
h2 = hashlib.sha256(json.dumps(current['PolicyVersion']['Document'], sort_keys=True).encode()).hexdigest()
if h1 != h2:
    print("ALERT: Vendor policy drift detected")
    # push to Slack/SIEM...
Enter fullscreen mode Exit fullscreen mode

7) Crisis-ready disclosure & investor comms

When source code is stolen, incident classification shifts: beyond “confidential data loss” to development environment compromise with product security implications and potential investor risk. Build comms templates and SEC-8K playbooks now. (F5’s public incident led to CISA’s emergency directive and heightened regulator/customer scrutiny—expect the same posture.)


What to do now (48-hour checklist)

  1. Vendor code access audit (Day 0–1):
  • Enumerate which vendors can read or clone crown-jewel repos; revoke broad read rights.
  • Enforce time-boxed, IP-restricted roles (example above).

    1. Source integrity checks (Day 0–2):
  • Turn on required signed commits and verify SLSA provenance for all release artifacts.

    1. Repository segmentation (Day 1–2):
  • Carve out sensitive modules into restricted repos; add 2-approver PR gates.

    1. Scorecards (Day 1–2):
  • Create a vendor security scorecard (access model, SOC 2/ISO 27001 status, SBOM cadence, incident SLA).

    1. Perimeter triage (Immediate):
  • Patch F5 devices promptly; verify no exposed admin panels; rotate credentials and tokens touched by vendors.


DEV playbook: Real-time snippets you can drop in today

Block vendor logins outside maintenance windows (OIDC + claim checks):

# example OPA policy (Rego) for your IDP app
package vendor.access
default allow = false
allow {
  input.user.group == "vendor-support"
  input.request.client_ip == "203.0.113.25"
  time.hour(time.now()) >= 10
  time.hour(time.now()) <= 18
  input.request.ticket_id != ""
}
Enter fullscreen mode Exit fullscreen mode

Webhook to quarantine suspicious clones:

from flask import Flask, request
import requests, os
app = Flask(__name__)

SECURITY_BOT = os.environ["BOT_TOKEN"]
QUARANTINE_BRANCH = "quarantine/"  # prefix

@app.post("/git/audit")
def audit():
    ev = request.json
    if ev["action"] == "clone" and ev["bytes"] > 2_147_483_648:
        repo = ev["repo"]
        # lock repo + force branch restrictions
        requests.put(f"https://api.github.com/repos/{repo}/interaction-limits",
                     headers={"Authorization": f"token {SECURITY_BOT}"}, json={"limit":"collaborators_only"})
        return {"status":"locked"}, 200
    return {}, 204
Enter fullscreen mode Exit fullscreen mode

CI: fail builds if SBOM new high-risk package appears:

jq -r '.components[].name+"@"+.components[].version' sbom.json > sbom.list
comm -13 baseline_sbom.list sbom.list | while read p; do
  risk=$(riskctl query "$p") # your internal risk DB
  [[ "$risk" == "high" ]] && echo "New high-risk dep: $p" && exit 1
done
Enter fullscreen mode Exit fullscreen mode

Disclosure & transparency: raise the bar

The F5 breach source code theft shows that source loss changes the calculus: you must assume differential exploit development. Your disclosure plan should include:

  • Impact to product security and customer configurations. ([Wall Street Journal][4])
  • Clear rotation guidance for tokens/keys and vendor accounts.
  • Commitments to provenance-verified builds and SBOM publishing cadence.

Sample findings report screenshot


Work with us on the hard parts


Final word

The F5 breach source code theft is a stress test for your third-party risk program and engineering guardrails. Use the controls and code above to close the gaps now—and if you want hands-on help, we’ll align remediation to OWASP ASVS, SOC 2, and ISO 27001 with audit-ready evidence.

Top comments (0)