Stealing Amazon EC2 Keys via an XSS Vulnerability

On a recent engagement, our testers were faced with a single page web application which was used to generate PDF documents. This web application contained a multi-step form that ultimately let the user download a PDF document containing the details they had entered.

As a user progressed through the form, the data entered would occasionally be redisplayed in future questions. We tried to find an XSS vulnerability in this workflow; and although the application itself correctly escaped user input, an interesting discovery was made when downloading the PDF file: it appeared that the PDF documents were rendered as an HTML page first. This conclusion was drawn from the fact that HTML tags submitted during the application process (specifically <strong>John Doe</strong>) were rendered in the PDF document as bold text.

Using a payload with script tags allowed us to retrieve the window location (<script>document.write(window.location);</script>). We found that the page was being accessed from localhost; and by replacing “localhost” with the actual hosting domain name, the page containing the XSS vulnerability was able to be viewed directly.

So to recap our current understanding, we have a web application accepting user input, insecurely reflecting the data into a HTML page, allowing JavaScript execution, rendering the page locally and saving it as a PDF file available for download.

Using an image tag (<img src=”attack.ip/owned.jpg”>) payload allowed us to see (via the User-Agent header) that Chrome 59 headless was being used server-side to create the PDF document. A reverse DNS lookup was also performed on the connecting IP, revealing it as an Amazon EC2 instance.

As the vulnerable page allowed execution of JavaScript on the remote server, this XSS attack had essentially become a Server-Side-Request-Forgery (SSRF) vulnerability. This allowed our testers to attack software and services running on localhost or within the internal network. Enumerating the environment revealed no vulnerable services to further the attack chain.

However, since the host was running on Amazon EC2, though, another attack was possible. Amazon EC2 has a web API to access the instance metadata, and using a JavaScript redirect (<script>window.location=”http://169.254.169.254/latest/meta-data/iam/security-credentials/”</script>), it was possible to disclose the machine Identity and Access Management (IAM) roles. A single role was found, and the corresponding AWS access keys for that role were extracted. These access keys can be used to make programmatic calls to the AWS API. In this instance, the permissions attached to the role were too restrictive to allow further exploitation.

In conclusion, the core vulnerability was the fact that user data was insecurely reflected into a webpage and executed on the remote server. This was patched within a day once brought to the attention of the application developers. Additional hardening techniques were suggested which would have limited the attack surface in the first instance. Implementing the PDF generation using a document templating library would have been a more secure and optimized solution. There would be less overhead involved and no need to rely on potentially-risky HTML rendering.

Despite the webpage used to generate the PDF being publicly accessible (if the correct URL was known), this was never intended or required. The page should be restricted to localhost access only. Disabling JavaScript on the page containing user data would have reduced impact, although even with that, iframes could allow other attacks in some configurations.

All IAM roles attached to the EC2 instance should have the absolute minimal set of permissions required. This appeared to be the case with role enumerated in this engagement. In addition, access to the instance metadata API itself should be restricted to allow only those users requiring access. This can be performed with iptables, and significantly reduces the impact of SSRF vulnerabilities found on Amazon EC2 instances.