Skip to content

Commit 505f1fd

Browse files
committed
Merge branch 'goliath' of github.com:clee-r7/metasploit-framework into goliath
2 parents 7712523 + ff9c69c commit 505f1fd

File tree

14 files changed

+347
-62
lines changed

14 files changed

+347
-62
lines changed

CURRENT.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
Active Metasploit 5 development will sometimes push aggressive changes.
2+
Integrations with 3rd-party tools, as well as general usage, may change quickly
3+
from day to day. Some of the steps for dealing with major changes will be
4+
documented here. We will continue to maintain the Metasploit 4.x branch until
5+
Metasploit 5.0 is released.
6+
7+
**2018/01/17 - [internal] module cache reworked to not store metadata in PostgreSQL**
8+
9+
Metasploit no longer stores module metadata in a PostgreSQL database, instead
10+
storing it in a cache file in your local ~/.msf4 config directory. This has a
11+
number of advantages:
12+
13+
* Fast searches whether you have the database enabled or not (no more slow search mode)
14+
* Faster load time for msfconsole, the cache loads more quickly
15+
* Private module data is not uploaded to a shared database, no collisions
16+
* Adding or deleting modules no longer displays file-not-found error messages on start in msfconsole
17+
* Reduced memory consumption
18+
19+
Code that reads directly from the Metasploit database for module data will need
20+
to use the new module search API.
Lines changed: 194 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,208 @@
1+
# Commvault Communications Service execCmd Vulnerability
2+
3+
## Introduction
4+
5+
Commvault is a data protection and information management software; an enterprise-level data
6+
platform that contains modules to back up, restore, archive, replicate, and search data.
7+
8+
According to public documentation, the data is protected by installing agent software on the
9+
physical or virtual hosts, which use the OS or application native APIs to protect data in a
10+
consistent state. Production data is processed by the agent on client computers and backed
11+
up through a data manager (the MediaAgent) to disk, tape, or cloud storage. All data
12+
management activity in the environment is tracked by a centralized server (called CommServe),
13+
and can be managed by administrators through a central user interface. End users can access
14+
protected data using web browsers or mobile devices.
15+
16+
One of the base services of Commvault is vulnerable to a remote command injection attack,
17+
specifically the cvd service.
18+
119
## Vulnerable Application
220

21+
Commvault v11 SP5 or prior are vulnerable to this vulnerability. The specific vulnerable
22+
version I tested was 11.0.80.0.
323

4-
This module exploits a remote command injection vulnerability in the Commvault Communications service (cvd.exe). Exploitation of this vulnerability can allow for remote command execution as SYSTEM.
24+
The version of the vulnerable DLL is:
525

26+
```
27+
Image path: C:\Program Files\Commvault\ContentStore\Base\CVDataPipe.dll
28+
Image name: CVDataPipe.dll
29+
Timestamp: Wed Dec 21 11:59:21 2016 (585AC2F9)
30+
CheckSum: 002ED404
31+
ImageSize: 002F0000
32+
File version: 11.80.50.60437
33+
Product version: 11.0.0.0
34+
File flags: 1 (Mask 3F) Debug
35+
File OS: 40004 NT Win32
36+
File type: 1.0 App
37+
File date: 00000000.00000000
38+
Translations: 0409.04b0
39+
CompanyName: Commvault
40+
ProductName: Commvault
41+
InternalName: CVDataPipe
42+
OriginalFilename: CVDataPipe.dll
43+
ProductVersion: 11.0.0.0
44+
FileVersion: 11.80.50.60437
45+
PrivateBuild:
46+
SpecialBuild:
47+
FileDescription:
48+
LegalCopyright: Copyright (c) 2000-2016
49+
LegalTrademarks:
50+
Comments:
51+
```
652

