Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

followup: BS / prototype lies #61

Open
Thorin-Oakenpants opened this issue Apr 15, 2021 · 51 comments
Open

followup: BS / prototype lies #61

Thorin-Oakenpants opened this issue Apr 15, 2021 · 51 comments

Comments

@Thorin-Oakenpants
Copy link
Contributor

Thorin-Oakenpants commented Apr 15, 2021

new thread, the last one #35 is getting too long and noisy

No need to list items: it's a work in progress as I make each section more bulletproof to errors, AOPR, etc. The logic is to either expose the real value via other methods/equivalency and/or detect that it's a lie: first through methods like known values etc, and lastly as untrustworthy via prototype lies

cydec total mode: 17 known lies (and not even close to being fully realized)
cydec lies

I also decided to keep track of bypasses

  • bypass: getting what we think is the real value
  • we display the lie, but record what we think is the real value in the fingerprint
    cydec bypass

x means I've coded recording the bypass [edit: updated]

bypassable
x "canvas:toDataURL/toBlob", (as long as one is not noisy)
  "css:computed styles",
x "css:forced-colors:",
x "css:prefers-color-scheme:",
x "css:prefers-reduced-motion:",
x "devices:any-hover:",
x "devices:any-pointer:",
x "devices:hover:",
x "devices:plugins", (FF85+ can only return none)
x "devices:pointer:",
x "devices:mimeTypes", (FF85+ can only return none)
x "domrect:xxx", (as long as one is not noisy and all non-noisy ones match)
x "misc:iframe window properties",
x "misc:navigator keys",
x "misc:performance.mark",
x "screen:color",
X "screen:inner", (needs work, also used to fix viewport scrollbar)
X "screen:screen", (needs work)

unbypassable so far (unless it leaks via an iframe)
  "fd:math", (at least in FF. Chrome on the other hand I think is the same known value for everyone)
  "useragent:navigator"

starting to like this game.. so many lies, so little time .. so many ideas

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Apr 16, 2021

actually, plugins and mimetypes are bypassable in FF since FF52+ only allowed flash, and now flash is ripped out: edit: actually only FF85+ (where the answer is always none), since I guess someone could fake Flash randomly

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot can you check my suspect list + lies for iframe.contentWindow properties with privacy badger (or is it privacy possum?) What's the one you got to change prototype lies on a global rerun?

@abrahamjuliot
Copy link
Collaborator

possom

image

image

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Apr 16, 2021

Oh. Is that all possum does? navigator stuff? I was worried about false positives in the (iframe) window properties. I guess I better install possum, but I could never get it to do anything (maybe it hates me)

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Apr 16, 2021

It returns gibberish on canvas 2d and webgl, randomizes screen and devicePixelRatio, blocks storage APIs, and a few more.

https://github.com/cowlicks/privacypossum/blob/a328104217e6bebc35ee48f9561255ef83c51c41/src/js/contentscripts/fingercounting.cjs#L109-L159

Testing requires a single script to trigger the fingerprinting threshold and then the Possom targets that single script. [EDIT: as far as know. Cowlicks is the expert there] On TZP, it looks like the canvas test triggers the threshold, so the noise is only applied to that script js file. I used to see all of the noise on creep, but then once I began using an iframe window (for most metrics), I realized Privacy Possom does not protect iframes - all of it is bypassed there.

One of the subtle advantages of separating scripts in small chunks (as on TZP) is site scrappers and extensions cannot easily bypass or block a script URL for fingerprinting.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Apr 16, 2021

the other way FPing scripts can't be blocked as scripts, is when they're first party and part of an essential js file. FPJS2 encourages this

On TZP, it looks like the canvas test triggers the threshold

Can't get it to trigger. Maybe it takes a few goes over several browser sessions (I did try reruns, reloads to make it learn). Cleaning up the false positives is not super essential: the overall fingerprint is usually already changed - the backend analysis can just treat that metric as suspicious if it's an outlier and the suspect list is not empty

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Apr 16, 2021

