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/ |
||
Description (last modified by kzar)
Environment
Chrome 58, Adblock Plus dev build built from 4d758880ad0b.
EasyList Hebrew+EasyList, AA disabled.
How to reproduce
- Open chrome://webrtc-internals/ in a new tab.
- Open a second tab, open the developer tools for that and switch to the Adblock Plus pane.
- 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:6 Changed on 05/04/2017 at 08:32:10 PM by kzar
- Cc Lain_13 added
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
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
is it related to the explanation here ?
https://github.com/gorhill/uBlock/issues/1930#issuecomment-301052117
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.
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
Some commits referencing this issue have landed:
comment:15 follow-up: ↓ 16 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).
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

How were they working around the original block?