<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: gaurav sharma</title>
    <description>The latest articles on DEV Community by gaurav sharma (@gauravgarv4u).</description>
    <link>https://dev.to/gauravgarv4u</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4094712%2Fa4d50df7-b706-4237-a298-48552f24b3d9.png</url>
      <title>DEV Community: gaurav sharma</title>
      <link>https://dev.to/gauravgarv4u</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gauravgarv4u"/>
    <language>en</language>
    <item>
      <title>Building a Continuous Threat Exposure Management (CTEM) Program — Open Source, Cloud-Agnostic, and Zero Hidden Cost</title>
      <dc:creator>gaurav sharma</dc:creator>
      <pubDate>Wed, 02 Sep 2026 07:27:10 +0000</pubDate>
      <link>https://dev.to/gauravgarv4u/building-a-continuous-threat-exposure-management-ctem-program-open-source-cloud-agnostic-and-3keg</link>
      <guid>https://dev.to/gauravgarv4u/building-a-continuous-threat-exposure-management-ctem-program-open-source-cloud-agnostic-and-3keg</guid>
      <description>&lt;h2&gt;
  
  
  What the CTEM program does
&lt;/h2&gt;

&lt;p&gt;Most security teams find out they're exposed the same way an attacker does — by scanning. The&lt;br&gt;
difference is who gets there first. This CTEM program flips that timeline: instead of waiting for&lt;br&gt;
a pentest, a bug bounty report, or an incident to reveal what's exposed, it runs the &lt;em&gt;same&lt;/em&gt;&lt;br&gt;
reconnaissance an attacker would run — continuously, against your own footprint — and turns the&lt;br&gt;
output into tracked, owned remediation work before it becomes an incident.&lt;/p&gt;

&lt;p&gt;The pipeline mirrors an actual attack chain, stage by stage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Discover&lt;/strong&gt; what exists (OSINT, subdomains, assets you may not even know are yours)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Confirm&lt;/strong&gt; what's alive and what it's running&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assess&lt;/strong&gt; what's vulnerable or misconfigured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Correlate&lt;/strong&gt; 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&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Why cloud-agnostic and ephemeral matters (and why "hidden cost" is the real enemy)
&lt;/h2&gt;

&lt;p&gt;Every major cloud provider now ships a native posture-management or "well-architected" tool —&lt;br&gt;
and on paper, that's convenient. In practice, a lot of these tools quietly meter you into&lt;br&gt;
consumption-based pricing for the AI/analysis layer underneath. AWS's Well-Architected Tool&lt;br&gt;
review itself is free, but plug it into Bedrock-backed recommendations or continuous AI-assisted&lt;br&gt;
analysis, and the bill scales with usage in a way that's hard to forecast and easy to lose track&lt;br&gt;
of — a classic hidden-cost trap for a program that's supposed to be about reducing risk, not&lt;br&gt;
opening a new unpredictable line item.&lt;/p&gt;

&lt;p&gt;This toolchain takes the opposite approach on purpose:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Cloud-agnostic&lt;/strong&gt; — 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ephemeral by design&lt;/strong&gt; — 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.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open source, self-hosted&lt;/strong&gt; — 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.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Tools in the pipeline
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Category&lt;/th&gt;
&lt;th&gt;Function&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;theHarvester&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;OSINT / Digital Footprint&lt;/td&gt;
&lt;td&gt;Passive discovery of hosts, emails, and exposure tied to the domain&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Amass / Subfinder&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Attack Surface Management&lt;/td&gt;
&lt;td&gt;Enumerate every subdomain forming the external asset inventory&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Naabu&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Attack Surface Management&lt;/td&gt;
&lt;td&gt;Identify open ports on discovered hosts&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;httpx&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Attack Surface Management&lt;/td&gt;
&lt;td&gt;Confirm which hosts are live and fingerprint the tech running&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Nuclei&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vulnerability Assessment&lt;/td&gt;
&lt;td&gt;Detect known CVEs, misconfigurations, and exposed cloud resources via community + custom templates&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;TruffleHog&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Secret / Credential Exposure&lt;/td&gt;
&lt;td&gt;Find live API keys and credentials leaked in code repositories&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;ScoutSuite&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Cloud Security Posture (CSPM)&lt;/td&gt;
&lt;td&gt;Audit cloud account configuration against security best practice&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;BloodHound + AzureHound / AWSHound&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Identity &amp;amp; Attack Path Analysis&lt;/td&gt;
&lt;td&gt;Map privilege-escalation and lateral-movement paths across cloud identity graphs&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A word of caution before you run any of this: verify what you trust&lt;/p&gt;

&lt;p&gt;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:&lt;/p&gt;

&lt;p&gt;A "security vendor" label is not a trust guarantee — even scanners get compromised&lt;br&gt;
Version tags (&lt;a class="mentioned-user" href="https://dev.to/v1"&gt;@v1&lt;/a&gt;, @latest) are mutable and can be force-pushed by anyone with write access&lt;br&gt;
Pin every GitHub Action and container image to an immutable full commit SHA or digest, never a tag&lt;br&gt;
Credential rotation must be atomic — revoke first, then reissue; a staggered rotation window is an open door&lt;br&gt;
Avoid pull_request_target in any workflow that touches forked PR code — it runs with base-repo secrets against untrusted input&lt;br&gt;
Any CI/CD runner that executed a since-compromised action should have every exposed secret treated as burned, not just rotated&lt;br&gt;
Don't trust an artifact because the filename or registry looks official — verify cryptographically, every time&lt;br&gt;
Use short-lived OIDC tokens for cloud access instead of long-lived static credentials in CI/CD&lt;br&gt;
Watch for anomalous tag movement or force-pushes on the dependencies you consume — it's a detectable signal, not just hindsight&lt;br&gt;
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&lt;br&gt;
Verify before you run: cosign + SLSA provenance&lt;/p&gt;

