Is Astar VPN - Free and fast VPN for everyone safe?

High risk

Astar VPN is high risk. The Astar VPN popup fetches config from hourly rotating hostnames under astarvpn.download and app707.org. Responses are decoded and api_website stored as xLine for proxy URLs. The hosts didn't resolve at test time; no body was captured.…

https://astarvpn.com/index.htmlv1.3.8Chrome Web Store
78Risk
Who publishes it

https://astarvpn.com/index.html - no other listings under this identity

What this publisher told the store about itself, and the other listings that told it the same thing.

Publisher
https://astarvpn.com/index.html

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

Publishers can request a review.

Findings

Remote config can replace Astar VPN API servers

The Astar VPN popup fetches config from hourly rotating hostnames under astarvpn.download and app707.org.

Responses are decoded and api_website stored as xLine for proxy URLs.

The hosts didn't resolve at test time; no body was captured.

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

You open the Astar VPN popup.

The popup mounts the backup-line selection component.

The extension did this

The extension checks two hourly generated configuration hosts and can replace the server list used by later proxy requests.

If the binfoc response contains api_website after decoding, that value is stored as xLine.

Captured request
GEThttps://w20264157cl.astarvpn.download/binfoc

DNS resolution failed during dynamic analysis; no response body was captured.

Fields that drive the remote server-list override
  • Configuration endpoint
    https://w20264157cl.astarvpn.download/binfoc

    This is where the extension looks for a remote value that can change which service hosts it uses later.

  • Decoded response field
    api_website

    This field is the value the extension trusts as the replacement server list.

  • Stored override key
    xLine

    This stored key lets the replacement list persist after the popup closes.

  • Selected server line
    serverLine

    This value selects which entry in the stored list is used for later requests.

  • Default API host
    iosedge.com/astarnew

    This is the hardcoded fallback host used when no remote override is available.

The code that does this

Popup code that fetches and decodes the remote configuration

Readable version

Readable popup fetch path

assets/index.html-773ce5b5.js:4229-4255
u = M([]), i = async () => {  let f = await Oe("xLine");  f && (u.value = f.split(","))}, c = async () => {  let f = [".astarvpn.download", ".app707.org"];  for (let d in f) try {    let p = f[d],      h = new Date,      m = "https://" + ("w" + h.getFullYear() + (h.getMonth() + 1) + h.getDate() + h.getHours() + "cl") + p + "/binfoc",      _ = await t({        url: m + "?" + new Date().getTime(),        method: "get"      }).catch(C => {        console.debug(C)      });    if (_ && _.data) {      let C = _.data.replace(/DEBUG/g, "");      C = C.split("").reverse().join(""), C = atob(C);      let w = JSON.parse(C).api_website;      Pt("xLine", w), window.localStorage.setItem("xLine", w), u.value = w.split(",")    }  } catch (p) {    console.debug(p)  }};return xe(() => {  l(), i(), c()})
The code that does this

Later requests read xLine to choose the API base URL

Readable version

Readable axios baseURL interceptor

assets/index.html-773ce5b5.js:56091-56115
s2.interceptors.request.use(e => {  try {    const t = {      one: "iosedge.com/astarnew",      two: "cloudpleace.com/astarnew",      three: "allinkstous.com/astarnew"    };    let n = "",      a = window.localStorage.getItem("serverLine"),      o = t[a];    if (o) o.indexOf("ip") == 0 ? n = o : n = "." + o;    else {      let i = window.localStorage.getItem("xLine");      i ? n = i.split(",")[Number(a)] + "/astarnew" : n = "." + t.one    }    let r = new Date,      l = "b" + r.getFullYear() + (r.getMonth() + 1) + r.getDate() + r.getHours() + "re";    n.indexOf("ip") == 0 ? n.indexOf("ips") == 0 ? e.baseURL = "https://" + n.substring(3) : e.baseURL = "http://" + n.substring(2) : e.baseURL = "https://" + l + n;    let u = window.localStorage.getItem("page_ext_lang") == "en" ? "en_US" : "zh_CN";    e.method === "post" && (e.data ? e.data = e.data + "&lang=" + u : e.data = "lang=" + u)  } catch (t) {    console.log(t)  }  return e}, e => (e.data = {}, e.data.msg = "Network anomaly", Promise.resolve(e)))

Readable service-worker helper with the same xLine logic

assets/index.ts-92abda20.js:2925-2943
async getHttpDomain(l) {  const a = {    one: "iosedge.com/astarnew",    two: "cloudpleace.com/astarnew",    three: "allinkstous.com/astarnew"  };  let e = "",    t = await n.storage.get("serverLine"),    v = a[t];  if (v) v.indexOf("ip") == 0 ? e = v : e = "." + v;  else {    let B = await n.storage.get("xLine");    B ? e = B.split(",")[Number(t)] + "/astarnew" : e = "." + a.one  }  let p = "",    w = new Date,    g = "b" + w.getFullYear() + (w.getMonth() + 1) + w.getDate() + w.getHours() + "re";  return e.indexOf("ip") == 0 ? e.indexOf("ips") == 0 ? p = "https://" + e.substring(3) : p = "http://" + e.substring(2) : p = "https://" + g + e, p + l}
Remote hosts involved in the configuration path
    • astarvpn.download

    Receives the hourly binfoc configuration request built by the popup.

    • app707.org

    Receives the same hourly binfoc configuration request as a second configured domain.

    • iosedge.com

    Hardcoded fallback API host used when no stored override is available.

    • cloudpleace.com
    • allinkstous.com

    Hardcoded alternate API host selected by the built-in server-line map.