check it out: b30b4fe - edit: read my comment at the end

@Thorin-Oakenpants
Copy link
Contributor Author

that was fun: b36f53b .. enjoy the read

the range limitations means we don't always get a bypass (when someone is lying)

We only need the bypass when someone is lying: if we don't get a bypass, then we fall back to prototype lies, so at least the stability of the section hash's zoom resistance is only two values (when someone lies). I have ideas to cover the non-bypassed results

I tested my screen at various zoom levels

  • system scaling is normal 100%
  • screen is 2560 x 1440
  • as you zoom in, values shrink and vice versa
  • current range is 500-2560 inclusive
  • system scaling would also affect this: but just the starting point

Anyway, I debugged and output on resize and got some missing ranges (as long as one is invalid I can't use the bypass)

  • everything over 200%
    • at 240% width = 417
    • at 300% width = 333, height = 480
    • at 400% width = 250, height = 360
    • at 500% width = 200, height = 288
  • everything under 100%
    • at 90% height = 2859
    • at 80% height = 3200
    • at 67% height = 3840
    • at 50% height = 5120, width = 2880
    • at 30% height = 8533, width = 3333

I don't know exactly at what combination of system-scaling and zoom-level that the bypass fails (remember, it only matters if someone lies), but as you can see, as soon as you zoom down, the numbers scale up way too fast: so increasing the upper limit from 2560 seems out of the question. As for reducing the lower limit from 500, I was thinking of dropping to e.g. 400 anyway for mobile, but I don't gain much in my setup (just 240%)

I'm not sure what happens with smaller screens and/or system-scaling (usually people scale up due to eyesight etc). I think it improves, or maybe the valid-zoom range just shifts but doesn't grow

Eager to here your thoughts. Also this bit "I have ideas to cover the non-bypassed results"

TZP/js/screen.js

Lines 1778 to 1780 in b36f53b

// ToDo: HARDEN: due to zoom and limited ranges: often a css pseudo value can be invalid
// since this is excluded in resize events, we can afford to spend some perf to
// inject bisecting css rules

So, if prototype lies shows some BS is going on, we start bisecting with css rules: NFI on the perf, or if we should first bisect by if it is under or over our range (500-2560) and then go from there

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented May 1, 2021

@abrahamjuliot Hmmm, so Chameleon seems to remove navigator keys

It's spoofing as Chrome, so it removes oscpu and buildID
navKeys

const get_navKeys = () => new Promise(resolve => {
	// reset
	navKeys = {}
	// build
	try {
		let keys = Object.keys(Object.getOwnPropertyDescriptors(Navigator.prototype))
		// true/fake keys
		if (runSL) {keys.push("iamfake")} // simulate fake keys being added
		let trueKeys = keys
		let lastKeyIndex = keys.length
		let fakeKeys = []
		// orig minus constructor
		let allKeys = keys
		allKeys = allKeys.filter(x => !["constructor"].includes(x))
		navKeys["allKeys"] = allKeys
		if (isFF) {
			// constructor is always last
			lastKeyIndex = keys.indexOf("constructor")
			trueKeys = keys.slice(0, lastKeyIndex+1)
			fakeKeys = keys.slice(lastKeyIndex+1)
		} else if (isEngine == "blink") {
			// last key inconsistent
			let knownPoison = ["SharedWorker","Worker","buildID","getVRDisplays","activeVRDisplays","oscpu","iamfake"]
			trueKeys = keys.filter(x => !knownPoison.includes(x))
			fakeKeys = keys.filter(x => knownPoison.includes(x))
		}
		// remove constructor
		trueKeys = trueKeys.filter(x => !["constructor"].includes(x))
		// set
		navKeys["trueKeys"] = trueKeys
		navKeys["fakeKeys"] = fakeKeys
		// set brave
		isBraveMode = "unknown"
		if (check_navKey("brave")) {
			isBrave = true
			Promise.all([
				get_isBraveMode(),
			]).then(function(results){
				isBraveMode = results[0]
				return resolve()
			})
		} else {
			return resolve()
		}
	} catch(e) {
		console.error("get_navKeys", e.name, e.message)
		return resolve()
	}
})

Can we harden navKeys? I thought extensions could only add, not remove? Yikes!

I'm wondering if we could use a knownGood and add in missing items. e.g. in FF there's no pref or legit means to remove some of these items: such as those two

Two ways

  • if items are missing it's a lie and move on
  • if items are missing put them back in for a bypass
    • currently we do not sort (I thought this provided extra entropy)
    • is FF always the same order regardless of platform/build parameters?

@abrahamjuliot
Copy link
Collaborator

My initial thoughts on deleting properties were upside down. Technically, extensions can delete or rewrite any object and/or object key values. As far as I know, there is no way to obtain the original object apart from relying on an unprotected iframe context.

Adding missing nav keys is possible if the known version is used to determine the expected keys and their order. At a minimum, we could restore long-standing/static keys as a partial bypass, but the proper order would have to be manually constructed. I only looked at Tor Browser and FF Nightly, but the order of long-standing keys is consistent and new keys in FF Nightly do not change the order of the long-standing keys

Example: permissions, mimeTypes, plugins, doNotTrack should remain in that order.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented May 2, 2021

Thanks. I was going to check a few linux VMs and debug some output on android, but I too believe sorting is OK to use

That said: it's important to not lose any entropy that the extension brings: hence we record the lies and fake keys etc: just not in the main FP (for stable data analysis). navKeys is special, because I use it elsewhere, but not for absolutely everything. It's good we can weed out those after constructor in FF, and knownPoison in blink .... adding in items we know exist and don't check for (just error handling etc like the useragent parts) is probably moot .. might be easier to just note missing items and tag it as untrustworthy. And after all that, if an extension did mutate it, so be it: at least we don't lose the entropy

I wonder how else we can harden navKeys: iframe test, test each missing line item in try catches ... food for thought

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented May 2, 2021

edit: 4d185f3

I don't think there is any legitimate way (outside of special builds) to remove some items

we just add missing expected keys to the "fake" list: the engine already tells you if it was added or removed: e.g. oscpu and buildID are in both poisonKeys (blink, I renamed knownPoison) and expectedKeys (gecko)

Does expectedKeys look alright to you?

		if (isFF) {
			// constructor is always last
			// track added keys
			lastKeyIndex = keys.indexOf("constructor")
			trueKeys = keys.slice(0, lastKeyIndex+1)
			fakeKeys = keys.slice(lastKeyIndex+1)
			// track missing keys: onLine incl because this is served over HTTPS
			let expectedKeys = ["appCodeName","appName","buildID","hardwareConcurrency",
				"language","languages","mimeTypes","onLine","oscpu","platform","plugins",
				"product","productSub","userAgent","vendor","vendorSub"]
			let missingKeys = expectedKeys.filter(x => !keys.includes(x))
			trueKeys = trueKeys.concat(missingKeys)
			fakeKeys = fakeKeys.concat(missingKeys)
		}

//snip
		// set
		navKeys["trueKeys"] = trueKeys.sort()
		navKeys["fakeKeys"] = fakeKeys.sort()
		navKeys["allKeys"] = allKeys.sort()

@abrahamjuliot
Copy link
Collaborator

The expected keys look good.

If there is no pref removing these keys, perhaps we could add them:

appVersion (deprecated but still standing)
cookieEnabled
clipboard (FF63+)
doNotTrack
mediaCapabilities (FF63+)
permissions
storage
webdriver (FF60+)

@Thorin-Oakenpants
Copy link
Contributor Author

expected keys is FF only, but we could populate it twice, once for gecko, once for blink

I need to test more, but I think you're right: all pass in TB alpha FWIW

appVersion (deprecated but still standing) - not deprecated in FF, totally missed this
cookieEnabled - hmmm, yeah, I think so
clipboard (FF63+) - dom.event.clipboardevents.enabled but it does not remove it from navKeys (restart applied)
doNotTrack
mediaCapabilities (FF63+) - media.media-capabilities.enabled = false removes this from navKeys
permissions
storage
webdriver (FF60+)

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented May 30, 2021

@abrahamjuliot ... https://github.com/arkenfox/TZP/blob/master/js/prototypeLies.js#L509

could/should we add Math.constants like Math.PI and Math.E, or can extensions/scripts not modify those?

@abrahamjuliot
Copy link
Collaborator

Yes, a script can modify the constants. We could add them to the prototype test, but it's quicker to do an equality check and then bypass the lie.

@Thorin-Oakenpants
Copy link
Contributor Author

Sorry, I'm confused: What do you mean an equality check. The whole point is that we don't know the right answer, so we need to check if the "function" is tampered with and the result deemed untrustworthy (regardless of if it ultimately tampered with or not) - e.g. hardwareConcurrency has no means to verify via other means (well, not timely ones)

Or do you mean it's different because it's a constant and not a function. And to test is to check the result against known results? Since browsers will only be with 1 or 2 floating points of each other?

Forget cydec with it's random per execution .. what if it did it persistent noise per eTLD+1 per tab session

@abrahamjuliot
Copy link
Collaborator

Correct, these are just numbers and not functions. In the case of a Math constant, we can check if it yields the correct constant value, and as far as I know, Math constants are consistent across JS engines and not implementation-dependent (contrary to Math functions).

const mathE = 2.718281828459045
const liedMathE = Math.E !== mathE

if (liedMathE) {
  // use mathE bypass
}
const mathConstantsValid = (
  Math.E == 2.718281828459045 &&
  Math.LN2 == 0.6931471805599453 &&
  Math.LN10 == 2.302585092994046 &&
  Math.LOG2E == 1.4426950408889634 &&
  Math.LOG10E == 0.4342944819032518 &&
  Math.PI == 3.141592653589793 &&
  Math.SQRT1_2 == 0.7071067811865476 &&
  Math.SQRT2 == 1.4142135623730951
)

cydec noise per eTLD+1 per tab session

That would be harder to detect. There's also persistent noise cross-site per time seed and static profiles per domain.

@Thorin-Oakenpants
Copy link
Contributor Author

Thanks. That's what I thought. If cydec did a persistent cached noise (not random per execution) and covered e.g. cos(-1) and Math.E etc, well then I guess in a math test with a few items, at least something should end up in prototype lies (cydec lists 19 math items)

Math constants are consistent across JS engines

I hope so: at least they should be with 16 decimal places - which is what I was looking at in that other issue

Math.E == 2.718281828459045 - I think i've seen this at 2.7182818284590455 (or maybe that was the polyfill talking)

@Thorin-Oakenpants
Copy link
Contributor Author

PS: just updated cydec: release notes says he opened a github tracker issue: https://github.com/heilig-defense/anti-fp/issues

@abrahamjuliot
Copy link
Collaborator

fyi - It appears, both CB and Chameleon use Proxies now. I have a patch that detects Proxies in Firefox. It's very basic, so I'm working on making it more robust (if possible).

const getIncompatibleProxyTypeErrorLie = apiFunction => {
	const isFirefox = 3.141592653589793 ** -100 == 1.9275814160560185e-50 // this could be improved
	try {
		apiFunction.arguments
		apiFunction.caller
		return true
	} catch (error) {
		return (
			error.constructor.name != 'TypeError' ||
			(isFirefox && /incompatible\sProxy/.test(error.message)) ? true : false
		)
	}
}

getIncompatibleProxyTypeErrorLie(HTMLCanvasElement.prototype.toDataURL)

@Thorin-Oakenpants
Copy link
Contributor Author

Sounds exciting :) Does this have any bearing on tests, prototype lies etc? I'm a little lost on the implications here. Or is this yet one more piece of entropy that we can add and yet another pitfall of extensions?

