开始
最近智慧树玩上了shadowroot
说实话本来不想参与这些东西了
但是看到好多作者被折磨的欲死欲活
我的大刀又开始饥渴难耐了
不禁在此吟诗两首
社会摇中万人迷
唯有油中招人迷
社会摇中没有将与帅
只有实力这一块
鸣谢
在此感谢涛之雨、cxxjackie对我的耐心教导
之前曾辅导我将shadowroot的各种奇技淫巧将其倾囊相授
授了又授
授了还授
授了再授
那么废话不多说了
我们开始实战
实战
我们的目标就是获取题目
首先查阅mdn文档
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/shadowRoot
通常来说是使用attachShadow函数来挂载shadowroot函数
mdn可以查阅
https://developer.mozilla.org/zh-CN/docs/Web/API/Element/attachShadow
我们如果想要hook attachShadow
那么需要修改ELEMENT的原型链附加shadowroot
我们编写简易代码
let old=Element.prototype.attachShadow
Element.prototype.attachShadow=function(...args){
console.log('attach劫持',...args)
return old.call(this,...args)
}
但是这时候智慧树提示了异常脚本
搜索一下文字找到了
先打印一下f.r是什么
function l() {
return a(window.XMLHttpRequest) && a(window.XMLHttpRequest.prototype.open) && (!S.a.state.globalProperty.supportShadom || a(document.body.attachShadow)) && !window.OCS
}
OCS作者榜上有名,笑死
然后看a.i是啥
单纯的变量返回,那么核心问题在f.r上
我们观察代码
发现每一个都进行了
a(document.body.attachShadow)
我们继续往a函数里走
function a(e) {
var t = void 0 === e ? "undefined" : _()(e);
return "function" == t ? ne.test(ee.call(e)) : e && "object" == t && Y.test(toString.call(e)) || !1
}
因为是三元表达式
根据调试来看t在attachshadow基本情况是一个函数
所以我们走的 条件?结果1:结果2
只需要注意结果1就可以了
这个ne是一个正则表达式
而ee是获取一个函数的源代码
那么逻辑基本通了
我们在hook之后
智慧树按一个 白名单的函数列表做遍历
然后判断是否出现了nativecode的字样
所以我们可以无限往里的函数追
一直追到了
这行打印了代码的字符串
但是因为这里在启动的时候
已经获取了Function.toString了
所以我们很难对其进行函数字符化的劫持
但是因为上层使用了正则
我们可以对正则劫持
可以参考
https://bbs.tampermonkey.net.cn/thread-2412-1-1.html
劫持attachShadow之后再对正则进行二次劫持
RegExp.prototype._test = RegExp.prototype.test;
RegExp.prototype.test = function (s) {
if (this.source.includes('function') || this.source.includes('native code')) {
return true;
}
return this._test(s);
};
但是那样就没啥意义了
所以我们随便再玩两三种方法吧
智慧树出现了一个小小的失误
就是他基于框架写了代码
只要有一个对框架研究的还不错的人
就可以基于框架做核心级别的劫持
因为回调和显示是无法被代码控制的
所以只能急于救火,而如果对框架进行魔改的话
又会导致无法跟随框架进度更新
甚至公司要招一个专门的懂框架的人进行补丁式维护
(这里暗示招我招我招我,月薪三千好养活,楼下保安工资都比我高)
我们之前看到异常代码在
我们可以看到代码就是一个校验
一个上报
而f.r也只有这一处
我们可以看到,他在这里调用了,那问题很简单
我们对setinterval和settimeout做劫持
这里可以看到,他故意传入了一个特定参数,恶心心
你以为这样我就拿捏不了了?
直接利用throw错误拿报错堆栈信息回溯堆栈干他
window.setInterval=function(...args){
let err= new Error('大赦天下');
console.log('setInterval大赦天下',err)
return oldset.call(this,...args)
}
window.setTimeout=function(...args){
let err= new Error('大赦天下');
console.log('setTimeout大赦天下',err)
return oldout.call(this,...args)
}
那么我们直接写代码
let oldset=window.setInterval
let oldout=window.setTimeout
window.setInterval=function(...args){
let err= new Error('大赦天下');
if(err.stack.indexOf('checkoutNotTrustScript')!==-1){
return
}
return oldset.call(this,...args)
}
window.setTimeout=function(...args){
let err= new Error('大赦天下');
if(err.stack.indexOf('checkoutNotTrustScript')!==-1){
return
}
return oldout.call(this,...args)
}
秒杀异常报告
然后我们编写劫持attachelemtn代码
let old=Element.prototype.attachShadow
Element.prototype.attachShadow=function(...args){
console.log('attach劫持',...args)
args[0].mode='open'
return old.call(this,...args)
}
测试一下
已经拿捏
你以为这里就结束了?
继续大赦天下
我们全局搜索attachShadow
只找到了一个closed
在
可以知道this.shadowDom是attachShadow的引用
因为之前我们大量的调试知道是vue页面
直接爆框架
document.querySelector('.subject_describe >div >div').parentElement.__vue__.shadowDom.innerHTML
结语
今天浅聊三种方式拿捏
只要是基于框架的
都有无数种可能无数种方式让我们尝试注入,修改,破坏
从技术的角度是浪漫的
是矛与盾,刀与剑,周瑜与黄盖的罗曼蒂克史
但是从道德的角度上
我一直推荐大家仅仅用来刷公共课
刷专业课是无趣且无聊的
我是一名土木工程的毕业生
当初转计算机的时候
你们一脸鄙夷的网络选修课计算机教程
是我日日夜夜不断反复观看的资料
每当我想到这个事情的时候
总觉得无比的讽刺。
严肃性撒花