Is Handz - פשוט להשוות מחירים safe?

High risk

Handz scrapes DOM data on every website and collects yad2 listing data via injected scripts, forwarding results to its own servers.

The extension runs a content script on all pages that fetches a remote configuration from check.zololo.co.il and, for supported sites, extracts DOM and React fiber data using server-defined selectors and posts it back to a server-controlled endpoint. A background crawler periodically injects into open yad2.co.il tabs and fetches vehicle and property listing JSON from yad2 APIs, forwarding the collected data to api.handz.co.il. The extension also tracks yad2 browsing activity with a persistent user identifier sent to Mixpanel.

Handzv8.3.4Chrome Web Store
75Risk

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

Publishers can request a review.

Findings

SeverityHIGH
ClassUNWANTED
TypeUnexpected
CWECWE-506
SourceAI SANDBOX

Background worker scrapes yad2 listings via your open tab, sends to handz

With a yad2.co.il tab open, a periodic crawler (schedule/URLs from a remote server) finds open tabs, prevents discard, and injects a script fetching yad2's listing API.

One capture: 40 vehicle listings POSTed with price and model data.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You leave a yad2.co.il tab open in the browser.

No further interaction is needed; the crawler runs on a background timer.

The extension did this

The extension scrapes yad2 listing data through that tab and uploads it.

It injects a fetch of a yad2 listing API into the page, then POSTs the results to api.handz.co.il.

02EvidenceTEMPORAL PATTERN
When this fires
Every 1 minute

A setInterval loop checks every minute and, once the server-supplied crawlIntervalMilis has elapsed, injects the next crawl. The interval value is read from the remote config on each cycle and the loop restarts if it changes.

03EvidenceCODE COMPARE
The code that does this

The crawl loop: keep the tab alive, inject a fetch into the page, upload the results

What it actually does
The interval loop that injects the crawl and uploads (background.js)deobfuscated/background.js:8075-8122
const C = setInterval(async () => {
  const O = await le();                                  // current remote config
  const A = await chrome.tabs.query({ url: "https://*.yad2.co.il/*" });
  A.filter(t => t.autoDiscardable).forEach(t =>           // keep tabs from being unloaded
    chrome.tabs.update(t.id, { autoDiscardable: false }));
  const l = A.filter(t => !t.discarded);
  if (l.length && l[0].id) {
    chrome.scripting.executeScript({
      target: { tabId: l[0].id }, world: "MAIN",
      args: [O.crawlPages[w]],                            // server-supplied URL
      func: async m => (await (await fetch(m)).json()).data
    }, m => {
      const x = m[0].result;
      const d = [...x.solo, ...x.private, ...x.commercial, ...x.platinum];
      io({ entities: d, endpoint: "https://api.handz.co.il/v2.0/entities/vehicles/automated" });
    });
  }
}, 60000);
04EvidenceNETWORK CAPTURE
Captured request
POSThttps://api.handz.co.il/v2.0/entities/vehicles/automated
Captured during dynamic analysis: a single ~60 KB POST carrying 40 full yad2 vehicle-listing objects (price, agency name, manufacturer/model, image URLs, order ids) plus hash/token fields. Values shown are illustrative of the captured structure.
Headers
X-ext-namehandz
Content-Typeapplication/json
X-ext-version8.2.0
Body
{"listings":[{"order_id":"a-1234567","price":89000,"customer":{"agency_name":"A.S Motors"},"address":{"city":"Tel Aviv"},"search_fields":{"manufacturer":"Toyota","model":"Corolla"},"meta_data":{"images":["https://img.yad2.co.il/Pic/202506/01/abc123.jpg"]}}, /* ...39 more listing objects... */ ],"token":"<persistent_token>","f":"<sha512 fingerprint>","s":"...","n":"..."}
05EvidenceFIELD TABLE
Fields present in each uploaded listing record
FieldValueWhy it matters
Order / listing ID
a-1234567Identifier of the specific yad2 listing.
Price
89000Asking price of the listing.
Agency name
A.S MotorsThe seller / agency associated with the listing.
Manufacturer / model
Toyota / CorollaVehicle make and model from the listing's search fields.
Listing image URLs
https://img.yad2.co.il/Pic/202506/01/abc123.jpgLinks to the listing's photos on the yad2 CDN.
06EvidenceTHIRD PARTY LIST
Remote endpoints involved in the crawl
  • extconfig-v3.handz.co.il

    Returns the crawl configuration, the list of yad2 API URLs to crawl (crawlPages) and the interval (crawlIntervalMilis), letting the operator change crawl targets server-side.

  • api.handz.co.il

    Receives the scraped yad2 listing records via POST to /v2.0/entities/vehicles/automated.

07EvidenceARTIFACT
Check if you're affected

Surfaces the crawler in a managed fleet by hunting proxy/SIEM logs for the remote crawl-config fetch and the listing-upload endpoint, and lists which managed devices contacted them.

