Skip to content

About

Static analysis of Instant Data Scraper v1.4.1 for further forensic analysis

Resources

Stars

1 star

Watchers

0 watching

Forks

Repository files navigation

instant-data-scraper-exposed

Static analysis of Instant Data Scraper v1.4.1 for further forensic analysis of its covert browser surveillance hidden in background.js. Beautified source + full findings report.

image

Instant Data Scraper v1.4.1

Extension ID: ofaokhiedipichpaobibbnahnkdoiiah
Version: 1.4.1.0
Primary file: src/background.js (42,882 bytes)
Date: April 2026

Overview

Instant Data Scraper was acquired by Flavr Technology LP from its original developer Web Robots. After the acquisition, src/background.js was rewritten to include a covert browser system that runs silently alongside the legitimate scraping code. The scraping functionality in src/onload.js was left completely untouched. Keeping the scraping code clean gives users no reason to look deeper.

This analysis is based on static analysis of the distributed .crx package at version 1.4.1.

The core findings are:

  • A hardcoded C2 endpoint at api.idscraper.com collecting full browsing history on every page navigation
  • AES-256-CBC encryption of all exfiltrated data using a static hardcoded key, meaning every payload ever sent by any user can be decrypted
  • A custom webpack-style module loader and fake WebAssembly opcode namespace used to evade automated security scanners
  • A remotely configurable collection system that can be updated, redirected, or shut down without any Chrome Web Store review
  • A persistent unique device fingerprint tied to every payload
  • Google Analytics instrumentation tracking install state, opt-in status, and daily activity per user
  • A columnar matrix transposition layer applied before encryption specifically to defeat DLP and proxy content inspection

The systems acts like a "surveillance" system and does not only activate after the user clicked "Agree" on the first-install options page. If they did, the extension would not have been sending their full browsing history to api.idscraper.com on every page load since that moment before viewing their privacy policy. Flawed by design.

If you want to get the source code for yourself, follow the below instructions:

  1. Copy the ID from the Google Chrome Extension link https://chromewebstore.google.com/detail/instant-data-scraper/ofaokhiedipichpaobibbnahnkdoiiah
  2. Input the ID as so and paste the following link https://clients2.google.com/service/update2/crx?response=redirect&prodversion=120.0.0.0&acceptformat=crx3&x=id%3Dofaokhiedipichpaobibbnahnkdoiiah%26uc in your browser to download the CRX file
  3. Go to https://crxextractor.com/ and drag/upload the CRX file to get the source code of the extension without downloading the extension itself
  4. Extract the ZIP and view the folder in an IDE of your choice

OR:

Use my tool to just paste the url of the extension on the Chrome Web Store and have automatically open your IDE (python crxreader.py --ide "notepad++.exe") with the project in place: https://github.com/Aureum01/crxreader/tree/main

Use https://deobfuscate.relative.im/ to de-obfuscate the JS in your IDE

I wanted to use wireshark but for the sake of this demostration, I went with MITM to reduce exposure of my network (even if i could clean it as I believe this is a time-sensitive project to a degree).

Behavior

(I have Parrot OS, but its still Debian based, no different than Ubuntu)

Make sure you have deIDS.py (located in project in the Notes folder)

pip install cryptography mitmproxy scapy --break-system-packages
python3 deIDS.py --mitm flows.dump
wget https://dl.google.com/linux/direct/google-chrome-stable_current_amd64.deb
sudo apt install ./google-chrome-stable_current_amd64.deb -y
google-chrome --proxy-server="http://127.0.0.1:8080" --no-sandbox &

Then in Chrome go to Settings → System → Open your computer’s proxy settings and set:

HTTP proxy: 127.0.0.1 Port: 8080

Tick “Use this proxy server for all protocols”

mitmproxy --version
mitmproxy --listen-port 8080

Then in Chrome go to http://mitm.it

Wait about 1-10 minutes depending

Refresh http://mitm.it

It should then display the certs

click “Get mitmproxy-ca-cert.pem” to download it.

Open another tab in your terminal and cd into your Downloads before running:

sudo mv mitmproxy-ca-cert.pem /usr/local/share/ca-certificates/mitmproxy.crt
sudo update-ca-certificates

Make sure to enter the following command to use certutil, if you are have a “command not found” issue:

sudo apt install libnss3-tools -y

Then:

find / -name "mitmproxy-ca-cert.pem" 2>/dev/null

Change path as necessary for the following command if not found:

certutil -d sql:$HOME/.pki/nssdb -A -t "CT,," -n mitmproxy -i ~/PATH/mitmproxy-ca-cert.pem

Like:

certutil -d sql:$HOME/.pki/nssdb -A -t "CT,," -n mitmproxy -i /home/user/.mitmproxy/mitmproxy-ca-cert.pem

Now close Chrome, and restart it. You should be good.

