Is Unfollower Tracker for Top Photo App safe?

Medium risk

Unfollower Tracker for IG receives server-controlled extraction patterns and can open arbitrary URLs on command from besocial.top.

Each time the popup opens, the extension contacts besocial.top to check its license token. The server response can supply updated regex-like delimiters that the extension stores and uses to extract the Instagram CSRF token and username from inline page scripts. The same response can also supply an open_link URL, which the extension immediately opens in a background tab with no validation.

BeSocialToolsv1.2.8Chrome Web Store
45Risk
Who publishes it

BeSocialTools - 4 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.

Publisher
BeSocialTools

Same store account

4 other listings published from this account, 11k+ 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.

hypercavs.com
Also called by 8 other listings

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

Publishers can request a review.

Findings

Server-Supplied Rules Extract Instagram Session Fields

MediumunwantedCWE-829Dynamic sandbox

Checking Instagram account status asks besocial.top for tokenizer rules and saves the returned CSRF-token and username delimiters.

What actually happens

You did this

You open the extension while it checks an Instagram page.

The extension did this

The extension asks besocial.top for tokenizer rules, stores the returned delimiters, and uses them to read Instagram session fields from page scripts.

Captured request

GEThttps://besocial.top/unfollower-tracker/check-token.php?token=<redacted>&version=1.2.1&chn=chrome-extension

Confirmed response JSON contained csrf_tokenizer and user_tokenizer objects with start and end delimiters.

Fields controlled or extracted by the tokenizer flow

  • CSRF-token delimiters
    csrf_tokenizer.start=',{"csrf_token":"' and csrf_tokenizer.end='"},'

    These rules decide where the extension starts and stops reading the Instagram CSRF token from page script text.

  • Username delimiters
    user_tokenizer.start='"username":"' and user_tokenizer.end='","'

    These rules decide where the extension starts and stops reading the Instagram username from page script text.

  • Instagram CSRF token
    HcT6rNp9vQ2mX4sLa8Kb3YdF0wJe1ZuG (illustrative)

    This credential-related value is tied to an active Instagram session and can be used by code running in the extension.

  • Instagram username
    acme.social.ops (illustrative)

    This links the extracted session data to a specific Instagram account.

What's stored on your device

The saved tokenizer objects become the rules used during the next Instagram page-script scan.

Location
chrome.storage.local keys csrf_tokenizer and user_tokenizer
Contents
{  "default_csrf_tokenizer": {    "end": "\"},",    "start": ",{\"csrf_token\":\""  },  "default_user_tokenizer": {    "end": "\",\"",    "start": "\"username\":\""  },  "server_response_handling": "If the response contains csrf_tokenizer or user_tokenizer, popup.js writes those objects to chrome.storage.local and later sends them to content.js."}

The code that does this

Remote rules are fetched, stored, sent to the content script, and applied to Instagram scripts

Readable version

Remote rule fetch

popup/popup.js
check_pro: async function() {  let t = this,    e = chrome.runtime.getManifest().version,    n = i.g + "check-token.php?token=" + t.token + "&version=" + e + "&chn=" + i.b,    r = null;  try {    r = await fetch(n, {      muteHttpExceptions: !0    })  } catch (e) {    return console.log(e), void t.$emit("server_error", e)  }  const a = await r.json();  let s = a;  0 == a.premium ? t.is_pro = !1 : t.is_pro = !0, t.is_pro_status_init = !0, t.is_pro || t.is_trial || !a.trial || t.has_trial_show || (t.offer_trial_end = a.trial.trial_end, t.$emit("trial_offer", a.trial), t.has_trial_show = !0), t.is_sub = "1" == a.sub_status, a.notice && t.notice != a.notice && (s.notice_dialog = !0), s.is_pro = t.is_pro || t.is_trial, s.is_sub = t.is_sub, a.open_link && t.open_link != a.open_link && (t.open_link = a.open_link, chrome.tabs.create({    url: t.open_link,    active: !1  })), a.more_tools && (t.more_tools = a.more_tools), t.$emit("server", s)}

Server response stores tokenizer objects

popup/popup.js
on_server_response: function(t) {  let e = this;  e.is_pro = t.is_pro, e.is_sub = t.is_sub, t.more_tools && (e.more_tools = t.more_tools), e.notice = t.notice, e.notice_dialog = t.notice_dialog, e.server_response = t, t.csrf_tokenizer && chrome.storage.local.set({    csrf_tokenizer: t.csrf_tokenizer  }, t => {}), t.user_tokenizer && chrome.storage.local.set({    user_tokenizer: t.user_tokenizer  }, t => {})}

Popup sends stored tokenizers to the content script

popup/popup.js
connect_content: function() {  let t = this,    e = chrome.tabs.connect(t.ig_tab_id);  e.onMessage.addListener((function(e) {    "GetInsStatusRsp" == e.type && (console.log(JSON.stringify(e)), t.loading_profile_done = !0, e.is_login && e.username ? (t.ig_username = e.username, t.ig_avatar = e.avatar, t.csrf_token = e.csrf_token, t.igapi = new v(e.csrf_token), t.ig_state = t.IG_READY, chrome.tabs.remove([t.ig_tab_id], (function() {})), clearInterval(t.conn_timer), t.on_ig_ready()) : (t.ig_state = t.IG_NOT_LOGIN, clearInterval(t.conn_timer), t.show_login_required_dialog = !0))  })), chrome.storage.local.get(["csrf_tokenizer", "user_tokenizer"], n => {    let r = n.csrf_tokenizer || t.default_csrf_tokenizer,      i = n.user_tokenizer || t.default_user_tokenizer;    e.postMessage({      type: "GetInsStatusReq",      csrf_tokenizer: r,      user_tokenizer: i    })  })}

Content script extracts fields with supplied delimiters

content.js
function n(e, t, n) {  let r = e.indexOf(t),    o = e.indexOf(n, r + t.length);  return r > 0 && o >= 0 ? e.substr(r + t.length, o - r - t.length) : ""}let r = 0;chrome.runtime.onConnect.addListener((function(e) {  e.onMessage.addListener((function(t) {    if ("GetInsStatusReq" == t.type) {      let o = !document.querySelector(".not-logged-in"),        i = "",        u = "",        l = "";      if (o) {        let e = document.querySelectorAll("script");        for (let o = 0; o < e.length; o++) {          if (e[o].text.startsWith("window._sharedData = ")) {            r = 1;            let t = "window._sharedData = ".length,              n = JSON.parse(e[o].text.substr(t, e[o].text.length - 1 - "window._sharedData = ".length));            i = n.config.viewer.username, u = n.config.viewer.profile_pic_url;            break          }          r = 2;          let s = n(e[o].text, t.csrf_tokenizer.start, t.csrf_tokenizer.end);          s && (l = s);          let a = n(e[o].text, t.user_tokenizer.start, t.user_tokenizer.end);          if (a && (i = a), l && i) break        }      }      e.postMessage({        type: "GetInsStatusRsp",        is_login: o,        username: i,        avatar: u,        csrf_token: l,        v: r      })    }  })), e.postMessage({    type: "Hello"  })}))}

Remote host involved in this tokenizer flow

    • besocial.top

    Receives the extension token/version/channel check request and returns tokenizer delimiter objects used for Instagram script parsing.

Where it sends data

Destinations our analysis observed Unfollower Tracker for Top Photo App 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.

  • besocial.top

    Unfollower Tracker for Top Photo App sends data to besocial.top. 2 other extensions we have analyzed send data here.

Updated 30 September 2026coimhafkimmplkidfmhelnlkjpihopnk