Repository navigation
Folders and files
Repository files navigation
Scan an AWS account for TLS endpoints that permit — or have actually negotiated — CBC-mode cipher suites, and get a concrete fix for every finding.
The goal: eliminate any possibility of a connection to your account being made with a CBC-family cipher.
| Scanner key | Surface | How ciphers are determined |
|---|---|---|
alb-nlb |
ALB/NLB HTTPS & TLS listeners | live, via elbv2 describe-ssl-policies |
clb |
Classic ELB SSL negotiation policies | live, per-cipher policy attributes |
cloudfront |
Distribution viewer security policies (global) | docs-derived static map, classifier-verified |
apigw |
API Gateway custom domains (REST + HTTP/WS) | AWS-managed: even TLS_1_2 includes CBC → flagged as not-removable |
opensearch |
Domain endpoint TLS policies | conservative: 2019 policies flagged; PFS-2023-10 gets a probe-to-verify INFO finding |
lightsail |
Lightsail load balancer TLS policies | live, via get-load-balancer-tls-policies |
With --usage, it additionally queries access logs via Athena (ALB, NLB, CLB, CloudFront — each records the negotiated cipher per connection) and reports every resource where clients actually negotiated CBC in the lookback window, including per-cipher connection counts and sample client IPs so you can find the offending clients before you break them.
TLS-serving resources with access logging disabled get a NO_VISIBILITY finding — you cannot prove CBC absence without logs.
| Type | Severity | Meaning |
|---|---|---|
OBSERVED_CBC_CONNECTIONS |
CRITICAL | real clients negotiated CBC in the lookback window |
CONFIG_ALLOWS_CBC |
HIGH | configuration permits CBC (INFO when unverifiable) |
CBC_NOT_REMOVABLE |
MEDIUM | AWS-managed cipher list includes CBC and cannot be tightened |
NO_VISIBILITY |
MEDIUM | TLS served, but no access logs to verify actual usage |
Every report also lists blind spots (self-managed TLS on EC2, API Gateway default endpoints, AWS service endpoints, managed-service data planes) so a clean scan is never mistaken for "no CBC anywhere".
Requires Python 3.11+.
uv venv && uv pip install -e . # or: pip install -e .# Config scan (read-only, no side effects)
cbcscan scan --region us-east-1 --profile myprofile
# Config + actual-usage scan via Athena
cbcscan scan --region us-east-1 --usage --days 30 \
--athena-output s3://my-athena-results/cbcscan/
# Outputs and CI
cbcscan scan --region us-east-1 --output findings.json --html report.html --fail-on high
cbcscan list-services # available scanner keys for --servicesExit codes: 0 clean, 2 findings at/above --fail-on threshold, 1 runtime/auth error.
Notes:
- CloudFront is a global service; it is scanned regardless of
--region. - The tool never modifies AWS configuration. In
--usagemode the only side effects are a Glue database/tables namedcbcscanand Athena query results written to--athena-output. - Athena cost is ~$5/TB scanned. ALB/NLB/CLB tables use date partition projection, so
--daysbounds the scan. CloudFront legacy standard logs are unpartitioned — the whole log prefix is scanned (the tool warns when this happens); consider CloudFront standard logging v2 with partitioning.
Config scan (pure read-only):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CbcscanConfigScan",
"Effect": "Allow",
"Action": [
"sts:GetCallerIdentity",
"elasticloadbalancing:Describe*",
"cloudfront:ListDistributions",
"cloudfront:GetDistributionConfig",
"apigateway:GET",
"es:ListDomainNames",
"es:DescribeDomains",
"lightsail:GetLoadBalancers",
"lightsail:GetLoadBalancerTlsPolicies"
],
"Resource": "*"
}
]
}Usage mode additionally needs:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "CbcscanAthena",
"Effect": "Allow",
"Action": [
"athena:StartQueryExecution",
"athena:GetQueryExecution",
"athena:GetQueryResults",
"athena:StopQueryExecution",
"athena:GetWorkGroup",
"glue:CreateDatabase",
"glue:GetDatabase",
"glue:CreateTable",
"glue:GetTable",
"glue:UpdateTable"
],
"Resource": "*"
},
{
"Sid": "CbcscanReadLogBuckets",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket", "s3:GetBucketLocation"],
"Resource": ["arn:aws:s3:::<log-bucket>", "arn:aws:s3:::<log-bucket>/*"]
},
{
"Sid": "CbcscanAthenaResults",
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:PutObject", "s3:ListBucket", "s3:GetBucketLocation"],
"Resource": ["arn:aws:s3:::<athena-results-bucket>", "arn:aws:s3:::<athena-results-bucket>/*"]
}
]
}- Create an ALB with an HTTPS listener pinned to
ELBSecurityPolicy-2016-08and access logs enabled to a test bucket. cbcscan scan --region us-east-1→ expect aCONFIG_ALLOWS_CBCfinding listing suites likeECDHE-RSA-AES128-SHA256,AES128-SHA, and the exactmodify-listenerremediation command; exit code 2.- Generate CBC traffic, then wait ~5 minutes for log delivery:
curl --ciphers ECDHE-RSA-AES128-SHA256 --tls-max 1.2 -k https://<alb-dns>/
cbcscan scan --region us-east-1 --usage --days 1 --athena-output s3://<results-bucket>/cbcscan/→ expectOBSERVED_CBC_CONNECTIONSwith the cipher count and your source IP.- Apply the emitted remediation command verbatim and re-run — the finding disappears, and the
curlfrom step 3 now fails to handshake. That failure is the point: CBC is no longer possible on that endpoint. - Disable the ALB's access logs and re-run → expect a
NO_VISIBILITYfinding.
uv pip install -e ".[dev]"
pytest # 137 tests: classifier tables, per-scanner moto/Stubber, Athena state machine, CLI contract
ruff check src testsAdding a service scanner: subclass cbcscan.scanners.base.Scanner, implement scan() (and discover_log_sources() if the service writes access logs with cipher fields), and register the class in cbcscan/scanners/__init__.py.