7-
Additional information can be found [here](https://www.securifera.com/advisories/sec-2017-0001/)
53+
## Root Cause Analysis
854

55+
Usually, there are two ways to execute a command in a C/C++ application, one of them is ```WinExec()```,
56+
and the other one is ```CreateProcess()```:
957

58+
```
59+
BOOL WINAPI CreateProcess(
60+
_In_opt_ LPCTSTR lpApplicationName,
61+
_Inout_opt_ LPTSTR lpCommandLine,
62+
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
63+
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
64+
_In_ BOOL bInheritHandles,
65+
_In_ DWORD dwCreationFlags,
66+
_In_opt_ LPVOID lpEnvironment,
67+
_In_opt_ LPCTSTR lpCurrentDirectory,
68+
_In_ LPSTARTUPINFO lpStartupInfo,
69+
_Out_ LPPROCESS_INFORMATION lpProcessInformation
70+
);
1071
11-
## Verification Steps
72+
```
1273

13-
1. Start msfconsole
74+
Since ```CreateProcess()``` is meant to replace ```WinExec()``` according to Microsoft, we can create a
75+
breakpoint there first in our debugger (WinDBG), and we hit it:
1476

15-
2. `use exploit/windows/misc/commvault_cmd_exec`
77+
```
78+
0:044> g
79+
Breakpoint 3 hit
80+
kernel32!CreateProcessA:
81+
00000000`76fe8730 4c8bdc mov r11,rsp
82+
```
1683

17-
3. `set RHOST [ip]`
84+
Looking at the callstack of this ```kernel32!CreateProcessA```, we already have a pretty good idea
85+
locating the vulnerability:
1886

19-
4. `exploit`
87+
```
88+
0:044> k
89+
Child-SP RetAddr Call Site
90+
00000000`11a36b78 000007fe`f378a40f kernel32!CreateProcessA
91+
00000000`11a36b80 000007fe`f377714e CVDataPipe!execCmd+0x7af
92+
00000000`11a3f340 000007fe`f3777a69 CVDataPipe!CVDMessageHandler+0x78e
93+
00000000`11a3fbd0 000007fe`f9cdc58d CVDataPipe!CVDMessageHandler+0x10a9
94+
00000000`11a3fd40 000007fe`f9cdc1b1 CvBasicLib!CvThreadPool::th_defaultWorkerObj+0x3cd
95+
00000000`11a3fe40 000007fe`f9cd2073 CvBasicLib!CvThreadPool::th_defaultWorker+0x51
96+
00000000`11a3fe90 000007fe`f9a84f7f CvBasicLib!CvThread::~CvThread+0x63
97+
00000000`11a3fee0 000007fe`f9a85126 MSVCR120!_callthreadstartex+0x17 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
98+
00000000`11a3ff10 00000000`76f6f56d MSVCR120!_threadstartex+0x102 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
99+
00000000`11a3ff40 00000000`770a3281 kernel32!BaseThreadInitThunk+0xd
100+
00000000`11a3ff70 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
101+
```
102+
103+
There are two things that are interesting. One of them is ```CVDataPipe!CVDMessageHandler```, and the
104+
other one is ```CVDataPipe!execCmd```.
105+
106+
```CVDataPipe!CVDMessageHandler``` is basically a function that handles our packet's message type.
107+
The Metasploit exploit specifically sends a code of ```9h```, which is the message type for ```execCmd```:
108+
109+
```
110+
.text:0000000180147103 loc_180147103: ; CODE XREF: CVDMessageHandler(int,selectStruct_t *,CQiSocket,void *):loc_180146D78j
111+
.text:0000000180147103 lea rax, [rsp+888h+var_220] ; jumptable 0000000180146D78 case 9
112+
.text:000000018014710B mov [rsp+888h+var_600], rax
113+
.text:0000000180147113 mov rdx, [rsp+888h+sock]
114+
.text:000000018014711B mov rcx, [rsp+888h+var_600]
115+
.text:0000000180147123 call cs:??0CQiSocket@@QEAA@AEBV0@@Z ; CQiSocket::CQiSocket(CQiSocket const &)
116+
.text:0000000180147129 mov [rsp+888h+var_5F0], rax
117+
.text:0000000180147131 mov r8, [rsp+888h+arg_18]
118+
.text:0000000180147139 mov rdx, [rsp+888h+var_5F0]
119+
.text:0000000180147141 mov rcx, [rsp+888h+structSelect]
120+
.text:0000000180147149 call ?execCmd@@YAXPEAUselectStruct_t@@VCQiSocket@@PEAX@Z ; execCmd(selectStruct_t *,CQiSocket,void *)
121+
```
122+
123+
If we take a closer look at the ```execCmd``` function, we can tell the purpose of it is for processes such as:
124+
125+
* ifind (For restoring purposes)
126+
* BackupShadow.exe (For archiving)
127+
* Pub (Map file)
128+
* createIndex (A Commvault process for building index)
20129

130+
131+
```
132+
.text:0000000180159F1B loc_180159F1B: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+261j
133+
.text:0000000180159F1B ; DATA XREF: .rdata:0000000180286258o
134+
.text:0000000180159F1B lea rdx, aIfind ; "ifind"
135+
.text:0000000180159F22 lea rcx, [rsp+87B8h+ApplicationName] ; Str
136+
.text:0000000180159F2A call cs:strstr
137+
.text:0000000180159F30 test rax, rax
138+
.text:0000000180159F33 jnz short loc_180159F6D
139+
.text:0000000180159F35 lea rdx, aBackupshadow_e ; "BackupShadow.exe"
140+
.text:0000000180159F3C lea rcx, [rsp+87B8h+ApplicationName] ; Str
141+
.text:0000000180159F44 call cs:strstr
142+
.text:0000000180159F4A test rax, rax
143+
.text:0000000180159F4D jnz short loc_180159F6D
144+
.text:0000000180159F4F lea rdx, aPub ; "Pub"
145+
.text:0000000180159F56 lea rcx, [rsp+87B8h+ApplicationName] ; Str
146+
.text:0000000180159F5E call cs:strstr
147+
...
148+
.text:000000018015A0BA loc_18015A0BA: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+307j
149+
.text:000000018015A0BA lea rdx, aCreateindex ; "createIndex"
150+
.text:000000018015A0C1 lea rcx, [rsp+87B8h+ApplicationName] ; Str
151+
.text:000000018015A0C9 call cs:strstr
152+
.text:000000018015A0CF test rax, rax
153+
.text:000000018015A0D2 jz loc_18015A220
154+
```
155+
156+
However, if you don't call one of these processes, the ```execCmd``` will assume you want to run your
157+
custom process, and pass it to ```CreateProcess``` anyway:
158+
159+
```
160+
.text:000000018015A361 loc_18015A361: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+675j
161+
.text:000000018015A361 call cs:GetEnvironmentStrings
162+
.text:000000018015A367 mov [rsp+87B8h+var_86A8], rax
163+
.text:000000018015A36F lea rax, [rsp+87B8h+StartupInfo]
164+
.text:000000018015A377 mov rdi, rax
165+
.text:000000018015A37A xor eax, eax
166+
.text:000000018015A37C mov ecx, 68h
167+
.text:000000018015A381 rep stosb
168+
.text:000000018015A383 mov [rsp+87B8h+StartupInfo.cb], 68h
169+
.text:000000018015A38E lea rax, [rsp+87B8h+ProcessInformation]
170+
.text:000000018015A396 mov rdi, rax
171+
.text:000000018015A399 xor eax, eax
172+
.text:000000018015A39B mov ecx, 18h
173+
.text:000000018015A3A0 rep stosb
174+
.text:000000018015A3A2 mov [rsp+87B8h+StartupInfo.dwFlags], 1
175+
.text:000000018015A3AD xor eax, eax
176+
.text:000000018015A3AF mov [rsp+87B8h+StartupInfo.wShowWindow], ax
177+
.text:000000018015A3B7 lea rax, [rsp+87B8h+ProcessInformation]
178+
.text:000000018015A3BF mov [rsp+87B8h+lpProcessInformation], rax ; lpProcessInformation
179+
.text:000000018015A3C4 lea rax, [rsp+87B8h+StartupInfo]
180+
.text:000000018015A3CC mov [rsp+87B8h+lpStartupInfo], rax ; lpStartupInfo
181+
.text:000000018015A3D1 mov [rsp+87B8h+lpCurrentDirectory], 0 ; lpCurrentDirectory
182+
.text:000000018015A3DA mov [rsp+87B8h+lpEnvironment], 0 ; lpEnvironment
183+
.text:000000018015A3E3 mov [rsp+87B8h+dwCreationFlags], 10h ; dwCreationFlags
184+
.text:000000018015A3EB mov [rsp+87B8h+bInheritHandles], 0 ; bInheritHandles
185+
.text:000000018015A3F3 xor r9d, r9d ; lpThreadAttributes
186+
.text:000000018015A3F6 xor r8d, r8d ; lpProcessAttributes
187+
.text:000000018015A3F9 lea rdx, [rsp+87B8h+CommandLine] ; lpCommandLine
188+
.text:000000018015A401 lea rcx, [rsp+87B8h+ApplicationName] ; lpApplicationName
189+
.text:000000018015A409 call cs:CreateProcessA
190+
```
191+
192+
It is unclear whether allowing an arbitrary custom process is intentional or not, it is unsafe
193+
anyway considering the cvd process binds to 0.0.0.0, so anybody can gain access to it under the
194+
context of SYSTEM.
195+
196+
## Using the Metasploit Module
197+
198+
1. Start msfconsole
199+
2. `use exploit/windows/misc/commvault_cmd_exec`
200+
3. `set RHOST [ip]`
201+
4. `exploit`
21202
5. shellz :)
203+
204+
205+
## References
206+
207+
* https://en.wikipedia.org/wiki/Commvault
208+
* https://www.securifera.com/advisories/sec-2017-0001/

