Is Blueticks: WhatsApp Scheduler, Marketing Campaigns & AI Agent safe?

Medium risk

Blueticks keeps a persistent connection to its MQTT broker and runs server-issued commands that read your WhatsApp contacts, chats, and messages.

On WhatsApp Web, Blueticks injects a page script that hooks WhatsApp's internal data store to read contacts, chat lists, and full message bodies. It maintains a constant WebSocket/MQTT connection to mqtt.blueticks.co keyed to your account ID, where the vendor's server can remotely request that data (get_contacts, get_chats, get_messages) and have results published back, or instruct the extension to send messages from your account. The extension's background worker also exposes a generic fetch-any-URL proxy to allowed origins and reports an install event with marketing-attribution cookies to Google Analytics.

blueticksv5.9.23Chrome Web Store
45Risk
Who publishes it

Serenix LTD - no other listings under this identity

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

Publisher
blueticks
Declared legal entity
Serenix LTD
Registered address
Agiou Andreou 365, Limassol 3035, CY
Registered contact
Yonatan Naor

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

Publishers can request a review.

Findings

Remote command can send WhatsApp messages

The extension subscribes to a Blueticks MQTT broker for the signed-in user.

Code shows a backend request can reach the function sending a WhatsApp message from the active session.

No live MQTT command captured; evidence is the code path.

Severity
Medium unwanted
Type
Unexpected
CWE
CWE-940
Source
Dynamic sandbox
What actually happens
You did this

A Blueticks backend request reaches the extension over the user-specific MQTT request topic.

The user must have WhatsApp Web open for the WhatsApp-interface code path to run.

The extension did this

The extension forwards the request method and parameters into the WhatsApp-interface tool path.

Behavioral verification confirmed this path can reach the WhatsApp message-sending function without a separate authorization check in the extension.

Captured request
GETwss://stg-mqtt.blueticks.co:443/mqtt

WebSocket MQTT connection; the code subscribes to users/{userId}/requests/+ and clients/{currentClientId}/requests/+ after connection.

Remote request fields used by the WhatsApp workflow
  • User request topic
    users/5521432/requests/req-20260712-01

    This routes backend requests to the extension instance associated with your signed-in account.

  • Command name
    send_message_from_whatsapp_interface_actions

    This chooses which WhatsApp-interface action the extension runs.

  • Message parameters
    contactList[0].id=15551234567@c.us; message=Reminder for today

    These values can provide the recipient and message content that the extension passes into WhatsApp Web.

The code that does this

MQTT requests are forwarded to the WhatsApp-interface tool path

Readable version

Deobfuscated app.js request dispatcher

deobfuscated/app.js
subscribeToTopics(){if(!this.client||!this.userId)return;[`users/${this.userId}/downstream/+`,`users/${this.userId}/requests/+`,`clients/${this.currentClientId}/requests/+`,"global/broadcast/+",`users/${this.userId}/presence/#`].forEach(e=>{this.client?.subscribe(e,t=>{t||(this.log(`[MQTT] Subscribed to ${e}`),this.subscribedTopics.add(e))})})}handleMessage(e,t){if(this.log(`[MQTT] Received message on topic ${e}:`,t),e.includes("/requests/"))return void this.handleRequest(e,t);if(e.endsWith("/client-events"))return void this.handleClientEvents(e,t);if(e.includes("/presence/"))return void this.handlePresenceMessage(e,t);const n=this.extractEventTypeFromTopic(e);if(n){if(this.log(`[MQTT] Mapped topic ${e} to event type: ${n}`),"global-broadcast"===n){const e=t.eventType;if(e){this.log(`[MQTT] Global broadcast event type: ${e}`);const n=this.convertToSocketIOFormat(t.data||t,e),r=this.messageHandlers.get(e);r&&(this.log(`[MQTT] Dispatching global broadcast ${e} event to handler`),r(n))}return}const r=this.convertToSocketIOFormat(t,n),a=this.messageHandlers.get(n);a&&(this.log(`[MQTT] Dispatching ${n} event to handler`),a(r))}}handleRequest(e,t){this.log(`[MQTT] Handling request on topic ${e}:`,t);const n=e.split("/");let r;if("users"===n[0]&&"requests"===n[2])r=n[3];else{if("clients"!==n[0]||"requests"!==n[2])return;r=n[3],this.log(`[MQTT] Received client-specific request for ${n[1]}`)}r&&t.command&&this.processRequest(r,t.command,t.params||{})}async processRequest(e,t,n){const r=void 0!==n?.limit&&-1===Number(n.limit),a=Date.now();ds("WA_TAB_MQTT_REQUEST_RECEIVED",{requestId:e,command:t});try{if(this.log(`[MQTT] Processing request ${e}: ${t}`),"ping"===t)return void this.publishResponse(e,{success:!0,data:{message:"pong",timestamp:(new Date).toISOString(),engineType:(0,Zo.yS)(),clientId:this.currentClientId}});const i=n&&"string"==typeof n._engineCmdId?n._engineCmdId:null;if(i)try{this.backendActionsClient||(this.backendActionsClient=(0,cs.y)());const t=await this.backendActionsClient.checkEngineCmdLock({engineCmdId:i});if(t&&!1===t.acquired)return void this.publishResponse(e,{success:!0,data:{deduplicated:!0,lockId:i,reason:"engine-cmd-lock"}})}catch(e){e instanceof Error?e.message:String(e)}if(!(0,Zo.JH)())return void this.log(`Silently ignoring ${t} request in non-WhatsApp Web context (letting WhatsApp client respond)`);const o=n=>{ds("WA_TAB_MQTT_RESPONSE_PUBLISH",{requestId:e,command:t,durationMs:Date.now()-a,success:!!n?.success,error:n?.success?void 0:n?.error}),this.publishResponse(e,n)};if("insertOnlyIfMissing"===t){const e={path:"insertOnlyIfMissing",method:t,entity:n};return void await this.handleInsertOnlyIfMissingRequest(e,o)}const s={path:"whatsapp-interface",method:t,entity:{params:n,owner:this.userId}};if(r){Date.now();await(0,os.$f)(s,o,this.log.bind(this))}else await(0,os.$f)(s,o,this.log.bind(this))}catch(t){this.publishResponse(e,{success:!1,error:t?.message||"Unknown error"})}}
The code that does this

The WhatsApp send helper passes the userMessage object to sendMessageThunkTool

Readable version

Deobfuscated app.js send helper

deobfuscated/app.js
static async sendMessageViaWhatsapp(e){return(0,r.XL)(a.f_.sendMessageThunkTool,{userMessage:e},{timeoutOverride:6e4})}}},57991(e,t,n){"use strict";n.d(t,{$:()=>o,O:()=>s});var r=n(24259),a=n(52732);function i(e,t={}){(0,a.G)({eventType:e,source:"client",...t})}function o(e,t="Server tool request failed"){if(!e)return t;if("string"==typeof e&&e.trim())return e;if("string"==typeof e?.message&&e.message.trim())return e.message;if("string"==typeof e?.error&&e.error.trim())return e.error;if("string"==typeof e?.data?.message&&e.data.message.trim())return e.data.message;if("string"==typeof e?.data?.error&&e.data.error.trim())return e.data.error;if("string"==typeof e?.data?.error?.message&&e.data.error.message.trim())return e.data.error.message;if("string"==typeof e?.response?.data?.message&&e.response.data.message.trim())return e.response.data.message;if("string"==typeof e?.response?.data?.error&&e.response.data.error.trim())return e.response.data.error;if("string"==typeof e?.response?.data?.error?.message&&e.response.data.error.message.trim())return e.response.data.error.message;try{const t=JSON.stringify(e);if(t&&"{}"!==t)return t}catch{}const n=String(e);return n&&"[object Object]"!==n?n:t}async function s(e,t){const n={};try{const e=new URLSearchParams(window.location.search),t=e.get("supportedUserId"),r=e.get("workspaceId");t&&(n.supportedUserId=t),r&&(n.supportedWorkSpaceId=r)}catch{}const a=Date.now();i("SERVER_TOOL_REQUEST_START",{tool:e});try{const o=await(0,r.H)("post",`/actions?cmd=${encodeURIComponent(e)}`,t||{},{headers:n});return i("SERVER_TOOL_REQUEST_END",{tool:e,durationMs:Date.now()-a,success:!0}),o&&"object"==typeof o&&"success"in o&&("data"in o||"error"in o)?o:{success:!0,data:o}}catch(t){const n=o(t);return i("SERVER_TOOL_REQUEST_END",{tool:e,durationMs:Date.now()-a,success:!1,error:n}),{success:!1,error:{code:"SERVER_ERROR",message:n}}}}

Deobfuscated app.js send result path

deobfuscated/app.js
const e=await(0,u.wj)(A.W.sendMessageViaWhatsapp(n),"sendMessageViaWhatsapp",v);if((0,i.w2)(e)){const t=e.error,a="string"==typeof t?t:String(t??"unknown");return(0,o.lV)(e.errorType??"PAGE_HANDLER_THREW",`${a} (userMessageId=${n._id}, chatId=${r})`)}const t=e?.id??"";return t?(0,o._x)({id:t,chatId:r,userMessageId:n._id,messageId:t,waMessageKey:t,response:e,waMeInfo:g}):(0,o.lV)("NO_MESSAGE_ID",`Send response did not include a message id (userMessageId=${n._id}, chatId=${r})`)}catch(e){return(0,o.lV)("SEND_THREW",`${e?.message||String(e)} (userMessageId=${n._id}, chatId=${r})`)}}const x=(0,p.zD)("sendScheduleMessageThunk",async({userMessage:e,isSendMessageNow:t},n)=>{const p=n.dispatch;if(!e._id&&e.id&&(e._id=e.id),!(0,a.JH)())return(0,f.O)(o.f_.sendMessageThunkTool,{userMessage:e,isSendMessageNow:t});
Remote systems involved in the command path
    • mqtt.blueticks.co

    Blueticks MQTT broker that delivers user-specific request messages to the extension.

    • stg-mqtt.blueticks.co

    Broker hostname present in the shipped app configuration for MQTT-over-WebSocket connections.

    • web.whatsapp.com

    WhatsApp Web page where the extension content script can invoke the message-sending workflow.

Where it sends data

Destinations our analysis observed Blueticks 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.

  • www.google-analytics.comwidely used

    Blueticks sends data to www.google-analytics.com. A widely used service: 346 other extensions we have analysed send data here.

  • app.blueticks.co

    Blueticks sends data to app.blueticks.co. No other extension we have analysed sends data here.

  • blueticks.co

    Blueticks sends data to blueticks.co. No other extension we have analysed sends data here.

  • mqtt.blueticks.co

    Blueticks sends data to mqtt.blueticks.co. No other extension we have analysed sends data here.

Updated 30 September 2026adgnjhngogijkkppficiiepmjebijinl