Requiresbashgrep, awkAccess to proxy/SIEM HTTP logs
detect_handz_crawl.sh · sh
#!/usr/bin/env bash
# Hunt web-proxy / SIEM access logs for the handz crawl config + upload endpoints.
# Usage: ./detect_handz_crawl.sh /path/to/proxy-access.log
set -euo pipefail
LOG="${1:?usage: detect_handz_crawl.sh <proxy-access-log>}"
echo '== crawl-config fetches (extconfig-v3.handz.co.il) =='
grep -E 'extconfig-v3\.handz\.co\.il' "$LOG" || echo '  none'
echo '== listing uploads (api.handz.co.il/v2.0/entities) =='
grep -E 'api\.handz\.co\.il/v2\.0/entities' "$LOG" || echo '  none'
echo '== distinct client IPs touching either host =='
grep -E 'handz\.co\.il' "$LOG" | awk '{print $1}' | sort -u
How to run it
  1. 1
    Export web-proxy or SIEM HTTP access logs covering the period of interest.
  2. 2
    Run ./detect_handz_crawl.sh <access-log>.
  3. 3
    Review the listed client IPs/devices and correlate to managed assets running the extension.
SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-359
SourceAI SANDBOX

Every yad2.co.il page you visit is sent to Mixpanel with a persistent ID

Browsing yad2.co.il, the background worker reports each page you open to Mixpanel: full URL plus a persistent ID reused across sessions, linking browsing to one profile.

Two visits left as POSTs to api.mixpanel.com/track with the ID.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You open a page on yad2.co.il.

For example a property or vehicle listing search such as /realestate/forsale.

The extension did this

The extension reports that page to a third-party analytics service.

It sends the full URL plus a persistent identifier to api.mixpanel.com/track.

02EvidenceFIELD TABLE
What each page-view report contains
FieldValueWhy it matters
Full page URL
https://www.yad2.co.il/realestate/forsaleThe complete address of the yad2 page you opened, including the path.
Host
www.yad2.co.ilThe yad2 hostname for the page.
Path and query
/realestate/forsale ?priceOnly=1The specific section and search filters you were viewing.
Persistent ID (distinct_id)
UID-uph880A stable identifier stored on the device and reused on every report, linking your visits over time.
Mixpanel project token
8449cd6ac45765b8fa14f5b0377033c3The analytics project the data is attributed to.
03EvidenceNETWORK CAPTURE
Captured request
POSThttps://api.mixpanel.com/track
Captured during dynamic analysis after navigating to two yad2 pages; the base64 data= body decodes to an 'ext_page_view' event carrying the full URL and the persistent distinct_id.
Headers
Content-Typeapplication/x-www-form-urlencoded
Body
data=W3siZXZlbnQiOiJleHRfcGFnZV92aWV3IiwicHJvcGVydGllcyI6eyJmdWxsVXJsIjoiaHR0cHM6Ly93d3cueWFkMi5jby5pbC9yZWFsZXN0YXRlL2ZvcnNhbGUiLCJob3N0Ijoid3d3LnlhZDIuY28uaWwiLCJwYXRoIjoiL3JlYWxlc3RhdGUvZm9yc2FsZSIsInF1ZXJ5IjoiIiwiaGFzaCI6IiIsImVudiI6InByb2R1Y3Rpb24iLCJkaXN0aW5jdF9pZCI6IlVJRC11cGg4ODAiLCJ0b2tlbiI6Ijg0NDljZDZhYzQ1NzY1YjhmYTE0ZjViMDM3NzAzM2MzIn19XQ==
04EvidenceOPAQUE REVEAL
Why you can't catch this in DevTools

The page-view report is base64-encoded as the data= form parameter, so the URL and identifier are not visible in plain text on the wire.

What's actually being sent
[
  {
    "event": "ext_page_view",
    "properties": {
      "fullUrl": "https://www.yad2.co.il/realestate/forsale",
      "host": "www.yad2.co.il",
      "path": "/realestate/forsale",
      "query": "",
      "hash": "",
      "env": "production",
      "distinct_id": "UID-uph880",
      "token": "8449cd6ac45765b8fa14f5b0377033c3"
    }
  }
]
05EvidenceCODE COMPARE
The code that does this

The shipped header comment says data goes only to Google Analytics; the code sends it to Mixpanel

What it actually does
The navigation listener and the actual destination (background.js)deobfuscated/background.js:68-145
chrome.tabs.onUpdated.addListener((tabId, w) => {
  if (w.url) {
    const f = new URL(w.url);
    if (f.host.indexOf("yad2.co.il") === -1) return;
    as("ext_page_view", { fullUrl: w.url, host: f.host, path: f.pathname, query: f.search, hash: f.hash });
  }
});

async function as(event, props) {
  const distinct_id = await _a() || await Ju();      // persistent ID
  const body = btoa(JSON.stringify([{ event, properties: { ...props, env: "production", distinct_id, token: "8449cd6ac45765b8fa14f5b0377033c3" } }]));
  await fetch("https://api.mixpanel.com/track", { method: "POST", body: `data=${body}` });
}
06EvidenceTHIRD PARTY LIST
Where the page-view data is sent
  • api.mixpanel.com

    Third-party product-analytics platform; receives the 'ext_page_view' events containing the full yad2 URL and the persistent distinct_id.

