其他
某知笔记服务端docker镜像授权分析
1
启动docker镜像
docker pull wiznote/wizserver:latest
docker run --name wiz --restart=always -it -d \
-v `pwd`/wizdata:/wiz/storage \
-v /etc/localtime:/etc/localtime \
-p 80:80 -p 9269:9269/udp wiznote/wizserver
2
初探授权
进入docker容器,查看基本信息。
执行 docker exec -it wiz bash 进入容器,可以看到:
后端主要使用node实现并用pm2管理,nginx反代,数据库mysqlnode版本v8.11.2,v8版本6.2.414.54。
# ps -aef
UID PID CMD
root 1 bash /wiz/app/entrypoint.sh
root 33 /usr/bin/redis-server 127.0.0.1:6379
mysql 53 /usr/sbin/mysqld
root 59 nginx: master process /usr/sbin/nginx -c /etc/nginx/nginx.conf
nginx 60 nginx: worker process
nginx 61 nginx: worker process
root 119 PM2 v4.5.0: God Daemon (/root/.pm2)
root 129 node /root/.pm2/modules/pm2-logrotate/node_modules/pm2-logrotate/app.js
root 137 node /wiz/app/wizserver/app.js
root 157 node /wiz/app/wizserver/app.js
root 173 node /wiz/app/wizserver/app.js
root 189 node /wiz/app/wizserver/app.js
root 205 node /wiz/app/wizserver/app.js
root 221 node /wiz/app/wizserver/app.js
root 240 node /wiz/app/wizserver/app.js
root 246 crond -n
root 256 bash
root 270 ps -aef
# node -v
v8.11.2
# node -p process.versions.v8
6.2.414.54
# pm2 list
┌─────┬──────────────────┬
│ id │ name │
├─────┼──────────────────┼
│ 1 │ as │
│ 6 │ cloud │
│ 4 │ index │
│ 2 │ note │
│ 7 │ office │
│ 5 │ search │
│ 3 │ ws │
└─────┴──────────────────┴
# ll /wiz/app/wizserver/common/
total 536
-rw-r--r-- 1 root root 8792 Dec 23 2020 alicloud_tools.jsc
-rw-r--r-- 1 root root 9984 Dec 23 2020 client_tools.jsc
-rw-r--r-- 1 root root 7024 Dec 23 2020 cookie_tools.jsc
-rw-r--r-- 1 root root 2352 Dec 23 2020 date_tools.jsc
-rw-r--r-- 1 root root 27528 Dec 23 2020 db_tools.jsc
......
3
反汇编v8字节码,改造d8
d8 https://v8.dev/docs/d8 是v8的开发工具,为了简单起见决定对d8进行修改。
反汇编jsc思路
jsc实际是由v8::internal::CodeSerializer::Serialize方法生成 反汇编需要调用v8::internal::BytecodeArray::Disassemble方法生成
v8:internal::CodeSerializer::Deserialize原型:
MUST_USE_RESULT static MaybeHandle<SharedFunctionInfo> Deserialize(
Isolate* isolate, ScriptData* cached_data, Handle<String> source);
BytecodeArray* SharedFunctionInfo::bytecode_array() const {
DCHECK(HasBytecodeArray());
return BytecodeArray::cast(function_data());
}
读取jsc,构造ScriptData对象 反序列化,获取SharedFunctionInfo对象 反汇编,通过bytecode_array获取BytecodeArray,并调用Disassemble反汇编
搭建v8编译环境
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=`pwd`/depot_tools:"$PATH"
export HTTPS_PROXY=http://172.31.0.1:18080 # 设置代理
gclient sync # 更新工具链
fetch v8 # 获取v8代码
cd v8
git checkout 6.2.414.46 # 切换至需要的分支
gclient sync # 根据分支再次更新工具链
tools/dev/v8gen.py x64.release -- \
v8_enable_disassembler=true \
v8_enable_object_print=true # 配置编译选项
ninja -C out.gn/x64.release d8 # 编译d8
v8_enable_disassembler和v8_enable_object_print一定要开启,否则反汇编时不显示常量内容。
修改d8代码(d8.cc)
static void Disassemble(v8::internal::BytecodeArray* bytecode) {
internal::OFStream os(stdout);
bytecode->Disassemble(os);
auto consts = bytecode->constant_pool();
for (int i = 0; i < consts->length(); i++) {
auto obj = consts->get(i);
if (obj->IsSharedFunctionInfo()) {
auto shared = v8::internal::SharedFunctionInfo::cast(obj);
os << "Function name " << shared->name()->ToCString().get() << "\n";
Disassemble(shared->bytecode_array());
}
}
}
void Shell::LoadJSC(const v8::FunctionCallbackInfo<v8::Value>& args) {
auto isolate = reinterpret_cast<i::Isolate*>(args.GetIsolate());
for (int i = 0; i < args.Length(); i++) {
String::Utf8Value filename(args.GetIsolate(), args[i]);
if (*filename == NULL) {
Throw(args.GetIsolate(), "Error loading file");
return;
}
int length = 0;
auto filedata = reinterpret_cast<uint8_t*>(ReadChars(*filename, &length));
if (filedata == NULL) {
Throw(args.GetIsolate(), "Error reading file");
return;
}
auto scriptdata = new i::ScriptData(filedata, length);
auto source = isolate->factory()
->NewStringFromUtf8(i::CStrVector("source"))
.ToHandleChecked();
auto fun = i::CodeSerializer::Deserialize(isolate, scriptdata, source)
.ToHandleChecked();
Disassemble(fun->bytecode_array());
}
}
global_template->Set(
String::NewFromUtf8(isolate, "loadjsc", NewStringType::kNormal)
.ToLocalChecked(),
FunctionTemplate::New(isolate, LoadJSC));
SerializedCodeData::SanityCheck
Deserializer::Initialize
使用loadjsc进行反汇编
function xxx(){
console.log("asdasd")
}
xxx()
其通过wiz的docker里bytenode编译为test.jsc命令/wiz/app/wizserver/node_modules/.bin/bytenode -c test.js再通过d8反汇编,结果如下:
Parameter count 1
Frame size 8
0 E> 0x17c42dd2c522 @ 0 : 91 StackCheck
0 S> 0x17c42dd2c523 @ 1 : 6e 00 00 00 CreateClosure [0], [0], #0
0x17c42dd2c527 @ 5 : 1e fb Star r0
115 S> 0x17c42dd2c529 @ 7 : 95 Return
Constant pool (size = 1)
0x17c42dd2c531: [FixedArray] in OldSpace
- map = 0x1b27f0f82309 <Map(HOLEY_ELEMENTS)>
- length: 1
0: 0x17c42dd2c549 <SharedFunctionInfo>
Handler Table (size = 16)
Function name
Parameter count 6
Frame size 8
0x17c42dd2c742 @ 0 : 6e 00 00 02 CreateClosure [0], [0], #2
0x17c42dd2c746 @ 4 : 1e fb Star r0
10 E> 0x17c42dd2c748 @ 6 : 91 StackCheck
106 S> 0x17c42dd2c749 @ 7 : 4f fb 01 CallUndefinedReceiver0 r0, [1]
0x17c42dd2c74c @ 10 : 04 LdaUndefined
113 S> 0x17c42dd2c74d @ 11 : 95 Return
Constant pool (size = 1)
0x17c42dd2c751: [FixedArray] in OldSpace
- map = 0x1b27f0f82309 <Map(HOLEY_ELEMENTS)>
- length: 1
0: 0x17c42dd2c769 <SharedFunctionInfo xxx>
Handler Table (size = 16)
Function name xxx
Parameter count 1
Frame size 24
74 E> 0x17c42dd2c862 @ 0 : 91 StackCheck
82 S> 0x17c42dd2c863 @ 1 : 0a 00 02 LdaGlobal [0], [2]
0x17c42dd2c866 @ 4 : 1e fa Star r1
90 E> 0x17c42dd2c868 @ 6 : 20 fa 01 04 LdaNamedProperty r1, [1], [4]
0x17c42dd2c86c @ 10 : 1e fb Star r0
0x17c42dd2c86e @ 12 : 09 02 LdaConstant [2]
0x17c42dd2c870 @ 14 : 1e f9 Star r2
90 E> 0x17c42dd2c872 @ 16 : 4c fb fa f9 00 CallProperty1 r0, r1, r2, [0]
0x17c42dd2c877 @ 21 : 04 LdaUndefined
104 S> 0x17c42dd2c878 @ 22 : 95 Return
Constant pool (size = 3)
0x17c42dd2c881: [FixedArray] in OldSpace
- map = 0x1b27f0f82309 <Map(HOLEY_ELEMENTS)>
- length: 3
0: 0x2e3b60d34a19 <String[7]: console>
1: 0x2e3b60d08e41 <String[3]: log>
2: 0x17c42dd2c8e9 <String[6]: asdasd>
Handler Table (size = 16)
4
分析授权逻辑
前台获取授权信息的api接口
{
"returnCode": 200,
"returnMessage": "OK",
"externCode": "",
"result": {
"start": 1571192332036,
"end": 4733432332036,
"count": 5,
"oem": "wiz",
"type": "license_free",
"version": 1,
"key": "4bc0cc40-efbb-11e9-bef0-0faf68675f7c",
"ext": {}
}
}
定位接口对应的文件
# pwd
/wiz/app/wizserver/as/admin
# ll
total 128
-rw-r--r-- 1 root root 7408 Dec 23 2020 admin_file_utils.jsc
-rw-r--r-- 1 root root 80720 Dec 23 2020 admin_router.jsc
-rw-r--r-- 1 root root 1680 Dec 23 2020 admin_static_files.jsc
-rw-r--r-- 1 root root 3384 Dec 23 2020 ldap_settings.jsc
drwxr-xr-x 4 root root 4096 Dec 23 2020 meta_files
-rw-r--r-- 1 root root 3304 Dec 23 2020 middleware.jsc
-rw-r--r-- 1 root root 15240 Dec 23 2020 oem_utils.jsc
-rw-r--r-- 1 root root 6304 Dec 23 2020 wizbox_search.jsc
admin_router.jsc:
wiz_name_utils.jsc
node-rsa
pm2 restart as #pm2重启as服务,并访问网页授权页面
tail -f /root/.pm2/logs/as-out.log #查看as日志
2021-06-29 18:15 +08:00: {"start":1571192332036,"end":4733432332036,"count":5,"oem":"wiz","type":"license_free","version":1,"key":"4bc0cc40-efbb-11e9-bef0-0faf68675f7c","ext":{}}
2021-06-29 18:15 +08:00: e58678339769ba7a9139202655ab345f
5
解除授权封印
NodeRSA.prototype.decryptPublic = function (buffer, encoding) {
var data = this.$$decryptKey(true, buffer, encoding);
try{
var v = JSON.parse(data);
if(v.count == 5){
v.count = 99999;
v.type = 'license_vip';
data = Buffer.from(JSON.stringify(v));
}
}catch(e){
}
return data;
};
看雪ID:HOWMP
https://bbs.pediy.com/user-home-353809.htm
*本文由看雪论坛 HOWMP 原创,转载请注明来自看雪社区
# 往期推荐
1. angr学习(三)一道自己模仿着出的简单题和angr-ctf符号化输入相关题目
5. 关于一次在pwnable.kr中input题目的经历(python3)
6. Pwn堆利用学习——Unsortedbin Attack——HITCON_Training_lab14_magicheap
球分享
球点赞
球在看
点击“阅读原文”,了解更多!