&lt;p&gt;Before any of the binaries or container images in this pipeline touch your infrastructure, verify both the signature and the build provenance:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# Verify the image was signed by the expected publisher via Sigstore/cosign keyless signing&lt;/span&gt;
cosign verify &lt;span class="nt"&gt;--certificate-identity-regexp&lt;/span&gt; &lt;span class="s2"&gt;"https://github.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--certificate-oidc-issuer&lt;/span&gt; https://token.actions.githubusercontent.com &lt;span class="se"&gt;\&lt;/span&gt;
  ghcr.io/&amp;lt;org&amp;gt;/&amp;lt;tool&amp;gt;:&amp;lt;version&amp;gt;

&lt;span class="c"&gt;# Verify SLSA provenance attestation matches this exact artifact, not just any release&lt;/span&gt;
slsa-verifier verify-image ghcr.io/&amp;lt;org&amp;gt;/&amp;lt;tool&amp;gt;:&amp;lt;version&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--source-uri&lt;/span&gt; github.com/&amp;lt;org&amp;gt;/&amp;lt;repo&amp;gt; &lt;span class="nt"&gt;--source-tag&lt;/span&gt; v&amp;lt;version&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;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.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment steps
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Clone the repo&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   git clone &amp;lt;your-repo-link-here&amp;gt;
   &lt;span class="nb"&gt;cd &lt;/span&gt;ctem-pipeline
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(Repo link to be added — this is a placeholder for your actual GitHub URL.)&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Install prerequisites&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Go 1.21+ (Amass, Subfinder, Naabu, httpx, Nuclei are Go-based)&lt;/li&gt;
&lt;li&gt;Python 3.10+ (theHarvester, TruffleHog wrapper scripts, ScoutSuite)&lt;/li&gt;
&lt;li&gt;Neo4j (for BloodHound's graph backend)&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Set up the Python virtual environment&lt;/strong&gt; (see caveat below before you skip this step)&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv ctem-venv
   &lt;span class="nb"&gt;source &lt;/span&gt;ctem-venv/bin/activate      &lt;span class="c"&gt;# Windows: ctem-venv\Scripts\activate&lt;/span&gt;
   pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Install the Go-based tools&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   go &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; github.com/owasp-amass/amass/v4/...@master
   go &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; github.com/projectdiscovery/subfinder/v2/cmd/subfinder@latest
   go &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; github.com/projectdiscovery/naabu/v2/cmd/naabu@latest
   go &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; github.com/projectdiscovery/httpx/cmd/httpx@latest
   go &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt; github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run the pipeline&lt;/strong&gt; (order matters — each stage feeds the next)
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   ./run_ctem.sh &lt;span class="nt"&gt;-d&lt;/span&gt; yourdomain.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This chains: theHarvester → Amass/Subfinder → Naabu → httpx → Nuclei, writing results into&lt;br&gt;
   a &lt;code&gt;/results&lt;/code&gt; directory as structured JSON for downstream ingestion into your SIEM or a&lt;br&gt;
   watchlist/dashboard.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Run cloud posture and identity modules separately&lt;/strong&gt; (these need cloud credentials scoped
read-only, not the external-recon flow above)
&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;   scout aws &lt;span class="nt"&gt;--profile&lt;/span&gt; readonly-audit
   azurehound list &lt;span class="nt"&gt;--tenant&lt;/span&gt; &amp;lt;tenant-id&amp;gt; &lt;span class="nt"&gt;-o&lt;/span&gt; azurehound-output.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Import outputs into your BloodHound Neo4j instance to visualize attack paths.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Schedule it.&lt;/strong&gt; A one-off scan tells you today's exposure. A scheduled scan (weekly cron or
CI/CD pipeline trigger) is what makes it &lt;em&gt;continuous&lt;/em&gt; — that's the difference between CTEM and
a one-time pentest.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;
  
  
  Important caveat: the Python virtual environment isn't optional
&lt;/h2&gt;

&lt;p&gt;If you run &lt;code&gt;pip install&lt;/code&gt; against your system Python directly, you will very likely hit dependency&lt;br&gt;
conflicts — theHarvester, ScoutSuite, and TruffleHog's Python wrapper each pin different versions&lt;br&gt;
of shared libraries (&lt;code&gt;requests&lt;/code&gt;, &lt;code&gt;boto3&lt;/code&gt;, &lt;code&gt;PyYAML&lt;/code&gt; are common collision points). The symptom is&lt;br&gt;
usually a cryptic &lt;code&gt;ImportError&lt;/code&gt; or version-resolution failure that looks like it's about one tool,&lt;br&gt;
but is actually a transitive dependency clash between two unrelated tools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fix:&lt;/strong&gt; always use an isolated venv per the steps above, and if you still hit an error:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--upgrade&lt;/span&gt; pip setuptools wheel
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt &lt;span class="nt"&gt;--no-cache-dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a specific tool's install still fails, isolate it into its &lt;strong&gt;own&lt;/strong&gt; venv rather than fighting&lt;br&gt;
version pins in a shared environment — it costs a few extra seconds of setup and saves hours of&lt;br&gt;
dependency debugging.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;This pipeline is designed to be run by anyone with basic CLI familiarity — no dedicated&lt;br&gt;
infrastructure, no recurring SaaS bill, and no cloud-vendor lock-in. The goal isn't a fancy&lt;br&gt;
dashboard; it's finding what an attacker would find, before they do.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>cybersecurity</category>
      <category>opensource</category>
      <category>security</category>
    </item>
  </channel>
</rss>
