记一次奇怪的 Cloudreve 反盗版检测
复现
- 使用 Brave + 开启默认的反指纹识别保护
- 访问
/home
或目录分享页
如果运气好被反指纹识别改了靠前内容的像素的话,稍等片刻就会自动跳转到 Cloudreve 网盘程序的授权验证错误页面。
反盗版检测
反盗版检测分为两部分:
- 授权信息获取;
- 利用授权信息验证当前域名是否合法。
验证未通过的场合会跳转到上述的授权验证错误页面。
利用图片隐写隐藏授权信息
抵达指定的 URL 后,程序会加载 /api/v3/bg
图片资源,提取每个像素的 RGB 值,然后根据一定规则解密出其隐写的字符串:
function decode_image(e) {
// e.data = canvas image data
let height = 272;
let width = 522;
let items = [];
// RGBA
for (let x = 0; x < width * height * 4; x += 4) {
// keep RGB only.
if (x + 4 !== 4 * width) {
items.push(...e.data.slice(x, x + 3));
}
}
let decode = "";
let last = null;
let repeatCount = 0;
for (let i = 17; i < items.length && repeatCount < 16; i++) {
const curr = (items[i] + 1) & 1;
decode += curr;
if (curr === last) {
repeatCount++;
} else {
last = curr;
repeatCount = 0;
}
}
// 删除额外的内容
decode = decode.slice(0, decode.length - (decode.length % 16));
// 解码二进制 0/1
return decode.replace(/[01]{16}/g, z => String.fromCharCode(parseInt(z, 2)));
}
(该片段来自 6.2e355853.chunk.js)
解码后得到下述内容(加入了换行方便阅读;注意反指纹不一定会改变同一处的内容):
开启 Brave Shield 时得到的值:
window.faf6fc=["pan.huang1111.cn","|est.huang1111.cn","127.0.0.1","localhost"]; window.edd3ec="2020112005261691997"; window.e7afcf=-353960891;
关闭 Brave Shield 后得到的值:
window.faf6fc=["pan.huang1111.cn","test.huang1111.cn","127.0.0.1","localhost"]; window.edd3ec="2020112005261691997"; window.e7afcf=-353960891;
可以看到其中的一个字符发生了变化。
进行域名验证
网盘程序加载时会每 2 秒检测一次 edd3ec
的值是否设定,然后进行检测:
- 当前域名是否在
faf6fc
的列表中 - 这三个值是否被篡改。
检测代码整理后如下:
const keyAllowedDomainList = 'faf6fc';
const keyDomainLicense = 'edd3ec';
const keyExpectedChecksum = 'e7afcf';
function idCheck(text) {
let checksum = 9541;
if (text.length == 0)
return checksum;
for (let i = 0; i < text.length; i++) {
checksum = ((checksum << 5) + checksum) - text.charCodeAt(i);
checksum &= checksum; // 确保数字在 32 位
}
return checksum
}
function domainCheck(retryCount) {
if (retryCount < 0)
throw 'LEFTBELOW0';
if (!window[keyDomainLicense]) {
window.setTimeout((function() {
try {
domainCheck(retryCount - 1);
} catch (code) {
document.write("<h3>Backend is not running.</h3><p>Please check your Cloudreve status. (Code: " + code + ", ID:" + window[keyDomainLicense] + ")</p>")
}
}
), 7000);
return ;
}
if (window[keyExpectedChecksum] !== idCheck(JSON.stringify(window[keyAllowedDomainList]) + window[keyDomainLicense]))
throw "HASH";
let domainMatch = false;
for (let i = 0; i < window[keyAllowedDomainList].length; i++) {
if (window.location.hostname === window[keyAllowedDomainList][i]) {
domainMatch = true;
}
}
if (!domainMatch)
throw "MATCH"
}
let checki;
window.onload = function() {
checki = window.setInterval((function() {
if (location.pathname.startsWith("/home") || window[keyDomainLicense]) {
window.clearInterval(checki);
try {
domainCheck(2)
} catch (o) {
window.location.href = "https://cloudreve.org/FixComplete?code=" + o + '&id=' + window[keyDomainLicense]
}
}
}
), 2000)
}
若是验证失败,则会跳转到前文所述的错误页面。
绕过验证
绕过的方法太多了,如重写 /api/v3/bg
回应为空白图片,或是添加 unsafe-eval
CSP 规则来禁止使用 eval
。
本文就不深入这个话题了,读者有兴趣的话就自己尝试吧。
结语
莫名其妙的 bug,因使用 canvas 画图拿像素点触发了浏览器的反指纹识别,导致隐写数据无法正常解析。
如果站长使用的是所谓「开心版」,去除这个检测相对容易。虽然加了混淆,但那几个关键的函数名并没有隐藏。我愿称之为「如混」。
从这个角度来看的话,受伤害的都是「正版用户」了吧。
如果是一名普通的使用者,遇到这类 bug 通常无法自行解决此类问题。莫名其妙的盗版提示也会对网站的声誉带来一定的打击。
修改建议:遇到错误时回报相关信息,授权服务器再对报错的域名进行二次验证是否在合法客户列表中,之后再决定是否展示盗版警告。
以上。