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

UXSS inside Vimium vomnibar feature #3832

Closed
barakolo opened this issue Jun 7, 2021 · 7 comments
Closed

UXSS inside Vimium vomnibar feature #3832

barakolo opened this issue Jun 7, 2021 · 7 comments

Comments

@barakolo
Copy link

barakolo commented Jun 7, 2021

Describe the bug
Attacker can use vimium omnibar feature to execute arbitrary JS code over any site.
This can be caused by combination of low entropy PRNG used as vimium "secretToken" as well as invalid messages sent between different content-scripts in page.

Full Technical Description:
https://docs.google.com/document/d/1BdsJ3IRV05dodVfgWHLDRgM1FTkgswiMPxGHl_ufNho/edit

Bug Video:
https://youtu.be/bklyYCtECF0

To Reproduce
Steps to reproduce the behaviour:

  1. Checkout this GitHub:
    https://github.com/barakolo/VimiumUXSS
  2. Find out current "VimiumSecret" (This can also be extracted by simple brute-force because of bad PRNG as I explained in the sharable docs):
    To do that -
    ** open Chrome DevTools, and change the Javascript context to “Vimium extension” (Under the Console tab -> click on the arrow sign below it).
    b. Now Run this line of code (we are now running it in zotero content-scripts context):
    chrome.storage.local.get('vimiumSecret', e=>console.log(e['vimiumSecret']))
    c. Save this code aside (this is a small number with a max 10-digits which is being used across all sites).
  3. Change Line 27 inside abc.js to the current "vimiumSecret" extracted.
  4. Now, open SimpleHTTPServer over this github repo:
    python2 -m SimpleHTTPServer 8080
  5. Surf to this site, and click enter once or two.
  6. Observe that alert() is executed over "example.com" now.

Browser and Vimium version
Vimium Version: 1.66 (March 2, 2020)
Chrome version: 91.0.4472.77
OS version: Windows 10 Home 19042.985

@barakolo
Copy link
Author

barakolo commented Jun 7, 2021

In short - vulnerable lines are:
*The first vulnerable line that generates small secret is here:

chrome.storage.local.set({vimiumSecret: Math.floor(Math.random() * 2000000000)});

*The second vulnerable line is actually the one that gets executeScript message from the background-script and executing this JS in the incorrect frame:

  • The background scripts triggers this vulnerable-line inside of the content-scripts:
    executeScript(request) { return DomUtils.injectUserScript(request.script); }

    This line Doesn't verify request.sender (there might be in there frameId we can use to validate and such).
    But probably it's best just to keep unique UUID in each content-script to use when sending/getting repsonses to avoid confusions.

Btw, the start inside of the background context is where it calls the openurlInTab (for javascript: scheme)
https://github.com/philc/vimium/blob/master/background_scripts/main.js#L123

@barakolo
Copy link
Author

@philc please help mitigate this, thanks

@gdh1995
Copy link
Contributor

gdh1995 commented Jun 25, 2021

The secret is not exposed to website pages, and this message listener only exists after <script> finishes and until "a real secret matches", which means up to several hundreds milliseconds. So, I doubt whether it's possible or not to find a correct number in such a short time.

@barakolo
Copy link
Author

Hi, I can place as many event listeners I want as one can embed in a iframe the vomnibar page on purpose for as long as he wants and then retry to find the corrent number till its there. (Also in background without user notice at all)

Aka do this:
d = document.createElement('iframe');
d.src='chrome-extension://dbepggeogbaibhgnhhndojpepiihcmeb/pages/vomnibar.html';
document.body.appendChild(d);

@barakolo
Copy link
Author

Also the secret stays the same as long as the browser is on so it gives an attacker alot of time and oportunity to find this secret (as users tend to keep their browsers open, and even if no internet con is available it would work as the extension is locally hosted and attacker just easily broteforce this one)

@gdh1995
Copy link
Contributor

gdh1995 commented Jun 25, 2021

Aka do this:

Oh yes, you're right.

@barakolo
Copy link
Author

Hi I would love to work on it with you guys, It should not be too much work, just one line change @gdh1995
Instead of Math.random() usage in secretToken init, we can use this code:

var array = new Uint8Array(32); // 32-byte random token.
let rnd_arr = window.crypto.getRandomValues(array); // crypto random according to system choice.
let secretToken =  rnd_arr.reduce(function f(a,b) {return a.toString(16) + b.toString(16);});

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

No branches or pull requests

2 participants