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

Detect lies in Workers #142

Open
7 of 9 tasks
abrahamjuliot opened this issue Jul 27, 2021 · 5 comments
Open
7 of 9 tasks

Detect lies in Workers #142

abrahamjuliot opened this issue Jul 27, 2021 · 5 comments
Labels

Comments

@abrahamjuliot
Copy link
Owner

abrahamjuliot commented Jul 27, 2021

Workers can be corrupt just like frames. Below is an unfinished concept showing the worker scope can be poisoned through the window scope.

Defensive mesures:

  • detect prototype lies
  • find inconsistent userAgentData
  • flag inconsistent platform/ua system
    • match ua system to system fonts
    • match platform to ua system
    • match Windows platformVersion to reported user agent version
  • catch language lies [comment]
  • canvas pixel trap
  • css features firewall
  • text metrics system

Worker Poison

This does not work for blob URLs that are revoked. This can also support direct path scripts if we check the script location.

// 
Worker = class extends Worker {
  constructor(scriptURL) {
    console.log(scriptURL)
    const fn = scriptURL => {
      Object.defineProperty(Object.getPrototypeOf(navigator), 'platform', {
        get: function platform() {
          return 'fake'
        }
      })
      console.log(self.navigator)
      return importScripts(scriptURL)
    }
    const response = `(${''+fn})('${''+scriptURL}')`
    const blobURL = URL.createObjectURL(new Blob(
      [response], {
        type: 'application/javascript'
      }
    ))
    super(blobURL)
    URL.revokeObjectURL(blobURL)
  }
}
newWorker = fn => {
  const response = `(${''+fn})()`
  try {
    const blobURL = URL.createObjectURL(new Blob(
      [response], {
        type: 'application/javascript'
      }
    ))

    const workerInstance = new Worker(blobURL)
    //URL.revokeObjectURL(blobURL) // does not work if this is revoked
    return workerInstance
  } catch (error) {
    return console.error(error)
  }
}
start = performance.now()
newWorker(async () => {
  const {
    platform
  } = self.navigator || {}

  postMessage({
    platform
  })
  close()
}).addEventListener('message', async event => {
  const {
    data
  } = event
  console.log(performance.now() - start)
  return console.log(data)
})

// returns 
{
  platform: "fake"
}
@Niek
Copy link
Contributor

Niek commented Jul 29, 2021

The implemented language lies introduce an issue. To reproduce:

  • Set the system local to e.g. English US
  • Set the Chrome language to Dutch
  • Now CreepJS will detect a lie because the primary locale and worker locale seem to mismatch (worker takes the system locale it seems).

@abrahamjuliot
Copy link
Owner Author

abrahamjuliot commented Jul 30, 2021

Thank you for catching that. I'm torn on whether or not this locale mismatch should be counted as a lie. I can see it being used for accessibility and development in which case the mismatch should be given respect. I think what I can do is remove the lie, and then remove the worker's engine locale from the stable fingerprint. If there is any switching the engine language monkey business, the score will slowly fall, but the fingerprint should remain stable with the system locale (if that is the only poison pill in workers).

@abrahamjuliot
Copy link
Owner Author

The change is now live on the dev site. It should go live this weekend or late Aug - maybe late Aug since this and recent updates will renew most fingerprints.

@abrahamjuliot
Copy link
Owner Author

abrahamjuliot commented Jul 30, 2021

@Niek EDIT: nevermind... they're both actually consistent with the engine.

Another interesting thing in workers is toLocaleString does not mirror the locale override but resolvedOptions does.

For example, this renders the system locale in currencyDisplay:

(1).toLocaleString(undefined, {
    style: 'currency',
    currency: 'USD',
    currencyDisplay: 'name',
    minimumFractionDigits: 0,
    maximumFractionDigits: 0
})

And, this gets the engine locale:

Intl.DateTimeFormat().resolvedOptions().locale

@Niek
Copy link
Contributor

Niek commented Jul 30, 2021

@abrahamjuliot looks like a Chrome bug to me. It's also unclear to me where the system locale it gathered from, in Linux setting LC_ALL and LANGUAGE doesn't affect the locale value - I assume it's getting it from some other place. I thought it was hardcoded to en-US first, but that doesn't appear to be the case.

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

No branches or pull requests

2 participants