Data WhiteOps collects
Data WhiteOps collects

    
    
    By
    
    · Jul 8, 2020
    
    
      
        
        
        
          Edit: White Ops has recently rebranded to HUMAN.
Today I noticed New Reddit was asking to use DRM. So I went on a source code analysis journey! See the main post for more details.
It appears Reddit uses White Ops for fingerprinting and ad fraud prevention (but only sometimes, I think it’s an A/B test). See this beautified code:
const t = e.user.account ? Jt()(e.user.account.id).toString() : void 0,
    s = document.createElement("script");
s.src = Object(es.a)("https://s.udkcrj.com/ag/386183/clear.js", {
    dt: "3861831591810830724000",
    pd: "acc",
    mo: 0,
    et: 0,
    ti: $t()(),
    ui: t,
    si: "d2x"
}), document.head.appendChild(s)
This code is only run if the show_white_ops feature flag is enabled (don’t know how to force this to be true), which is why I assume the tracking is done by WhiteOps.
Further research into the https://s.udkcrj.com/ag/386183/clear.js file reveals that it ends up loading another script, https://s.udkcrj.com/2/4.71.0/main.js.
Note: here is a function I used to deobfuscate many strings in the code:
const deob = x => x.replace(/[A-Za-z]/g, function(e) {
    return String.fromCharCode(e.charCodeAt(0) + (e.toUpperCase() <= "M" ? 13 : -13))
})
Note 2: Ozoki appears to be some sort of internal codename used by WhiteOps.
This very obfuscated script does many things, including (this is an incomplete list, I have missed many things):

Communicates with vprza.com and minkatu.com
Does device/OS detection
Attempts to detect if DevTools is open (looks at __IE_DEVTOOLBAR_CONSOLE_COMMAND_LINE)
Something weird with the text-underline-offset CSS property
Has the names of a bunch of ad servers, presumably as to not serve ads to bots
Attempts to create an ActiveXObject (only exists in old IE versions), and checks if the created ActiveX object isSandboxed
Checks for maxConnectionsPerServer and onhelp properties on window (for browser engine detection I think)
Attempts to exploit an IE11 vuln!

it checks the value of document.documentMode, which in IE11 is 11. If it is, then it runs the obfuscated code:



// o == document.documentMode == 11
"}o|B65niitbmd,ahg)Z[i$_g".replace(/./g, function(e) {
    return String.fromCharCode(e.charCodeAt(0) - o--)
})

this evaluates to res://ieframe.dll/acr.js, but only on IE. This string has only one purpose, exploiting the fact that you can put arbitrary HTML in the hash part of the URL and have it get evaluated, I beleive in a privledged context of some sort. This file actually resolves in IE to a internal Windows system file, which is only accessible in JS due to a bug.
tries to run VBScript:

execScript("e71012934811a=false::On Error Resume Next::" + e + "::if Err.Number=-2147024891 or Err.Number=5002 then e71012934811a=true::Err.Clear", "VBScript"), t = "e71012934811a" in window ? window.e71012934811a ? o.EXISTS : o.MISSING : o.UNKNOWN

checks for these strings on window (de-obfuscated, I also have the original obfuscated ones in case you are also looking through the source):

boltsWebViewAppLinkResolverResult, GoogleJsInterface, googleAdsJsInterface, accessibilityTraversal, accessibility, FbPlayableAd, __REACT_WEB_VIEW_BRIDGE
obfuscated in code as obygfJroIvrjNccYvaxErfbyireErfhyg, TbbtyrWfVagresnpr, tbbtyrNqfWfVagresnpr, npprffvovyvglGenirefny, npprffvovyvgl, SoCynlnoyrNq, __ERNPG_JRO_IVRJ_OEVQTR


checks the screen’s availHeight, availWidth, width, and `height
checks the screen’s colorDepth, pixelDepth, and devicePixelRatio
checks for these automation-related properties on window: domAutomation, domAutomationController, _WEBDRIVER_ELEM_CACHE, _phantom, callPhantom, window.chrome._commandLineAPI, window.Debug.debuggerEnabled, __BROWSERTOOLS_CONSOLE, window._FirebugCommandLine, and also if document.documentElement.hasAttribute("webdriver") is true
checks if "function () { return Function.apply.call(x.log, x, arguments); }" == window.console.log.toString() (also to check for browser automation I guess
tries to check if the browser is headless with isExternalUrlSafeForNavigation, window.opera._browserjsran, window.opera.browserjsran
checks if navigator.onLine is true
check the window.chrome object if in Chrome
extracts data from window.FirefoxInterfaces("wdICoordinate", "wdIMouse", "wdIStatus")
checks if window.XPCOMUtils exists
checks if window.netscape.security.privilegemanager exists
appears to check browser console timing
checks referer against "https://www.google.com", "https://apps.skype.com", "http://apps.skype.com", "https://www.youtube.com", "http://en.wikipedia.org/", "https://en.wikipedia.org/", "https://www.netflix.com", "http://www.netflix.com", "http://www3.oovoo.com"
checks for these properties in window.external: ["AddChannel", "AddDesktopComponent", "AddFavorite", "AddSearchProvider", "AddService", "AddToFavoritesBar", "AutoCompleteSaveForm", "AutoScan", "bubbleEvent", "ContentDiscoveryReset", "ImportExportFavorites", "InPrivateFilteringEnabled", "IsSearchProviderInstalled", "IsServiceInstalled", "IsSubscribed", "msActiveXFilteringEnabled", "msAddSiteMode", "msAddTrackingProtectionList", "msClearTile", "msEnableTileNotificationQueue", "msEnableTileNotificationQueueForSquare150x150", "msEnableTileNotificationQueueForSquare310x310", "msEnableTileNotificationQueueForWide310x150", "msIsSiteMode", "msIsSiteModeFirstRun", "msPinnedSiteState", "msProvisionNetworks", "msRemoveScheduledTileNotification", "msReportSafeUrl", "msScheduledTileNotification", "msSiteModeActivate", "msSiteModeAddButtonStyle", "msSiteModeAddJumpListItem", "msSiteModeAddThumbBarButton", "msSiteModeClearBadge", "msSiteModeClearIconOverlay", "msSiteModeClearJumpList", "msSiteModeCreateJumpList", "msSiteModeRefreshBadge", "msSiteModeSetIconOverlay", "msSiteModeShowButtonStyle", "msSiteModeShowJumpList", "msSiteModeShowThumbBar", "msSiteModeUpdateThumbBarButton", "msStartPeriodicBadgeUpdate", "msStartPeriodicTileUpdate", "msStartPeriodicTileUpdateBatch", "msStopPeriodicBadgeUpdate", "msStopPeriodicTileUpdate", "msTrackingProtectionEnabled", "NavigateAndFind", "raiseEvent", "setContextMenu", "ShowBrowserUI", "menuArguments", "onvisibilitychange", "scrollbar", "selectableContent", "version", "visibility", "mssitepinned", "AddUrlAuthentication", "CloseRegPopup", "FeatureEnabled", "GetJsonWebData", "GetRegValue", "Log", "LogShellErrorsOnly", "OpenPopup", "ReadFile", "RunGutsScript", "SaveRegInfo", "SetAppMaximizeTimeToRestart", "SetAppMinimizeTimeToRestart", "SetAutoStart", "SetAutoStartMinimized", "SetMaxMemory", "ShareEventFromBrowser", "ShowPopup", "ShowRadar", "WriteFile", "WriteRegValue", "summonWalrus" and also "AddSearchProvider", "IsSearchProviderInstalled", "QueryInterface", "addPanel", "addPersistentPanel", "addSearchEngine"
checks maxConnectionsPerServer (on Window I think)
attempts to detect Brave Browser with window.brave
checks for a bunch more properties on window:

                m("document_referrer", h), m("performance_timing", h, function(e) {
                    return x.qKjvb(e)
                }), m("maxConnectionsPerServer", h), m("status", h), m("defaultStatus", h), m("performance_memory", h, _.flatten), m("screen", h, function(e) {
                    return e || {}
                }), m("devicePixelRatio", h), m("mozPaintCount", h), m("styleMedia_type", h), m("history_length", h), m("opener", h, "top_opener", function(e) {
                    return null !== e ? typeof e : "null"
                }), m("navigator_userAgent", h), m("fileCreatedDate", h), m("fileModifiedDate", h), m("fileUpdatedDate", h), m("lastModified", h), m("location_href", h), m("RemotePlayback", h), m("Permissions", h), m("Notification", h), m("MediaSession", h), m("name", h, function(e) {
                    return e.substr(0, 128)
                }), m("defaultView", h, "document_defaultView", function(e) {
                    return void 0 === e ? "undefined" : e === window
                }), m("console", h, _.flatten), m("localStorage", h, v), m("webkitNotifications", h, v), m("webkitAudioContext", h, v), m("chrome", h, k.Keys), m("opera", h, k.Keys), m("navigator_standalone", h), m("navtype", h, "performance_navigation_type"), m("navredirs", h, "performance_navigation_redirectCount"), m("loadTimes", h, "chrome_loadTimes"), m("vendor", h, "navigator_vendor"), m("codename", h, "navigator_appCodeName"), m("navWebdriver", h, "navigator_webdriver"), m("navLanguages", h, "navigator_languages"), m("ServiceWorker", h), m("ApplePaySetup", h), m("ApplePaySession", h), m("Cache", h), m("PaymentAddress", h), m("MerchantValidationEvent", h), m("mediaDevices", h, "navigator_mediaDevices"), m("share", h, "navigator_share"), m("onorientationchange", h), m("document_defaultView_safari", h), m("navigator_brave", h), m("navigator_brave_isBrave", h), le.hco && m("navHardwareConc", h, "navigator_hardwareConcurrency"), le.ndm && m("navDevMem", h, "navigator_deviceMemory"), le.nvl && (m("navLocks", h, "navigator_locks", function(e) {
                    var t = typeof e;
                    return "undefined" === t ? "undefined" : "object" === t ? "obj" : e
                }), m("navLocksR", h, "navigator_locks_request", function(e) {
                    return "undefined" === typeof e ? "undefined" : e
                })), le.wor && m("origin", h, "origin"), "object" == typeof location && m("protocol", location), "object" == typeof document && (m("hidden", document), m("documentMode", document), m("compatMode", document), m("visibilityState", document), m("d_origin", document, "origin"), m("ddl_origin", document, "defaultView_location_origin"));

checks mozIsLocallyAvailable (which is only available on FF <= 35, and could be for checking if someone is trying to debug this script offline)
checks how rounding of large and small numbers works, with Math.round(.49999999999999994) and Math.round(Math.pow(2, 52))
checks the installed plugins (with navigator.plugins)
checks the browser viewport
attempts to get the “frame depth” (not sure what exactly that is)
checks for various status bars on window ("locationbar", "menubar", "personalbar", "scrollbars", "statusbar", "toolbar")
checks for window.showModalDialog
checks the -webkit-text-size-adjust property
checks if using xx-small as a font size works
Checks if image loading is enabled, by trying to load a 1x1 GIF
Tries to load an image with a URL of https:/, and checking if it loaded or errored
Checks the stringification o
checks for these properties on window: StorageItem AnimationTimeline MSFrameUILocalization HTMLDialogElement mozCancelRequestAnimationFrame SVGRenderingIntent SQLException WebKitMediaKeyError SVGGeometryElement SVGMpathElement Permissions devicePixelRatio GetSVGDocument HTMLHtmlElement CSSCharsetRule ondragexit MediaSource external DOMMatrix InternalError ArchiveRequest ByteLengthQueuingStrategy ScreenOrientation onwebkitwillrevealbottom onorientationchange WebKitGamepad GeckoActiveXObject mozInnerScreenX WeakSet PaymentRequest Generator BhxWebRequest oncancel fluid media onmousemove HTMLCollection OpenWindowEventDetail FileError SmartCardEvent guest CSSConditionRule showModelessDialog SecurityPolicyViolationEvent EventSource ServiceWorker EventTarget origin VRDisplay onpointermove HTMLBodyElement vc2hxtaq9c TouchEvent DeviceStorage DeviceLightEvent External webkitPostMessage HTMLAppletElement ErrorEvent URLSearchParams BluetoothRemoteGATTDescriptor RTCStatsReport EventException PERSISTENT MediaKeyStatusMap HTMLOptionsCollection dolphinInfo MSGesture SVGPathSegLinetoRel webkitConvertPointFromNodeToPage doNotTrack CryptoDialogs HTMLPictureElement MediaController
checks for these CSS properties: scroll-snap-coordinate flex-basis webkitMatchNearestMailBlockquoteColor MozOpacity textOverflow position columns msTextSizeAdjust animationDuration msImeAlign webkitBackgroundComposite MozTextAlignLast MozOsxFontSmoothing borderRightStyle webkitGridRow cssText parentRule webkitShapeOutside justifySelf isolation -moz-column-fill offsetRotation overflowWrap OAnimationFillMode borderRight border-inline-start-color webkitLineSnap MozPerspective touchAction enableBackground borderImageSource MozColumnFill webkitAnimationFillMode MozOSXFontSmoothing XvPhonemes length webkitFilter webkitGridAutoColumns
checks the stringification of native functions, and verifies some functions are implemented in native code (including performance.mark, and Function.prototype.toString itself)
attempts to get the width of the string mmmmmmmmmmlli in all of these fonts: "Ubuntu", "Utopia", "URW Gothic L", "Bitstream Charter", "FreeMono", "DejaVu Sans", "Droid Serif", "Liberation Sans", "Vrinda", "Kartika", "Sylfaen", "CordiaUPC", "Angsana New Bold Italic", "DFKai-SB", "Ebrima", "Lao UI", "Segoe UI Symbol", "Vijaya", "Roboto", "Apple Color Emoji", "Baskerville", "Marker Felt", "Apple Symbols", "Chalkboard", "Herculanum", "Skia", "Bahnschrift", "Andalus", "Yu Gothic", "Aldhabi", "Calibri Light"
checks various user agent properties in navigator
Listens to the voiceschanged event, and keeps track of what text-to-speech voices are available
Attempts to load chrome://browser/skin/feeds/feedIcon.png (I think this used to be an RSS icon in old Firefox versions)
checks for scripts injected into the page
checks availble video codecs and formats: 'video/mp4;codecs="avc1.42E01E, mp4a.40.2"', 'video/mp4;codecs="avc1.58A01E, mp4a.40.2"', 'video/mp4;codecs="avc1.4D401E, mp4a.40.2"', 'video/mp4;codecs="avc1.64001E, mp4a.40.2"', 'video/mp4;codecs="mp4v.20.8, mp4a.40.2"', 'video/mp4;codecs="mp4v.20.240, mp4a.40.2"', 'video/3gpp;codecs="mp4v.20.8, samr"', 'video/ogg;codecs="theora, vorbis"', 'video/ogg;codecs="theora, speex"', "audio/ogg;codecs=vorbis", "audio/ogg;codecs=speex", "audio/ogg;codecs=flac", 'video/ogg;codecs="dirac, vorbis"', 'video/x-matroska;codecs="theora, vorbis"', 'video/mp4; codecs="avc1.42E01E"', 'audio/mp4; codecs="mp4a.40.2"', 'audio/mpeg;codecs="mp3"', 'video/webm; codecs="vorbis,vp8"'
checks for window.$cdc_asdjflasutopfhvcZLmcfl_ (property used by Chrome Webdriver) (obfuscated as "$" + x.qKjBI("pqp") + "_" + x.qKjBI("nfqwsynfhgbcsuipMYzpsy") + "_")
gets WEBGL_debug_renderer_info, which contains GPU-specific info
tries to detect if IntersectionObserver returns correct data
checks what DRM solutions are available (although doesn’t actaully use them, this is what causes the DRM popup in Firefox): looks for Widevine, PlayReady, Clearkey, and Primetime
Attempts to load files stored in Chrome extension’s data (presumably to detect if they are installed):

chrome-extension://ianldemdppnbbojbafdkpdofceajhica/img/info.svg
chrome-extension://elfchnpigjboibngodkiamfemllklmge/img/info.svg
chrome-extension://npfnhmfcalmmkbpgkhjpdaiajfdhpndm/img/info.svg
chrome-extension://pfobdhfgohkddopcdbifhccbbpjlakaa/img/info.svg
chrome-extension://gfpioeglfjecbkeeomdidlndcagpbmj/img/info.svg
chrome-extension://mnbkfnehljejnebgbgfhdkmjicgmangi/img/info.svg
chrome-extension://adcggpckpldlkcobapimobdijchkigmb/img/info.svg
chrome-extension://poifgggpiofkbhafbjljpbbajafcjafi/img/info.svg
chrome-extension://plkjhgplpjlokmchnngcaeneiigkipeb/img/info.svg
chrome-extension://hhhpajpnecmhngfgkclokcghcpfgbape/img/info.svg
chrome-extension://cnncicmafnkgbonafdjnikijbhjkeink/img/info.svg
chrome-extension://aoeceebmempjbabimmnfkeeioccbjkea/img/info.svg
chrome-extension://pbclflhfplnkbgfokopkmjpejkokcaec/img/info.svg
chrome-extension://pogfikdkppcfejpknaclddnpcjjbalbn/img/info.svg
chrome-extension://mdjpmjaonahjbjncdlkjgeggjfdnnmme/img/info.svg
chrome-extension://cefomhonapiagddecgpooacpnoomabne/img/info.svg
chrome-extension://iokdapkmdldpeomcloobkajedcdleoib/img/info.svg
chrome-extension://cndipecijohebobplligphncocjamhei/content/images/icons/16.png
chrome-extension://lmpknllkkhpbfahgbkgjgopandmdbopi/blocked-notification-bar.html
chrome-extension://blddopfbnibgpgfeheedhjaheikanamn/img/128x128g.png
chrome-extension://nfjcghppefdfgmnblhgelkjhmljpndhh/img/icon16.png

checks if the source code of window.close (which is normally [native code]) contains ELECTRON
there is the string “haha jit go brrrrr”, as part of what appears to be some sort of either test for a JS engine bug or exploit
collects performance data
checks if Safari is creating a “Web clip” screenshot of the page


        
        
      
    
    
  