System Font as Proprietary Cookies: An Unusual Method of Tagging Your Device

Man

Professional
Messages
3,070
Reaction score
606
Points
113
Let's say a developer is asked the following question: how can a website determine that a user has a specific application installed? This is an interesting question. There are several ways to answer it. How about this option - installing a unique font in the system when installing a program? After all, the browser always returns a list of system fonts upon request. So, the problem is solved.

Various programs do this, although you can't call it an example of correct programming. The method has its advantages and disadvantages.

For example, in older versions of TeamViewer 8 the method worked as follows.

  1. To open a session with another user, the program generates an invitation URL of the following type:
    Code:
    https://get.teamviewer.com/v15/en/sXXXXXXXX
    … where XXXXXXXXis the session code.
  2. The site checks via JavaScript that the user has the TeamViewer font. This means that the TeamViewer software is installed on the system.

    Since the installer has registered the protocol handler in the operating system teamviewer8://, the website (JavaScript code) can directly call TeamViewer at the following URL:
    Code:
    teamviewer8://instantsupport/?sid=XXXXXXXX
  3. If the font is not found in the system, the site prompts the user to download and install the application.

The actual script for determining the presence of the font was located here (now removed):
Code:
https://get.teamviewer.com/get/res/scripts/fontdetect.js

mirror

Deobfuscated code
Code:
if (!self.__WB_pmw) {
self.__WB_pmw = function (obj) {
this.__WB_source = obj;
return this;
};
}
{
let window = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("window") || self.window;
let self = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("self") || self.self;
let document = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("document") || self.document;
let location = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("location") || self.location;
let top = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("top") || self.top;
let parent = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("parent") || self.parent;
let frames = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("frames") || self.frames;
let opener = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("opener") || self.opener;
function detect(a) {
var c = document.getElementsByTagName("body")[0], b = document.createElement("span");
b.style.fontSize = "72px";
b.innerHTML = a;
b.style.fontFamily = "nonexistingfonttoforcedefault";
c.appendChild(b);
var e = b.offsetWidth, d = b.offsetHeight;
c.removeChild(b);
b.style.fontFamily = a + ",nonexistingfonttoforcedefault";
c.appendChild(b);
a = b.offsetWidth != e || b.offsetHeight != d;
c.removeChild(b);
return a;
}
function minimalisticMajorCheck(a) {
a = "TeamViewer" + String(a);
return detect(a) ? true : detect(a + "Host") ? true : false;
}
function getMajorVersionArray() {
for (var a = 0, c = [], b = 99; 1 <= b; b--) minimalisticMajorCheck(b) && (c[a++] = b);
return c;
}
function getCharWidth(a, c, b) {
var e = "TeamViewer" + String(a);
a = document.getElementsByTagName("body")[0];
var d = document.createElement("span");
d.style.fontSize = "10px";
d.style.fontFamily = e;
d.innerHTML = String(c);
a.appendChild(d);
c = d.offsetWidth;
a.removeChild(d);
b && 10 == c && (c = 0);
10 < c && (c = -1);
return c;
}
function getVersionFromString(a, c) {
for (var b = 0, e = false, d = 0; d < c.length; d++) {
var f = getCharWidth(a, c[d], true);
if (0 > f || 9 < f) e = true;
b = 10 * b + f;
}
return e ? -1 : b;
}
function CheckForTeamViewer() {
var a = getMajorVersionArray();
return 0 < a.length ? a[0] : 0;
}
;
}

Script for connection:

mirror