@abrahamjuliot
Copy link
Collaborator

abrahamjuliot commented Jun 29, 2021

Yes, it's another extension pitfall, but only has bearing on the prototype tests. As of a recent CB update to Proxies, the prototype tampering is near undetected (only toBlob is exposed). However, we can detect proxies from the unique errors they leak. I've expanded the above method to 3 tests and should have a patch incoming today or this week.

Side note [edit after reading: I'm terrible at making sense of this one]: I'm also looking at providing a secondary reduced detail list that filters out proxy detection on Function.prototype.toString. If we take that function into account and it has a proxy on it (typically does when proxies are used), then every API function will fail the toString test since every function inherits the toString method, and that would only signal API tampering by inheritance vs direct tampering. A reduced detail list would keep Function.prototype.toString tampering separate.


3499782

  • added 3 Firefox Proxy tests
  • added tamperingList here to filter direct tampering

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jun 30, 2021

Nice 👍

I love how CB works with file:// scheme. Using CB with canvas, audio, domrect and textmetrics, I get 58 items in tamperingList, and those items are all in the lieList (which is at 171)

So I should use tamperingList.includes('example.property') when falling back to checking for lies: e.g. currently it says user agent etc is a lie (when it is not: cuz lieList contains all those)

Is that right?

edit: Ahhh, OK. I just tested the old version, and I see what you mean, with CB only showing 1 lie (toBlob). So I guess the logic here is

  • if it's in the lieList then it can't be trusted
  • the tamperingList picks up items using a proxy, but that doesn't mean it has to be in here to be used (e.g. lies without a proxy) - right? amiright?
  • so we really have to use the lieList (tamperingList is a always a subset of lieList, right?)

I think I'll add a global tampering list to the fingerprint section, not sure what to call it .. prototype tampering, proxy lies? I like "proxy lies", whaddaya think

This is why I ultimately prefer to use other methods: known results, multiple ways to measure, two-pass, the patented dom-shift-of-doom, feature detection, game theory + logic (e.g. touch support is false on android, WTF?)

and I see you replied below...

@abrahamjuliot
Copy link
Collaborator

That's right. tamperingList.includes('example.property') will yield properties that are directly tampered with. lieList will include indirect tampering or properties that are indirectly manipulated to protect toString leaks.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jun 30, 2021

I just edited my comment above. Not sure what you mean by "directly tampered with" vs "indirect tampering" (edit: they're both "tampering", i.e untrustworthy: not everyone will use a proxy like CB) - my understanding is if it's in the lieList it can't be trusted

@abrahamjuliot
Copy link
Collaborator

lieList - may trust some
tamperingList - can't trust any

I use lieList to get a broad pattern metric, and I use tamperingList to determine if the property is manipulated. Both pick up proxies, but lieList includes proxies on example.property.toString. If the proxy is done right, its just 1 proxy on Function.prototype.toString and since every property inherits this toString method, every property will fail the test. This failed test only signals toString is protected, but the property can still be free from tampering. If there is a proxy on Function.prototype.toString, virtually every property's toString method is then protected by it.

@Thorin-Oakenpants
Copy link
Contributor Author

OK, still confusing :) But yeah ... lieList "may trust some" has always been the issue (there's some logic to be had here, but IDK if it's worth it)

  • e.g. Chameleon lieList included hardwareConcurrency but didn't actually change it unless you were spoofing as android
  • e.g. here where CB now has a full lieList and tzp think's it's lying about useragent (as a final check after feature detection pinochios) etc when it isn't in fact lying (I might change that to be ignored for isFF: but that does nothing for other engines)

Anyway, I'm about to add the proxy lies so at least in testing I can easily see what is where.

Good work my padawan grasshopper .. have some 🍰

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jun 30, 2021

After known lies (math, dom-shift-of-doom etc) I'll probably include a global proxyLies (updated on every click and section rerun) and use that as a definite BS and then protoLies if no proxyLies yet, but with some logic? IDK about these protoLies - great for extension fingerprinting, but ultimately RFP + TB don't use extensions .. and that's what tzp is for (not that I'm not having fun)