lib/metasploit/framework/data_service/proxy/host_data_proxy.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ def hosts(wspace = workspace, non_dead = false, addresses = nil)
1313
end
1414
end
1515

16+
# TODO: Shouldn't this proxy to RemoteHostDataService#find_or_create_host ?
17+
# It's currently skipping the "find" part
1618
def find_or_create_host(opts)
1719
puts 'Calling find host'
1820
report_host(opts)

lib/metasploit/framework/data_service/remote/http/remote_credential_data_service.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,22 @@
33
module RemoteCredentialDataService
44
include ResponseDataHelper
55

6-
CREDENTIAL_PATH = '/api/1/msf/credential'
6+
CREDENTIAL_API_PATH = '/api/1/msf/credential'
7+
# "MDM_CLASS" is a little misleading since it is not in that repo but trying to keep naming consistent across DataServices
8+
CREDENTIAL_MDM_CLASS = 'Metasploit::Credential::Core'
79

810
def creds(opts = {})
9-
json_to_open_struct_object(self.get_data(CREDENTIAL_PATH, opts), [])
11+
data = self.get_data(CREDENTIAL_API_PATH, opts)
12+
rv = json_to_mdm_object(data, CREDENTIAL_MDM_CLASS, [])
13+
parsed_body = JSON.parse(data.response.body)
14+
parsed_body.each do |cred|
15+
private_object = to_ar(cred['private_class'].constantize, cred['private'])
16+
rv[parsed_body.index(cred)].private = private_object
17+
end
18+
rv
1019
end
1120