Deobfuscated code
Code:
if (!self.__WB_pmw) {
self.__WB_pmw = function (obj) {
this.__WB_source = obj;
return this;
};
}
{
let window = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("window") || self.window;
let self = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("self") || self.self;
let document = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("document") || self.document;
let location = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("location") || self.location;
let top = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("top") || self.top;
let parent = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("parent") || self.parent;
let frames = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("frames") || self.frames;
let opener = self._wb_wombat && self._wb_wombat.local_init && self._wb_wombat.local_init("opener") || self.opener;
function isBlackListed(e) {
if (blackList = new Array(42, 43, 44, 47, 59, 61, 91, 92, 93, 106, 107, 110, 111), 65 <= e && e <= 82) return true;
if (84 <= e && e <= 90) return true;
if (145 <= e && e <= 188) return true;
if (190 <= e) return true;
for (i = 0; i < blackList.length; i++) if (blackList[i] == e) return true;
return false;
}
function checkIdFormat(e, t) {
var n;
if ((t = t || window.event).which ? n = t.which : t.keyCode && (n = t.keyCode), t.ctrlKey) return true;
if (t.altKey) return false;
if (isBlackListed(n)) return false;
var i = "";
return e && e.value && (i = e.value), theMidLength = i.length, (83 != n || 0 == theMidLength) && (48 <= n && n <= 57 || 96 <= n && n <= 105 ? (t.shiftKey || (0 == theMidLength && (e.value = "s"), 3 != theMidLength && 7 != theMidLength || (e.value = e.value + "-"), 96 <= n && n <= 105 && (n -= 48), e.value = e.value + String.fromCharCode(n)), false) : 109 != n && 189 != n && 45 != n && 32 != n || (3 != theMidLength && 7 != theMidLength || (e.value = e.value + "-"), false));
}
var timestamp;
function SetFallbackUrl(e) {
timestamp = new Date, setTimeout(function () {
new Date - timestamp < 1e3 && window.location.replace(e);
}, 500);
}
function SetFallbackUrlNoCheck(e) {
timestamp = new Date, setTimeout(function () {
window.location.replace(e);
}, 10);
}
function OpenLinkWithFallbackNoFrame(e, t) {
SetFallbackUrl(t), window.location.replace(e);
}
function OpenLinkWithFallback(e, t) {
SetFallbackUrl(t);
t = document.createElement("iframe");
t.style.display = "none", document.body.appendChild(t), t.src = e;
}
function OpenLinkWithFallbackWithoutIframe(e, t) {
SetFallbackUrlNoCheck(t), window.location.replace(e);
}
function OpenLinkWithoutFallback(e) {
window.location.replace(e);
}
function TryConnectWithTV(e, t) {
var n, i;
8 < CheckForTeamViewer() ? (n = e, i = document.getElementById("dDownloadTeamViewer"), e = document.getElementById("dStartTeamViewer"), i && e && (i.style.display = "none", e.style.display = "")) : n = t, window.location.replace(n);
}
function TryConnectWithTVWithPreferenceCheck(e, t, n, i) {
var o, a, r;
8 < CheckForTeamViewer() ? (o = e, a = document.getElementById("dDownloadTeamViewer"), r = document.getElementById("dStartTeamViewer"), a && r && (a.style.display = "none", r.style.display = ""), window.location.replace(o)) : "" === (a = getCookie("getuserpreference")) ? ($("#userPreferenceDialog").attr("title", "Connect to " + n), r = $(window).width(), n = $(window).height(), $("#userPreferenceDialog").dialog({autoOpen: false, height: 0.32 * n, width: 0.3 * r, modal: true, show: {effect: "fade", duration: 1e3}}).dialog("open"), $("#tdFullClient").click(function () {
HandlePreferenceSelection(e, "launch");
}), "true" === i ? $("#tdQuickSupport").remove() : $("#tdQuickSupport").click(function () {
HandlePreferenceSelection(t, "download");
})) : (o = "launch" === a ? e : t, window.location.replace(o));
}
function HandlePreferenceSelection(e, t) {
window.location.replace(e), $("#chkRememberSelection").is(":checked") && setCookie("getuserpreference", t, 365);
}
function setCookie(e, t, n) {
var i = new Date;
i.setTime(i.getTime() + 24 * n * 60 * 60 * 1e3);
i = "expires=" + i.toUTCString();
document.cookie = e + "=" + t + ";" + i + ";path=/";
}
function getCookie(e) {
for (var t = e + "=", n = decodeURIComponent(document.cookie).split(";"), i = 0; i < n.length; i++) {
for (var o = n[i]; " " === o.charAt(0);) o = o.substring(1);
if (0 === o.indexOf(t)) return o.substring(t.length, o.length);
}
return "";
}
function GetTextLength(e, t, n, i) {
var o = $("#divMeasureText");
return o.css("font-family", t).css("font-size", n).css("font-weight", i), o.text(e), o.width();
}
}

Robert Heist, Chief Information Security Officer at TeamViewer, commented on the situation as follows:
The TeamViewer font is used to implement a smooth user transition from the web client to the native client, such as when connecting via an invitation link, prompting for installation, or directly initiating a connection. This has proven to be useful in improving the user experience for all user groups. However, given the concerns raised, we have decided to review and change this approach in a future release to prevent possible detection of TeamViewer installation via the font.

Device tagging​


Installing a proprietary font on a user's computer is one way to mark a device. The developers did not see anything immoral or illegal in this (in fact, there is nothing illegal here). The user is not informed about this method of marking, because he is not supposed to understand the intricacies of the application. The information security director said that this is convenient "for all user groups" (including "ordinary people").

However, a few days after the exposure and public discussion of the native font, TeamViewer decided to abandon it. But this does not mean that other programs do not use this method of recognizing users. Technically, this is just one of the options for fingerprinting. In a sense, this is a proprietary type of cookies.

In general, there are a huge number of ways to recognize an "infected" system. But these "friend or foe" methods are used mainly by the creators of malicious programs (for example, to control and manage a botnet).

Benefits of Proprietary Cookies​


If you come up with your own original way of marking your computer, you will receive a number of advantages, for example:
  • the method works even if the user's system does not support or blocks cookies;
  • the system font is not deleted by a timer, it does not have an expiration date, like cookies;
  • the system font is not deleted when clearing cookies from the browser;
  • permission to save cookies is required under EU law, but installing a system font is not yet required;
  • and others.

Basically, this is one example of the application of the principle of "security through obscurity". The advantages and disadvantages of this principle are well known. It is usually considered dangerous and harmful, but in some individual cases there are still rational grounds for resorting to secrecy. Although there are also negative aspects. The main problem is that any third-party site can determine what software is installed on your computer, which is not very good from a security point of view (which is why TeamViewer decided to abandon this method).

Standard way - Web App Manifest​


The standard way to launch a native application from a browser is the Web App Manifest. For example, iOS launches native programs via a /.well-known/apple-app-site-associationdomain address. Manifest support is still related_applicationsat an experimental stage in various browsers. It is likely that the information security community will not have any complaints about the developer if the standard method of launching applications is used.prefer_related_applications

Source
 
Top