Arkadiy Tetelman A security blog

Getting Partial AWS Account IDs for any Cloudfront Website

Yesterday Amazon released a new Cloudfront API that returns partial AWS account ids and Cloudfront distribution ids associated with some given domain name, to help you determine which of your own AWS accounts serves traffic for that domain.

In Cloudfront, a domain alias can only be associated with a single distribution globally across all AWS accounts, and for companies that have a lot of assets it can be difficult to track down which account owns a given domain - this API helps solve that problem. Of course it would be problematic if we could lookup account ids (even partial ones) for arbitrary websites, so to help protect against this information leakage Amazon requires you to have a valid TLS certificate for the domain name you want to query. This is called out in their documentation:

To list conflicting aliases, you provide the alias to search and the ID of a distribution in your account that has an attached SSL/TLS certificate that includes the provided alias.

As it turns out it’s possible to completely bypass this restriction, because ACM has a bug that lets you import certificates without a valid private key.

Most people are aware that RSA consists of a public and private keypair which correspond to each other, with the public key completely derived from the private key. That is - if you give me only a private key, I can easily give you the public key that matches the private one. However what some people find surprising is that even though the public key is derived, the private key contains a full copy of the public key (the N and e parameters, in RSA parlance) in order to save on computation time.

So if we want to find the partial AWS account id for some domain, we can fetch the real public certificate for that domain, generate a random private key, and update the precomputed public key parameters on our private key to be the same as the public key on the certificate we want to impersonate. ACM has a bug in that it does not validate the private key truly corresponds to the public key - it only checks the precomputed values on the private key, which are under our control.

For example:

  • Fetch the certificate chain from some website hosted on Cloudfront, e.g. buzzfeed.com:
def fetch_chain(domain)
  uri = URI.parse("https://#{domain}")
  Net::HTTP.start(uri.host, uri.port, use_ssl: true) do |http|
    return http.instance_variable_get(:@socket).io.peer_cert_chain
  end
end

chain = fetch_chain('buzzfeed.com')
  • Modify the private key parameters to match those on the real certificate that we’re impersonating:
def make_nonmatching_private_key(chain)
  leaf = chain.shift
  private_key = OpenSSL::PKey::RSA.new(2048)
  private_key.set_key(leaf.public_key.n, leaf.public_key.e, nil)

  [leaf, chain, private_key]
end

make_nonmatching_private_key(chain)

And the result is a leaf certificate, intermediate certificate chain, and private key that ACM will happily let us import: Valid Buzzfeed Certificate

Now that we have a “valid” TLS certificate, we can create our own cloudfront distribution with the certificate attached and use the new list_conflicting_aliases API:

Buzzfeed AWS Account

So we know that buzzfeed’s AWS account is ******087808.

I actually reported this issue to Amazon several years ago, in February of 2018, but received the following response:

Hi Arkadiy,

Thank you for bringing your security concern to our attention. We greatly appreciate and encourage reports from the security community worldwide. We do not believe the behaviour you describe in this report presents a security concern, rather, it is expected behaviour. The import process checks to see that the attributes match up - if you edit them as described, they appear to match and so can be imported. They will fail in use though as it is not a valid public/private key combination, which is not only expected but desired.

Can you share more information about why you think this is an issue?

If you discover or become aware of other security concern specific to AWS products and services, please do not hesitate to contact us again at aws-security@amazon.com

Best Regards,

We had some more back and forth but ultimately they declined to fix the bug. In their defense it is not possible to use this fake certificate to intercept traffic, and up until yesterday there was likely no real impact to this bug.

edit (8/10/2021): AWS has now fixed the ACM certificate import bug! If you attempt to import a mismatched certificate and private key you’ll get a Passed PEM could not be parsed error.

Credit

I originally learned about this private key trick from Hanno Böck’s blog post, How I tricked Symantec with a Fake Private Key. I learned about this new Cloudfront API and the TLS certificate restriction from a conversation on Scott Piper’s cloudsecurityforum slack group.

P.S. If you enjoy this kind of content feel free to follow me on Twitter: @arkadiyt