给 V 友发个 100 元红包,口令在这个不完整的 MEGA 网盘分享链接中

2023-05-27 21:28:56 +08:00
 cuso45h2o

在 YouTube 刷视频的时候看见了个 MEGA 链接末 6 位被复制按钮挡住了,想知道内容,Google 了下貌似没有现成的工具,于是花 20 分钟糊了个脚本把链接暴力破解出来了(其实还花了 10 分钟看文档)

用相同的做法发个支付宝口令红包的口令,看看周末多少人像我一样无聊能解出来

https://mega.nz/file/yUIlFaJY#eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hII?????

3914 次点击
所在节点    程序员
20 条回复
LxExExl
2023-05-27 22:48:50 +08:00
我来抛砖引玉,顺便请教一些问题:
- 密码学都还给老师了,白皮书看了一眼也没头绪,甚至连 XOR 都看迷糊了,于是想到干脆暴力尝试算了
- 然后暴力尝试的字典多大呢?白皮书懒得读了,看未隐藏的部分,大概是{数字,大小写字母,下划线}的组合。
- 那么写个 for 循环拼凑 10 + 26 + 26 + 1 = 63 ^ 5 个可能性,应该很好写
- 然后我的问题来了:


下一步应该就是一个一个去尝试了:
- 这个是不是要用 python 的爬虫库类似 Requests 呢?还是直接发 Post 请求?
- 怎么从返回的 response 里判断是否拼凑正确呢? 难道要写个网页的解析?
- 如果就是这个思路,楼主怎么做到 10 分钟写出来呢? 是平时手头就有模板吗?
- 要是我的话,真的去做就是先查 Requests 文档,然后 python 写个简单的 for 循环,去请求网页,发一个请求试试,再从 response 里面看,是否包含 “The provided key is invalid" ? 如果包含,说明 key 不对,如果不包含,可以认为 key 对了,这时候打印一下 URL ,手动贴到浏览器里面去看看。