SeverityMEDIUM
ClassUNWANTED
TypeUnexpected
CWECWE-829
SourceAI SANDBOX

Remote config inserts ads into Yad2 listings

Browsing matching Yad2 pages, the config endpoint returns a popOverAds iframe for extension.zololo.co.il/embed/hf/.

The content script fetches it, validates the URL, and inserts the image, iframe, or video near listing prices.

01EvidenceCAUSE EFFECT
What actually happens
You did this

You browse a matching Yad2 listing page while the extension has a non-empty ad configuration.

The bundled page rules cover real-estate and vehicle listing pages under yad2.co.il.

The extension did this

The extension inserts a configured ad element into the listing page.

The inserted element can be an image, iframe, or video, depending on the remote configuration entry.

02EvidenceNETWORK CAPTURE
Captured request
GEThttps://extconfig-v3.handz.co.il/
Dynamic analysis captured the response returning popOverAds with an iframe source of https://extension.zololo.co.il/embed/hf/.
03EvidenceFIELD TABLE
Remote ad fields that control the page insertion
FieldValueWhy it matters
Ad element type
iframeThis decides what kind of element is added to the listing page you are viewing.
Ad source URL
https://extension.zololo.co.il/embed/hf/This is the external content that the inserted page element loads.
Insertion position
BEFOREThis controls whether the added element appears before, after, inside, or at the start of the matched listing content.
Listing page target
[data-testid="price"] inside a listing cardThe extension uses Yad2 listing page elements to decide where the configured ad appears.
04EvidenceCODE COMPARE
The code that does this

Config fetch, ad validation, message passing, and DOM insertion

What it actually does
Background fetch stores popOverAds from remote configbackground.js
async function Qs() {
  try {
    const t = await fetch("https://extconfig-v3.handz.co.il/");
    if (!t.ok) throw new Error(`HTTP error! status: ${t.status}`);
    const e = await t.json();
    await chrome.storage.local.set({
      config_sites_ids: {
        ids: e.sites.map(n => n.id),
        timestamp: Date.now()
      }
    });
    const r = e.sites.reduce((n, a) => (n[`config_site_${a.id}`] = a, n), {});
    return await chrome.storage.local.set(r), await chrome.storage.local.set({
      crawlPages: e.crawlPages,
      crawlIntervalMilis: e.crawlIntervalMilis,
      liSyncRatio: e.liSyncRatio,
      zolSyncRatio: e.zolSyncRatio,
      popOverAds: e.popOverAds ?? []
    }), e
  } catch (t) {
    throw t
  }
}
Content script asks for and validates popOverAdscontent-scripts/content.js
function oa(i) {
  if (typeof i != "string") return !1;
  try {
    return new URL(i).protocol === "https:"
  } catch {
    return !1
  }
}

function Ol(i) {
  if (!Array.isArray(i)) return [];
  const a = [];
  for (const r of i) {
    if (!r || typeof r != "object") continue;
    const o = r;
    if (!Pl.includes(o.type) || !oa(o.src)) continue;
    const f = {
      ...o
    };
    f.targetUrl !== void 0 && !oa(f.targetUrl) && delete f.targetUrl, a.push(f)
  }
  return a
}

async function Ll() {
  try {
    const i = await chrome.runtime.sendMessage(s0({
      type: Tl
    }));
    return Ol(i)
  } catch {
    return []
  }
}
Validated ads are passed into the inserted micro-appcontent-scripts/content.js
function fx(i, a, r, o, f, d) {
  const _ = {};
  return sx(), i.map(c => {
    if (c[r] && a[c[r]]) {
      const v = a[c[r]].querySelector(o);
      if (v) {
        const l = Math.random().toString(36).replace(/[^a-z]+/g, "").substring(0, 5),
          h = document.createElement(l),
          [w, g] = _r(void 0),
          m = ax({
            entityId: c[r],
            syncedServerData: w,
            popOverAds: d
          }),
          y = v.matches('[data-testid="price"]') && v.closest('[class*="item-data-content_priceSlot"]');
        switch (f) {
          case An.APPEND:
            v.append(h);
            break;
          case An.PREPEND:
            y ? v.before(h) : v.prepend(h);
            break;
          case An.AFTER:
            v.after(h);
            break;
          case An.BEFORE:
            v.before(h);
            break;
          default:
            v.append(h)
        }
        Ea.push(jl(() => m, h)), _[c[r]] = {
          microApp: m,
          setSyncedServerData: g,
          randonContainerElement: h
        }
      }
    }
  }), _
}
05EvidenceTHIRD PARTY LIST
Hosts involved in the observed path
  • extconfig-v3.handz.co.il

    Remote configuration endpoint that returned the popOverAds descriptor.

  • extension.zololo.co.il

    Iframe source URL present in the observed popOverAds entry.

Data recipients

check.zololo.co.ilapi.handz.co.ilextconfig-v3.handz.co.ilapi.mixpanel.com
Updated 17 September 2026gjgdhpnmmpcpgjaepjehbijfgdblflbi