TBH, there doesn't seem to be much that can't be detected as a known lie: textmetrics I have solutions for, audio we seem to know it's a limited set of results, fonts have numerous measuring methods, domrect and canvas are covered, feature detection gets almost all useragent (firefox), and css pseudo gets screen etc

TZP/js/globals.js

Lines 37 to 39 in 2ead93f

// fluid
let protoLies = [],
navKeys = {}

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jun 30, 2021

Cydec is 177 lies in both .. aren't we meant to be sorting (I could have sworn we added this to prototype)? the hashes differ. I mean, I can easily sort mine, but it's better if we bake it into the return since we use the same "library"

proto vs proxy


edit

return {

	return {
		lieList: Object.keys(props).sort(),
		lieDetail: props,
		lieCount: Object.keys(props).reduce((acc, key) => acc + props[key].length, 0),
		propsSearched,
		// filter out lies on Function.prototype.toString
		tamperingList: (props => {
// here, add sort()
			return Object.keys(props).filter(key => {
				const totalTamperingLies = getCountOfNonFunctionToStringLies(props[key])
				if (!totalTamperingLies) {
					return false
				}
				return true
			})
		})(props)
	}

edit: @abrahamjuliot ^^ in case you missed it

@Thorin-Oakenpants
Copy link
Contributor Author

And looks like Chameleon is 55 in both (in my chameleon config) .. so I'm starting to think proxyLies is always the way to go, which is kinda what you said, I need to drink on it

@abrahamjuliot
Copy link
Collaborator

ultimately RFP + TB don't use extensions

NoScript throws 2 prototype lies. I'm not sure how unique they are against similar extensions, but we can hash the detail pattern and estimate if NoScript is on in TB.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jul 10, 2021

Yeah, I saw TB returning two lies (wasn't sure when it started, and didn't realize it was NS) .. I just ignored it, because the lies weren't anything I use: currently I'm only using some navigator lies (hardware concurrency and a bunch of user agent stuff), I think that's all

not sure if estimating if NS is on in TB is worth it? Do you mean if the extension has been enabled/disabled? I don;t think anyone would bother to disable. Or do you mean if they whitelisted/blocked a page, in which case, I think the lies would still persist (?) and it wouldn't be a stable metric ... call me confused, what did you mean

Oooohhhh .. and while I have you here, do you have a Mac, I desperately need a font test - I've just split the mac list of 755 fonts up

  • 1 base+ RFP = does anything NOT GET picked up edit: outside of the base list (meaning my base list needs to expand)
  • 2 RFP off + styles = does anything GET picked up from styles (meaning I trimmed too much off)
  • edited to add NOTE: these will be auto listed for you to copypasta

If you're not sure what these entail, it's to reduce the OS font list in TZP to the bare minimum: either the main one or the base one depending on if the user has RFP on/off

@abrahamjuliot
Copy link
Collaborator

not sure if estimating if NS is on in TB is worth it? Do you mean if the extension has been enabled/disabled?

This estimation or unmasking is non-essential. In the case of NS, the lies persist in both whitelist/block settings.

Mac

I do. It's my trusty Mac VMware running in Windows on a MacBook Pro device (I deleted the real OS 🙃). I'll jump on this in #34.

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Aug 10, 2021

I think Cydec decided to not lie about Firefox any more. Latest update always seems to return FF90 (and thus a bunch of things aren't spoofed as a result) .. I think from memory I had 21 lies detected, 19 bypassed (hadn't finished TBH) .. now it's 5 lies, 4 bypassed

added to my todo list

  • I'm going to add bypassing the version lying if that's the only pinnochio in all the user agent nav stuff
  • cydec still alters eval.toString().length, so that's worth recording

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Oct 3, 2021

@abrahamjuliot FYI

some quick notes, haven't checked much more

  • also didn't pay too much attention to levels, I just went with level 3 to break shit
  • see 0d75e07 for additional suspect iframe entries,. i.e they come after Performance if isFF
JS Redirector
====
https://jshelter.org/levels/
	- DONE: hardwareConcurrency = fake
	- DONE: domrect: all blocked
		- ^ revamp needs to collect e.name, e.message and
	- DONE: canvas: all blocked
	- DONE: performance.now = caught "_global" as tampered with = end up with xxx perf
	- DONE: wasm: generates an error: bypassed as enabled, as only when wasm is enabled is an error thrown
	- DONE: suspect iframe properties: whitelist known good
		- ^ some quite unique items

changes
	- timezoneOffSet, getTime, Date.parse()
		- ^ on level 3
		- timeZoneOffSet = all the same, DST is missing
		- getTime, Date.parse() = "0,0,0,0"
	- timezone PoC: tzp hash
		- 796b61e3578ba3bda329e76ce271ada37c699966 = lies = UTC0
	- date and time is changed
		- ^ if time is not 00:00 then fake?
		- ^ if year is not 2019 then fake?
	- Intl.DateTimeFormat	is blocked
	- media devices = fake: none or bogus
	- sendBeacon is returned as true
		- ^ investigate
	- check sharedArrayBuffer
	- performance.now
		- ^ revisit: I use a bypass on it, but it's not an all-zero vs no-zero lie
		- original bypass was for Cudec which rounded in hundreds
		- note: we already record a global method perfnow lie
		- it does repeat duplicate values in a row which it shouldn't if it's not tampered with

One thing I noticed was that ac1c639 - pretty sure you use Date.split()

@Thorin-Oakenpants
Copy link
Contributor Author

catching performance.now tampering is intermittent. We used this for cydec, it's patchy for JS Redirector

	if (Math.trunc(performance.now() - performance.now()) !== 0) {
		// set vars, record stuff etc, dodgy MFers
		if (gRun) {gMethods.push("_global:performance.now:tampered")}
	}

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Oct 7, 2021

	- performance.now
		- ^ revisit: I use a bypass on it, but it's not an all-zero vs no-zero lie
		- original bypass was for Cydec which rounded in hundreds
		- note: we already record a global method perfnow lie
		- it does repeat duplicate values in a row which it shouldn't if it's not tampered with

OK, I tweaked that test and since it's not part of the global fingerprint, then I won't bother with any lies or bypasses. Instead I will just use some logic and add a tampering detected notation

test

  • with a setInterval of 13ms, I get 10 values (the first time is to set the start time)
  • I loop the 10 values and anything not ending in 00 (hundreds rounding) - is00 is set to false (default true)
  • as I loop, if RFP is off, I compare the diff to the previous value (9 results)
    • if a diff is outside a range, then I set isTamper = true (default false)
    • the diff range I used was < 5 and > 30. Had to use a safe range due to variations in FF vs chrome etc
    • EDIT: and I also allow one false positive in case of yank

logic

  • if isRFP && !is00 then isTamper = true (RFP but not rounded in hundreds)
  • if !isRFP && is00 then isTamper =true (no RFP but rounded in hundreds) <- redundant I think as isTamper is already false (I need coffee)
  • if !isPerf then isTamper = true (not sure I need this - this was the cydec check)

JShelter on level 3 rounds to hundreds, and on lower level(s?) looks like 10ms, IDK but I do catch it out, but don't record anything since it's not part of the global FP


Not sure what to do about hardening isPerf is which is part of get_isRFP(). isPerf is part of the global FP when it catches BShittery.

	isPerf = true // assume no fuckery
	if (Math.trunc(performance.now() - performance.now()) !== 0) {
		isPerf = false
		if (gRun) {gMethods.push("_global:performance.now:tampered")}
	}

I also used isPerf to not output performance stats if false, but that was because cydec was so far off (thousands of ms)

resetting global vars on every run (section/global) because they can change: so mindful not to add too much timing wasting

		setTimeout(function() {
			gt0 = performance.now()
			Promise.all([
				get_isRFP(),
				get_navKeys(),
				outputPrototypeLies(),
				get_isOS64(),
			]).then(function(results){
				output()
			})
		}, delay)

time for a ☕

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Oct 27, 2021

apparently cydec had a bug: it's back to normal it's old ways after the last update - yay, now I can keep detecting lies and creating bypasses - got a lovely one here for connection and connection.type (navigator) navigator connection object

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Nov 14, 2021

@abrahamjuliot do you know of any chrome extensions that fake keyboard? I want to test my keyboard BS detection :) - edit cdbfbee

