Is Social Lead Extractor — Email & Phone Scraper safe?
Social Lead Extractor uploads scraped Instagram profiles, including emails and phones, to the vendor's own server.
While scraping Instagram followers, likers or hashtags, the extension sends each profile's username, bio, email, phone, address and follower counts in batches to the vendor's backend. It can also send profile links to that server to find emails and look profiles up in a shared lead database. The data is public profile information about third parties, uploaded once a scrape is started.
Who publishes itEchoWebBot Studio - 9 other listings from the same operator, none carrying a finding
EchoWebBot Studio - 9 other listings from the same operator, none carrying a finding
What this publisher told the store about itself, and the other listings that told it the same thing.
Same store account
9 other listings published from this account, 4k+ users between them, none of them carrying a finding.
Shared hosts - 1 hostname
Hostnames hardcoded in this extension that few other listings call. That can mean one operator behind both, and it can equally mean a small shared vendor, so it is context rather than a conclusion. Hosts that many listings call are left out: they are services, not connections.
AI-generated. Findings may contain errors. Those marked Verified have been manually reviewed.
Publishers can request a review.
Findings
Scraped Instagram profiles are uploaded in bulk to the vendor's server
Code analysis shows each scraped profile (bio, email, phone, address) is POSTed to igleadextractor.echobot.dev, which can also return stored profiles to lookups.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-359
- Source
- Static analysis
You start a scrape task from the extension dashboard.
Each scraped batch of profiles is sent to the vendor's PocketBase server.
The upload runs automatically after every batch.
Bulk upsert of scraped profiles
batchUpsertToServer
chunks/dashboard-Bfl6suez.jsasync function s(n) { if (!n || n.length === 0) return; let r = n.filter(e => e?.ig_id).map(e => ({ ig_id: e.ig_id, username: e.username || ``, full_name: e.full_name || ``, bio: e.bio || ``, email: e.email || ``, phone: e.phone || ``, phone_country_code: e.phone_country_code || ``, city: e.city || ``, address: e.address || ``, external_url: e.external_url || ``, followers: e.followers || 0, following: e.following || 0, media_count: e.media_count || 0, is_private: e.is_private || !1, is_verified: e.is_verified || !1, is_business: e.is_business || !1, category: e.category || ``, remark: e.remark || `` })); if (r.length !== 0) try { let n = {}; return n.users = r, await t.send(`/api/users-batch-upsert`, { method: `POST`, body: n }) } catch (t) { console.error(`batchUpsertToServer failed:`, t) } }Upload is invoked after every scraped batch
async function Re(e, t) { if (t.ids.length > 0 && (await S.addBatch(e, t.ids, { emailDelta: t.emailDelta || 0, phoneDelta: t.phoneDelta || 0 }), T.batchUpsertToServer(t.profiles).catch(e => console.error(`batchUpsertToServer failed:`, e)), t.ids = [], t.profiles = [], t.emailDelta = 0, t.phoneDelta = 0, !J.value)) try { let e = await S.getTotalEmailCount(); ke = Math.max(0, e - V.value) } catch {} }- Username and namejane_doe_studio / Jane Doe
Public handle and display name of each scraped account.
- Bio textWedding photographer, DM for rates. jane@janedoe.studio
The profile biography, which often holds contact details.
- Email addressjane@janedoe.studio
Contact email found on the profile.
- Phone number+44 7700 900123 (44)
Phone number and country code from the profile.
- City and addressLeeds, 12 Park Row
Business location fields.
- Linked websitehttps://janedoe.studio
The external URL on the profile.
- Audience counts12840 / 531 / 296
Followers, following and post counts.
- Account flags and categoryis_business: true; Photographer
Private, verified and business flags plus category.
- igleadextractor.echobot.dev
Receives profile batches, task batches, and serves stored profiles via /api/users-lookup.
Lists the lines in the dashboard bundle that post or look up profile records on the vendor server.
- Node.js 18+
// Static check: list the vendor endpoints the dashboard bundle posts profile data to.const fs = require('fs');const f = process.argv[2];const src = fs.readFileSync(f, 'utf8');for (const p of ['/api/users-batch-upsert', '/api/users-upsert', '/api/users-lookup']) { const lines = src.split('\n').map((l, i) => [i + 1, l]).filter(([, l]) => l.includes(p)); console.log(p, lines.map(([n]) => n).join(','));}console.log('base URL present:', src.includes('igleadextractor.echobot.dev') || 'see __uno chunk b_()');- 1Unpack the CRX.
- 2Run: node find_profile_upload.js chunks/dashboard-Bfl6suez.js.
- 3Open the listed lines.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Where it sends data
Destinations our analysis observed Social Lead Extractor — Email & Phone Scraper contacting. Sending data somewhere is not a finding on its own - an extension that syncs your settings has to talk to its own server - but it is where your data can go, and who else it goes to.
- igleadextractor.echobot.dev
Social Lead Extractor — Email & Phone Scraper sends data to igleadextractor.echobot.dev. One other extension we have analysed sends data here.