JavaScript offers several methods for sending and receiving network data. This post attempts to enumerate all forms known at this time, to make it possible to audit code for potentially malicious activity (I’m looking at you, Chrome extensions requiring access to read or modify all data on a page)
Update: February 26, 2019
A few days after posting this, I became aware of https://crxcavator.io which performs security assessments of chrome plugins at scale. Check it out!
Ajax and XMLHttpRequest
Asynchronous JavaScript and XML (Ajax) is a turn-of-the-millenium technology
that enabled web clients to retrieve dynamic content from the server without
requiring a full page reload to update. These days, Ajax is typically used to
request data via JavaScript Object Notation (JSON) instead of XML, though the
name stuck. The API for retrieving data from the server using Ajax is
XMLHttpRequest
.
Searching for XMLHttpRequests across a chrome plugin’s content can be efficiently performed using grep, as shown:
$ grep -n -e "XMLHttpRequest" -R dbepggeogbaibhgnhhndojpepiihcmeb/
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/options.js:492: xhr = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:16: branchRefRequest = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:140: req = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/completion_search.js:56: xhr = new XMLHttpRequest();
Now I’m going to audit options.js, logging.js, main.js, and completion_search.js for network activity.
Note that developers quite frequently name the XMLHttpRequest object ‘xhr’.
Fetch
The Fetch API is a modern API for defining request and response objects that can be abstracted to include any future use cases for manipulating network requests and responses.
The fetch()
method is used to make a request to the resource specified as an
argument. Thus, we can search for potential network activity as follows:
grep -n -e "fetch(" -R dbepggeogbaibhgnhhndojpepiihcmeb/
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/lib/utils.js:418: return fetch(function(data) {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/options.js:24: this.fetch();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/options.js:43: return this.fetch();
As fetch()
defines the concept of a resource Request
, it’s worthwhile to
search for uses of this invocation as well:
$ grep -n -e "Request" -R dbepggeogbaibhgnhhndojpepiihcmeb/
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/options.js:492: xhr = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:10: var branchRefRequest;
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:16: branchRefRequest = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:17: branchRefRequest.addEventListener("load", function() {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:19: branchRefParts = branchRefRequest.responseText.split("refs/heads/", 2);
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:27: branchRefRequest.open("GET", chrome.extension.getURL(".git/HEAD"));
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/pages/logging.js:28: return branchRefRequest.send();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/completion.js:515: SearchEngineCompleter.prototype.preprocessRequest = function(request) {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/completion.js:787: if (typeof completer.preprocessRequest === "function") {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/completion.js:788: completer.preprocessRequest(request);
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:3: var BackgroundCommands, DISABLED_ICON, ENABLED_ICON, Frames, HintCoordinator, PARTIAL_ICON, TabOperations, completers, completionHandlers, completionSources, cycleToFrame, fn, forCountTabs, frameIdsForTab, handleCompletions, handleFrameFocused, i, icon, iconImageData, j, len, len1, mkRepeatCommand, moveTab, onURLChange, portHandlers, ref, ref1, removeTabsRelative, root, scale, selectSpecificTab, selectTab, sendRequestHandlers, showUpgradeMessage, toggleMuteTab,
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:122: if (sendRequestHandlers[request.handler]) {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:123: sendResponse(sendRequestHandlers[request.handler](request, sender));
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:140: req = new XMLHttpRequest();
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/main.js:870: sendRequestHandlers = {
dbepggeogbaibhgnhhndojpepiihcmeb//1.64.3_0/background_scripts/completion_search.js:56: xhr = new XMLHttpRequest();
Quick Check for hardcoded IP address or domain
Though malicious activity could be obfuscated, it’s usually not a bad idea to quickly grep for any hardcoded IP addresses or hostnames.
IP address search
$ grep "\b(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])\." -R dbepggeogbaibhgnhhndojpepiihcmeb/
{no results}
Hostname search
$ grep "^[a-z0-9]+([\-\.]{1}[a-z0-9]+)*\.[a-z]{2,11}$" -R dbepggeogbaibhgnhhndojpepiihcmeb/
{no results}
Subdomain search (note the escaped ! to prevent the terminal emulator from interpreting this as an event)
$ grep "%(?^(?\!.{256})(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+(?:[a-z]{1,63}| xn--[a-z0-9]{1,59})$%xi" -R dbepggeogbaibhgnhhndojpepiihcmeb/
{no results}
I am not a regex expert, nor did I write these (the fine posters in this
StackOverflow
article
and this StackOverflow
article did),
so these may not have complete coverage. An attacker may also use string
manipulation to dynamically construct the malicious hostname to contact using
.join
or another method I’m not aware of.