@abrahamjuliot
Copy link
Collaborator

I'm not aware of any. I have not yetexplored keyboard focused extensions. There might be something out there.

@Thorin-Oakenpants
Copy link
Contributor Author

cdbfbee .. i wonder if anyone has ever had a commit covfefe

@Thorin-Oakenpants
Copy link
Contributor Author

Thorin-Oakenpants commented Jan 2, 2022

	searchLies(() => window, {
		target: [
			'innerWidth',
			'innerHeight',
			'outerWidth',
			'outerHeight',
		]
	})

@abrahamjuliot can we add this? to detect inner/outer window BS ?

@abrahamjuliot
Copy link
Collaborator

This should work, with the capital Window object.

searchLies(() => Window, {
    target: [
	    'innerWidth',
	    'innerHeight',
	    'outerWidth',
	    'outerHeight',
    ]
})

@Thorin-Oakenpants
Copy link
Contributor Author

doesn't add anything, at least with Cydec

@Thorin-Oakenpants
Copy link
Contributor Author

FWIW, cydec alters HTMLElementKeys

DIFFS: Nightly 97
-  real 258
- cydec 254 : note sort order is slightly different

cydec removed
- clientHeight
- clientWidth
- scrollHeight
- scrollWidth

my solution is going to be

  • add back in missing knownGood + sort
  • record the lie + bypass
  • check other extensions

