Opened on 05/03/2017 at 03:51:20 PM

Closed on 05/16/2017 at 01:06:43 PM

Last modified on 07/05/2017 at 02:54:22 PM

#5207 closed defect (fixed)

Uponit circumvention of WebRTC blocking

Reported by: kzar Assignee: kzar
Priority: P2 Milestone: Adblock-Plus-1.13.3-for-Chrome-Opera
Module: Platform Keywords:
Cc: dzr156, fanboy, mapx, sebastian, Lain_13, SMed79 Blocked By:
Blocking: Platform: Chrome
Ready: yes Confidential: no
Tester: Ross Verified working: yes
Review URL(s):

https://codereview.adblockplus.org/29437555/
https://codereview.adblockplus.org/29437560/

Description (last modified by kzar)

Environment

Chrome 58, Adblock Plus dev build built from 4d758880ad0b.
EasyList Hebrew+EasyList, AA disabled.

How to reproduce

  1. Open chrome://webrtc-internals/ in a new tab.
  2. Open a second tab, open the developer tools for that and switch to the Adblock Plus pane.
  3. Browse to http://www.ynet.co.il in the second tab.

Observed behaviour

  • No WebRTC connection is listed in the Adblock Plus pane.
  • A WebRTC connection is listed on chrome://webrtc-internals/.

Expected behaviour

The WebRTC connection should be listed in the Adblock Plus pane and blocked.

Notes

This is a simplified version of what they're doing:

function unwrapAPIs(...args)
{
  let iframe = document.createElement("iframe");
  iframe.style.display = "none";
  iframe.style.width = "1px";
  iframe.style.height = "1px";
  iframe.srcdoc = "a";
  document.body.appendChild(iframe);

  let iframeWindow = iframe.contentDocument.defaultView;
  iframeWindow.document.write("a");
  try
  {
    iframeWindow.stop();
  } catch (e) {}

  for (let apiName of args)
    window[apiName] = iframeWindow.eval(apiName);
}

unwrapAPIs("RTCPeerConnection", "RTCSessionDescription",
           "RTCIceCandidate", "WebSocket");

Their code is heavily obfuscated, they even have some crazy encoding for sensitive strings. Here's the code to decode those:

let decoder = {};
decoder.u = function(a) {
  this.C = [];
  this.D = 256;
  for (var b = 0; b < this.D; b++)
    this.C[b] = a.charCodeAt(b % a.length);
  this.L = function(a) {
    for (var b = "", d = 0; d < a.length; d++)
      b += String.fromCharCode(a.charCodeAt(d) ^ this.C[d % this.D]);
    return b
  }
}
;
decoder.F = new decoder.u("!np!PoayN9DfrmHLy5moQDP4vVe1fZ41");
decoder.G = function(a) {
  return decoder.F.L(a)
}
;
decoder.decode = function(a, e) {
  !1 !== e && (a = atob(a));
  return decoder.G(a)
};

decoder.decode("VgceRT8YTysaehcDAR4hIxdxCBwyNjlEAj8KX0YmSBFWBx5FPxhPDitbLw8GPxwPKlAeHDgrPnATJQZDDypAWE4AUF0sTxYQIF0rEVwAJzYrYS48NDcjXRk4IVQVOUZYURoZTj5PHQVuTi0IFgI/YhRGPzsSFzVHBT8KXyI/R1JTBwBVOQAP");

For reference here's the original unwrapping code, with only the strings decoded:

