V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
cuso45h2o
V2EX  ›  程序员

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

  •  
  •   cuso45h2o · 2023-05-27 21:28:56 +08:00 via iPad · 3873 次点击
    这是一个创建于 559 天前的主题,其中的信息可能已经有所发展或是发生改变。

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

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

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

    • 总共 10 份,合计 100 元,知道很少随便玩一下啦
    • 请不要在 2023/5/28 1:00 PM (UTC) 前公开口令或完整分享链接
    • 解法或许不止一种,为了降低环境要求我只挡住了 5 位,因为被挡住的是尾部,测试了下在普通笔记本 CPU (例如 Apple M2 Pro )上单线程暴力破解耗时应该在 15 分钟内
    • 如果最终领取人数<5 ,我会在 Blog https://38b8fu.com 上发篇 write-up (自认为技术含量过低感觉没有什么必要)
    20 条回复    2023-05-28 17:48:09 +08:00
    LxExExl
        1
    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
        2
    dsg001  
       2023-05-27 23:01:38 +08:00
    难点是判断正常页面与错误页面

    其他可以交给 gpt
    token10086
        3
    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
        4
    cuso45h2o  
    OP
       2023-05-27 23:38:29 +08:00 via iPad
    提示:#后是一个 base64 编码后的密钥,可用于离线破解 API 中返回的加密 attributes ,正确解密的 attributes 有特征。
    cc @LxExExl @token10086
    mengyx
        5
    mengyx  
       2023-05-28 00:07:28 +08:00
    吼吼,老板大气!
    mengyx
        6
    mengyx  
       2023-05-28 00:08:13 +08:00
    mengyx
        7
    mengyx  
       2023-05-28 00:11:14 +08:00
    @mengyx #6 一个小提示:可以根据文件 ID 直接获取到 Attr 的密文,然后在本地暴力破解
    SunriseFox
        8
    SunriseFox  
       2023-05-28 01:08:11 +08:00
    解出来了!呜呜呜谢谢老板,老板最棒了 0v0
    Zzdex
        9
    Zzdex  
       2023-05-28 01:13:10 +08:00
    谢谢老板。。。虽然只有 1.8
    Nugine0
        10
    Nugine0  
       2023-05-28 03:57:17 +08:00
    找对应的逻辑找了好久……
    谢谢老板,虽然只拿到 2.89 元……
    sencat31
        11
    sencat31  
       2023-05-28 12:57:28 +08:00
    自己的思路就是先下载下来加密的文件,然后用穷举法尝试解密。

    自己只会一点 python ,于是乎,找到了 mega.py 这个库,然后魔改了逻辑,循环跑解密流程。
    iOCZ
        12
    iOCZ  
       2023-05-28 12:59:09 +08:00
    刚才没有保存 attributes ,花了好大力气才重新获取到。正在暴力破解中,估计解出来要一点多了,来不及了😂
    sencat31
        13
    sencat31  
       2023-05-28 13:03:16 +08:00
    @iOCZ 已经无了
    Basstorm
        14
    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
        15
    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
        16
    iOCZ  
       2023-05-28 15:55:46 +08:00
    @sencat31 无是无了,问题是我还没跑完,笑死
    iOCZ
        17
    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
        18
    mengyx  
       2023-05-28 17:15:25 +08:00
    https://gist.github.com/ix64/bf04400cd3a3394210449663d1b0ca52

    昨晚的代码,用 Go 写的,在笔记本上 48 秒跑出来了结果
    mengyx
        19
    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
        20
    oldshensheep  
       2023-05-28 17:48:09 +08:00
    @mengyx 知道了!我之前是写了个函数替换…… s.replace("-", "+").replace("_", "/").replace(",", "")

    后 5 位是 r2Wsg
    给没搞出来的贴一下链接
    https://mega.nz/file/yUIlFaJY#eWhpXnJOmswYAY_Vx93VF2moxjM4uQuhv_7hIIr2Wsg
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1698 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 16:52 · PVG 00:52 · LAX 08:52 · JFK 11:52
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.