hexo_blog_encrypt_analysis

hexo-blog-encrypt 是一个用来加密 hexo 博文的插件, 其核心的技术点就是在 hexo 渲染时将原文进行 AES 加密, 然后在展示的时候使用密码进行解密。

0x01 探索

进入一个页面搜集信息。

image-20240128160800457

给请求过程下断点,开始调试:

image-20240128174904306

于是追踪到相关逻辑。接下来,进入分析工作。

也可以从https://github.com/D0n9X1n/hexo-blog-encrypt直接下载开发版本,这里从样本分析的角度切入。

node_modules-blog-encrypt.js

0x02 hbe.js

部署在hexo中的文件结构长这样,很美观:

image-20240128175614617

直奔lib/hbe.js:

文件开头:

image-20240128181206595
  1. 严格模式声明:

    1
    'use strict';
  2. 常量声明:

    1
    2
    const cryptoObj = window.crypto || window.msCrypto;
    const storage = window.localStorage;

    crytoObj 用于访问浏览器的加密功能,storage 用于本地存储。

  3. 加密相关参数:

    1
    2
    3
    const storageName = 'hexo-blog-encrypt:#' + window.location.pathname;
    const keySalt = textToArray('hexo-blog-encrypt的作者们都是大帅比!');
    const ivSalt = textToArray('hexo-blog-encrypt是地表最强Hexo加密插件!');
    • storageName: 存储加密数据的键名,基于当前页面的路径。

    • keySaltivSalt: 用于生成密钥和初始化向量的盐值,通过 textToArray 函数将文本转换为字节数组。

  4. 解析 HTML 元素:

1
2
3
4
5
6
const mainElement = document.getElementById('hexo-blog-encrypt');
const wrongPassMessage = mainElement.dataset['wpm'];
const wrongHashMessage = mainElement.dataset['whm'];
const dataElement = mainElement.getElementsByTagName('script')['hbeData'];
const encryptedData = dataElement.innerText;
const HmacDigist = dataElement.dataset['hmacdigest'];
  • mainElement 获取 id 为 'hexo-blog-encrypt' 的 HTML 元素。
  • wrongPassMessagewrongHashMessage 分别是用于显示密码错误和哈希校验失败的消息。
  • dataElement 获取标签名为 'script' 且具有属性 'hbeData' 的元素。
  • encryptedData 获取该元素的文本内容,即加密的数据。
  • HmacDigist 获取加密数据的 HMAC 值。
  1. 辅助函数:
  • hexToArray(s):将十六进制字符串转换为字节数组。
  • textToArray(s):将文本转换为字节数组。
  • arrayBufferToHex(arrayBuffer):将 ArrayBuffer 转换为十六进制字符串。
  • getExecutableScript(oldElem):根据给定的元素创建可执行的脚本。
  • convertHTMLToElement(content):将 HTML 字符串转换为 HTML 元素。

密钥的生成与处理

在代码中,使用了 getKeyMaterialgetHmacKeygetDecryptKeygetIv 函数来生成密钥和初始化向量。这些函数使用了PBKDF2 导出密钥材料,使用 HMAC 进行数据完整性校验,以及使用 AES-CBC 进行对称加密。

image-20240128182522073

加密过程中,使用 AES-CBC 加密算法对明文进行加密,并使用 HMAC 对加密后的密文进行签名,以确保数据的完整性。解密过程中,则先使用密码解密数据,然后校验 HMAC,最后返回解密后的明文。

image-20240128182613626

在解密过程中,除了解密数据外,还需要对解密后的明文进行 HMAC 校验,以确保数据在传输过程中未被篡改。

image-20240128182653973

0x03 存在的问题

在分析源码时,我注意到一个细节:

1
2
3
const keySalt = textToArray('hexo-blog-encrypt的作者们都是大帅比!');
const ivSalt = textToArray('hexo-blog-encrypt是地表最强Hexo加密插件!');

PBKDF2算法使用的salt每次都必须是唯一的,AES算法的IV也每次都必须唯一,否则会存在字节翻转的安全问题。

不过,这个漏洞已经在去年九月提了pr, 目前官方最新的版本得到了修复。

image-20240128183255815

如图,将salt改成了一次一密,并在所有主题下加入了keysaltivsalt两个变量。

0x04 总结

这个插件的开发者有一定的密码学基础,在 hexo 渲染和展示时将原文用AES进行加密。但AES的具体操作上存在安全问题。

这是笔者第一次做js代码的分析工作,感觉动态调试的作用相当大。与之类似地,二进制可执行文件的逆向工程也常常可以通过动态调试获取到一些关键线索。同时对于解决博客加密的问题,以前一直考虑部署在服务端情况下如何实现(并用Go写了demo),js的强大功能令我耳目一新,用短短二百余行核心代码就实现了加密/解密/校验的核心功能。