Imagevue X3对于未授权网站引入了一种全新的保护机制,就是在打开网页的2秒后,如果认证失败或未授权,会自动删除页面所有内容,仅保留分享按钮,留下一片空白。
在登录Imagevue X3的控制台里,Tool-》Authorize也中会显示网站域名未授权。
授权保护机制的研究
以下通过对绕过授权保护的几种尝试,来详细分析Imagevue X3的授权保护机制。
尝试一:直接修改关键词
经过代码分析,发现Imagevue X3的授权验证功能全部由 x3.min.js 文件控制,文件位置位于 public\js\0.17.0\ 。
既然Imagevue X3的授权保护方式是通过联网验证域名,那么第一种尝试,延续Imagevue X2的破解思路,直接通过修改关键语句绕过域名验证
找到包含验证网址 auth.flamepix.com 的代码片段,将关键词直接替换,或通过构造返回值绕过验证即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
$(function () { function a() { $.ajax({ type: "POST", data: {domain: $.url().attr("host"), product: 3}, url: "https://auth.flamepix.com/", dataType: "json", success: function (a) { 200 == a.status ? xlo() : xla() } }) } ta++ > 2 && a() }) |
但是更改代码后,通过Network监视器分析,发现这段代码并没有实际执行,仅仅是一个吸引火力的伪装,所以想通过简单的修改并绕过关键语句的企图没有成功。
尝试二:更改JS行为
遂准备反过来思考,既然保护机制的实际行为是删除页面内容,那么在JS文件里肯定用到了 remove() 或者 empty() 方法,或者其他有 remove 关键词的方法,那么只要删除或者让这些方法失效,就能禁止保护机制删除页面内容。
遂批量查找并替换了带有 remove 关键词的方法,上传覆盖后,果然页面有内容了,但是出现了多个js错误,遂一个个修正,在修正到 removeEventListener 时,发现只要这个方法一修正,网页内容就会被清空,遂保留了这个js错误。
至此,网站已能够正常显示内容,各项主要功能也都可以正常使用,只是在使用手机访问时,页面依然会变为空白,虽然并不完美,但是确实是一个进步。
尝试三:更改POST行为
之前观察Network监视器的时候就发现,在页面第一次读取时,会POST两个参数 domain 以及 product ,至 auth.flamepix.com 进行授权验证,并返回如下值
1 |
{"status":301,"message":"License not Found"} |
如果是授权域名,则返回
1 |
{"status":200,"message":"X3 Private License"} |
通过在XHR行为日志中逐项分析代码,发现第三行 a.(anonymous function) 即是POST执行时调用的代码片段
定位到相应的代码片段
1 2 3 4 5 6 7 8 9 10 11 |
a.each(["get", "post"], function (c, b) { a[b] = function (d, c, e, f) { return a.isFunction(c) && (f = f || e, e = c, c = void 0), a.ajax(a.extend({ url: d, type: b, dataType: f, data: c, success: e }, a.isPlainObject(d) && d)) } }) |
以上代码的功能是每次和远程服务器联网验证授权时进行POST操作,可以看到,这段代码拥有十分清晰的POST结构,两个关键的参数值也在其中,即url和data。
只是参数值使用了十分隐晦的变量名称
这个时候,就十分的简单了,根本没必要去分析每个变量具体是怎么赋值的(之前有尝试分析,不过试了下还是放弃了),只需要将POST操作的授权服务器地址更改为自己的服务器地址,由于之前已经分析了授权服务器的返回值,只需要在自己架设一个授权服务器并模拟授权服务器的返回值就行了
授权服务器的PHP文件可以这样写,主要控制方法是返回的status值,不为200的话,就会删除所有页面内容。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
<?php header('Access-Control-Allow-Origin:*'); header('Access-Control-Allow-Methods:POST'); header('Access-Control-Allow-Headers:x-requested-with,content-type'); $status = 200; // 301 $msg = "X3 Private License"; // "License not Found" //output $outputArray = array( "status" => $status, "message" => $msg ); echo $outputJSON = json_encode($outputArray); ?> |
将这个PHP文件放入本地授权服务器中,并更改之前的 x3.min.js 代码
1 2 3 4 5 6 7 8 9 10 11 12 |
a.each(["get", "post"], function (c, b) { a[b] = function (d, c, e, f) { return a.isFunction(c) && (f = f || e, e = c, c = void 0), a.ajax(a.extend({ // url: d, url: "https://dev.minirplus.com/auth/", type: b, dataType: f, data: c, success: e }, a.isPlainObject(d) && d)) } }) |
OK,上传修改后的 x3.min.js 文件
上传完成后,注意Imagevue X3采用了本地存储,即在chrome的Application监视器Local Storage部分可以查看到有本地内容,需要清空本地存储才能够使得对js文件做的更新生效。
清空所有缓存和本地存储,刷新页面,All Done!
尝试四:控制台授权验证
经过分析代码,控制台的授权验证和前台分离,后台验证主要依靠 panel\filemanager_js\x3_panel.js ,而经过前面的分析,思路就很清晰了,找到POST方法,然后替换授权服务器地址
找到如下代码
1 |
var b, d = "https://auth.flamepix.com/", f = window.location.hostname; |
将其中的 "https://auth.flamepix.com/" 替换为本地授权服务器地址 "https://dev.minirplus.com/auth/" 。
All Done!这样,控制台的授权验证也被绕过了。
分析总结
通过替换POST方法中的授权服务器地址,并架设一个本地授权服务器,可以直接绕过Imagevue X3的域名授权验证机制。
Imagevue X3的授权验证机制依旧延续了Imagevue V2的架构,即通过将host和版本发送至授权服务器获得的返回值来判断是否已授权。
只是相比Imagevue V2的简单明了,Imagevue X3的授权代码明显要复杂许多,不但完全删除了函数和变量名称、加入了诱饵代码,甚至还使用了嵌套函数,各种变量的交叉替换。使得就算读懂Imagevue X3的代码都变得十分的困难,更不要说找到授权逻辑了。
这次能够顺利的成功找到授权验证机制的漏洞,完全是由于在研究后台的授权验证时发现前后台的授权验证方法十分的相似,通过关键结构的对比,才发现了前台的验证代码片段所在,同时,chrome的Console也十分的给力,XHR的每个步骤,都可以直接定位到在代码中的所在。
解决这个漏洞的方法,可以是将授权的post方法合并在其他的post方法中,这样就无法通过替换post参数中的授权服务器来绕过了,因为这样会影响到其他的post行为。
下载
(仅本地研究测试使用)
Imagevue.X3.v0.17.0.PHP.NULL
链接:http://pan.baidu.com/s/1gfs0zAV 密码:09xz
或者
进入官网https://imagevuex.com/buy/,选择试用trial,可以下载最新版本X3.23.0
或者
直接官方下载链接
Imagevue X3.24.3 (2018-01-13)
https://imagevuex.com/download/X3.24.3.zip
Imagevue.X3.24.2
https://imagevuex.com/download/X3.24.2.zip
PS
经过分析,官网下载的最新X3.23.0版本的目录结构和之前流出的版本有些许差别,但是通过相同的原理,使用Chrome的XHR分析授权代码,修改JS文件,依旧能够使用相同的方法绕过X3的认证机制。
不过最新版本已经可以免费下载和使用,仅在最下方有Imagevue链接,所以破解授权已经没有了实质性的意义。
后续
最近又研究了一下代码,发现ImagevueX3的加载过程中,如果开启了audio plugin,会多一个XHR行为,post一个参数action=audio到x3/app/x3.api.php,而这个XHR行为和获取License使用了相同的代码段,也就是说之前尝试通过修改post行为的方法其实是错误的。为了临时解决这个问题,将x3.api.php中的返回播放列表功能段加入到auth文件中,另外,如果依旧放置在外部,则无法准确获取mp3的文件路径,遂将auth文件夹调整到x3目录下。
在解决音乐播放器的问题的时候,发现x3.api.php中除了返回播放列表的功能外,还有邮件功能,所以更改post行为是不可取的。
重新研究了一下两个XHR过程,发现一个奇怪的函数 jd() ,这个函数里面总是有一些奇怪的字符,例如 jd("dGltbw==")
遂搜索找到了这个函数的定义
1 2 3 |
function jd(a) { return atob(a) } |
atob是什么?是用来解码base64字符串的函数。OK,这下就好办了。
获取License时访问的域名是什么? https://auth.photo.gallery/
那好的,用在线Base64工具将gallery转成base64格式Z2FsbGVyeQ==,搜索关键词,定位到如下语句
1 |
var c = ["aG9zdA==", "aHR0cHM6Ly9hdXRoLg==", "ZmxhbWVwaXguY29t", "aW1hZ2V2dWV4LmNvbQ==", "cGhvdG8uZ2FsbGVyeQ==", |
其中 "cGhvdG8uZ2FsbGVyeQ==" 就是要查找的关键词
发现了这个特点之后,重新检查License时调用的方法就简单了
1 2 3 |
function _() { Ma(R + p + "/", j, ba, X) } |
以上语句中Ma函数调用了post函数,而其中的第一个参数 R + p + "/" 就是之前修改的post的url地址。
在下面有对应的赋值
1 2 3 4 5 |
var T = a(0) , R = a(1) , v = a(2) , W = a(3) , p = a(4) |
可以看到,其中a函数是一个中转的作用,定位到a函数位置
1 2 3 |
function a(a) { return jd(c[a]) } |
一切都说得通了,其实a函数就是jd函数的反函数。而 R + p + "/" 其实就是 c[1]+c[4]+/ 其实也就是 "aHR0cHM6Ly9hdXRoLg=="+"cGhvdG8uZ2FsbGVyeQ=="+/ ,翻译过来也就是 https://auth.photo.gallery/
All Done!
Updated on 2019.5.3
测试了最新的X3.27.6版本,前台的bypass方式还是老方法,后台的稍稍有些变化,查找关键词aHR0cHM6Ly9hdXRoLnBob3RvLmdhbGxlcnkv替换成自己的Auth服务器地址即可。另外,还是要提醒注意需要关闭设置—>高级里的使用CDN选项。否则前台的bypass方法不会生效。
54 comments