function I() {
  function a(a, e, f) {
    try {
      if (!e)
        return {};
      var g = d(a), k = g.Object, v = g.hasOwnProperty, h = k(), m;
      for (m in e)
        if (v.call(e, m)) {
          var l = e[m]
          , n = g["eval"](m);
          void 0 !== l.bind && (n = n.bind(l.bind));
          h[l.name] = n
        }
      c(g) && f && a.parentElement && a.parentElement.removeChild(a);
      return h
    } catch (N) {
      return {}
    }
  }
  function e() {
    var a = document.createElement("iframe");
    a.style.display = "none";
    a.style.width = "1px";
    a.style.height = "1px";
    a["srcdoc"] = "a";
    (document.body || document.head || document.documentElement).appendChild(a);
    var c = d(a);
    "undefined" === typeof c.document.documentElement && c.document.write("a");
    try {
      c["stop"]()
    } catch (t) {}
    return a
  }
  function d(a) {
    var c = "contentDocument"
    , e = "defaultView"
    , d = "contentWindow";
    return a[c] ? a[c][e] || a[d] : a[d]
  }
  function c(a) {
    return "undefined" !== typeof a["InstallTrigger"]
  }
  function g(a) {
    return !!a["chrome"] && !!a["chrome"]["webstore"] && !!a["webkitResolveLocalFileSystemURL"] && !("safari" in a)
  }
  var f = "window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection"
  , l = "window.RTCSessionDescription || window.webkitRTCSessionDescription || window.mozRTCSessionDescription || window.msRTCSessionDescription"
  , k = "window.RTCIceCandidate || window.webkitRTCIceCandidate || window.mozRTCIceCandidate || window.msRTCIceCandidate"
  , h = "window.WebSocket"
  , n = e()
  , m = d(n);
  if (900 >= (m["innerWidth"] || document["documentElement"]["clientWidth"] || document.body["clientWidth"]) || !(g(m) || m["opr"] && m["opr"]["addons"] && m["chrome"] || c(m) && "undefined" !== typeof m["mozInnerScreenX"] && "undefined" !== typeof m["mozRTCIceCandidate"]) && (g(m) || m["opr"] && m["opr"]["addons"] || m["opera"] || void 0 === m["webkitAudioContext"])) {
    var p = {};
    p["RTCPeerConnection"] = window["eval"](f);
    p["RTCSessionDescription"] = window["eval"](l);
    p["RTCIceCandidate"] = window["eval"](k);
    p["WebSocket"] = window["eval"](h);
    return p;
  }
  var q = null
  , u = {};
  u[f] = {
    bind: void 0,
    name: "RTCPeerConnection"
  };
  u[l] = {
    bind: void 0,
    name: "RTCSessionDescription"
  };
  u[k] = {
    bind: void 0,
    name: "RTCIceCandidate"
  };
  f = {
    bind: void 0,
    name: "WebSocket"
  };
  q = {};
  c(m) ? (q = {},
          q[h] = f,
          h = e(),
          q = a(h, q, !0)) : u[h] = f;
  h = a(n, u, !1);
  for (p in q)
    q.hasOwnProperty(p) && (h[p] = q[p]);
  return h
}

Also see #4586 which is very similar.

Hints for testers

I recommend this is tested at the same time as #4586.

  • Please test that the blocking of WebRTC connections, WebSockets and element hiding still work for both new and old Chrome versions.
  • Check that complex webpages that use lots of frames still work, things like Google Drive.
  • Test that websites that use WebRTC like Google Hangouts.
  • Check that WebRTC connections are listed (and blocked) by Adblock Plus for merriam-webster.com.
  • Test that you can't find websites that open WebRTC connections that are listed in the chrome://webrtc-internals page but not in the Adblock Plus connections panel.

Attachments (0)

Change History (20)

comment:1 Changed on 05/03/2017 at 03:51:31 PM by kzar

  • Sensitive set

comment:2 Changed on 05/03/2017 at 03:51:52 PM by kzar

  • Platform changed from Firefox to Chrome

comment:3 Changed on 05/04/2017 at 08:10:52 PM by kzar

  • Description modified (diff)

comment:4 Changed on 05/04/2017 at 08:12:07 PM by kzar

  • Description modified (diff)

comment:5 Changed on 05/04/2017 at 08:17:29 PM by kzar

  • Description modified (diff)

comment:6 Changed on 05/04/2017 at 08:32:10 PM by kzar

  • Cc Lain_13 added

comment:7 Changed on 05/11/2017 at 09:59:46 AM by kzar

  • Description modified (diff)

comment:8 Changed on 05/12/2017 at 07:57:53 AM by mapx

  • Cc SMed79 added