1221
def create_credential(opts)
13-
self.post_data_async(CREDENTIAL_PATH, opts)
22+
self.post_data_async(CREDENTIAL_API_PATH, opts)
1423
end
1524
end

lib/metasploit/framework/data_service/remote/http/remote_host_data_service.rb

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,27 +3,28 @@
33
module RemoteHostDataService
44
include ResponseDataHelper
55

6-
HOST_PATH = '/api/1/msf/host'
7-
HOST_SEARCH_PATH = HOST_PATH + "/search"
6+
HOST_API_PATH = '/api/1/msf/host'
7+
HOST_SEARCH_PATH = HOST_API_PATH + "/search"
8+
HOST_MDM_CLASS = 'Mdm::Host'
89

910
def hosts(opts)
10-
json_to_open_struct_object(self.get_data(HOST_PATH, opts), [])
11+
json_to_mdm_object(self.get_data(HOST_API_PATH, opts), HOST_MDM_CLASS, [])
1112
end
1213

1314
def report_host(opts)
14-
json_to_open_struct_object(self.post_data(HOST_PATH, opts))
15+
json_to_mdm_object(self.post_data(HOST_API_PATH, opts), HOST_MDM_CLASS, []).first
1516
end
1617

1718
def find_or_create_host(opts)
18-
json_to_open_struct_object(self.post_data(HOST_PATH, opts))
19+
json_to_mdm_object(self.post_data(HOST_API_PATH, opts), HOST_MDM_CLASS, []).first
1920
end
2021

