CORS-anywhere: The Dangers of Misconfigured Third-Party Software

CertiK | Jun 10, 2020

Article's Poster

Written By CertiK’s Pentesting Team

Part 1: The Background

In a recent web application penetration test of a blockchain explorer, we discovered a critical vulnerability that we didn’t expect to find. After obtaining the permission from the client, we’re here to share the findings and help people avoid making the same mistake.

The target web application is a known blockchain explorer that allows users to view block information, transaction history, and deployed contracts. It’s written in React, a web framework that handles XSS (cross-site scripting) and HTML injection pretty well, and the front-end JavaScript fetches new blocks data periodically from the blockchain RPC APIs. Normally it would be hard to find interesting vulnerabilities in a blockchain explorer application, because explorers are a simple application type that doesn’t have a “traditional” backend-server, doesn’t use authentication and authorization, and doesn’t take much user input.

During a routine penetration test however, we spotted something interesting about the request URL used to fetch block data. The URL looks like this:

https://cors.x.y/http://load-balancer.us-east-1.elb.amazonaws.com/blocks/270865

If we look closely, the full URL consists of one url followed by another.

The second URL looks like the DNS name for an AWS load balancer, but what is the first one pointing to?

Visiting the first URL ”https://cors.x.y” brings us to the default page for an open source tool called “CORS-anywhere”. We then discover the tool is misconfigured, which allows us to access sensitive information. We’ll further explain the background, walk you through our findings, and provide the additional research we conducted.

Part 2: CORS

Before jumping into our findings, let’s briefly talk about CORS (Cross-Origin Resource Sharing). If you have some experience with web development, you may have seen this (annoying) error countless times:A web application executes a cross-origin HTTP request when it requests a resource that has a different origin (domain, protocol, or port) from its own. Modern browsers will block the response to the cross-origin request if the response doesn’t contain the correct “access-control-allow-origin” header (cite). We won’t get too deep into the SOP (Same Origin Policy) and CORS (Cross-Origin Resource Sharing) mechanisms in this post, but in short, SOP prevents JavaScript from reading the response in a cross-origin request and CORS is a way to circumvent the restriction imposed by the same origin policy.

You might be wondering: where does the cross-origin request come from, and why do we need to deal with it?

In this situation, the explorer serves a Cosmos-based blockchain. In Cosmos, the way to interact with the node is by utilizing the JSON RPC APIs (https://cosmos.network/rpc). The hostname for the node will generally be the one assigned by the developer or the DNS name for an AWS application load balancer.

Imagine the hostname for the blockchain explorer is “explorer.mychain.com” and the hostname for the RPC APIs is “api.mychain.com”. When the explorer “explorer.mychain.com” issues a request to “api.mychain.com”, it becomes a cross-origin request. The browser will block the application from reading the response returned from the RPC APIs if the response doesn’t contain the correct CORS header.

There are many ways to correctly handle cross origin requests, which we will talk about later. For the explorer, we found that it uses a proxy-like tool called, “CORS-anywhere,” as the solution to handle the CORS header.

It was time to investigate this “CORS-anywhere”.

Part 3: Intro to CORS-anywhere

CORS-anywhere is an open source tool that provides developers a way to deal with cross-origin requests. The project repository has 3.4k stars on Github, demonstrating its popularity. As per the project’s README: “CORS Anywhere is a NodeJS proxy which adds CORS headers to the proxied request.”

During our investigation of this tool, we spotted a Github issue where a user asks about the potential security risk of CORS-anywhere. The author(Rob--W) provides his point of view:

If we consolidate his concerns in three bullet points, they would be:

  • Denial of Service attacks
  • IP address spoofing
  • SSRF (Server-side request forgery)

In the instance of our explorer web application penetration test, the most interesting and relevant point is the last one, server-side request forgery.

“Server-side request forgery (also known as SSRF) is a web security vulnerability that allows an attacker to induce the server-side application to make HTTP requests to an arbitrary domain of the attacker's choosing. In typical SSRF examples, the attacker might cause the server to make a connection back to itself, or to other web-based services within the organization's infrastructure, or to external third-party systems.”—PortSwigger, Web Security Academy

If you want to learn more about SSRF, check out their primer.

Common ways to utilize SSRF vulnerability

  • Perform port scan, network reconnaissance in the internal network.
  • Send requests to internal server’s APIs.
  • Access sensitive resources in the internal network.

So what can a malicious hacker do on the explorer with the SSRF vulnerability found in the “CORS-anywhere”? Here’s a hint: look where it’s hosted — in this case, an AWS EC2 instance.

Part 4: AWS metadata

For AWS EC2 instances, there is a special endpoint http://169.254.169.254/latest/meta-data/ that is only accessible inside the instance. This endpoint contains AWS instance metadata such as instance ID, hostname, public/private IP, and AWS role credentials.

From a quick Google search, “http://169.254.169.254” returns coverage from Y Combinator, as “EC2's most dangerous feature”.

If an EC2 instance is assigned with an IAM (Identity and Access Management) role, its credentials would appear in the metadata. And with access to that role credential, anybody can gain privileges of the IAM role attached to that EC2 instance.

For example, the IAM role has a default instance profile called “aws-elasticbeanstalk-ec2-role”, which is created while launching an environment using Elastic Beanstalk service. According to the AWS documentation, this role has full access to critical AWS resources such as s3 bucket and CloudWatch Logs.

If we get access to IAM role credentials from the metadata endpoint, we could have access to s3 buckets within the organization.

There are two versions of instance metadata service: IMDSv1 (Instance Metadata Service Version 1), and IMDSv2 (Instance Metadata Service Version 2).

For IMDSv1, retrieving instance metadata requires only a GET request:

For IMDSv2, before querying any metadata, a session token that defines the session duration must be created. The session is created via sending a PUT request to “http://169.254.169.254/latest/api/token”. Then, we can request the metadata using the token returned by the PUT request.

For IMDSv2, before querying any metadata, a session token that defines the session duration must be created. The session is created via sending a PUT request to “http://169.254.169.254/latest/api/token”. Then, we can request the metadata using the token returned by the PUT request.

(1) The token must be acquired with a PUT request, while most SSRF attacks only support GET and POST methods.

(2) The PUT request contains an HTTP header “X-aws-ec2-metadata-token-ttl-seconds”. In most SSRF attacks, the attacker usually can’t insert additional HTTP headers into the request.

For more information about the difference between IMDSv1 and IMDSv2, check out this AWS security blog.

Part 5: Exploiting the SSRF Vulnerability

As mentioned previously, CORS-anywhere can be used to perform the SSRF attack; and now that we know it’s hosted on an EC2 Instance, we’ll show how the exploit is executed.

First, we’ll launch an EC2 instance with Elastic Beanstalk. For the purpose of this demonstration, we’ll assume the URL of the CORS-anywhere EC2 instance is located at http://cors.x.y.

The exploit for IMDSv1:

Exploiting IMDSv1 is straightforward. We issue a GET request to the server that hosts the CORS-anywhere to access the AWS credentials for the attached IAM role.

The exploit for IMDSv2:

Although IMDSv2 is more secure than v1, it is still possible to retrieve role credentials by exploiting the SSRF vulnerability in CORS-anywhere. This is because CORS-anywhere supports HTTP PUT method and will forward all of the headers to the metadata service.

To exploit this vulnerability in IMDSv2, we need to send two requests. First, we send a PUT request that contains the “X-aws-ec2-metadata-token-ttl-seconds” HTTP header to get the session token. Then we send a GET request that contains the “X-aws-ec2-metadata-token” HTTP header whose value is acquired from the previous request.

We can use those credentials to gain full (read & write) access to S3 bucket and Cloudwatch log.

Part 6: Finding “CORS-anywhere” in the wild

Because of the popularity of “CORS-anywhere” and wide usage of AWS cloud, we wanted to find out how many EC2 instances suffer from the SSRF vulnerability caused by “CORS-anywhere”.

The default CORS-anywhere page has auto-generated page content that makes it easier for would-be hackers to find them, including this notable first line: “This API enables cross-origin requests to anywhere.” So we use Shodan.io and Zoomeye, two search engines to find devices connected to the internet, in our search to find exploitable instances

The default page for cors-anywhere.

Shodan returns 6 results and Zoomeye returns 447 results. To remove false positives and further verify the result from search engines, we write a script to confirm the host is up and we can access the metadata service with help of “CORS-anywhere”. After some filtering, we find there are a total of 100 AWS EC2 instances on the internet that are vulnerable to SSRF by hosting CORS-anywhere. We do not attempt to retrieve any AWS credentials because we don’t have the permissions to do so.

Shodan

ZoomEye

Part 7: Conclusion

During the penetration test, we were able to exploit the SSRF vulnerability in CORS-anywhere used by the blockchain explorer, retrieve EC2 role credentials, and thus gain full (read & write) access to the company's S3 buckets and CloudWatch Logs. We did not perform further testing after gaining access to the AWS cloud because that was not in scope of the penetration test.

So what can we learn from this story?

Takeaways and solutions

  1. Vulnerabilities in web applications can be found not only in the front-end and back-end of a system, but also in-between, the infrastructure layer.
  2. Before deploying third-party tools on your system, take caution and understand potential security risks.
  3. Performing security audits and penetration tests, whether by an internal security team or third-party firm, are important to ensure the security of your system. Security professionals will attempt to break the system with a malicious hacker’s mindset, helping identify and remediate vulnerabilities before a bad actor exploits them.
  4. Invest time in understanding the AWS Shared Responsibility Model. You’re responsible for the software running on your system; don’t let misconfigured software compromise your cloud infrastructure.
  5. You can turn off the metadata service in AWS
  • In AWS, access to the metadata service can be turned off by disabling access to the HTTP endpoint. This can be done by executing the following command in AWS CLI:

  1. When communicating with the Cosmos RPC APIs, there are more secure ways to handle cross-origin requests:
  • Specifying allowed values for “cors_allowed_origins” in the config/config.toml configuration file.
  • Configuring the application and the RPC APIs under the same hostname.
  • Placing a Nginx reverse proxy in front of the node(Chain) server to insert the CORS header into the HTTP response
  • Using WebSocket instead of HTTP to communicate with the RPC APIs.

The moral of this pentest story is that when you inherit the value and features of a third party’s code, you can also inherit the risks and security flaws that may exist.

In this case, we were able to catch the SSRF vulnerability before malicious hackers had the chance to exploit it; not everyone is as lucky, and so conducting a security review, whether in-house or through an external security group, is vital to identifying risk factors and mitigating them to keep your code and your users safe.

If you’re looking to conduct a thorough review on the security posture of your blockchain ecosystem—whether it’s a smart contract, the implementation of your underlying blockchain protocol, or a web application—CertiK can help.

Our team has a wide range of blockchain experience and expertise in different languages like Solidity, RUST, and Go; as well as in platforms like Ethereum, Cosmos, and Substrate. Our expertise also covers non-blockchain-specific applications, including front-end, back-end, and infrastructure penetration testing.

References

  1. https://github.com/Rob--W/cors-anywhere/issues/152
  2. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS
  3. https://aws.amazon.com/blogs/security/defense-in-depth-open-firewalls-reverse-proxies-ssrf-vulnerabilities-ec2-instance-metadata-service/
  4. https://docs.aws.amazon.com/elasticbeanstalk/latest/dg/iam-instanceprofile.html
  5. https://cosmos.network/rpc/v0.37.9
  6. https://www.shodan.io/
  7. https://www.zoomeye.org/
  8. https://portswigger.net/web-security/ssrf
  9. https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/configuring-instance-metadata-service.html
  10. https://github.com/Azure/WALinuxAgent/wiki/VMs-without-WALinuxAgent
  11. https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity