Breaking Down the PhaaS Platform: collector.js Fingerprinting to Bypass Sandboxes
A Gmail Workspace email about delayed messages was sent via SendGrid with valid SPF, DKIM, and ARC. Sender: sandeep@oesplindia[.]com, Reply-To: td@sevensounds[.]ae. The link points to maxillae890[.]bitnest[.]za[.]com/testatrix449/*email@victim. The bitnest[.]za[.]com domain uses wildcard DNS via Cloudflare, resolving any subdomain to CDN IPs 188[.]114[.]96[.]11 or 188[.]114[.]97[.]11.
The infrastructure is two-stage: a landing page collects fingerprints, then the backend decides whether to show the form. The domain's SPF record is v=spf1 -all, ruling out legitimate email.
Analyzing collector.js: Fingerprint Collection
The collector.js?v=21e981d0 script loads first and gathers data via the collect() function. It iterates over browser properties using Object.getOwnPropertyNames() to build a data object.
Standard parameters:
- Screen: resolution, color depth.
- Navigator: User-Agent, language, platform, hardwareConcurrency, plugins.
WebGL for GPU Fingerprinting
The code extracts GPU details:
var gl = d.createElement("canvas").getContext("webgl"),
ext = gl.getExtension("WEBGL_debug_renderer_info");
data.webgl = {
vendor: gl.getParameter(ext.UNMASKED_VENDOR_WEBGL),
renderer: gl.getParameter(ext.UNMASKED_RENDERER_WEBGL)
};
In sandboxes, the renderer reports SwiftShader or llvmpipe, flagging virtualization.
WebRTC for IP Leaks
Bypassing VPNs via STUN:
var RTC = w.RTCPeerConnection || w.webkitRTCPeerConnection;
var pc = new RTC({
iceServers: [{ urls: "stun:stun.l.google.com:19302" }]
});
pc.createDataChannel("");
pc.onicecandidate = function(e) {
if (e.candidate) {
var p = e.candidate.candidate.split(" ");
data._rtc.push({ i: p[4], t: p[7], p: p[2] });
}
};
pc.createOffer().then(function(o) { return pc.setLocalDescription(o); });
Mismatches between HTTP and WebRTC IPs signal proxies.
Anti-Detection Techniques
The script checks for automation and DevTools:
- Patching Array.prototype.includes for canPlayType:
var orig = Array.prototype.includes;
Array.prototype.includes = function() { data.proto = true; };
try { d.createElement("video").canPlayType("video/mp4"); } catch (e) {}
Array.prototype.includes = orig;
This triggers in Puppeteer.
- DevTools Detection via toString:
var fn = function() {}, cnt = 0;
fn.toString = function() { ++cnt; return ""; };
console.log(fn);
data.tostring = cnt;
Open DevTools calls toString during logging.
Extras: touch events, iframe detection, visibilityState, pagehide.
Data Submission and Backend
After 120 ms, data is sent via POST form to demo[.]bitnest[.]za[.]com/testatrix449/:
- analytics: JSON fingerprint.
- _h: URL hash.
- _p: victim's email.
The backend returns a phishing form or blocks access. Expired licenses show "Your license has expired".
Infrastructure Ties and IOCs
IP 91[.]193[.]42[.]16 hosts sevensounds[.]ae and exquisite[.]za[.]com. Domain oesplindia[.]com exposes FTP/MySQL/SNMP ports on 170[.]10[.]163[.]134.
Key IOCs:
- Domains: bitnest[.]za[.]com, oesplindia[.]com, sevensounds[.]ae.
- IPs: 188[.]114[.]96[.]11, 149[.]72[.]123[.]24 (SendGrid).
- Script: collector.js?v=21e981d0.
- STUN: stun.l.google.com:19302.
- SendGrid: user_id=60417945.
Key Takeaways
- Two-Stage Setup: Landing page skips the form, evading scanners.
- Licensing Model: PhaaS runs like SaaS with expiring access.
- Sandbox Evasion: WebGL renderer, WebRTC IP leaks, DevTools toString checks.
- Wildcard DNS: Quick campaign spins under Cloudflare.
- MITRE Tactics: T1566.002 (Spearphishing), T1217 (Browser Discovery).
— Editorial Team
No comments yet.