User-Agent Header Modified to Append ' fs' for /loveUbady/ Requests

On install, the extension registers a declarativeNetRequest rule modifying the User-Agent for URLs containing '/loveUbady/', appending ' fs' to identify its users.

DA confirmed: getDynamicRules() returned the rule and modified header.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-200
Source
Dynamic sandbox
What actually happens
You did this

Your browser makes any request, XHR or page navigation, to a URL containing '/loveUbady/'.

The extension is already installed and the rule is active from the moment of install.

The extension did this

The User-Agent header on that request is replaced with your normal User-Agent string plus the suffix ' fs'.

This applies before the request leaves your browser; the receiving server sees the modified header.

The code that does this

declarativeNetRequest rule that modifies User-Agent

Readable version

Rule registration (readable)

chrome.runtime.onInstalled.addListener(() => {  chrome.declarativeNetRequest.updateDynamicRules({    removeRuleIds: [1],    addRules: [{      id: 1,      priority: 1,      condition: {        regexFilter: '.*/loveUbady/.*',        resourceTypes: ['xmlhttprequest', 'main_frame']      },      action: {        type: 'modifyHeaders',        requestHeaders: [{          header: 'User-Agent',          operation: 'set',          value: `${navigator.userAgent} fs`  // appends ' fs' to browser UA        }]      }    }]  }, () => {});});
DA-confirmed rule state
  • Rule ID
    1

    The dynamic rule installed by the extension that modifies outgoing headers.

  • URL pattern matched
    .*/loveUbady/.*

    Any URL whose path contains '/loveUbady/' triggers the header modification.

  • Modified header
    User-Agent

    The standard browser User-Agent is replaced with an extended version that includes a tracking suffix.

  • Appended marker
    Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/146.0.0.0 Safari/537.36 fs

    This two-character suffix is added to the User-Agent, allowing the server to distinguish requests from extension users.

Anti-debug loop reloads extension when DevTools breakpoints are active

The extension runs a timing loop that detects debugger pauses over 100ms, when DevTools breakpoints are active, and reloads itself when this happens.

It fires every 600ms via Chrome's alarm API and triggered 309 times in a 3-minute test.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-506
Source
Dynamic sandbox
What actually happens
You did this

You open DevTools and set a breakpoint in the extension's service worker.

Any pause in the JavaScript engine, including Chrome DevTools breakpoints, causes the timing measurement to exceed 100ms.

The extension did this

The extension calls chrome.runtime.reload(), resetting the service worker and clearing all state.

This terminates the current debugging session and restarts the extension, preventing analysis.

When this fires

Every 0.1 seconds

The 'checkSafe' alarm reschedules itself after every invocation, creating a continuous anti-debug polling loop that runs for the entire lifetime of the extension.

The code that does this

The checkSafe timing probe and alarm loop (assets/index.ts-92abda20.js)

Readable version
checkSafe() {  let startTime = performance.now();  debugger;  if (performance.now() - startTime > 100) {    chrome.runtime.reload();  }}// Alarm handler (line 4119):chrome.alarms.onAlarm.addListener(function(alarm) {  if (alarm.name === 'checkSafe') {    n.users.checkSafe();    chrome.alarms.create('checkSafe', { when: Date.now() + 100 });  }});// Initial alarm registration (line 4139):chrome.alarms.create('checkSafe', { when: Date.now() + 200 });
Check if you're affected

Counts how many times the 'checkSafe' alarm fires in a 60-second window by intercepting chrome.alarms.create in the extension's service worker context.

Requires
  • Chrome DevTools access to extension service worker
detect-checksafe.js · js
// Run in the DevTools console of the extension's service worker// (chrome-extension://<id>/service-worker-loader.js)// Intercept chrome.alarms.create to count checkSafe callslet checkSafeCount = 0;const origCreate = chrome.alarms.create.bind(chrome.alarms);chrome.alarms.create = function(name, info) {  if (name === 'checkSafe') checkSafeCount++;  return origCreate(name, info);};setTimeout(() => {  console.log(`checkSafe alarm created ${checkSafeCount} times in 60s`);  console.log(`Expected: ~600 invocations if loop is active`);}, 60000);console.log('Monitoring checkSafe alarm for 60 seconds...');
How to run it
  1. 1Open chrome://extensions, enable Developer mode, click 'Service Worker'.
  2. 2Paste this script into DevTools console.
  3. 3Wait 60s and observe the count.
  4. 4A count near 600 confirms the anti-debug loop is active.

+1 more finding not shown

What it can do

Permissions this extension asks for, as declared in version 1.3.8. 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

    <all_urls>

  • Route all of your browsing through a server of its choosing

    proxy

  • Watch every request your browser makes

    webRequest

  • Block and redirect the requests your browser makes

    declarativeNetRequest

  • See which of your requests its blocking rules matched

    declarativeNetRequestFeedback

  • Change your browser's privacy and security settings

    privacy

  • See, disable and uninstall your other extensions, including your security ones

    management

  • Store data in your browser

    storage

  • Store an unlimited amount of data in your browser

    unlimitedStorage

  • See the address and title of every tab you have open

    tabs

  • Schedule its own background tasks

    alarms

  • Show you desktop notifications

    notifications

  • Run its own code inside the pages you visit

    scripting

  • Read and change your full browsing history

    history

webRequestAuthProviderdeclarativeNetRequestWithHostAccess
Updated 30 September 2026jajilbjjinjmgcibalaakngmkilboobh