可是问题来了:
- 直接发请求,会不会缺少必要的 cookie 或者 params 呢? 这个还得手动在 chrome 开发者模式里面观察一下吗? (有更好的办法吗?比如 Mega 提供了 API ?


感觉我越来越眼高手低,上述每一步都懒得去搞,等楼主或者楼下公布答案,以及解题过程了。
dsg001
2023-05-27 23:01:38 +08:00
难点是判断正常页面与错误页面

其他可以交给 gpt
token10086
2023-05-27 23:22:35 +08:00
// 生成指定长度的随机字符(仅包含英文大小写字母和数字)
function generateRandomString(length) {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let randomString = '';
for (let i = 0; i < length; i++) {
const randomIndex = Math.floor(Math.random() * characters.length);
randomString += characters[randomIndex];
}
return randomString;
}

// 获取输入框和提交按钮的 XPath 路径
const inputXPath = '/html/body/section[5]/div[10]/section/div/div[2]/div/input';
const submitButtonXPath = '/html/body/section[5]/div[10]/footer/div/button';

// 获取输入框和提交按钮元素
const inputElement = document.evaluate(inputXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;
const submitButtonElement = document.evaluate(submitButtonXPath, document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue;

// 原始链接
const originalUrl = "eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hII?????";

// 枚举替换问号
for (let i = 0; i <= 9; i++) {
for (let j = 0; j < 26; j++) {
const randomString = generateRandomString(5);
const newUrl = originalUrl.replace("?????", randomString);
console.log("Testing URL: " + newUrl);

// 模拟手动输入,设置输入框的值并触发 input 事件
inputElement.value = newUrl;
inputElement.dispatchEvent(new Event('input'));

// 点击提交按钮
submitButtonElement.click();

// 在这里可以添加其他操作,例如执行其他逻辑或等待页面加载完成
// ...

// 适当延时,以免触发频率限制
await new Promise(resolve => setTimeout(resolve, 1000));
}
}
开了 6 个窗口,你那 10 块钱看缘分赚了
cuso45h2o
2023-05-27 23:38:29 +08:00
提示:#后是一个 base64 编码后的密钥,可用于离线破解 API 中返回的加密 attributes ,正确解密的 attributes 有特征。
cc @LxExExl @token10086
mengyx
2023-05-28 00:07:28 +08:00
吼吼,老板大气!
mengyx
2023-05-28 00:08:13 +08:00
mengyx
2023-05-28 00:11:14 +08:00
@mengyx #6 一个小提示:可以根据文件 ID 直接获取到 Attr 的密文,然后在本地暴力破解
SunriseFox
2023-05-28 01:08:11 +08:00
解出来了!呜呜呜谢谢老板,老板最棒了 0v0
Zzdex
2023-05-28 01:13:10 +08:00
谢谢老板。。。虽然只有 1.8
Nugine0
2023-05-28 03:57:17 +08:00
找对应的逻辑找了好久……
谢谢老板,虽然只拿到 2.89 元……
A8bM3D6FrJTw9l7Y
2023-05-28 12:57:28 +08:00
自己的思路就是先下载下来加密的文件,然后用穷举法尝试解密。

自己只会一点 python ,于是乎,找到了 mega.py 这个库,然后魔改了逻辑,循环跑解密流程。
iOCZ
2023-05-28 12:59:09 +08:00
刚才没有保存 attributes ,花了好大力气才重新获取到。正在暴力破解中,估计解出来要一点多了,来不及了😂
A8bM3D6FrJTw9l7Y
2023-05-28 13:03:16 +08:00
@iOCZ 已经无了
Basstorm
2023-05-28 13:22:41 +08:00
已经无了(没赶上),那就发出来吧,脚本:

function bruteforce() {
const characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
var attr = 'NQf1ctaRp0DwVAPJYybfRvoGQ_OOYJ8YOfZDXbqa0KpalCK-KHJSdTya0Eo-k01r87bdu6yfCaCNbUFsUSC1ng';
var ab = base64_to_ab(attr);
for (let i = 0; i < characters.length; i++) {
const char1 = characters[i];
console.log('char 1: ' + char1);
for (let j = 0; j < characters.length; j++) {
const char2 = characters[j];
console.log('char 2: ' + char2);
for (let k = 0; k < characters.length; k++) {
const char3 = characters[k];
for (let l = 0; l < characters.length; l++) {
const char4 = characters[l];
for (let m = 0; m < characters.length; m++) {
const char5 = characters[m];
const string = char1 + char2 + char3 + char4 + char5;
var keystr = 'eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hII' + string
var key = base64_to_a32(keystr).slice(0, 8);
var o = dec_attr(ab, key)
if (o) {
console.log("OK: " + keystr);
return;
}
}
}
}
}
}
}
oldshensheep
2023-05-28 14:50:35 +08:00
以前没搞过类似的东西,说一下我的思路吧

首先打开链接观察到 https://g.api.mega.co.nz/cs?id=0&domain=meganz…………………… 这个链接请求,是一个 json ,里面的[0]['at']
NQf1ctaRp0DwVAPJYybfRvoGQ_OOYJ8YOfZDXbqa0KpalCK-KHJSdTya0Eo-k01r87bdu6yfCaCNbUFsUSC1ng
是加密后的东西。

然后就是加密算法了看 https://mega.nz/SecurityWhitepaper.pdf 26 页

5.1 Public file links
When a public file link is shared publicly, the following is embedded into the public link:
https://mega.nz/#! || Base64( File Handle ) || ! || Base64( Obfuscated File Key )
也就是说链接后面是 Obfuscated File Key ,这个是一个 32 字节的数据

再看 24 页
4.1 File upload encryption

The File Key is then obfuscated as follows:
Obfuscated File Key = [
File Key[0] ⊕ IV[0],
File Key[1] ⊕ IV[1],
File Key[2] ⊕ Condensed MAC[0] ⊕ Condensed MAC[1],
File Key[3] ⊕ Condensed MAC[2] ⊕ Condensed MAC[3],
IV[0],
IV[1],
Condensed MAC[0] ⊕ Condensed MAC[1],
Condensed MAC[2] ⊕ Condensed MAC[3]
];

也就是说 Obfuscated File Key 包含了 File Key 和 IV
观察得到(以 4 字节为一组)
File Key[0] = Obfuscated File Key[0] ⊕ IV[0]
File Key[1] = Obfuscated File Key[1] ⊕ IV[1]
File Key[2] = Obfuscated File Key[2] ⊕ Obfuscated File Key[6]
File Key[3] = Obfuscated File Key[3] ⊕ Obfuscated File Key[7]

IV[0] = Obfuscated File Key[4]
IV[1] = Obfuscated File Key[5]


4.2 File attribute, preview and thumbnail encryption
When the file is ready to be sent, the file attributes (e.g. the file name, thumbnail, preview) also need
to be encrypted. These are encrypted with:
AES-CBC(File Key, File Attribute Data)

然后通过 File Key 用 AES CBC 解密就行了

还有一些细节,比如 base64 里包含的-_等字符串不是标准的,缺了几个=,还有一些加密算法等等。

乍一看确实挺简单的,但是如果没做过 CTF 类似的东西,还是要花一点时间熟悉一下流程。
iOCZ
2023-05-28 15:55:46 +08:00
@sencat31 无是无了,问题是我还没跑完,笑死
iOCZ
2023-05-28 16:12:18 +08:00
不跑了,贴一下代码:
enc = "NQf1ctaRp0DwVAPJYybfRvoGQ_OOYJ8YOfZDXbqa0KpalCK-KHJSdTya0Eo-k01r87bdu6yfCaCNbUFsUSC1ng"
words = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_'

re=5
suffix = its.product(words,repeat=re)
for i in suffix:
key = 'eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hII' + "".join(i)

file_key = base64_to_a32(key)
key = (file_key[0] ^ file_key[4],
file_key[1] ^ file_key[5],
file_key[2] ^ file_key[6],
file_key[3] ^ file_key[7])

attribs = base64urldecode(enc)
attribs = dec_attr(attribs, key)
if attribs:
print(attribs)
break
mengyx
2023-05-28 17:15:25 +08:00
https://gist.github.com/ix64/bf04400cd3a3394210449663d1b0ca52

昨晚的代码,用 Go 写的,在笔记本上 48 秒跑出来了结果
mengyx
2023-05-28 17:18:29 +08:00
@oldshensheep 这个是标准的 Base 64 Encoding with URL and Filename Safe Alphabet (without Padding)

可以参考:
https://www.rfc-editor.org/rfc/rfc4648#section-5
https://www.rfc-editor.org/rfc/rfc4648#section-3.2
oldshensheep
2023-05-28 17:48:09 +08:00
@mengyx 知道了!我之前是写了个函数替换…… s.replace("-", "+").replace("_", "/").replace(",", "")

后 5 位是 r2Wsg
给没搞出来的贴一下链接
https://mega.nz/file/yUIlFaJY#eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hIIr2Wsg

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/943485

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX