CVE-2017-0906 (published 2017-11-13T17:29Z)

The Recurly Client Python Library before 2.0.5, 2.1.16, 2.2.22, 2.3.1, 2.4.5, 2.5.1, 2.6.2 is vulnerable to a Server-Side Request Forgery vulnerability in the "Resource.get" method that could result in compromise of API keys or other critical resources.

Severity: HIGH
CVSS v2 base score: 7.5 (AV:N/AC:L/Au:N/C:P/I:P/A:P)
CVSS v3 base score: 9.8, CRITICAL (CVSS:3.0/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H)

Weakness information:
cwe_id: CWE-918
cwe_name: Server-Side Request Forgery (SSRF)
description: The web server receives a URL or similar request from an upstream component and retrieves the contents of this URL, but it does not sufficiently ensure that the request is being sent to the expected destination.
extended_description: By providing URLs to unexpected hosts or ports, attackers can make it appear that the server is sending the request, possibly bypassing access controls such as firewalls that prevent the attackers from accessing the URLs directly. The server can be used as a proxy to conduct port scanning of hosts in internal networks, use other URLs such as that can access documents on the system (using file://), or use other protocols such as gopher:// or tftp://, which may provide greater control over the contents of requests.
         

Fix information:
repo_url: https://github.com/recurly/recurly-client-python
commit_details: repo_url: https://github.com/recurly/recurly-client-python
author: Benjamin Eckel
author_date: 2017-11-08 18:45:28-06:00
msg: Fix SSRF: do not use urljoin, quote uuids
repository: repo_url: https://github.com/recurly/recurly-client-python
repo_name: recurly/recurly-client-python
description: Recurly Python Client
repo_language: Python
owner: recurly
file_changes: filename: resource.py
change_type: ModificationType.MODIFY
diff: @@ -12,8 +12,7 @@ import recurly
 import recurly.errors
 from recurly.link_header import parse_link_value
 from six.moves import http_client
-from six.moves.urllib.parse import urlencode, urljoin, urlsplit
-
+from six.moves.urllib.parse import urlencode, urlsplit, quote
 
 class Money(object):
 
@@ -338,7 +337,8 @@ class Resource(object):
         can be directly requested with this method.
 
         """
-        url = urljoin(recurly.base_uri(), cls.member_path % (uuid,))
+        uuid = quote(str(uuid))
+        url = recurly.base_uri() + (cls.member_path % (uuid,))
         resp, elem = cls.element_for_url(url)
         return cls.from_element(elem)
 
@@ -606,7 +606,7 @@ class Resource(object):
         parameters.
 
         """
-        url = urljoin(recurly.base_uri(), cls.collection_path)
+        url = recurly.base_uri() + cls.collection_path
         if kwargs:
             url = '%s?%s' % (url, urlencode(kwargs))
         return Page.page_for_url(url)
@@ -616,7 +616,7 @@ class Resource(object):
         """Return a count of server side resources given
         filtering arguments in kwargs.
         """
-        url = urljoin(recurly.base_uri(), cls.collection_path)
+        url = recurly.base_uri() + cls.collection_path
         if kwargs:
             url = '%s?%s' % (url, urlencode(kwargs))
         return Page.count_for_url(url)
@@ -638,7 +638,7 @@ class Resource(object):
         return self.put(self._url)
 
     def _create(self):
-        url = urljoin(recurly.base_uri(), self.collection_path)
+        url = recurly.base_uri() + self.collection_path
         return self.post(url)
 
     def put(self, url):

programming_language: Python