Detecting AdsPower: Dual-Engine Spoofing Contradictions and Extension Leaks
How AdsPower leaks its identity through Gecko vs Blink engine collisions, SpiderMonkey stack traces, and unauthenticated extension stylesheet assets.
- ✔ FlowerBrowser runs on Gecko but attempts to spoof Chromium, causing CSS and stack trace contradictions.
- ✔ SpiderMonkey stack traces leak alongside fake window.chrome objects.
- ✔ SunBrowser exposes an internal extension stylesheet accessible to all webpages in under 20ms.
- ✔ System speech voices on Windows and macOS are incorrectly marked with localService: false.
Detecting AdsPower: Dual-Engine Spoofing Contradictions and Extension Leaks
AdsPower is one of the most widely used commercial anti-detect browsers in the marketing, multi-accounting, and web scraping industries. To cater to different user preferences, AdsPower maintains two separate browser engines:
- SunBrowser: Based on Chromium
- FlowerBrowser: Based on Mozilla Firefox (Gecko)
AdsPower claims that both engines offer complete protection against fingerprinting and bot detection platforms. However, when you analyze how AdsPower manipulates these engines, you find severe architectural contradictions.
In this deep dive, we will examine how AdsPower leaks its identity through dual-engine spoofing mistakes and unauthenticated browser extension assets.
# 1. FlowerBrowser: The Dual-Engine Identity Crisis
One common use case in AdsPower is creating a profile that emulates Google Chrome, but running it inside FlowerBrowser (Gecko/Firefox) because Firefox handles certain privacy features differently.
This creates an immediate, fatal contradiction: you cannot spoof the Blink engine inside the Gecko engine without leaking the difference.
CSS Feature Support Leak
Gecko implements Mozilla-specific CSS extensions that Blink will never support. For example:
const hasMozAppearance = typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports('-moz-appearance: none');In genuine Google Chrome, CSS.supports('-moz-appearance: none') returns false. In FlowerBrowser, because the underlying layout engine is still Gecko, it returns true.
JavaScript Engine Error Stack Formatting
Chromium uses Google's V8 JavaScript engine. Firefox uses Mozilla's SpiderMonkey.
When you instantiate new Error().stack in JavaScript, the two engines format call stacks completely differently:
- ◆V8 (Chrome): Each stack frame begins with four spaces and the word
at:
javascript
window.detectadspower = async function () {
try {
// 1. Check FlowerBrowser (Gecko engine claiming to be Chromium)
const hasMozAppearance = typeof CSS !== 'undefined' &&
typeof CSS.supports === 'function' &&
CSS.supports('-moz-appearance: none');
const errStack = (new Error()).stack || '';
const hasGeckoStackFormat = !errStack.includes('at ') && errStack.includes('@');
const isGecko = hasMozAppearance || hasGeckoStackFormat;
if (isGecko) {
const hasChromeGlobal = typeof window !== 'undefined' && typeof window.chrome !== 'undefined';
const hasUserAgentData = typeof navigator !== 'undefined' && typeof navigator.userAgentData !== 'undefined';
const ua = (typeof navigator !== 'undefined' && navigator.userAgent) ? navigator.userAgent : '';
const claimsChromium = hasChromeGlobal || hasUserAgentData || (ua.includes('Chrome/') && !ua.includes('Firefox/'));
const hasChromePdfPlugin = typeof navigator !== 'undefined' && !!navigator.plugins &&
(navigator.plugins.namedItem('Chrome PDF Viewer') !== null || navigator.plugins.namedItem('Chromium PDF Viewer') !== null);
if (claimsChromium || hasChromePdfPlugin) {
return true;
}
return false;
}
// 2. Check SunBrowser internal extension asset leak
let hasAssistantExtension = false;
if (typeof document !== 'undefined' && document.createElement) {
const root = document.head || document.documentElement;
if (root) {
const link = document.createElement('link');
link.rel = 'stylesheet';
link.href = 'chrome-extension://gcaiimgaiohlnlflkjjmcohobkpbbnfi/dist/contentScripts/style.css';
hasAssistantExtension = await new Promise(resolve => {
let done = false;
const cleanup = (val) => {
if (done) return;
done = true;
link.onload = null;
link.onerror = null;
try { link.remove(); } catch (_) {}
resolve(val);
};
link.onload = () => cleanup(true);
link.onerror = () => cleanup(false);
root.appendChild(link);
setTimeout(() => cleanup(false), 20);
});
}
}
if (hasAssistantExtension) {
return true;
}
// 3. Check SunBrowser impossible speech localService values
if (typeof window !== 'undefined' && window.speechSynthesis) {
let voices = window.speechSynthesis.getVoices();
if (Array.isArray(voices) && voices.length > 0) {
const hasFakeMsVoice = voices.some(v => v && v.name && v.name.startsWith('Microsoft') && v.localService === false);
const hasFakeAppleVoice = voices.some(v => v && v.name && ['Alex', 'Samantha', 'Victoria', 'Fred'].includes(v.name) && v.localService === false);
if (hasFakeMsVoice || hasFakeAppleVoice) {
return true;
}
}
}
return false;
} catch (_) {
return false;
}
};
```
# Conclusion
AdsPower proves a fundamental rule in anti-detection research: superficial spoofing always introduces secondary contradictions.
When you attempt to mask one engine as another, or inject extensions to handle profile logistics, you leave traces that advanced bot detection platforms like Verisoul, Cloudflare, and DataDome spot immediately.
If you are running high-value scraping pipelines or managing sensitive accounts, commercial tools will constantly risk your operational security. The only true mitigation is running a clean, native Chromium engine patched at source level where no dual-engine or extension shims are needed.