CVE-2018-3774 (published 2018-08-12T22:29Z)

Incorrect parsing in url-parse <1.4.3 returns wrong hostname which leads to multiple vulnerabilities such as SSRF, Open Redirect, Bypass Authentication Protocol.

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

Weakness information:
cwe_id: CWE-601
cwe_name: URL Redirection to Untrusted Site ('Open Redirect')
description: A web application accepts a user-controlled input that specifies a link to an external site, and uses that link in a Redirect. This simplifies phishing attacks.
extended_description: An http parameter may contain a URL value and could cause the web application to redirect the request to the specified URL. By modifying the URL value to a malicious site, an attacker may successfully launch a phishing scam and steal user credentials. Because the server name in the modified link is identical to the original site, phishing attempts have a more trustworthy appearance. Whether this issue poses a vulnerability will be subject to the intended behavior of the application. For example, a search engine might intentionally provide redirects to arbitrary URLs.
         
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/unshiftio/url-parse
commit_details: repo_url: https://github.com/unshiftio/url-parse
author: Arnout Kazemier
author_date: 2018-07-29 14:42:38+02:00
msg: [security] Added missing SECURITY.md
repository: repo_url: https://github.com/unshiftio/url-parse
repo_name: unshiftio/url-parse
description: Small footprint URL parser that works seamlessly across Node.js and browser environments.
repo_language: JavaScript
owner: unshiftio
file_changes: filename: SECURITY.md
change_type: ModificationType.ADD
diff: @@ -0,0 +1,47 @@
+# Security Guidelines
+
+Please contact us directly at **security@3rd-Eden.com** for any bug that might
+impact the security of this project. Please prefix the subject of your email
+with `[security]` in lowercase and square brackets. Our email filters will
+automatically prevent these messages from being moved to our spam box. All
+emails that do not include security vulnerabilities will be removed and blocked
+instantly.
+
+In addition to a dedicated email address to receive security related reports,
+we also have a [Hacker1 account][hacker1] that can be used be used for
+communicating security related issues.
+
+You will receive an acknowledgement of your report within **24 hours** of
+notification.
+
+## Exceptions
+
+If you do not receive an acknowledgement within the said time frame please give
+us the benefit of the doubt as it's possible that we haven't seen it yet. In
+this case please send us a message **without details** using one of the
+following methods:
+
+- Give a poke on Twitter [@3rdEden](https://twitter.com/3rdEden)
+- Contact the lead developers of this project on their personal e-mails. You
+  can find the e-mails in the git logs, for example using the following command:
+  `git --no-pager show -s --format='%an <%ae>' <gitsha>` where `<gitsha>` is the
+  SHA1 of their latest commit in the project.
+
+Once we have acknowledged receipt of your report and confirmed the bug
+ourselves we will work with you to fix the vulnerability and publicly
+acknowledge your responsible disclosure, if you wish.
+
+## History
+
+> url-parse returns wrong hostname which leads to multiple vulnerabilities such
+> as SSRF, Open Redirect, Bypass Authentication Protocol.
+
+- Hacker1 report: https://hackerone.com/reports/384029
+- Reported by [lolwaleet](https://hackerone.com/lolwalee)
+- Triaged by [Liran Tal](https://hackerone.com/lirantal)
+- Fixed in: 1.4.3
+
+---
+
+[twitter]: https://twitter.com/3rdEden
+[hacker1]: https://hackerone.com/3rdeden

programming_language: Markdown
repo_url: https://github.com/unshiftio/url-parse
commit_details: repo_url: https://github.com/unshiftio/url-parse
author: Arnout Kazemier
author_date: 2018-07-29 14:26:05+02:00
msg: [security] Sanitize paths, hosts before parsing.
repository: repo_url: https://github.com/unshiftio/url-parse
repo_name: unshiftio/url-parse
description: Small footprint URL parser that works seamlessly across Node.js and browser environments.
repo_language: JavaScript
owner: unshiftio
file_changes: filename: index.js
change_type: ModificationType.MODIFY
diff: @@ -20,6 +20,9 @@ var required = require('requires-port')
 var rules = [
   ['#', 'hash'],                        // Extract from the back.
   ['?', 'query'],                       // Extract from the back.
+  function sanitize(address) {          // Sanitize what is left of the address
+    return address.replace('\\', '/');
+  },
   ['/', 'pathname'],                    // Extract from the back.
   ['@', 'auth', 1],                     // Extract from the front.
   [NaN, 'host', undefined, 1, 1],       // Set left over value.
@@ -47,7 +50,7 @@ var ignore = { hash: 1, query: 1 };
  *
  * @param {Object|String} loc Optional default location object.
  * @returns {Object} lolcation object.
- * @api public
+ * @public
  */
 function lolcation(loc) {
   var location = global && global.location || {};
@@ -89,7 +92,7 @@ function lolcation(loc) {
  *
  * @param {String} address URL we want to extract from.
  * @return {ProtocolExtract} Extracted information.
- * @api private
+ * @private
  */
 function extractProtocol(address) {
   var match = protocolre.exec(address);
@@ -107,7 +110,7 @@ function extractProtocol(address) {
  * @param {String} relative Pathname of the relative URL.
  * @param {String} base Pathname of the base URL.
  * @return {String} Resolved pathname.
- * @api private
+ * @private
  */
 function resolve(relative, base) {
   var path = (base || '/').split('/').slice(0, -1).concat(relative.split('/'))
@@ -140,11 +143,14 @@ function resolve(relative, base) {
  * create an actual constructor as it's much more memory efficient and
  * faster and it pleases my OCD.
  *
+ * It is worth noting that we should not use `URL` as class name to prevent
+ * clashes with the global URL instance that got introduced in browsers.
+ *
  * @constructor
  * @param {String} address URL we want to parse.
  * @param {Object|String} location Location defaults for relative paths.
  * @param {Boolean|Function} parser Parser for the query string.
- * @api public
+ * @private
  */
 function Url(address, location, parser) {
   if (!(this instanceof Url)) {
@@ -190,10 +196,16 @@ function Url(address, location, parser) {
   // When the authority component is absent the URL starts with a path
   // component.
   //
-  if (!extracted.slashes) instructions[2] = [/(.*)/, 'pathname'];
+  if (!extracted.slashes) instructions[3] = [/(.*)/, 'pathname'];
 
   for (; i < instructions.length; i++) {
     instruction = instructions[i];
+
+    if (typeof instruction === 'function') {
+      address = instruction(address);
+      continue;
+    }
+
     parse = instruction[0];
     key = instruction[1];
 
@@ -284,8 +296,8 @@ function Url(address, location, parser) {
  *                               used to parse the query.
  *                               When setting the protocol, double slash will be
  *                               removed from the final url if it is true.
- * @returns {URL}
- * @api public
+ * @returns {URL} URL instance for chaining.
+ * @public
  */
 function set(part, value, fn) {
   var url = this;
@@ -370,8 +382,8 @@ function set(part, value, fn) {
  * Transform the properties back in to a valid and full URL string.
  *
  * @param {Function} stringify Optional query stringify function.
- * @returns {String}
- * @api public
+ * @returns {String} Compiled version of the URL.
+ * @public
  */
 function toString(stringify) {
   if (!stringify || 'function' !== typeof stringify) stringify = qs.stringify;

programming_language: JavaScript
filename: test.js
change_type: ModificationType.MODIFY
diff: @@ -192,6 +192,28 @@ describe('url-parse', function () {
     assume(parsed.pathname).equals('/b/c');
   });
 
+  it('ignores \\ in pathnames', function () {
+    var url = 'http://google.com:80\\@yahoo.com/#what\\is going on'
+      , parsed = parse(url);
+
+    assume(parsed.port).equals('');
+    assume(parsed.username).equals('');
+    assume(parsed.password).equals('');
+    assume(parsed.hostname).equals('google.com');
+    assume(parsed.hash).equals('#what\\is going on');
+
+    parsed = parse('//\\what-is-up.com');
+    assume(parsed.pathname).equals('/what-is-up.com');
+  });
+
+  it('correctly ignores multiple slashes //', function () {
+    var url = '////what-is-up.com'
+      , parsed = parse(url);
+
+    assume(parsed.host).equals('');
+    assume(parsed.hostname).equals('');
+  });
+
   describe('origin', function () {
     it('generates an origin property', function () {
       var url = 'http://google.com:80/pathname'
@@ -252,6 +274,13 @@ describe('url-parse', function () {
       o = parse('wss://google.com:80/pathname');
       assume(o.origin).equals('wss://google.com:80');
     });
+
+    it('maintains the port number for non-default port numbers', function () {
+      var parsed = parse('http://google.com:8080/pathname');
+
+      assume(parsed.host).equals('http://google.com:8080');
+      assume(parsed.href).equals('http://google.com:8080/pathname');
+    });
   });
 
   describe('protocol', function () {

programming_language: JavaScript