Data Exfiltration through VPC Endpoints
It is common for me to see VPCs that are unable to communicate with the public internet to achieve a “private” network. This is typically done to reduce the attack surface of the network, aiding in its security. For example, making it difficult to establish Command and Control (C2) channels, reducing public exposure of sensitive endpoints, preventing data exfiltration, etc. It is also quite simple to do, just don’t deploy resources such as an Internet Gateway. However, as cloud deployments move to a more serverless nature with extensive use of services such as AWS Lambda, S3, etc, the requirement for systems within the VPC to talk to endpoints outside of the network increase. Luckily, AWS have us covered through the use of VPC endpoints, enabling us to talk to AWS APIs and other services through AWS networking magic. However, even these aren’t foolproof and could lead to unintended exposure to the wider internet.
VPC endpoints are configured with a destination service, commonly an AWS API however it could also be a service deployed in another VPC through PrivateLink. For the purposes of this blog, we are solely looking at the case of AWS APIs. When using AWS APIs, endpoints can also be configured with an endpoint policy, however this is not supported for all AWS services1. This would perform validation at the IAM layer allowing for relevant access controls to be placed on what traffic is permitted through the endpoint. To be clear, this policy doesn’t grant any allow permissions to an entity, but are constraining requests that are made through the endpoint akin to a permissions boundary.
The default VPC endpoint policy can be seen below, and permits all traffic.
{
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "*",
"Resource": "*"
}
]
}
The main source of risk associated with VPC endpoints is that the destination is multi-tenant in nature. Whilst a fully locked down network control such as a security group limits access to purely the HTTP endpoint, the same endpoint is used to access resources within various AWS accounts, including potential attackers. As such, this can lead to a communication channel between the locked-down VPC and an attackers AWS account.
How I attack them
There are two generic methods that I typically use to communicate to external services through the endpoint, the method used would be dependant on what is restricted within its policy. The first is where the Principal
field of the policy is insufficiently restricted, at which point I can use credentials for my own accounts. This can be done by generating IAM Access Keys for an IAM user within my own account, and have injected into the network. This would then have permissions to the service accessible by the endpoint, and I can transmit data out of the network. This is particularly useful when Action
is limited to read-only operations, as CloudTrail logs can be leveraged to view the requests being transmitted, and thus data being transferred. For example, if trying to exfiltrate password
, I could attempt to list objects in a S3 bucket called password
. Whilst the bucket does not exist, the attempt would still be logged and recoverable.
The other method when Principal
is locked down, and Resource
is not, is making use of resource policies. By updating the resource policies of resources within an attackers account, permissions can directly be granted to IAM entities of the target account, and the principals that are permitted through the endpoint. For example, the following bucket policy could be used to allow any identity, including those within the compromised network, to upload files to the bucket. It should be noted that this requires the endpoint to talk to a service that supports resource policies.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "",
"Effect": "Allow",
"Principal": "*",
"Action": [
"s3:*",
],
"Resource": "arn:aws:s3:::MYBUCKET/*"
}
]
}
I have used this before to setup a C2 channel through services such as S3. A compromised EC2 was granted permissions to access my control bucket, which it would regularly poll for the command
object. Upon detection of this object, it would download and execute the command within. Uploading the results of the output back to an output
object in the bucket and deleting the original command
. This allowed me to interact with a locked-down VPC from outside the network, and enumerate and exfiltrate data as needed.
What can you do about it?
So, clearly misconfigured VPC endpoints aren’t that great from a security perspective. However, a thoroughly restricted endpoint policy can severely restrict what an attacker could do through it. At minimum, I tend to say the Principal
and Resource
fields should be restricted either to the same account, the overall AWS Organization, or in some cases specifically allowing certain accounts that are needed. This can be done by adding IAM Conditions to the policy which gives a lot of flexibility on the restrictions applied, for example using aws:PrincipalOrgId
or aws:ResourceOrgId
to restrict to an Organization. If you are using endpoints extensively, another quick side tip is to use conditions in identity policies as well, conditions such as aws:SourceVpce
allow restricting access through specific endpoints. This way were credentials compromised and stolen, their permissions would significantly reduce when not being used within the VPC.
Finally, I know this blog post has been all about the AWS APIs, however thought I’d also add a quick bit of using PrivateLink to communicate with endpoint services. These don’t support endpoint policies, and while most of the time I’ve not found these to communicate with multi-tenant endpoints and thus not readily exploitable. The cases where the endpoint service is multi-tenant, for example a 3rd party SaaS solution, there is no way to stop an attacker making their own account with that SaaS provider and performing a similar style of attack, as far as I know at least.