What is a cloud attack surface?
Traditional IT security protects the network perimeter with firewalls, vulnerability scanners and patching processes. Discovering the attack surface of your network perimeter was a simple matter of knowing your public IP range and scanning it from an external host. With the introduction of public cloud technologies, the attack surface expanded to include cloud provider’s APIs. "Identity is the new perimeter" became the new mantra.
The old network perimeter still exists, of course, and it has evolved. No longer are all the IP addresses that make up your perimeter assigned to your company. In the cloud, your network perimeter is part of the cloud provider’s IP space. And, cloud-provider-managed resources provide a direct path to your application code. This combination of cloud-provider IP addresses assigned to you, and the URLs of cloud-provider resources that lead to your application and data, comprise your cloud’s network perimeter and define part of the cloud attack surface.
An Organization must monitor and understand the network perimeter of their cloud estate. Resources comprising the externally facing network components of your cloud attack surface can be broadly grouped into IP addresses, hostnames, and URLs. In this blog post, we will provide step-by-step instructions for mapping the network aspects of the cloud attack surface using Steampipe.
Enumerate IP Addresses using Steampipe
For IP Addresses, we'll look mainly at the Elastic Network Interfaces. While other resources like CloudFront, S3, and API Gateway also leverage IP addresses, the network security protections of those services are the cloud provider's responsibility.
This query will download a list of all public IP addresses tied to the customer’s VPC.
selecteni.association_public_ip AS public_ipfromaws_ec2_network_interface AS eniwhereeni.association_public_ip is not Null;
Enumerate Hostnames using Steampipe
To determine the DNS Hostnames used as part of your cloud perimeter, Steampipe can query all of the A records and CNAMEs in your Route 53 Hosted Zones. A records point directly to IP addresses under your control. CNAMEs are references that can point to hosts or other cloud-provider-managed resources. In either case, you need to understand what exists in your environment.
To query all of the hostnames that point to CNAME and A Records in your Route 53 Hosted Zones, use this SQL query:
selectr.name as hostname,type,jsonb_array_elements_text(records) as resource_recordfromaws_route53_zone as z,aws_route53_record as rwhere r.zone_id = z.idand (type LIKE 'A' OR type LIKE 'CNAME')and z.private_zone=falseand jsonb_pretty(records) not like '%dkim%'and jsonb_pretty(records) not like '%acm-validations.aws.%';
Review the list of IP addresses returned, and if permitted by the terms of service, scan these hostnames for exposed ports and services using nmap or Nessus.
Note: The above query excludes private DNS for VPCs z.private_zone=false
and excludes common CNAMEs needed for ACM and email validation.
Enumerate the URLs using Steampipe
A number of AWS services, including CloudFront, S3, API Gateway, and AWS Lambda, produce URLs that can be vulnerable. For example, S3 Buckets exist as URLs on the public internet and can be accessed if the bucket is not properly secured. To get a list of all of the URLs for the public buckets in your cloud environment, you can use this query:
select'https://' || name || '.s3.' || region || '.amazonaws.com/' as urlfromaws_s3_bucketwherebucket_policy_is_public is True;
To request all of the URLs from CloudFront, this SQL query will return both the distribution name and any aliases that are part of that distribution:
select'https://' || domain_name as urlfromaws_cloudfront_distributionUNION ALLselect'https://' || jsonb_array_elements_text(aliases -> 'Items') as urlfromaws_cloudfront_distribution;
API Gateways themselves do not have URLs, but when the customer creates a "stage" a URL is created. To get a list of all the API Gateway v2 URLs, this SQL query can be used:
select 'https://' || api_id || '.execute-api.' || region || '.amazonaws.com/' || stage_name as urlfrom aws_api_gatewayv2_stage;
API Gateway URLs typically have the following format:
https://[api-id].execute-api.[region].amazonaws.com/[stage]/[path]
where [api-id] is the ID of the API Gateway API, [region] is the AWS region where the API is deployed, [stage] is the stage of the API (such as "prod" or "dev"), and [path] is the path to the specific Lambda function being accessed. For example, the URL "https://abc123.execute-api.us-east-1.amazonaws.com/prod/hello
" could be used to invoke a Lambda function named "hello" that is associated with the API Gateway API with ID "abc123", and is deployed in the "us-east-1" region.
Lambda URLs are a new feature of AWS Lambda, released in April of 2022. AWS allows the creation of Lambda URLs that do not require authentication. Therefore, these Lambda URLs are part of your cloud attack surface. To find the Lambda URLs in your environment, you can use this Steampipe query:
select url_config ->> 'FunctionUrl' as urlfrom aws_lambda_functionwhere url_config is not Null;
You can pull all these together using SQL UNION queries as demonstrated by this query. You can run it like:
steampipe query all_urls.sql
+-----------------------------------------------------------------------+-------------------------------+ | url | type | +-----------------------------------------------------------------------+-------------------------------+ | https://dev-FooliApiStack-780272371.us-east-1.elb.amazonaws.com:443 | application_load_balancer | | https://prod-FooliApiStack-1829703886.us-east-1.elb.amazonaws.com:443 | application_load_balancer | | https://rwpfqhvj6g.execute-api.us-east-1.amazonaws.com/dev_system | api_gateway | | https://v5johmngyd.execute-api.us-east-1.amazonaws.com/prod_system | api_gateway | | https://d7iaso3riah1ur.cloudfront.net | cloudfront_distribution | | https://d65z8r284bm5f6.cloudfront.net | cloudfront_distribution | | https://da6r321d6ljywr.cloudfront.net | cloudfront_distribution | | https://d28s6da8wtxvjm.cloudfront.net | cloudfront_distribution | | https://memes.dev1.fooli.media | cloudfront_distribution_alias | | https://memes.memes.fooli.media | cloudfront_distribution_alias | | https://oshehk7tpbxfqtkfhco5ojkdyq0fbenx.lambda-url.us-east-1.on.aws/ | lambda_url | | https://f00li-prod-1.s3.us-east-1.amazonaws.com/ | s3_bucket_url | | https://f00li-dev-1.s3.us-east-1.amazonaws.com/ | s3_bucket_url | | https://f00li-dev-0.s3.us-east-1.amazonaws.com/ | s3_bucket_url | | https://f00li-public-bucket-policy.s3.us-east-1.amazonaws.com/ | s3_bucket_url | | https://f00li-prod-0.s3.us-east-1.amazonaws.com/ | s3_bucket_url | | https://pht-blockchain.s3.eu-central-1.amazonaws.com/ | s3_bucket_url | +-----------------------------------------------------------------------+-------------------------------+ Time: 115.3s. Rows fetched: 215. Hydrate calls: 317.
Empowered with a list of all the exposed URLs in your organization, you can then set up a process to scan these using a number of web-focused Dynamic Application Security Testing (DAST) tools and scanners such as Zed Attack Proxy, dirsearch (Web path scanner), Aquatone, and Nikto2. The OWASP® Foundation maintains a full list of scanning tools that could be used.
If you are a larger organization that runs a bug bounty program, scanning your URLs for these low-hanging fruit is a quick and easy way to avoid payouts to researchers who use the same tools.
Conclusion
Steampipe is a powerful way to find and enumerate your AWS IP addresses, hostnames, and URLs. Follow the steps outlined here to gather the information needed to define and manage the network component of your cloud attack surface, ensure the security and integrity of your data and applications, and prevent unauthorized access to your network resources. Of course everyone's situation is unique, and you may find a solution that works better for you. If so, please let us know: we love to learn from our community!