What the CTEM program does
Most security teams find out they're exposed the same way an attacker does — by scanning. The
difference is who gets there first. This CTEM program flips that timeline: instead of waiting for
a pentest, a bug bounty report, or an incident to reveal what's exposed, it runs the same
reconnaissance an attacker would run — continuously, against your own footprint — and turns the
output into tracked, owned remediation work before it becomes an incident.
The pipeline mirrors an actual attack chain, stage by stage:
- Discover what exists (OSINT, subdomains, assets you may not even know are yours)
- Confirm what's alive and what it's running
- Assess what's vulnerable or misconfigured
- Correlate exposure back to identity — because a vulnerable host is a problem, but a vulnerable host with a path to Domain Admin or a cloud admin role is an incident waiting to happen
Why cloud-agnostic and ephemeral matters (and why "hidden cost" is the real enemy)
Every major cloud provider now ships a native posture-management or "well-architected" tool —
and on paper, that's convenient. In practice, a lot of these tools quietly meter you into
consumption-based pricing for the AI/analysis layer underneath. AWS's Well-Architected Tool
review itself is free, but plug it into Bedrock-backed recommendations or continuous AI-assisted
analysis, and the bill scales with usage in a way that's hard to forecast and easy to lose track
of — a classic hidden-cost trap for a program that's supposed to be about reducing risk, not
opening a new unpredictable line item.
This toolchain takes the opposite approach on purpose:
- Cloud-agnostic — none of these tools care whether your footprint is AWS, Azure, GCP, or on-prem. The same pipeline that maps your Azure attack paths (AzureHound) maps AWS ones (AWSHound) with the same BloodHound backend. You're not locked into a single vendor's visibility model.
- Ephemeral by design — every tool here is a CLI binary or a short-lived container. Spin it up, run the scan, tear it down. No standing infrastructure, no idle compute burning budget between scan windows, no persistent SaaS subscription cost creeping upward as your asset count grows.
- Open source, self-hosted — the cost is your compute time, not a per-asset or per-scan metered fee. A weekly CTEM run against a mid-size external footprint costs you a few dollars of transient VM time, full stop — no surprise invoice at the end of the month.
Tools in the pipeline
| Tool | Category | Function |
|---|---|---|
| theHarvester | OSINT / Digital Footprint | Passive discovery of hosts, emails, and exposure tied to the domain |
| Amass / Subfinder | Attack Surface Management | Enumerate every subdomain forming the external asset inventory |
| Naabu | Attack Surface Management | Identify open ports on discovered hosts |
| httpx | Attack Surface Management | Confirm which hosts are live and fingerprint the tech running |
| Nuclei | Vulnerability Assessment | Detect known CVEs, misconfigurations, and exposed cloud resources via community + custom templates |
| TruffleHog | Secret / Credential Exposure | Find live API keys and credentials leaked in code repositories |
| ScoutSuite | Cloud Security Posture (CSPM) | Audit cloud account configuration against security best practice |
| BloodHound + AzureHound / AWSHound | Identity & Attack Path Analysis | Map privilege-escalation and lateral-movement paths across cloud identity graphs |
A word of caution before you run any of this: verify what you trust
In March 2026, Aqua Security's Trivy — one of the most widely used open-source scanners in the cloud-native ecosystem — was itself compromised. A threat actor tracked as TeamPCP force-pushed malicious tags across the trivy, trivy-action, and setup-trivy GitHub repos and published tampered Docker Hub images, turning a trusted security tool into a credential-harvesting weapon inside thousands of CI/CD pipelines. It's a sharp reminder that a CTEM pipeline built entirely on open-source tooling is only as trustworthy as your verification discipline. Ten lessons worth carrying into how you run this stack:
A "security vendor" label is not a trust guarantee — even scanners get compromised
Version tags (@v1, @latest) are mutable and can be force-pushed by anyone with write access
Pin every GitHub Action and container image to an immutable full commit SHA or digest, never a tag
Credential rotation must be atomic — revoke first, then reissue; a staggered rotation window is an open door
Avoid pull_request_target in any workflow that touches forked PR code — it runs with base-repo secrets against untrusted input
Any CI/CD runner that executed a since-compromised action should have every exposed secret treated as burned, not just rotated
Don't trust an artifact because the filename or registry looks official — verify cryptographically, every time
Use short-lived OIDC tokens for cloud access instead of long-lived static credentials in CI/CD
Watch for anomalous tag movement or force-pushes on the dependencies you consume — it's a detectable signal, not just hindsight
Build a culture where your own tooling gets the same scrutiny you'd apply to a stranger's code — trust is earned per-run, not once
Verify before you run: cosign + SLSA provenance
Before any of the binaries or container images in this pipeline touch your infrastructure, verify both the signature and the build provenance:
# Verify the image was signed by the expected publisher via Sigstore/cosign keyless signing
cosign verify --certificate-identity-regexp "https://github.com/<org>/<repo>" \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
ghcr.io/<org>/<tool>:<version>
# Verify SLSA provenance attestation matches this exact artifact, not just any release
slsa-verifier verify-image ghcr.io/<org>/<tool>:<version> \
--source-uri github.com/<org>/<repo> --source-tag v<version>
If either check fails, stop — don't run it "just this once." That two-command habit, run before every tool in this pipeline gets pulled, is the actual signature of a security engineer who double-checks everything rather than trusting the label on the box.
Deployment steps
- Clone the repo
git clone <your-repo-link-here>
cd ctem-pipeline
(Repo link to be added — this is a placeholder for your actual GitHub URL.)
-
Install prerequisites
- Go 1.21+ (Amass, Subfinder, Naabu, httpx, Nuclei are Go-based)
- Python 3.10+ (theHarvester, TruffleHog wrapper scripts, ScoutSuite)
- Neo4j (for BloodHound's graph backend)
Set up the Python virtual environment (see caveat below before you skip this step)
python3 -m venv ctem-venv
source ctem-venv/bin/activate # Windows: ctem-venv\Scripts\activate
pip install -r requirements.txt
- Install the Go-based tools
go install -v github.com/owasp-amass/amass/v4/...@master
go install -v github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
go install -v github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
go install -v github.com/projectdiscovery/httpx/cmd/httpx@latest
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
- Run the pipeline (order matters — each stage feeds the next)
./run_ctem.sh -d yourdomain.com
This chains: theHarvester → Amass/Subfinder → Naabu → httpx → Nuclei, writing results into
a /results directory as structured JSON for downstream ingestion into your SIEM or a
watchlist/dashboard.
- Run cloud posture and identity modules separately (these need cloud credentials scoped read-only, not the external-recon flow above)
scout aws --profile readonly-audit
azurehound list --tenant <tenant-id> -o azurehound-output.json
Import outputs into your BloodHound Neo4j instance to visualize attack paths.
- Schedule it. A one-off scan tells you today's exposure. A scheduled scan (weekly cron or CI/CD pipeline trigger) is what makes it continuous — that's the difference between CTEM and a one-time pentest.
Important caveat: the Python virtual environment isn't optional
If you run pip install against your system Python directly, you will very likely hit dependency
conflicts — theHarvester, ScoutSuite, and TruffleHog's Python wrapper each pin different versions
of shared libraries (requests, boto3, PyYAML are common collision points). The symptom is
usually a cryptic ImportError or version-resolution failure that looks like it's about one tool,
but is actually a transitive dependency clash between two unrelated tools.
Fix: always use an isolated venv per the steps above, and if you still hit an error:
pip install --upgrade pip setuptools wheel
pip install -r requirements.txt --no-cache-dir
If a specific tool's install still fails, isolate it into its own venv rather than fighting
version pins in a shared environment — it costs a few extra seconds of setup and saves hours of
dependency debugging.
This pipeline is designed to be run by anyone with basic CLI familiarity — no dedicated
infrastructure, no recurring SaaS bill, and no cloud-vendor lock-in. The goal isn't a fancy
dashboard; it's finding what an attacker would find, before they do.
Top comments (0)