comment:9 Changed on 05/12/2017 at 12:25:18 PM by kzar

  • Review URL(s) modified (diff)
  • Status changed from new to reviewing

comment:10 Changed on 05/12/2017 at 01:17:18 PM by fanboy

How were they working around the original block?

comment:11 Changed on 05/12/2017 at 01:25:17 PM by kzar

Something like creating an iframe where our content script doesn't run (thanks Chrome...), using that to access the unwrapped APIs, then using those to open connections without us knowing.

I have a fix up for review now that's actually working pretty well, I'll keep you posted.

comment:12 Changed on 05/12/2017 at 01:27:16 PM by mapx

comment:13 Changed on 05/12/2017 at 01:35:45 PM by kzar

Yes and no.

Their discussion did start from looking at some similar circumvention code. But they're actually discussing adding a $csp filter option which would allow filter list authors to specify content security policies themselves. That's not what I'm doing here, but it's actually a pretty interesting idea. I'll open an issue for that.

Edit: There you go #5241.

Last edited on 05/12/2017 at 01:51:19 PM by kzar

comment:14 Changed on 05/16/2017 at 01:06:43 PM by kzar

  • Description modified (diff)
  • Milestone set to Adblock-Plus-for-Chrome-Opera-next
  • Resolution set to fixed
  • Status changed from reviewing to closed

comment:15 follow-up: Changed on 05/18/2017 at 10:32:21 AM by dzr156

Hey, I just tested the filter with the latest code on yad2.co.il (Uponit site) and it doesn't work for there, seems like a different WebRTC circumvention version (the connections weren't blocked).

http://www.yad2.co.il/

Last edited on 05/18/2017 at 10:34:09 AM by dzr156

comment:16 in reply to: ↑ 15 Changed on 05/21/2017 at 02:00:19 PM by dzr156

Replying to dzr156:

Hey, I just tested the filter with the latest code on yad2.co.il (Uponit site) and it doesn't work for there, seems like a different WebRTC circumvention version (the connections weren't blocked).
http://www.yad2.co.il/

Hey guys, my mistake, the new wrapper works great. Thanks, Kzar!

comment:17 Changed on 06/27/2017 at 01:52:52 PM by Ross

Appears fine:

  • Blocking of WebSockets, WebRTC and element hiding still work as expected.
  • Sites with lots of frames like Drive seem fine.
  • Video/audio in Hangouts works as expected with ABP installed (on both ends, or either).
  • Various sites from the WebRTC tracking census. Their WebRTC calls display in the ABP devtools panels and can be blocked/unblocked.

Not sure about:

  • Merriam Webster no longer appears to use a WebRTC connection?
  • The ynet.co.il site. It creates a WebRTC connection which appears in the ABP devtools list as expected, but isn't blocked using either EasyList or EasyList Hebrew. I could block it manually. Is that expected?

ABP 1.13.2.1785
Chrome 49 / Windows 7
Chrome 59 / Windows 10

comment:18 Changed on 06/27/2017 at 03:39:44 PM by kzar

Merriam Webster no longer appears to use a WebRTC connection?

Yea, that seems to be the case. I can't see any WebRTC connections being opened for that site now either.

The ynet.co.il site. It creates a WebRTC connection which appears in the ABP devtools list as expected, but isn't blocked using either EasyList or EasyList Hebrew. I could block it manually. Is that expected?

Sounds OK to me. (I see the same behaviour.)

comment:19 Changed on 07/04/2017 at 01:34:28 PM by kzar

  • Sensitive unset

comment:20 Changed on 07/05/2017 at 02:54:22 PM by Ross

  • Tester changed from Unknown to Ross
  • Verified working set

Done. WebRTC blocking is working as in #4455.

ABP 1.13.2.1785
Chrome 49 / 58 / Windows 7
Opera 39 / 45 / Windows 7

Add Comment

Modify Ticket

Change Properties
Action
as closed .
The resolution will be deleted. Next status will be 'reopened'.
to The owner will be changed from kzar.
 
Note: See TracTickets for help on using tickets.