Is Qui-Quo safe?

High risk

Qui-Quo is high risk. When an agency user clicks Qui-Quo's Quick Login on a booking site, the script asks for the saved password. The worker POSTs to extension.qui-quo.ru/info/get-password with agency, manager, password, OS fields, returning it for decryption.…

Qui-Quov2.3.2Chrome Web Store
77Risk
Who publishes it

ООО "ДАТАГРАМ" - no other listings under this identity, 30 shared hostnames

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

Publisher
Qui-Quo
Declared legal entity
ООО "ДАТАГРАМ"
Registered address
бульвар Гагарина, 8-67, Смоленск 214000, Россия
Registered contact
Санатин Дмитрий Владимирович

Shared hosts - 30 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.

dmsu.gov.ua
Also called by 1 other listing
erp.karpaten.ro
Also called by 1 other listing
export.bgoperator.ru
Also called by 1 other listing
geograftour.com
Also called by 1 other listing
gettransfer.com
Also called by 1 other listing
hbpro.expert
Also called by 1 other listing
help.qui-quo.support
Also called by 1 other listing
hotels-geo-suggest.tutu.ru
Also called by 1 other listing
itaka.pl
Also called by 1 other listing
kolomna-turktv.ru
Also called by 1 other listing
library.sunmar.ru
Also called by 1 other listing: XO Pulse
online.plantravel.ru
Also called by 1 other listing: XO Pulse
agency.sanatory.ru
Also called by 2 other listings, including Qui-Quo
agent.anextour.com
Also called by 2 other listings
agent.sletat.ru
Also called by 2 other listings
agent.tours.ua
Also called by 2 other listings, including Qui-Quo
api-crs.vodohod.com
Also called by 2 other listings
api.asialuxe.app
Also called by 2 other listings
apigate-tui.fstravel.com
Also called by 2 other listings, including Qui-Quo
apigate.fstravel.com
Also called by 2 other listings, including Qui-Quo
cp.traveltainment.de
Also called by 2 other listings
cruisescanner.ru
Also called by 2 other listings
cyprusflightpass.gov.cy
Also called by 2 other listings
intercity.by
Also called by 2 other listings, including All-Inclusive CRM
laspi.com
Also called by 2 other listings, including All-Inclusive CRM
miramar.turhome.com
Also called by 2 other listings: XO Pulse, All-Inclusive CRM
travelsoft.ru
Also called by 2 other listings: XO Pulse, All-Inclusive CRM
account.mail.ru
Also called by 3 other listings, including Счетчик писем Mail
files.anextour.com
Also called by 3 other listings
ittour.com.ua
Also called by 4 other listings

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

Publishers can request a review.

Findings

Quick Login Posts Agency Password Requests

When an agency user clicks Qui-Quo's Quick Login on a booking site, the script asks for the saved password.

The worker POSTs to extension.qui-quo.ru/info/get-password with agency, manager, password, OS fields, returning it for decryption.

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

You click the Qui-Quo Quick Login button for a saved booking-site account.

The selected option provides both a login value and a password record ID.

The extension did this

The extension requests that saved password from Qui-Quo and fills it into the booking-site form.

The same path adds agency and manager identifiers to the password request.

Fields the password-request path handles
  • Selected login
    login=agency.operator@example-travel.ru (illustrative)

    This identifies which saved booking-site account you selected for Quick Login.

  • Password record ID
    pwdId=746392 (illustrative)

    This tells Qui-Quo which saved password record to return for the selected login.

  • Agency and manager IDs
    agencyId=58214, managerId=31827 (illustrative)

    These fields tie the password request to an agency account and manager profile.

  • Add-on installation ID
    addonGuid=550e8400-e29b-41d4-a716-446655440000 (illustrative)

    This can distinguish one browser installation of the extension from another.

  • Browser and extension version
    addonVersion=2.1.37, browser=Chrome 126.0.6478.127 (illustrative)

    This adds device context to the password request and helps identify the client environment.

Captured request
POSThttps://extension.qui-quo.ru/info/get-password

The source code parses a successful response as JSON and forwards it as a password object; dynamic analysis did not capture a successful password-response body because the test account state was rejected before this path completed.

The code that does this

The code path from button click to password request and form fill

Readable version

Readable copy: Quick Login click handler sends the selected password ID

data/operators/quickLogin.js
function loginAction(event, quickLoginStatus) {    event.preventDefault();    event.stopPropagation();    const wrapper = event.target.closest('.qq-auto-login-wrapper');    const select = (wrapper || document).querySelector(".qq-quick-login-select");    const option = selectedOption(select, false);    if ( select && option ) {        const login = option.getAttribute("value");        const id = option.getAttribute("id");        if ( id && login ) {            sendMessageToAddon("get password by id", {id, login, isActive: quickLoginStatus.isActive, inactivityReason: quickLoginStatus.inactivityReason});            localStorage.setItem("quickLoginLastSelected", option.id);        }    }}

Readable copy: Service worker posts the password request

lib/main.js
addWorkerMessageListener("get password by id", async function (data, worker, sendResponse) {    sendResponse(true)    if (data.isActive === false) {        sendMessageToWorker(worker, "password response", {            login: data["login"],            isActive: data.isActive,            inactivityReason: data.inactivityReason        });        return;    }    const {"agency-id": agencyId, managerId} = await getStorageData(["agency-id", "managerId"]);    const userSalt = checkActualUserSalt();    const actor = await getActorData();    let fd = new FormData();    fd.append('agencyId', agencyId);    fd.append('pwdId', data["id"]);    fd.append('managerId', managerId);    fd.append('addonVersion', chrome.runtime.getManifest().version);    fd.append('operatingSystem', getOS());    fd.append('addonGuid', actor['addonGuid']);    fd.append('browser', actor['browser']);    const passObj = await fetch(`https://${DOMAIN}/info/get-password`,        {            method: 'POST',            body: fd        })        .then(async resp => resp.ok ? resp.json() : {            "error": true,            "status": resp.status,            "text": await resp.text() || resp.statusText        })        .catch(_ => {            return {"error": true, "status": 0, "text": 'Internet failure'}        });    sendMessageToWorker(worker, "password response", {passObj, login: data["login"], userSalt})});

Readable copy: Password response is decrypted and pasted

data/operators/quickLogin.js
addAddonMessageListener("password response", async (data) => {    if ( data.isActive === false ) {        await pasteLoginAndPassword(Object.assign(data, {insertPassword: false}));        createQuickLoginPopup(null, null, "alert", data.inactivityReason || 'Нет описания');        return;    }    if ( !data.passObj ) {        createQuickLoginPopup(null, null, 'alert', `Неизвестная ошибка!`);        return;    }    if ( data.passObj.error ) {        createQuickLoginPopup(null, null, 'alert', `${data.passObj.text}`);        return;    }    if ( data.passObj.isUserSalted ) {        data.userSalt ? decryptAndPastePassword(data, data.userSalt) : createQuickLoginPopup(data.passObj.password, data, 'promt');        return;    }    decryptAndPastePassword(data, (data.passObj && data.passObj.id) || AGENCY_ID);});async function decryptAndPastePassword(data, salt) {    const password = window.crypto.subtle ? await decrypt(data.passObj.password, `resolve${salt}`)                                          : await decryptFromBackground('decrypt password', {password: data.passObj.password, salt: `resolve${salt}`});    pasteLoginAndPassword(Object.assign(data, {password: password}));}

Readable copy: Form fields receive the login and decrypted password

data/operators/quickLogin.js
async function pasteLoginAndPassword({login, password, insertPassword}) {    const {form, usernameInp, passwordInp, loginBtn, event, specialInputs, eventsDelay, notHideInputs, isReact, loginFunction} = getElementsForQuickLoginAction();    if ( specialInputs === true ) {        pasteSpecialLoginAndPassword({login, password, insertPassword});        return;    }    if ( typeof loginFunction !== 'undefined') {        return loginFunction(login, password);    }    if ( usernameInp && insertPassword === false ) {        usernameInp.value = login;        return;    }    // if ( !passwordInp ) {    //     return;    // }    setInputsValue(usernameInp, passwordInp, login, password, notHideInputs, isReact);    if ( event ) {        simulateEvent(usernameInp, event);        if (eventsDelay) {            await waitingFor(() => null, 2, eventsDelay)        }        simulateEvent(passwordInp, event);        if (eventsDelay) {            await waitingFor(() => null, 2, eventsDelay)        }    }    if ( loginBtn ) {        loginBtn.removeAttribute('disabled');        setTimeout(()=> {            loginBtn.click()            clearInputsValue(usernameInp, passwordInp);        }, 100);        return;    }    if ( form ) {        form.submit();    }}
Host involved in the password retrieval path
    • extension.qui-quo.ru

    Qui-Quo extension service that receives the password lookup request and returns the password object used by Quick Login.

Service worker pulls remote JS manifest every 5 min and injects into all tabs

Qui-Quo's service worker contacts extension.qui-quo.ru on startup and every 5 minutes for a JSON file controlling which scripts inject into your tabs.

Since the server picks the scripts, altering its response lets it run code in any tab.

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

The extension loads in your browser.

On startup and every 5 minutes, loadContentScriptsLoop() calls loadContentScriptsProduction().

The extension did this

The extension downloads a server-controlled list of JavaScript files and injects them into your open tabs.

The server at extension.qui-quo.ru specifies which scripts run in which sites; chrome.scripting.executeScript delivers them.

Captured request
GEThttps://extension.qui-quo.ru/content-scripts/data/content-scripts.json

JSON object with contentScriptsVersion and content_scripts array, each entry has matches[], js[] file paths, and optional all_frames flag. Replaced in memory on every successful fetch.

Headers
Cache-Control
no-cache
When this fires

Every 5 minutes

loadContentScriptsLoop() recursively reschedules itself with setTimeout every 5 minutes (CONTENT_SCRIPTS_POLL_INTERVAL = 5 * 60 * 1000 ms). The loop starts unconditionally on module load.

The code that does this

loadContentScriptsProduction(), fetches and applies remote manifest

Readable version

setContentScriptsData() — replaces in-memory routing table

lib/main.js
function setContentScriptsData(data) {    CONTENT_SCRIPTS_DATA["contentScriptsVersion"] = data.contentScriptsVersion || null;    CONTENT_SCRIPTS_DATA["content_scripts"] = data.content_scripts;    CONTENT_SCRIPTS_DATA["ignore_bug_fixes_for_files"] = data.ignore_bug_fixes_for_files;    filterBugFixesData(data.bug_fixes);}
Remote-code delivery infrastructure
    • extension.qui-quo.ru

    Primary CDN for the content-scripts.json manifest and associated JS files. Controls which scripts are injected into user tabs.

    • extension.qui-quo.com

    Fallback domain for manifest delivery (one of three SERVER_DOMAINS; selected by getFastestDomain() race).

    • extension.qui-quo.online

    Second fallback domain for manifest delivery.

Updated 30 September 2026bcfkiidkpkfdmdnmafkkhffacoeapeed