前言
最近使用docker创建了个人的rocket chat应用,看着后台提示没有license总觉得缺了点啥。于是通过github进行review code 分析它的许可验证逻辑。
定位license
随意添加license信息,可以通过F12看到请求的路径为 api/v1/licenses.add
因为rocket chat是开源的,所以可以在github 上随时查看源码
直接查找关键词 licenses.add 可以看到有很多结果,这里看到 apps/meteor/ee/app/license/server/license.ts 这个文件中存在加载license、解密等过程。
跟进解密方法
查看decrypt方法,在 apps/meteor/ee/app/license/server/decrypt.ts 文件中存在相关的解密方法。
根据代码,公钥是经过base64编码后的公钥,位数是2048位
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAqWSskd9/6zRx8kyPcics
b32w2wxVuw7yBT96rQ/8D+yMeCMO9wSSpHa/9nFywowEtigpt/wroPN+VGSwbtwP
FXBeqElBndGFAl86e5+EliH8Kz/hGnCmJNmXpxEK2RE03X4IxsYX7DDB7Mtx/iqs
cjB/OuvSBkjiSleS7blNIT/dA7K4/CJ3oiu02bL4ExclCHepzqNMeP3wUZgpxOnf
NOud8IXYK73zScuE8E157YwpzCKpVaHX7ZJf8QuNsOO5W/aIjKl3L6269+eIOErG
wONmaHnzfg9FLpJhzgpO38aVn76vD5KKjBajWskY+4a2gSQmKNeFqaqOozyEFM0e
cFWZVVZ3Leh4vEMoYVPyIzx96x8f2/ymPnhIuvQv5wN4fyepa7EY5UCcp71z8kfP
4FcUzPA0IDWyMihXR/G6XgQQZ4Gb/qBBhvrviJCFzfYDcJgL7FegFYHP3PGL07Qg
vLevMK+iQZPrxrbxySqdPOkgurKjVrXTUr4A9TgiLyIX5UlJq3E/RV7mfOqZnLTa
SCVXHBhuPlnCGZR01ToTCfKhMG1u0CFnL2+15hC9fqOmWv9Qke43qlJ0PgF3VJ/X
ux/mTpnk9gnbG9JH+mfH39RoFvTNinYwSMvYzutVOn69sOzdwhDla90l3ACh4xCV
K7JOX+uHkoNu3g2iVxiZUM0CAwEAAQ==
-----END PUBLIC KEY-----
这种RSA 2048基本是无法破解的,只能采用公钥替换的形式。
获取license格式
采用在线试用的方式获取到了30天的试用license
JnIab9mmS9Vg4GIkTA/9zXLiexyrWIguhmhI85coxHIgk7Y6KOXO3g2KU92VfxR7GSgFJE3B/7lEagDlzZi8orWKZ0lAxLTORyO0wXPgqFY/42M2iXGyo3FacwINbooZA+sPiKJLUavplatdCBQF+v9UYa33fA8Yw+0E44V5Vr6VddyC7Bezpcmn2HxLXpL9XRZxvSl/LIeiUT2u8LtUu3HmW3hmCx3eCFAdE2ZIKLosdVrRYl9tG+FTPQINC7p7iQc/QZw8iX3JKAE1Aw3WmhbkWI63FQfsL/W1DM46vaZlCR1eX7beY5mbb4pBnfsBAsX7WxBi8nWsQ1yGDprmaQofG0YagIu/NGFA6Sw+7i0qN8jVqq/634d4hXmZQntGkhFckuUpMsX2qITyNpcZCcOn8lR7rhMy5fyLdrd6q6LXKeF5dW1y0fMUH5vYsc6EmBBnodC9neX8fkaewXEIvHc/BHxWVOsOrW2Skz4s3lkzz4wqkArlNdsIhEBRKB3OhZhgHbWyMPtYuoo4LfC0FLVmEEJrKXxJ8YibIYGaLfZbfuBwx3kBkBg8p2rlafEqZ/+5ca32RCuN0Ly3TGdOnOY6EFFD0OuP24JslcO5MpXgzXVTuhnAxG9FtZEjqYTamHnAgKD4G1ZOmNWkczpUf7yYsMQ0lEjf4jr10Z1Y6dk=
使用在线解密对其密文解密
明文license信息如下:
{"version":2,"url":"chat.synology.pub","expiry":"2023-05-03","maxRoomsPerGuest":1,"modules":["enterprise:*"],"tag":{"name":"Enterprise","color":"#F3BE08"},"meta":{"trial":true,"trialEnd":"2023-05-03T14:44:27.63697163Z","workspaceId":"642ae64cec4c1b00011e954d"}}
替换公钥
docker运行环境中的源码都是被打包过的文件,通过 grep 公钥头确定文件在 /app/bundle/programs/server/app/app.js 中
因为默认的docker容器中没有vim,只能先导出文件修改完成再复制回去。
大小为50多M,只能通过vim进行修改了。
替换为我们自己的公钥就行,然后替换到docker容器内。
然后就是我们使用自己的私钥进行对license内容进行加密
使用nodejs生成破解代码:
const fs = require('fs');
const crypto = require('crypto');
const publicKey =
'LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQ0lqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FnOEFNSUlDQ2dLQ0FnRUFxV1Nza2Q5LzZ6Ung4a3lQY2ljcwpiMzJ3Mnd4VnV3N3lCVDk2clEvOEQreU1lQ01POXdTU3BIYS85bkZ5d293RXRpZ3B0L3dyb1BOK1ZHU3didHdQCkZYQmVxRWxCbmRHRkFsODZlNStFbGlIOEt6L2hHbkNtSk5tWHB4RUsyUkUwM1g0SXhzWVg3RERCN010eC9pcXMKY2pCL091dlNCa2ppU2xlUzdibE5JVC9kQTdLNC9DSjNvaXUwMmJMNEV4Y2xDSGVwenFOTWVQM3dVWmdweE9uZgpOT3VkOElYWUs3M3pTY3VFOEUxNTdZd3B6Q0twVmFIWDdaSmY4UXVOc09PNVcvYUlqS2wzTDYyNjkrZUlPRXJHCndPTm1hSG56Zmc5RkxwSmh6Z3BPMzhhVm43NnZENUtLakJhaldza1krNGEyZ1NRbUtOZUZxYXFPb3p5RUZNMGUKY0ZXWlZWWjNMZWg0dkVNb1lWUHlJeng5Nng4ZjIveW1QbmhJdXZRdjV3TjRmeWVwYTdFWTVVQ2NwNzF6OGtmUAo0RmNVelBBMElEV3lNaWhYUi9HNlhnUVFaNEdiL3FCQmh2cnZpSkNGemZZRGNKZ0w3RmVnRllIUDNQR0wwN1FnCnZMZXZNSytpUVpQcnhyYnh5U3FkUE9rZ3VyS2pWclhUVXI0QTlUZ2lMeUlYNVVsSnEzRS9SVjdtZk9xWm5MVGEKU0NWWEhCaHVQbG5DR1pSMDFUb1RDZktoTUcxdTBDRm5MMisxNWhDOWZxT21XdjlRa2U0M3FsSjBQZ0YzVkovWAp1eC9tVHBuazlnbmJHOUpIK21mSDM5Um9GdlROaW5Zd1NNdll6dXRWT242OXNPemR3aERsYTkwbDNBQ2g0eENWCks3Sk9YK3VIa29OdTNnMmlWeGlaVU0wQ0F3RUFBUT09Ci0tLS0tRU5EIFBVQkxJQyBLRVktLS0tLQo=';
const encrypted = 'JnIab9mmS9Vg4GIkTA/9zXLiexyrWIguhmhI85coxHIgk7Y6KOXO3g2KU92VfxR7GSgFJE3B/7lEagDlzZi8orWKZ0lAxLTORyO0wXPgqFY/42M2iXGyo3FacwINbooZA+sPiKJLUavplatdCBQF+v9UYa33fA8Yw+0E44V5Vr6VddyC7Bezpcmn2HxLXpL9XRZxvSl/LIeiUT2u8LtUu3HmW3hmCx3eCFAdE2ZIKLosdVrRYl9tG+FTPQINC7p7iQc/QZw8iX3JKAE1Aw3WmhbkWI63FQfsL/W1DM46vaZlCR1eX7beY5mbb4pBnfsBAsX7WxBi8nWsQ1yGDprmaQofG0YagIu/NGFA6Sw+7i0qN8jVqq/634d4hXmZQntGkhFckuUpMsX2qITyNpcZCcOn8lR7rhMy5fyLdrd6q6LXKeF5dW1y0fMUH5vYsc6EmBBnodC9neX8fkaewXEIvHc/BHxWVOsOrW2Skz4s3lkzz4wqkArlNdsIhEBRKB3OhZhgHbWyMPtYuoo4LfC0FLVmEEJrKXxJ8YibIYGaLfZbfuBwx3kBkBg8p2rlafEqZ/+5ca32RCuN0Ly3TGdOnOY6EFFD0OuP24JslcO5MpXgzXVTuhnAxG9FtZEjqYTamHnAgKD4G1ZOmNWkczpUf7yYsMQ0lEjf4jr10Z1Y6dk=';
function decrypt(encrypted) {
const decrypted = crypto.publicDecrypt(Buffer.from(publicKey, 'base64').toString('utf-8'), Buffer.from(encrypted, 'base64'));
return decrypted.toString('utf-8');
}
console.log(decrypt(encrypted));
// 读取私钥文件
const privateKey = `-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAkNZsLxCKSwENCKk40fNqH6t9ZNOzbEZXmy89/8Ie7DdA/QP/
N6X26oAJssrKL4Ym927T4hq8vZvZvHdHIj5DBv6C0JdwQHmhtzqwgfJoUtDFSgd7
66fokd4ns2NYdiIztsFPzuQj84Ys2Rbxf/dn19buIgXevM6wni1g4faYErLHEIaK
zczFE91wq6kbkeO4KrpuhFUplwFsWJ2SBhiiA8+mYclhfhiAgGMhBAJKHXUs8qmS
u+37vkLtlHxtragAv2hUWjwi0JKAmAE7VgB2Jz4bYhLVEWzVcpBFPq4ikvvwTkXg
Tcxhj9Wq2PRy9iiDBPia8DIqzxjAoEQrSF4iYwIDAQABAoIBAAd0qKHNMPempWXq
VoM+pU13Pz+b3JyYpZUJ2+zFsJNp5+238VqLL8HnUJh98xjgx8vs58aEqDqXQm1L
mcNosq9lfC5oqhEBMd+D8ebk9GHVXvjuMbILzqw5/cLKPMxRlMy1+GAm3LYbrhG7
kJ4mijEeK0JgTJSQWNJYdUHftvkfWwsUyzdGjHtj9St1piOyxk3cyiBMFf/H3vKK
YJlUkEbS7ZZgi+/6f3xnozb+Ei18UZnITrFcWfMEn0EdS56Ms05e0v0q9f/aFLBY
4i4RtqOHx3xOisBacMsIOi/ynuje3KKwm4nu82gwDlF+WrxKoQH3k/6JHSShKKG7
hV75TKECgYEAkgeDhLAPnJIbuS96hD3zYnd5/yqnNM3Q1nEvbsoYUurShHqsSx4G
px+0baKZFEkLTKHdPFeosyknnWpSYYBgYjlL2JpDAQsH507FxK4EwRpn5rLtI8Ap
c/UjaG34Eyu1vJeOlW8VX+xaWHyewUyrbijqud8NZoSo603HovdJrr0CgYEA/ekn
Nn5+KcGGqtdz+po6fN5NAIn0oGnrqJnOqnNHCB4G74njbCy1jhAw8gMoSWkyw2GX
Lt3RNDucsFBOmRa7u08ClHVHBWPrQ3HY3cE+y4nkHyB0QgEpT7ZpfergCNMymdN1
ExHiP86QQem673FaUcOLg7jKw16Jx95nWmpQN58CgYBuuTvDtCtiMHbM528iLkcI
9kaOb6zwoM4kixXID3x6Aos04D8bhdzNg4CvUIZ5lxj2NhUl1+GWVzIubZuhSlHK
qF8WEYGUnOSVQmk6RChessLtbeXZIa9MuSbr29Yp0w6tvMzkCaJPZUrrpTJKpvOl
R2kTHklu3k+mewdQTeiUkQKBgDHK6zmwjKU7omEWZ1QZsqaSIZ+dbi+XFfO2VeTv
PlrFKK8I52RrUB9P5YlQPTJIQwA1vyQds8z+c7fPx9oVrzMIR4U9inPwKE7NoK28
G8hmfinsf2ACQkuzhfR/fve8Eww/f5IBy9CffYKvh001eXTXWCC4uGqfu31KjBIb
DygZAoGBAINF9lv8nRn/jh1ZJy/7xZzKYSEDg8QxD+Gj1bgD8ixnWbJdQoWHs+Bx
aIVu5bDNk+Pg01OEcwvDlyt0K4DLRz84143MzoFK2h59A0x6XTw388EihKmSJScr
6cFIoYu6BEIUuZ7uiEaRaWOhk8Lav/5UOIMWyf6aLpL8c9Nb8Hju
-----END RSA PRIVATE KEY-----
`;
// 读取明文文件
const plainText = `{"version":2,"url":"chat.synology.pub","expiry":"2099-12-01","maxRoomsPerGuest":999,"modules":["enterprise:*"],"tag":{"name":"Enterprise","color":"#F3BE08"},"meta":{"trial":false,"workspaceId":"642ae64cec4c1b00011e954d"}}`;
function encrypt(message) {
const bufferContent = Buffer.from(message);
const encrypted = crypto.privateEncrypt(privateKey, bufferContent);
console.log(encrypted.toString("base64"));
}
encrypt(plainText);
结果:
XZ+1eHE+hezUeV85wapf3ZdX9+6Rvs9yllpTX/K9Lgf+oh2IeRL3VZGlmdAYyGekJB60puixEfG3Sfgdw5hvdgqwh2uc1z2E/QjIblvI/AaKAdSj5bwvp8Vjz0Na2zrEwwsALbMjQha2fHQl8lYyU1CrNohtKCV8G6gRZIpYsurCV9TIScgozvmdoyDU8Pp8/fC6+usY4FAsU1HNowRL9xro+RzeBF3ZZqdglhF9US8QPkDRQ3raEAGougt+9m9kSlW06VNXN2HVqtKsRxmuc4R7hT77H3hdUXOPn2JsFnIu+SaMRF2lGfNWsDkbsPuYy8KV3emJsQEBouClS9F5Tg==
最终展示
替换到容器文件后需要重启容器,粘贴上面生成的license信息
遇到的坑
RSA私钥2048位加密最多只能加密245个字节好像,所以要缩减license的长度。