2018-01-08 15:42:01 -06:00
|
|
|
# Commvault Communications Service execCmd Vulnerability
|
|
|
|
|
|
|
|
|
|
## Introduction
|
|
|
|
|
|
|
|
|
|
Commvault is a data protection and information management software; an enterprise-level data
|
|
|
|
|
platform that contains modules to back up, restore, archive, replicate, and search data.
|
|
|
|
|
|
|
|
|
|
According to public documentation, the data is protected by installing agent software on the
|
|
|
|
|
physical or virtual hosts, which use the OS or application native APIs to protect data in a
|
2018-01-09 11:20:02 -06:00
|
|
|
consistent state. Production data is processed by the agent on client computers and backed
|
2018-01-08 15:42:01 -06:00
|
|
|
up through a data manager (the MediaAgent) to disk, tape, or cloud storage. All data
|
|
|
|
|
management activity in the environment is tracked by a centralized server (called CommServe),
|
|
|
|
|
and can be managed by administrators through a central user interface. End users can access
|
|
|
|
|
protected data using web browsers or mobile devices.
|
|
|
|
|
|
|
|
|
|
One of the base services of Commvault is vulnerable to a remote command injection attack,
|
|
|
|
|
specifically the cvd service.
|
|
|
|
|
|
2017-12-22 14:34:35 -05:00
|
|
|
## Vulnerable Application
|
|
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
Commvault v11 SP5 or prior are vulnerable to this vulnerability. The specific vulnerable
|
|
|
|
|
version I tested was 11.0.80.0.
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
The version of the vulnerable DLL is:
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
```
|
|
|
|
|
Image path: C:\Program Files\Commvault\ContentStore\Base\CVDataPipe.dll
|
|
|
|
|
Image name: CVDataPipe.dll
|
|
|
|
|
Timestamp: Wed Dec 21 11:59:21 2016 (585AC2F9)
|
|
|
|
|
CheckSum: 002ED404
|
|
|
|
|
ImageSize: 002F0000
|
|
|
|
|
File version: 11.80.50.60437
|
|
|
|
|
Product version: 11.0.0.0
|
|
|
|
|
File flags: 1 (Mask 3F) Debug
|
|
|
|
|
File OS: 40004 NT Win32
|
|
|
|
|
File type: 1.0 App
|
|
|
|
|
File date: 00000000.00000000
|
|
|
|
|
Translations: 0409.04b0
|
|
|
|
|
CompanyName: Commvault
|
|
|
|
|
ProductName: Commvault
|
|
|
|
|
InternalName: CVDataPipe
|
|
|
|
|
OriginalFilename: CVDataPipe.dll
|
|
|
|
|
ProductVersion: 11.0.0.0
|
|
|
|
|
FileVersion: 11.80.50.60437
|
|
|
|
|
PrivateBuild:
|
|
|
|
|
SpecialBuild:
|
|
|
|
|
FileDescription:
|
|
|
|
|
LegalCopyright: Copyright (c) 2000-2016
|
|
|
|
|
LegalTrademarks:
|
|
|
|
|
Comments:
|
|
|
|
|
```
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
## Root Cause Analysis
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
Usually, there are two ways to execute a command in a C/C++ application, one of them is ```WinExec()```,
|
|
|
|
|
and the other one is ```CreateProcess()```:
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
```
|
|
|
|
|
BOOL WINAPI CreateProcess(
|
|
|
|
|
_In_opt_ LPCTSTR lpApplicationName,
|
|
|
|
|
_Inout_opt_ LPTSTR lpCommandLine,
|
|
|
|
|
_In_opt_ LPSECURITY_ATTRIBUTES lpProcessAttributes,
|
|
|
|
|
_In_opt_ LPSECURITY_ATTRIBUTES lpThreadAttributes,
|
|
|
|
|
_In_ BOOL bInheritHandles,
|
|
|
|
|
_In_ DWORD dwCreationFlags,
|
|
|
|
|
_In_opt_ LPVOID lpEnvironment,
|
|
|
|
|
_In_opt_ LPCTSTR lpCurrentDirectory,
|
|
|
|
|
_In_ LPSTARTUPINFO lpStartupInfo,
|
|
|
|
|
_Out_ LPPROCESS_INFORMATION lpProcessInformation
|
|
|
|
|
);
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
```
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
Since ```CreateProcess()``` is meant to replace ```WinExec()``` according to Microsoft, we can create a
|
2018-01-09 11:20:02 -06:00
|
|
|
breakpoint there first in our debugger (WinDBG), and we hit it:
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
```
|
|
|
|
|
0:044> g
|
|
|
|
|
Breakpoint 3 hit
|
|
|
|
|
kernel32!CreateProcessA:
|
|
|
|
|
00000000`76fe8730 4c8bdc mov r11,rsp
|
|
|
|
|
```
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
Looking at the callstack of this ```kernel32!CreateProcessA```, we already have a pretty good idea
|
|
|
|
|
locating the vulnerability:
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
```
|
|
|
|
|
0:044> k
|
|
|
|
|
Child-SP RetAddr Call Site
|
|
|
|
|
00000000`11a36b78 000007fe`f378a40f kernel32!CreateProcessA
|
|
|
|
|
00000000`11a36b80 000007fe`f377714e CVDataPipe!execCmd+0x7af
|
|
|
|
|
00000000`11a3f340 000007fe`f3777a69 CVDataPipe!CVDMessageHandler+0x78e
|
|
|
|
|
00000000`11a3fbd0 000007fe`f9cdc58d CVDataPipe!CVDMessageHandler+0x10a9
|
|
|
|
|
00000000`11a3fd40 000007fe`f9cdc1b1 CvBasicLib!CvThreadPool::th_defaultWorkerObj+0x3cd
|
|
|
|
|
00000000`11a3fe40 000007fe`f9cd2073 CvBasicLib!CvThreadPool::th_defaultWorker+0x51
|
|
|
|
|
00000000`11a3fe90 000007fe`f9a84f7f CvBasicLib!CvThread::~CvThread+0x63
|
|
|
|
|
00000000`11a3fee0 000007fe`f9a85126 MSVCR120!_callthreadstartex+0x17 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 376]
|
|
|
|
|
00000000`11a3ff10 00000000`76f6f56d MSVCR120!_threadstartex+0x102 [f:\dd\vctools\crt\crtw32\startup\threadex.c @ 354]
|
|
|
|
|
00000000`11a3ff40 00000000`770a3281 kernel32!BaseThreadInitThunk+0xd
|
|
|
|
|
00000000`11a3ff70 00000000`00000000 ntdll!RtlUserThreadStart+0x1d
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
There are two things that are interesting. One of them is ```CVDataPipe!CVDMessageHandler```, and the
|
|
|
|
|
other one is ```CVDataPipe!execCmd```.
|
|
|
|
|
|
|
|
|
|
```CVDataPipe!CVDMessageHandler``` is basically a function that handles our packet's message type.
|
|
|
|
|
The Metasploit exploit specifically sends a code of ```9h```, which is the message type for ```execCmd```:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
.text:0000000180147103 loc_180147103: ; CODE XREF: CVDMessageHandler(int,selectStruct_t *,CQiSocket,void *):loc_180146D78j
|
|
|
|
|
.text:0000000180147103 lea rax, [rsp+888h+var_220] ; jumptable 0000000180146D78 case 9
|
|
|
|
|
.text:000000018014710B mov [rsp+888h+var_600], rax
|
|
|
|
|
.text:0000000180147113 mov rdx, [rsp+888h+sock]
|
|
|
|
|
.text:000000018014711B mov rcx, [rsp+888h+var_600]
|
|
|
|
|
.text:0000000180147123 call cs:??0CQiSocket@@QEAA@AEBV0@@Z ; CQiSocket::CQiSocket(CQiSocket const &)
|
|
|
|
|
.text:0000000180147129 mov [rsp+888h+var_5F0], rax
|
|
|
|
|
.text:0000000180147131 mov r8, [rsp+888h+arg_18]
|
|
|
|
|
.text:0000000180147139 mov rdx, [rsp+888h+var_5F0]
|
|
|
|
|
.text:0000000180147141 mov rcx, [rsp+888h+structSelect]
|
|
|
|
|
.text:0000000180147149 call ?execCmd@@YAXPEAUselectStruct_t@@VCQiSocket@@PEAX@Z ; execCmd(selectStruct_t *,CQiSocket,void *)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
If we take a closer look at the ```execCmd``` function, we can tell the purpose of it is for processes such as:
|
|
|
|
|
|
|
|
|
|
* ifind (For restoring purposes)
|
|
|
|
|
* BackupShadow.exe (For archiving)
|
|
|
|
|
* Pub (Map file)
|
|
|
|
|
* createIndex (A Commvault process for building index)
|
2017-12-22 14:34:35 -05:00
|
|
|
|
2018-01-08 15:42:01 -06:00
|
|
|
|
|
|
|
|
```
|
|
|
|
|
.text:0000000180159F1B loc_180159F1B: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+261j
|
|
|
|
|
.text:0000000180159F1B ; DATA XREF: .rdata:0000000180286258o
|
|
|
|
|
.text:0000000180159F1B lea rdx, aIfind ; "ifind"
|
|
|
|
|
.text:0000000180159F22 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
|
|
|
|
.text:0000000180159F2A call cs:strstr
|
|
|
|
|
.text:0000000180159F30 test rax, rax
|
|
|
|
|
.text:0000000180159F33 jnz short loc_180159F6D
|
|
|
|
|
.text:0000000180159F35 lea rdx, aBackupshadow_e ; "BackupShadow.exe"
|
|
|
|
|
.text:0000000180159F3C lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
|
|
|
|
.text:0000000180159F44 call cs:strstr
|
|
|
|
|
.text:0000000180159F4A test rax, rax
|
|
|
|
|
.text:0000000180159F4D jnz short loc_180159F6D
|
|
|
|
|
.text:0000000180159F4F lea rdx, aPub ; "Pub"
|
|
|
|
|
.text:0000000180159F56 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
|
|
|
|
.text:0000000180159F5E call cs:strstr
|
|
|
|
|
...
|
|
|
|
|
.text:000000018015A0BA loc_18015A0BA: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+307j
|
|
|
|
|
.text:000000018015A0BA lea rdx, aCreateindex ; "createIndex"
|
|
|
|
|
.text:000000018015A0C1 lea rcx, [rsp+87B8h+ApplicationName] ; Str
|
|
|
|
|
.text:000000018015A0C9 call cs:strstr
|
|
|
|
|
.text:000000018015A0CF test rax, rax
|
|
|
|
|
.text:000000018015A0D2 jz loc_18015A220
|
|
|
|
|
```
|
|
|
|
|
|
2018-01-09 11:20:02 -06:00
|
|
|
However, if you don't call one of these processes, the ```execCmd``` will assume you want to run your
|
2018-01-08 15:42:01 -06:00
|
|
|
custom process, and pass it to ```CreateProcess``` anyway:
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
.text:000000018015A361 loc_18015A361: ; CODE XREF: execCmd(selectStruct_t *,CQiSocket,void *)+675j
|
|
|
|
|
.text:000000018015A361 call cs:GetEnvironmentStrings
|
|
|
|
|
.text:000000018015A367 mov [rsp+87B8h+var_86A8], rax
|
|
|
|
|
.text:000000018015A36F lea rax, [rsp+87B8h+StartupInfo]
|
|
|
|
|
.text:000000018015A377 mov rdi, rax
|
|
|
|
|
.text:000000018015A37A xor eax, eax
|
|
|
|
|
.text:000000018015A37C mov ecx, 68h
|
|
|
|
|
.text:000000018015A381 rep stosb
|
|
|
|
|
.text:000000018015A383 mov [rsp+87B8h+StartupInfo.cb], 68h
|
|
|
|
|
.text:000000018015A38E lea rax, [rsp+87B8h+ProcessInformation]
|
|
|
|
|
.text:000000018015A396 mov rdi, rax
|
|
|
|
|
.text:000000018015A399 xor eax, eax
|
|
|
|
|
.text:000000018015A39B mov ecx, 18h
|
|
|
|
|
.text:000000018015A3A0 rep stosb
|
|
|
|
|
.text:000000018015A3A2 mov [rsp+87B8h+StartupInfo.dwFlags], 1
|
|
|
|
|
.text:000000018015A3AD xor eax, eax
|
|
|
|
|
.text:000000018015A3AF mov [rsp+87B8h+StartupInfo.wShowWindow], ax
|
|
|
|
|
.text:000000018015A3B7 lea rax, [rsp+87B8h+ProcessInformation]
|
|
|
|
|
.text:000000018015A3BF mov [rsp+87B8h+lpProcessInformation], rax ; lpProcessInformation
|
|
|
|
|
.text:000000018015A3C4 lea rax, [rsp+87B8h+StartupInfo]
|
|
|
|
|
.text:000000018015A3CC mov [rsp+87B8h+lpStartupInfo], rax ; lpStartupInfo
|
|
|
|
|
.text:000000018015A3D1 mov [rsp+87B8h+lpCurrentDirectory], 0 ; lpCurrentDirectory
|
|
|
|
|
.text:000000018015A3DA mov [rsp+87B8h+lpEnvironment], 0 ; lpEnvironment
|
|
|
|
|
.text:000000018015A3E3 mov [rsp+87B8h+dwCreationFlags], 10h ; dwCreationFlags
|
|
|
|
|
.text:000000018015A3EB mov [rsp+87B8h+bInheritHandles], 0 ; bInheritHandles
|
|
|
|
|
.text:000000018015A3F3 xor r9d, r9d ; lpThreadAttributes
|
|
|
|
|
.text:000000018015A3F6 xor r8d, r8d ; lpProcessAttributes
|
|
|
|
|
.text:000000018015A3F9 lea rdx, [rsp+87B8h+CommandLine] ; lpCommandLine
|
|
|
|
|
.text:000000018015A401 lea rcx, [rsp+87B8h+ApplicationName] ; lpApplicationName
|
|
|
|
|
.text:000000018015A409 call cs:CreateProcessA
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
It is unclear whether allowing an arbitrary custom process is intentional or not, it is unsafe
|
2018-01-09 11:20:02 -06:00
|
|
|
anyway considering the cvd process binds to 0.0.0.0, so anybody can gain access to it under the
|
|
|
|
|
context of SYSTEM.
|
2018-01-08 15:42:01 -06:00
|
|
|
|
|
|
|
|
## Using the Metasploit Module
|
|
|
|
|
|
|
|
|
|
1. Start msfconsole
|
|
|
|
|
2. `use exploit/windows/misc/commvault_cmd_exec`
|
|
|
|
|
3. `set RHOST [ip]`
|
|
|
|
|
4. `exploit`
|
2017-12-22 14:34:35 -05:00
|
|
|
5. shellz :)
|
2018-01-08 15:42:01 -06:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## References
|
|
|
|
|
|
|
|
|
|
* https://en.wikipedia.org/wiki/Commvault
|
|
|
|
|
* https://www.securifera.com/advisories/sec-2017-0001/
|