Back to terminal, make a new tab and then:

sudo wireshark

Start a capture on your network interface (usually eth0 / enp0s3).

(One thing to add is that I need to filter it out, so after checking what the mitm has, I’ll note any sus post requests, note any IPs I have found that are commonly rotated, and so forth before adding it to the wireshark filter to save. So my vm doesn’t crash lol)

Once all of that is running, then install Instant Data Scraper. That way you capture everything from first load including the initial /history/setup call.

Go the process and test Instant Data Scraper (IDS) with the www.worldpopulationreview.com/country-rankings/crime-rate-by-country

Then after clicking on the extension and letting it do its business.

Filter mitmproxy to only show api.idscraper.com: Press f in mitmproxy to open the filter, then type:

~d api.idscraper.com

Press Enter. That will show only traffic to that domain.

Then press ‘w’ to save the flow as a file to whatever path you want like:

/home/user/ids_capture.dump

You can use the following script in the repo to decrypt what was captured:

python3 deIDS.py --mitm /home/user/ids_capture.dump /home/user/ids_capture2.dump /home/user/ids_td_bank.dump

The extension’s default installation state is “On all sites”. In this mode, chrome.webNavigation.onCommitted fires on every page navigation across every tab, silently assembling and POSTing a payload to api.idscraper.com/table without any user interaction. The user never needs to open or click the extension for this to happen — simply browsing with the extension installed and opted in is sufficient.

When the permission is changed to “When you click the extension”, Chrome restricts the content script from running automatically. The noDataForWebsite event that triggers the POST pipeline never fires for that tab, so no data is transmitted. However, this only applies to tabs loaded after the permission change. Any tab already open before the change retains the extension’s access until manually refreshed. The _SpeakerGroupSharp module iterates all currently open tabs on startup and re-registers them — meaning a browser restart with open tabs can reactivate collection even after a permission change.

The question is what do we need to do to find out that server or any server just based on its context?

The MainLocator function is the URL resolver:

MainLocator = () => t.hasOwnProperty("u") ? t["u"] : "api.idscraper.com"

From the background.js file, "u" is the remote override field. The api.idscraper.com endpoint is only the default. Given the logic, the server can push a different URL to all 1 million users silently via the x-session-id response header on any /table response. Feels like a C2. That base64-encoded payload goes through setSettings and updates t["u"] from that point, so every POST goes to a completely different server with no extension update, no Web Store review, and no user notification.

The sus URL (api.idscraper.com) is the default but not fixed. The MainLocator function checks for a u field in the remote config pushed via /history/setup. If present, all subsequent POSTs go to that override URL instead of a different server entirely.

When set to “On all sites” (default), the resolveWithNewModule function in GrandRight fires on every navigation via chrome.webNavigation.onCommitted with no interaction needed, just a page load.

When set to “When you click”, Chrome restricts the content script injection, so the noDataForWebsite event never fires and no POST goes out.

However, any tab already loaded before you changed the permission continues to have the extension active until refreshed. And the _SpeakerGroupSharp module from the background.js runs on startup to scan all currently open tabs.

For Wireshark it should show the same api.idscraper.com traffic unless the server has already pushed a u override to your session.

So I’ll have to check all my mitmproxy captures, by looking at the response headers on the /table responses for any x-session-id values and what the base64 decodes to.

Took a little bit to debug but essentially:

from mitmproxy.io import FlowReader
from mitmproxy.http import HTTPFlow
import json, base64

for dump in ["/home/user/ids_capture.dump", "/home/user/ids_capture2.dump", "/home/user/ids_td_bank.dump"]:
    with open(dump, "rb") as f:
        for flow in FlowReader(f).stream():
            if isinstance(flow, HTTPFlow):
                if "/history/setup" in flow.request.path:
                    print(f"\n=== {dump} ===")
                    print(f"REQUEST: {flow.request.get_text()}")
                    print(f"RESPONSE: {flow.response.get_text()}")
=== /home/user/ids_capture.dump ===
REQUEST: {"sid":"a8db79741","hash":"d264e18e9cfbf7dabf3bd1e4f883de584f9487b95f329d18a0f91c60e15441ba"}
RESPONSE: {"64":"AAEAAAAAAAURCwISMAAAAAAAAAAAAAAAAAAAAAAAAAA=","65":"piks","66":"e30=","67":"urlparams","68":"=","69":"urlparams_m","70":"sitewhitelist","71":"blacklist","72":"object","73":"paramwhitelist","74":"paths"}

=== /home/user/ids_capture2.dump ===
REQUEST: {"sid":"a8db79741","hash":"d264e18e9cfbf7dabf3bd1e4f883de584f9487b95f329d18a0f91c60e15441ba"}
RESPONSE: {"64":"AAEAAAAAAAURCwISMAAAAAAAAAAAAAAAAAAAAAAAAAA=","65":"piks","66":"e30=","67":"urlparams","68":"=","69":"urlparams_m","70":"sitewhitelist","71":"blacklist","72":"object","73":"paramwhitelist","74":"paths"}