not sure if any entropy will be lost by sorting

@Thorin-Oakenpants
Copy link
Contributor Author

just a cydec update, and I am by no means finished here with unTruths and ReTruths

I honestly believe that trying to using the web in cydec total mode must the world's shittiest experience with site breakage basically set to max

cydec

@Thorin-Oakenpants
Copy link
Contributor Author

@abrahamjuliot so I was checking engine props in edgeHTML (windows 10 VM)

  • chrome in window is also in edgeHTML : TZP uses this but hopefully never gets to it because math is solid, and we're going to replace enginemath anyway
  • didn't check your error length

Anyway, I fired up TZP itself out of interest and .. well, ugh. Returns 148 lies, and prereq takes like 20+ seconds

edgeHTML

after the patch, overall perf (the entire TZP) is more like 2 to 3 seconds, and I do get six false positive lies (domrect x 4, keyboard, plugins) which is not your problem, and I can code/fix those

@Thorin-Oakenpants
Copy link
Contributor Author

FYI: here are edgeHTML unique properties AFAICT. I get that prototypeLies calculates the engine on the fly (TZP has already set isEngine). I've also listed that it has a mix of blink-only, gecko-only, and webkit-only properties (and pairs of onlys is a total mess as well).

IDK if prototypeLies is meant to support edgeHTML, but my strategy here would be to test for edgeHTML only first, then blink/gecko/webkit

// edgeHTML only

@Thorin-Oakenpants
Copy link
Contributor Author

interesting read re prototype

@Thorin-Oakenpants
Copy link
Contributor Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Development

No branches or pull requests

2 participants