2122
def report_hosts(hosts)
22-
self.post_data(HOST_PATH, hosts)
23+
self.post_data(HOST_API_PATH, hosts)
2324
end
2425

2526
def delete_host(opts)
26-
json_to_open_struct_object(self.delete_data(HOST_PATH, opts))
27+
json_to_mdm_object(self.delete_data(HOST_API_PATH, opts), HOST_MDM_CLASS, [])
2728
end
2829

2930
# TODO: Remove? What is the purpose of this method?

lib/metasploit/framework/data_service/remote/http/remote_loot_data_service.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,12 @@
33
module RemoteLootDataService
44
include ResponseDataHelper
55

6-
LOOT_PATH = '/api/1/msf/loot'
6+
LOOT_API_PATH = '/api/1/msf/loot'
7+
LOOT_MDM_CLASS = 'Mdm::Loot'
78

89
def loot(opts = {})
910
# TODO: Add an option to toggle whether the file data is returned or not
10-
loots = json_to_open_struct_object(self.get_data(LOOT_PATH, opts), [])
11+
loots = json_to_mdm_object(self.get_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
1112
# Save a local copy of the file
1213
loots.each do |loot|
1314
if loot.data
@@ -19,14 +20,14 @@ def loot(opts = {})
1920
end
2021

2122
def report_loot(opts)
22-
self.post_data_async(LOOT_PATH, opts)
23+
self.post_data_async(LOOT_API_PATH, opts)
2324
end
2425

2526
def find_or_create_loot(opts)
26-
json_to_open_struct_object(self.post_data(LOOT_PATH, opts))
27+
json_to_mdm_object(self.post_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
2728
end
2829

2930
def report_loots(loot)
30-
self.post_data(LOOT_PATH, loot)
31+
self.post_data(LOOT_API_PATH, loot)
3132
end
3233
end

lib/metasploit/framework/data_service/remote/http/remote_session_data_service.rb

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
module RemoteSessionDataService
22

33
SESSION_API_PATH = '/api/1/msf/session'
4+
SESSION_MDM_CLASS = 'Mdm::Session'
45

56
def report_session(opts)
67
session = opts[:session]
@@ -12,7 +13,7 @@ def report_session(opts)
1213
end
1314

1415
opts[:time_stamp] = Time.now.utc
15-
sess_db = json_to_open_struct_object(self.post_data(SESSION_API_PATH, opts))
16+
sess_db = json_to_mdm_object(self.post_data(SESSION_API_PATH, opts), SESSION_MDM_CLASS, []).first
1617
session.db_record = sess_db
1718
end
1819

lib/metasploit/framework/data_service/remote/http/remote_session_event_data_service.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,15 @@
33
module RemoteSessionEventDataService
44
include ResponseDataHelper
55

6-
SESSION_EVENT_PATH = '/api/1/msf/session_event'
6+
SESSION_EVENT_API_PATH = '/api/1/msf/session_event'
7+
SESSION_EVENT_MDM_CLASS = 'Mdm::SessionEvent'
78

89
def session_events(opts = {})
9-
json_to_open_struct_object(self.get_data(SESSION_EVENT_PATH, opts), [])
10+
json_to_mdm_object(self.get_data(SESSION_EVENT_API_PATH, opts), SESSION_EVENT_MDM_CLASS, [])
1011
end
1112

1213
def report_session_event(opts)
1314
opts[:session] = opts[:session].db_record
14-
self.post_data_async(SESSION_EVENT_PATH, opts)
15+
self.post_data_async(SESSION_EVENT_API_PATH, opts)
1516
end
1617
end

0 commit comments

Comments
 (0)