=== /home/user/ids_td_bank.dump ===
REQUEST: {"sid":"a8db79741","hash":"d264e18e9cfbf7dabf3bd1e4f883de584f9487b95f329d18a0f91c60e15441ba"}
RESPONSE: {"64":"AAEAAAAAAAURCwISMAAAAAAAAAAAAAAAAAAAAAAAAAA=","65":"piks","66":"e30=","67":"urlparams","68":"=","69":"urlparams_m","70":"sitewhitelist","71":"blacklist","72":"object","73":"paramwhitelist","74":"paths"}

More on what happened in notes, but essentially this isn't an ordinary analytics section at all. Just because it looks legal, doesn't mean they are ethical. As soon as my/your data is being taken without you knowing...that's a problem.

Session 2

I'll be using Juice Shop is the best fit for session 2 because it's local and easier to identify all the connections.

sudo docker run -d -p 3000:3000 --security-opt apparmor=unconfined bkimminich/juice-shop  

Then visit http://localhost:3000 in Chrome with the extension active and mitmproxy running.

Then browse those Juice Shop pages and watch mitmproxy specifically for:

POST google-analytics.com /mp/collect?measurement_id=G-G7PJ1V076F  

Every page navigation should trigger a GA hit. What you're documenting is:

  • optIn status being sent on every single hit
  • client_id persisting across all hits (same UUID on every session)
  • The event name on each hit
  • Does GA (Google Analytics) fire before or after /history/setup

When you feel you have explored the page and tried any tricks. Save mitm again, like so, /home/user/session2_1.dump.

Install the following for your script.

pip install pycryptodome mitmproxy scapy --break-system-packages  

Then run gaIDS.py.

python3 gaIDS.py --mitm /home/user/session2_1.dump

Conclusion

What the data tells us is that every time Chrome opens, Instant Data Scraper contacts Flavr's server before you visit a single page. You haven't clicked anything. You haven't searched for anything. Just because the fine print looks legal, doesn't mean it's going to be ethical.

Once you start browsing, the extension sends your permanent ID and the full address of the page you're on to Flavr's Google Analytics account. It sends an encrypted package of up to 14 pieces of information about your navigation to Flavr's server. And it sends the name of the website you're on in plain text, which is readable by anyone watching the connection before it's even decrypted.

There's a detail in the data that sounds small but isn't. Every beacon the extension sends claims you spent exactly 100 milliseconds on every page. Always 100. Never 99, never 150, never different based on how long you actually stayed. Real analytics measures real time. This doesn't. From this, we can deduce that the real purpose of sending it isn't to measure your engagement. It's to register your ID and your location with Flavr's server on every single page load.

The extension also has a hidden feature that was never activated against the test device. There's a field called u in the server's configuration response. If Flavr ever puts a web address in that field, the extension silently starts sending all your browsing data to that new address instead without any notice to the end user. It didn't show in these tests, but it is possible based on what has been collect from the user to create a behavioral profile.

LayerX Security studied a group of Chrome extensions that went through similar ownership transfers and found that "malicious functionality was introduced immediately after ownership transfers in the Chrome Web Store, a pattern observed across several cases. The C2 domains used to deliver remote instructions were registered shortly before the compromised updates were published". Instant Data Scraper (IDS) changed hands from Web Robots to Flavr Technology LP without any public announcement that would count as noticeable even for existing users. The server infrastructure such as the configuration endpoint, the Google Analytics channel, and the silent redirect mechanism appeared after that transfer.

A nearly identical case already played out with a Chrome extension called Save Image as Type. Google didn't remove the Chrome version until March 2026, months after it had been running malicious code on over a million browsers. A lean extension, new owner, and spyware type code added silently.

The redirect capability built into IDS is similar to a piece of malware called GlassWorm, which was found in a Chrome extension that "pulls monitored site rules from the server and ships with specific financial sites pre-configured as targets. The C2 can also supply redirect rules that force active tabs to attacker-controlled URLs." The logic of IDS for the u field works the same way, where one instruction from Flavr's server and every payload from every one of the million users goes somewhere else entirely. Those who use IDS are OSINT researchers, journalists, security professionals, and investigators whose browsing history makes them a more valuable target than the average consumer extension user.

Related to :

https://github.com/AabyssZG/Chrome-RCE-Poc/blob/main/README.md

https://chromium.googlesource.com/v8/v8/+/bc3b9332accf7c3b0263ccbdd9fdaed411343a26/test/fuzzer/wasm-compile.cc

About

Static analysis of Instant Data Scraper v1.4.1 for further forensic analysis

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages