Skip to content

aws-java-sdk-core: EC2MetadataClient.java - Adds sending of a User-Ag… #1562

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 25, 2018

Conversation

willbengtson
Copy link

Adds the sending of a User-Agent other than default User-Agent in Java when making requests to metadata service

Similar to AWS Golang SDK and now AWS Ruby SDK, send a User-Agent when making requests to metadata for credentials.
Golang uses a structure like aws-sdk-go/1.8.19 and now Ruby uses aws-sdk-ruby3/ as of PR #1762. PR #1445 adds this for python as well.

This will enable protection of AWS credentials from Server Side Request Foregery (SSRF) vectors by use of a metadata proxy and rejecting User-Agents that do not meet a regex.

Will Bengtson added 2 commits April 23, 2018 14:32
…ent other than default User-Agent in Java when making requests to metadata service

Similar to AWS Golang SDK and now AWS Ruby SDK, send a User-Agent when making requests to metadata for credentials.
Golang uses a structure like aws-sdk-go/1.8.19 and now Ruby uses aws-sdk-ruby3/ as of PR aws#1762.  PR aws#1445 adds this
for python as well.

This will enable protection of AWS credentials from Server Side Request Foregery (SSRF) vectors by use of a metadata
proxy and rejecting User-Agents that do not meet a regex.
@varunnvs92
Copy link
Contributor

Can you provide more details on how change this would prevent SSRF?

@willbengtson
Copy link
Author

@varunnvs92 sure thing! By providing a User-Agent that is predictable, you can build a metadata proxy to put on your instance that inspects the User-Agent sent in the request. If the User-Agent does not match or fit the pattern you are expecting then you can block that request.

Right now, there is not a reasonable way to do so since the default User-Agent is Java/<jre version>.

With SSRF, the attacker is tricking your application to make the request and if an attacker knows you run in AWS, a common tactic is to pull AWS credentials via SSRF to the metadata service.

@willbengtson
Copy link
Author

I should add that with a successful SSRF, the request would be coming from the library the application uses for HTTP requests, which if no User-Agent is set would be the same as what the SDK uses currently.

headers.put("User-Agent", USER_AGENT);
headers.put("Accept", "*/*");
headers.put("Connection", "keep-alive");

return readResource(endpoint, CredentialsEndpointRetryPolicy.NO_RETRY, new HashMap<String, String>());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the third parameter be headers variable created above?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch. Will update!

@varunnvs92
Copy link
Contributor

I have made the change internally. It will be included in today's release.

@varunnvs92 varunnvs92 merged commit 1b10356 into aws:master Apr 25, 2018
willbengtson pushed a commit to willbengtson/aws-sdk-java-v2 that referenced this pull request May 7, 2018
…her than default User-Agent in Java when making requests to metadata service

Similar to AWS Golang SDK and Ruby SDK, send a User-Agent other than the default User-Agent when making requests to the EC2 metadata for credentials.
aws/aws-sdk-java#1562 for aws-sdk-java addresses this also.  PR aws#1445 for botocore implements this too.

This will enable protection of AWS credentials from Server Side Request Forgery (SSRF) vectors by being able to use a metadata proxy to block requests
that do not have the right User-Agent set.  User-Agent is not controllable by an attacker via SSRF.
zoewangg pushed a commit to aws/aws-sdk-java-v2 that referenced this pull request May 7, 2018
…her than default User-Agent in Java when making requests to metadata service

Similar to AWS Golang SDK and Ruby SDK, send a User-Agent other than the default User-Agent when making requests to the EC2 metadata for credentials.
aws/aws-sdk-java#1562 for aws-sdk-java addresses this also.  PR #1445 for botocore implements this too.

This will enable protection of AWS credentials from Server Side Request Forgery (SSRF) vectors by being able to use a metadata proxy to block requests
that do not have the right User-Agent set.  User-Agent is not controllable by an attacker via SSRF.
@muhqu
Copy link
Contributor

muhqu commented Jul 30, 2018

@willbengtson, @varunnvs92 It looks like this PR was incomplete. When the SDK fetches credentials for from the EC2 metadata service it does so by making two http requests.

  1. GET /latest/meta-data/iam/security-credentials/
    To get the name of the IAM Instance Profile. Here User-Agent: aws-sdk-java/{version} is added.
  2. GET /latest/meta-data/iam/security-credentials/some-ec2-iam-role-abcdef
    To retrieve the credentials. Here the SDK User-Agent is still missing.

This is because the EC2CredentialsFetcher is directly supplying the headers map...

String credentialsResponse = EC2CredentialsUtils.getInstance().readResource(
credentialsEndpointProvider.getCredentialsEndpoint(),
credentialsEndpointProvider.getRetryPolicy(),
credentialsEndpointProvider.getHeaders());

... and therefor does not get the defaults defined here…

public String readResource(URI endpoint) throws IOException {
Map<String, String> headers = new HashMap<String, String>();
headers.put("User-Agent", USER_AGENT);
headers.put("Accept", "*/*");
headers.put("Connection", "keep-alive");
return readResource(endpoint, CredentialsEndpointRetryPolicy.NO_RETRY, headers);
}

I think it would be better to add the default headers here…

public String readResource(URI endpoint, CredentialsEndpointRetryPolicy retryPolicy, Map<String, String> headers) throws IOException {
int retriesAttempted = 0;
InputStream inputStream = null;

WDYT?

@willbengtson
Copy link
Author

@muhqu I think this makes sense. Good catch.

@willbengtson willbengtson mentioned this pull request Jul 31, 2018
shorea pushed a commit to aws/aws-sdk-java-v2 that referenced this pull request Aug 3, 2018
…her than default User-Agent in Java when making requests to metadata service

Similar to AWS Golang SDK and Ruby SDK, send a User-Agent other than the default User-Agent when making requests to the EC2 metadata for credentials.
aws/aws-sdk-java#1562 for aws-sdk-java addresses this also.  PR #1445 for botocore implements this too.

This will enable protection of AWS credentials from Server Side Request Forgery (SSRF) vectors by being able to use a metadata proxy to block requests
that do not have the right User-Agent set.  User-Agent is not controllable by an attacker via SSRF.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants