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?