Updated indentation to use tabs

unstable
Tasos Laskos 2012-11-16 23:11:48 +02:00
parent 4051cb3296
commit c659b37c94
19 changed files with 518 additions and 510 deletions

View File

@ -1,7 +1,7 @@
class Anemone::Extractors::Anchors < Anemone::Extractors::Base
def run
doc.search( '//a[@href]' ).map { |a| a['href'] }
end
def run
doc.search( '//a[@href]' ).map { |a| a['href'] }
end
end

View File

@ -8,5 +8,5 @@ class Anemone::Extractors::Dirbuster < Anemone::Extractors::Base
return @@dirs if @@dirs
@@dirs = IO.read( File.dirname( __FILE__ ) + '/dirbuster/directories' ).split( "\n" )
end
end

View File

@ -1,7 +1,7 @@
class Anemone::Extractors::Forms < Anemone::Extractors::Base
def run
doc.search( '//form[@action]' ).map { |a| a['action'] }
end
def run
doc.search( '//form[@action]' ).map { |a| a['action'] }
end
end

View File

@ -1,7 +1,7 @@
class Anemone::Extractors::Frames < Anemone::Extractors::Base
def run
doc.css( 'frame', 'iframe' ).map { |a| a.attributes['src'].content rescue next }
end
def run
doc.css( 'frame', 'iframe' ).map { |a| a.attributes['src'].content rescue next }
end
end

View File

@ -3,48 +3,48 @@ require 'uri'
class Anemone::Extractors::Generic < Anemone::Extractors::Base
def run
URI.extract( doc.to_s, %w(http https) ).map do |u|
#
# This extractor needs to be a tiny bit intelligent because
# due to its generic nature it'll inevitably match some garbage.
#
# For example, if some JS code contains:
#
# var = 'http://blah.com?id=1'
#
# or
#
# var = { 'http://blah.com?id=1', 1 }
#
#
# The URI.extract call will match:
#
# http://blah.com?id=1'
#
# and
#
# http://blah.com?id=1',
#
# respectively.
#
if !includes_quotes?( u )
u
else
if html.include?( "'#{u}" )
u.split( '\'' ).first
elsif html.include?( "\"#{u}" )
u.split( '"' ).first
else
u
end
end
end
rescue
[]
end
URI.extract( doc.to_s, %w(http https) ).map do |u|
#
# This extractor needs to be a tiny bit intelligent because
# due to its generic nature it'll inevitably match some garbage.
#
# For example, if some JS code contains:
#
# var = 'http://blah.com?id=1'
#
# or
#
# var = { 'http://blah.com?id=1', 1 }
#
#
# The URI.extract call will match:
#
# http://blah.com?id=1'
#
# and
#
# http://blah.com?id=1',
#
# respectively.
#
if !includes_quotes?( u )
u
else
if html.include?( "'#{u}" )
u.split( '\'' ).first
elsif html.include?( "\"#{u}" )
u.split( '"' ).first
else
u
end
end
end
rescue
[]
end
def includes_quotes?( url )
url.include?( '\'' ) || url.include?( '"' )
end
def includes_quotes?( url )
url.include?( '\'' ) || url.include?( '"' )
end
end

View File

@ -1,7 +1,7 @@
class Anemone::Extractors::Links < Anemone::Extractors::Base
def run
doc.search( "//link[@href]" ).map { |a| a['href'] }
end
def run
doc.search( "//link[@href]" ).map { |a| a['href'] }
end
end

View File

@ -1,24 +1,24 @@
class Anemone::Extractors::MetaRefresh < Anemone::Extractors::Base
def run
doc.search( "//meta[@http-equiv='refresh']" ).map do |url|
begin
_, url = url['content'].split( ';', 2 )
next if !url
unquote( url.split( '=', 2 ).last )
rescue
next
end
end
rescue
nil
end
def run
doc.search( "//meta[@http-equiv='refresh']" ).map do |url|
begin
_, url = url['content'].split( ';', 2 )
next if !url
unquote( url.split( '=', 2 ).last )
rescue
next
end
end
rescue
nil
end
def unquote( str )
[ '\'', '"' ].each do |q|
return str[1...-1] if str.start_with?( q ) && str.end_with?( q )
end
str
end
def unquote( str )
[ '\'', '"' ].each do |q|
return str[1...-1] if str.start_with?( q ) && str.end_with?( q )
end
str
end
end

View File

@ -1,7 +1,7 @@
class Anemone::Extractors::Scripts < Anemone::Extractors::Base
def run
doc.search( '//script[@src]' ).map { |a| a['src'] }
end
def run
doc.search( '//script[@src]' ).map { |a| a['src'] }
end
end

View File

@ -21,7 +21,7 @@ module Auxiliary::Web
include Auxiliary::Report
attr_reader :target
attr_reader :http
attr_reader :http
attr_reader :parent
attr_reader :page
@ -29,15 +29,15 @@ module Auxiliary::Web
super
end
# String id to push to the #checklist
def checked( id )
parent.checklist << "#{shortname}#{id}".hash
end
# String id to push to the #checklist
def checked( id )
parent.checklist << "#{shortname}#{id}".hash
end
# String id to check against the #checklist
def checked?( id )
parent.checklist.include? "#{shortname}#{id}".hash
end
# String id to check against the #checklist
def checked?( id )
parent.checklist.include? "#{shortname}#{id}".hash
end
#
# Called directly before 'run'
@ -46,7 +46,7 @@ module Auxiliary::Web
@parent = opts[:parent]
@target = opts[:target]
@page = opts[:page]
@http = opts[:http]
@http = opts[:http]
end
# Should be overridden to return the exploits to use for this
@ -69,7 +69,7 @@ module Auxiliary::Web
#
# Should be overridden to return a pattern to be matched against response
# bodies in order to identify a vulnerability.
# bodies in order to identify a vulnerability.
#
# You can go one deeper and override #find_proof for more complex processing.
#
@ -92,34 +92,34 @@ module Auxiliary::Web
end
end
# Checks whether a resource exists based on a path String.
# Checks whether a resource exists based on a path String.
def resource_exist?( path )
res = http.get( path )
res.code.to_i == 200 && !http.custom_404?( path, res.body )
end
alias :file_exist? :resource_exist?
# Checks whether a directory exists based on a path String.
# Checks whether a directory exists based on a path String.
def directory_exist?( path )
dir = path.dup
dir << '/' if !dir.end_with?( '/' )
resource_exist?( dir )
end
# Logs the existence of a resource in the path String.
# Logs the existence of a resource in the path String.
def log_resource_if_exists( path )
log_resource( :location => path ) if resource_exist?( path )
end
alias :log_file_if_exists :log_resource_if_exists
# Logs the existence of the directory in the path String.
# Logs the existence of the directory in the path String.
def log_directory_if_exists( path )
dir = path.dup
dir << '/' if !dir.end_with?( '/' )
log_resource_if_exists( dir )
end
# Matches fingerprint pattern against the current page's body and logs matches
# Matches fingerprint pattern against the current page's body and logs matches
def match_and_log_fingerprint( fingerprint )
page.body.to_s.match( fingerprint ) && log_fingerprint( :fingerprint => fingerprint )
end
@ -149,35 +149,38 @@ module Auxiliary::Web
parent.increment_request_counter
end
# Should be overridden and return an Integer (0-100) denoting the confidence
# in the accuracy of the logged vuln.
# Should be overridden and return an Integer (0-100) denoting the confidence
# in the accuracy of the logged vuln.
def calculate_confidence( vuln )
100
end
def log_fingerprint( opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, opts[:fingerprint], mode, opts[:location]].map { |x| x.to_s }.join( '|' ).hash
vhash = [target.to_url, opts[:fingerprint], mode, opts[:location]].
map { |x| x.to_s }.join( '|' ).hash
return if parent.vulns.include?( vhash )
parent.vulns[vhash] = true
location = opts[:location] ? page.url.merge( URI( opts[:location].to_s )) : page.url
location = opts[:location] ?
page.url.merge( URI( opts[:location].to_s )) : page.url
info = {
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:fingerprint],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:fingerprint],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => details[:category] || opts[:confidence] || 100,
:owner => self
:owner => self
}
report_web_vuln( info )
@ -188,28 +191,28 @@ module Auxiliary::Web
def log_resource( opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, mode, opts[:location]].map { |x| x.to_s }.join( '|' ).hash
vhash = [target.to_url, mode, opts[:location]].
map { |x| x.to_s }.join( '|' ).hash
return if parent.vulns.include?( vhash )
parent.vulns[vhash] = true
parent.vulns[vhash] = true
location = URI( opts[:location].to_s )
info = {
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:location],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:web_site => target.site,
:path => location.path,
:query => location.query,
:method => 'GET',
:params => [],
:pname => 'path',
:proof => opts[:location],
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => details[:category] || opts[:confidence] || 100,
#:payload => nil,
:owner => self
:owner => self
}
report_web_vuln( info )
@ -220,23 +223,24 @@ module Auxiliary::Web
def process_vulnerability( element, proof, opts = {} )
mode = details[:category].to_sym
vhash = [target.to_url, mode, element.altered].map{ |x| x.to_s }.join( '|' ).hash
vhash = [target.to_url, mode, element.altered].
map{ |x| x.to_s }.join( '|' ).hash
parent.vulns[mode] ||= {}
return parent.vulns[mode][vhash] if parent.vulns[mode][vhash]
parent.vulns[mode][vhash] = {
:target => target,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:mode => mode,
:pname => element.altered,
:proof => proof,
:form => element.model,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:target => target,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:mode => mode,
:pname => element.altered,
:proof => proof,
:form => element.model,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description]
}
@ -252,29 +256,28 @@ module Auxiliary::Web
uri = URI( element.action )
info = {
:web_site => element.model.web_site,
:path => uri.path,
:query => uri.query,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:pname => element.altered,
:proof => proof,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:web_site => element.model.web_site,
:path => uri.path,
:query => uri.query,
:method => element.method.to_s.upcase,
:params => element.params.to_a,
:pname => element.altered,
:proof => proof,
:risk => details[:risk],
:name => details[:name],
:blame => details[:blame],
:category => details[:category],
:description => details[:description],
:confidence => confidence,
:payload => payload,
:owner => self
:payload => payload,
:owner => self
}
report_web_vuln( info )
print_good " VULNERABLE(#{mode.to_s.upcase}) URL(#{target.to_url}) PARAMETER(#{element.altered}) VALUES(#{element.params})"
print_good " VULNERABLE(#{mode.to_s.upcase}) URL(#{target.to_url})" +
" PARAMETER(#{element.altered}) VALUES(#{element.params})"
print_good " PROOF(#{proof})"
return
end
end

View File

@ -20,29 +20,29 @@ module Analysis::Differential
#
# Fuzzer must provide:
# - #boolean_seeds_for - array of boolean injection strings
# (these are supposed to not alter the webapp behavior when interpreted)
# (these are supposed to not alter the webapp behavior when interpreted)
# - #fault_seeds_for - array of fault injection strings
# (these are supposed to force erroneous conditions when interpreted)
# (these are supposed to force erroneous conditions when interpreted)
#
# Here's how it goes:
# * let _default_ be the default/original response
# * let _fault_ be the response of the fault injection
# * let _bool_ be the response of the boolean injection
# * let _bool_ be the response of the boolean injection
#
# A vulnerability is logged if:
# default == bool AND bool.code == 200 AND fault != bool
# default == bool AND bool.code == 200 AND fault != bool
#
# The "bool" response is also checked in order to determine if it's a custom 404,
# if it is it'll be skipped.
#
# @param [Hash] opts Options Hash (default: {})
# :precision - amount of refinement iterations (default: 2)
# @param [Hash] opts Options Hash (default: {})
# :precision - amount of refinement iterations (default: 2)
#
def differential_analysis( opts = {}, &block )
opts = DIFFERENTIAL_OPTIONS.merge( opts )
return if fuzzed? :type => :differential
fuzzed :type => :differential
return if fuzzed? :type => :differential
fuzzed :type => :differential
# don't continue if there's a missing value
params.values.each { |val| return if !val || val.empty? }
@ -64,10 +64,10 @@ module Analysis::Differential
opts[:precision].times do
# get the default responses
submit_async do |res|
responses[:orig] ||= res.body.to_s
# remove context-irrelevant dynamic content like banners and such
responses[:orig] = Rex::Text.refine( responses[:orig], res.body.to_s )
end
responses[:orig] ||= res.body.to_s
# remove context-irrelevant dynamic content like banners and such
responses[:orig] = Rex::Text.refine( responses[:orig], res.body.to_s )
end
end
# perform fault injection opts[:precision] amount of times and
@ -76,18 +76,18 @@ module Analysis::Differential
opts[:precision].times do
params.map do |name, value|
fuzzer.fault_seeds_for( value ).map { |seed| permutation_for( name, seed ) }
end.flatten.uniq.each do |elem|
end.flatten.uniq.each do |elem|
# submit the mutation and store the response
elem.submit_async do |res|
responses[:bad][elem.altered] ||= res.body.to_s.dup
responses[:bad][elem.altered] ||= res.body.to_s.dup
# remove context-irrelevant dynamic content like banners and such
# from the error page
responses[:bad][elem.altered] =
Rex::Text.refine( responses[:bad][elem.altered], res.body.to_s.dup )
end
end
# remove context-irrelevant dynamic content like banners and such
# from the error page
responses[:bad][elem.altered] =
Rex::Text.refine( responses[:bad][elem.altered], res.body.to_s.dup )
end
end
end
# get injection variations that will not affect the outcome of the query
@ -97,40 +97,38 @@ module Analysis::Differential
# submit the mutation and store the response
elem.submit_async do |res|
responses[:good][elem.altered] ||= []
# save the response and some data for analysis
responses[:good][elem.altered] << {
'res' => res,
'elem' => elem
}
end
responses[:good][elem.altered] ||= []
# save the response and some data for analysis
responses[:good][elem.altered] << {
'res' => res,
'elem' => elem
}
end
end
http.after_run do
http.after_run do
responses[:good].keys.each do |key|
responses[:good][key].each do |res|
responses[:good].keys.each do |key|
responses[:good][key].each do |res|
# if default_response_body == bool_response_body AND
# fault_response_body != bool_response_body AND
# bool_response_code == 200
if responses[:orig] == res['res'].body &&
responses[:bad][key] != res['res'].body &&
res['res'].code.to_i == 200
# if default_response_body == bool_response_body AND
# fault_response_body != bool_response_body AND
# bool_response_code == 200
if responses[:orig] == res['res'].body &&
responses[:bad][key] != res['res'].body &&
res['res'].code.to_i == 200
# check to see if the current boolean response we're analyzing
# is a custom 404 page
http.if_not_custom_404( action, res['res'].body ) do
# if this isn't a custom 404 page then it means that
# the element is vulnerable, so go ahead and log the issue
fuzzer.process_vulnerability( res['elem'], 'Manipulatable responses.',
:payload => res['elem'].altered_value )
end
end
end
end
end
# check to see if the current boolean response we're analyzing
# is a custom 404 page
http.if_not_custom_404( action, res['res'].body ) do
# if this isn't a custom 404 page then it means that
# the element is vulnerable, so go ahead and log the issue
fuzzer.process_vulnerability( res['elem'], 'Manipulatable responses.',
:payload => res['elem'].altered_value )
end
end
end
end
end
end
end

View File

@ -20,10 +20,10 @@ module Analysis::Taint
# opts - Options Hash (default: {})
#
def taint_analysis( opts = {} )
return if fuzzed? :type => :taint
fuzzed :type => :taint
return if fuzzed? :type => :taint
fuzzed :type => :taint
fuzz_async do |response, permutation|
fuzz_async do |response, permutation|
next if !response || !(proof = fuzzer.find_proof( response, permutation ))
fuzzer.process_vulnerability( permutation, proof )
end

View File

@ -37,9 +37,9 @@ module Analysis::Timing
# * Logs the vulnerability.
#
# opts - Options Hash (default: {})
# :timeout - Integer amount of seconds to wait for the request to complete (default: 5)
# :timeout - Integer amount of seconds to wait for the request to complete (default: 5)
# :stub - String stub to be replaced by delay * multi (default: __TIME__)
# :multi - Integer multiplier (stub = timeout * multi) (default: 1)
# :multi - Integer multiplier (stub = timeout * multi) (default: 1)
#
def timeout_analysis( opts = {} )
opts = TIMING_OPTIONS.merge( opts )
@ -47,8 +47,8 @@ module Analysis::Timing
multi = opts[:multi]
stub = opts[:stub]
return if fuzzed? :type => :timing
fuzzed :type => :timing
return if fuzzed? :type => :timing
fuzzed :type => :timing
permutations.each do |p|
timeout = opts[:delay]
@ -57,48 +57,46 @@ module Analysis::Timing
payload = fuzzer.payloads.select{ |pl| seed.include?( pl ) }.first
# 1st pass, make sure the webapp is responsive
if_responsive do
# 2nd pass, see if we can manipulate the response times
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
if_responsive do
# 2nd pass, see if we can manipulate the response times
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
p.if_unresponsive( timeout - 1 ) do
# 3rd pass, make sure that the previous step wasn't a fluke (like a dead web server)
if_responsive do
p.if_unresponsive( timeout - 1 ) do
# 3rd pass, make sure that the previous step wasn't a fluke (like a dead web server)
if_responsive do
# 4th pass, increase the delay and timeout to make sure that we are the ones
# manipulating the webapp and this isn't all a coincidence
timeout *= 2
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
# 4th pass, increase the delay and timeout to make sure that we are the ones
# manipulating the webapp and this isn't all a coincidence
timeout *= 2
timeout += 1
p.altered_value = seed.gsub( stub, (timeout * multi).to_s )
p.if_unresponsive( timeout - 1 ) do
# log it!
fuzzer.process_vulnerability( p, 'Manipulatable response times.',
:payload => payload.gsub( stub, (timeout * multi).to_s ) )
end
end
end
end
p.if_unresponsive( timeout - 1 ) do
# log it!
fuzzer.process_vulnerability( p, 'Manipulatable response times.',
:payload => payload.gsub( stub, (timeout * multi).to_s ) )
end
end
end
end
end
end
def responsive?( timeout = 120 )
!submit( :timeout => timeout ).timed_out?
end
!submit( :timeout => timeout ).timed_out?
end
def responsive_async?( timeout = 120, &callback )
submit_async( :timeout => timeout ) { |r| callback.call !r.timed_out? }
end
def responsive_async?( timeout = 120, &callback )
submit_async( :timeout => timeout ) { |r| callback.call !r.timed_out? }
end
def if_responsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if b }
end
def if_responsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if b }
end
def if_unresponsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if !b }
end
def if_unresponsive( timeout = 120, &callback )
responsive_async?( timeout ) { |b| callback.call if !b }
end
end
end

View File

@ -90,7 +90,7 @@ class Form < Fuzzable
#
def params
@params ||= inputs.reject{ |i| i[:name].to_s.empty? }.
inject( {} ) { |h, i| h[i[:name]] = i[:value]; h }
inject( {} ) { |h, i| h[i[:name]] = i[:value]; h }
end
#
@ -125,11 +125,12 @@ class Form < Fuzzable
return {} if query.empty?
query.split( '&' ).inject( {} ) do |h, pair|
k, v = pair.to_s.split( '=', 2 )
k, v = pair.to_s.split( '=', 2 )
h[Rex::Text.uri_decode( k.to_s )] = Rex::Text.uri_decode( v.to_s )
h
end
end
def query_to_params( query )
self.class.query_to_params( query)
end
@ -228,11 +229,13 @@ class Form < Fuzzable
def self.from_model( form )
inputs = form.params.map do |name, value, extra|
extra = extra.first if extra.is_a? Array
extra ||= {}
extra = extra.first if extra.is_a? Array
extra ||= {}
{ :name => name, :value => value, :type => extra[:type] }
end
e = new( :action => "#{form.path}?#{form.query}", :method => form.method, :inputs => inputs )
end
e = new( :action => "#{form.path}?#{form.query}", :method => form.method,
:inputs => inputs )
e.model = form
e
end

View File

@ -22,46 +22,46 @@ class Fuzzable
attr_accessor :fuzzer
def fuzzed?( opts = {} )
fuzzer.checked? fuzz_id( opts )
end
def fuzzed?( opts = {} )
fuzzer.checked? fuzz_id( opts )
end
def fuzzed( opts = {} )
fuzzer.checked fuzz_id( opts )
end
def fuzzed( opts = {} )
fuzzer.checked fuzz_id( opts )
end
def fuzz_id( opts = {} )
"#{opts[:type]}:#{fuzzer.shortname}:#{method}:#{action}:#{params.keys.sort.to_s}:#{altered}=#{altered_value}"
end
def fuzz_id( opts = {} )
"#{opts[:type]}:#{fuzzer.shortname}:#{method}:#{action}:#{params.keys.sort.to_s}:#{altered}=#{altered_value}"
end
def fuzz( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| callback.call( p.submit, p ) }
end
def fuzz( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| callback.call( p.submit, p ) }
end
def fuzz_async( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| p.submit_async { |res| callback.call( res, p ) } }
end
def fuzz_async( cfuzzer = nil, &callback )
fuzz_wrapper( cfuzzer ) { |p| p.submit_async { |res| callback.call( res, p ) } }
end
def submit( opts = {} )
fuzzer.increment_request_counter
def submit( opts = {} )
fuzzer.increment_request_counter
resp = http.request_async( *request( opts ) )
handle_response( resp )
resp
end
resp = http.request_async( *request( opts ) )
handle_response( resp )
resp
end
def submit_async( opts = {}, &callback )
fuzzer.increment_request_counter
def submit_async( opts = {}, &callback )
fuzzer.increment_request_counter
http.request_async( *request( opts ) ) do |resp|
handle_response( resp )
callback.call resp if callback
end
http.request_async( *request( opts ) ) do |resp|
handle_response( resp )
callback.call resp if callback
end
nil
end
nil
end
def http
def http
fuzzer.http
end
@ -79,28 +79,29 @@ class Fuzzable
ce = Marshal.load( Marshal.dump( self ) )
self.fuzzer = ce.fuzzer = cf
ce
end
end
private
def fuzz_wrapper( cfuzzer = nil, &block )
self.fuzzer ||= cfuzzer
permutations.each do |p|
block.call p
end
end
private
def fuzz_wrapper( cfuzzer = nil, &block )
self.fuzzer ||= cfuzzer
permutations.each do |p|
block.call p
end
end
def handle_response( resp )
str = " #{fuzzer.shortname}: #{resp.code} - #{method.to_s.upcase} #{action} #{params}"
def handle_response( resp )
str = " #{fuzzer.shortname}: #{resp.code} - #{method.to_s.upcase}" +
" #{action} #{params}"
case resp.code.to_i
when 200,404,301,302,303
#fuzzer.print_status str
when 500,503,401,403
fuzzer.print_good str
else
fuzzer.print_error str
end
end
case resp.code.to_i
when 200,404,301,302,303
#fuzzer.print_status str
when 500,503,401,403
fuzzer.print_good str
else
fuzzer.print_error str
end
end
end

View File

@ -10,70 +10,70 @@ require 'uri'
module Msf
class Auxiliary::Web::HTTP
class Request
attr_accessor :url
attr_reader :opts
attr_reader :callbacks
class Request
attr_accessor :url
attr_reader :opts
attr_reader :callbacks
def initialize( url, opts = {}, &callback )
@url = url.to_s.dup
@opts = opts.dup
def initialize( url, opts = {}, &callback )
@url = url.to_s.dup
@opts = opts.dup
@opts[:method] ||= :get
@opts[:method] ||= :get
@callbacks = [callback].compact
end
@callbacks = [callback].compact
end
def method
opts[:method]
end
def method
opts[:method]
end
def handle_response( response )
callbacks.each { |c| c.call response }
end
end
def handle_response( response )
callbacks.each { |c| c.call response }
end
end
class Response < Rex::Proto::Http::Response
class Response < Rex::Proto::Http::Response
def self.from_rex_response( response )
return empty if !response
def self.from_rex_response( response )
return empty if !response
r = new( response.code, response.message, response.proto )
response.instance_variables.each do |iv|
r.instance_variable_set( iv, response.instance_variable_get( iv ) )
end
r
end
r = new( response.code, response.message, response.proto )
response.instance_variables.each do |iv|
r.instance_variable_set( iv, response.instance_variable_get( iv ) )
end
r
end
def self.empty
new( 0, '' )
end
def self.empty
new( 0, '' )
end
def self.timed_out
r = empty
r.timed_out
r
end
def self.timed_out
r = empty
r.timed_out
r
end
def timed_out?
!!@timed_out
end
def timed_out?
!!@timed_out
end
def timed_out
@timed_out = true
end
end
def timed_out
@timed_out = true
end
end
attr_reader :opts
attr_reader :headers
attr_reader :framework
attr_reader :opts
attr_reader :headers
attr_reader :framework
attr_accessor :redirect_limit
attr_accessor :redirect_limit
def initialize( opts = {} )
@opts = opts.dup
@framework = opts[:framework]
@framework = opts[:framework]
@headers = {
'Accept' => '*/*',
@ -85,56 +85,56 @@ class Auxiliary::Web::HTTP
@request_opts = {}
if opts[:auth].is_a? Hash
@request_opts['basic_auth'] = [ opts[:auth][:user].to_s + ':' +
opts[:auth][:password] ]. pack( 'm*' ).gsub( /\s+/, '' )
opts[:auth][:password] ]. pack( 'm*' ).gsub( /\s+/, '' )
end
self.redirect_limit = opts[:redirect_limit] || 20
@queue = Queue.new
@queue = Queue.new
@after_run_blocks = []
end
@after_run_blocks = []
end
def after_run( &block )
@after_run_blocks << block
end
def after_run( &block )
@after_run_blocks << block
end
def connect
c = Rex::Proto::Http::Client.new(
opts[:target].host,
opts[:target].port,
{},
opts[:target].ssl,
'SSLv23'
)
def connect
c = Rex::Proto::Http::Client.new(
opts[:target].host,
opts[:target].port,
{},
opts[:target].ssl,
'SSLv23'
)
c.set_config(
'vhost' => opts[:target].vhost,
'agent' => opts[:user_agent] || 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
)
c
end
c.set_config(
'vhost' => opts[:target].vhost,
'agent' => opts[:user_agent] || 'Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)',
)
c
end
def run
return if @queue.empty?
def run
return if @queue.empty?
tl = []
loop do
# Spawn threads for each host
while tl.size <= (opts[:max_threads] || 5) && !@queue.empty? && (req = @queue.pop)
tl << framework.threads.spawn( "#{self.class.name} - #{req})", false, req ) do |request|
request.handle_response request( request.url, request.opts )
end
end
tl = []
loop do
# Spawn threads for each host
while tl.size <= (opts[:max_threads] || 5) && !@queue.empty? && (req = @queue.pop)
tl << framework.threads.spawn( "#{self.class.name} - #{req})", false, req ) do |request|
request.handle_response request( request.url, request.opts )
end
end
break if tl.empty?
tl.reject! { |t| !t.alive? }
break if tl.empty?
tl.reject! { |t| !t.alive? }
select( nil, nil, nil, 0.05 )
end
select( nil, nil, nil, 0.05 )
end
call_after_run_blocks
end
call_after_run_blocks
end
def request( url, opts = {} )
rlimit = self.redirect_limit
@ -145,19 +145,19 @@ class Auxiliary::Web::HTTP
return res if !opts[:follow_redirect] || !url = res.headers['location']
end
nil
end
end
def request_async( url, opts = {}, &callback )
queue Request.new( url, opts, &callback )
end
def request_async( url, opts = {}, &callback )
queue Request.new( url, opts, &callback )
end
def get_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :get ), &callback )
end
def get_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :get ), &callback )
end
def post_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :post ), &callback )
end
def post_async( url, opts = {}, &callback )
request_async( url, opts.merge( :method => :post ), &callback )
end
def get( url, opts = {} )
request( url, opts.merge( :method => :get ) )
@ -167,132 +167,132 @@ class Auxiliary::Web::HTTP
request( url, opts.merge( :method => :post ) )
end
def if_not_custom_404( path, body, &callback )
custom_404?( path, body ) { |b| callback.call if !b }
end
def if_not_custom_404( path, body, &callback )
custom_404?( path, body ) { |b| callback.call if !b }
end
def custom_404?( path, body, &callback )
return if !path || !body
def custom_404?( path, body, &callback )
return if !path || !body
precision = 2
precision = 2
trv_back = File.dirname( path )
trv_back << '/' if trv_back[-1,1] != '/'
trv_back = File.dirname( path )
trv_back << '/' if trv_back[-1,1] != '/'
# 404 probes
generators = [
# get a random path with an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# 404 probes
generators = [
# get a random path with an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# get a random path without an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) },
# get a random path without an extension
proc{ path + Rex::Text.rand_text_alpha( 10 ) },
# move up a dir and get a random file
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) },
# move up a dir and get a random file
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) },
# move up a dir and get a random file with an extension
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# move up a dir and get a random file with an extension
proc{ trv_back + Rex::Text.rand_text_alpha( 10 ) + '.' + Rex::Text.rand_text_alpha( 10 )[0..precision] },
# get a random directory
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '/' }
]
# get a random directory
proc{ path + Rex::Text.rand_text_alpha( 10 ) + '/' }
]
synchronize do
@@_404 ||= {}
@@_404[path] ||= []
synchronize do
@@_404 ||= {}
@@_404[path] ||= []
@@_404_gathered ||= Set.new
@@_404_gathered ||= Set.new
gathered = 0
if !@@_404_gathered.include?( path.hash )
generators.each.with_index do |generator, i|
@@_404[path][i] ||= {}
gathered = 0
if !@@_404_gathered.include?( path.hash )
generators.each.with_index do |generator, i|
@@_404[path][i] ||= {}
precision.times {
get_async( generator.call, :follow_redirect => true ) do |res|
gathered += 1
precision.times {
get_async( generator.call, :follow_redirect => true ) do |res|
gathered += 1
if gathered == generators.size * precision
@@_404_gathered << path.hash
callback.call is_404?( path, body )
else
@@_404[path][i]['rdiff_now'] ||= false
if gathered == generators.size * precision
@@_404_gathered << path.hash
callback.call is_404?( path, body )
else
@@_404[path][i]['rdiff_now'] ||= false
if !@@_404[path][i]['body']
@@_404[path][i]['body'] = res.body
else
@@_404[path][i]['rdiff_now'] = true
end
if !@@_404[path][i]['body']
@@_404[path][i]['body'] = res.body
else
@@_404[path][i]['rdiff_now'] = true
end
if @@_404[path][i]['rdiff_now'] && !@@_404[path][i]['rdiff']
@@_404[path][i]['rdiff'] = Rex::Text.refine( @@_404[path][i]['body'], res.body )
end
end
end
}
end
else
callback.call is_404?( path, body )
end
end
if @@_404[path][i]['rdiff_now'] && !@@_404[path][i]['rdiff']
@@_404[path][i]['rdiff'] = Rex::Text.refine( @@_404[path][i]['body'], res.body )
end
end
end
}
end
else
callback.call is_404?( path, body )
end
end
nil
end
nil
end
private
private
def call_after_run_blocks
while block = @after_run_blocks.pop
block.call
end
end
def call_after_run_blocks
while block = @after_run_blocks.pop
block.call
end
end
def synchronize( &block )
(@mutex ||= Mutex.new).synchronize( &block )
end
def synchronize( &block )
(@mutex ||= Mutex.new).synchronize( &block )
end
def is_404?( path, body )
@@_404[path].each { |_404| return true if Rex::Text.refine( _404['body'], body ) == _404['rdiff'] }
false
end
def is_404?( path, body )
@@_404[path].each { |_404| return true if Rex::Text.refine( _404['body'], body ) == _404['rdiff'] }
false
end
def queue( request )
@queue << request
end
def queue( request )
@queue << request
end
def _request( url, opts = {} )
body = opts[:body]
timeout = opts[:timeout] || 10
method = opts[:method].to_s.upcase || 'GET'
url = url.is_a?( URI ) ? url : URI( url.to_s )
def _request( url, opts = {} )
body = opts[:body]
timeout = opts[:timeout] || 10
method = opts[:method].to_s.upcase || 'GET'
url = url.is_a?( URI ) ? url : URI( url.to_s )
param_opts = {}
if !(vars_get = Auxiliary::Web::Form.query_to_params( url.query )).empty?
param_opts['vars_get'] = vars_get
end
if !(vars_get = Auxiliary::Web::Form.query_to_params( url.query )).empty?
param_opts['vars_get'] = vars_get
end
if method == 'GET'
param_opts['vars_get'] ||= {}
param_opts['vars_get'] ||= {}
param_opts['vars_get'].merge!( opts[:params] ) if opts[:params].is_a?( Hash )
elsif method == 'POST'
param_opts['vars_post'] = opts[:params] || {}
end
opts = @request_opts.merge( param_opts ).merge(
'uri' => url.path || '/',
'method' => method,
'uri' => url.path || '/',
'method' => method,
'headers' => headers.merge( opts[:headers] || {} )
)
opts['data'] = body if body
opts['data'] = body if body
c = connect
c = connect
Response.from_rex_response c.send_recv( c.request_cgi( opts ), timeout )
rescue ::Timeout::Error
Response.timed_out
rescue ::Errno::EPIPE, Rex::ConnectionTimeout
Response.empty
rescue ::Timeout::Error
Response.timed_out
rescue ::Errno::EPIPE, Rex::ConnectionTimeout
Response.empty
end
end

View File

@ -104,7 +104,8 @@ class Auxiliary::Web::Target
@forms << element
when Mdm::WebForm
self.<< element.method.to_s.downcase == 'path' ?
Auxiliary::Web::Path.from_model( element ) : Auxiliary::Web::Form.from_model( element )
Auxiliary::Web::Path.from_model( element ) :
Auxiliary::Web::Form.from_model( element )
end
end

View File

@ -2373,8 +2373,8 @@ class DBManager
desc = opts[:description].to_s.strip
conf = opts[:confidence].to_i
cat = opts[:category].to_s.strip
payload = opts[:payload].to_s
owner = opts[:owner] ? opts[:owner].shortname : nil
payload = opts[:payload].to_s
owner = opts[:owner] ? opts[:owner].shortname : nil
site = nil
@ -2429,8 +2429,8 @@ class DBManager
vuln.blame = blame
vuln.description = desc
vuln.confidence = conf
vuln.payload = payload
vuln.owner = owner
vuln.payload = payload
vuln.owner = owner
msf_import_timestamps(opts, vuln)
vuln.save!

View File

@ -28,7 +28,7 @@ module Exploit::Remote::Web
super
register_options([
OptString.new( 'PATH', [ true, 'The path to the vulnerable script.', '/' ] ),
OptString.new( 'PATH', [ true, 'The path to the vulnerable script.', '/' ] ),
OptString.new( 'GET', [ false, "GET parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
OptString.new( 'POST', [ false, "POST parameters. ('foo=bar&vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
OptString.new( 'COOKIES', [ false, "Cookies to be sent with the request. ('foo=bar;vuln=#{WEB_PAYLOAD_STUB}', #{WEB_PAYLOAD_STUB} will be substituted with the payload.)", "" ] ),
@ -75,6 +75,11 @@ module Exploit::Remote::Web
def exploit
print_status "Sending HTTP request for #{path}"
perform_request
handler
end
def perform_request
res = send_request_cgi({
'global' => true,
'uri' => path,
@ -83,7 +88,7 @@ module Exploit::Remote::Web
'vars_post' => post,
'headers' => headers,
'cookie' => cookies
}, 0.01 )
}, 0.01 )
if res
print_status "The server responded with HTTP status code #{res.code}."
@ -91,7 +96,7 @@ module Exploit::Remote::Web
print_status 'The server did not respond to our request.'
end
handler
res
end
#
@ -135,4 +140,3 @@ module Exploit::Remote::Web
end
end

View File

@ -219,32 +219,32 @@ module Text
end
end
#
# Returns the words in +str+ as an Array.
#
# strict - include *only* words, no boundary characters (like spaces, etc.)
#
def self.to_words( str, strict = false )
splits = str.split( /\b/ )
splits.reject! { |w| !(w =~ /\w/) } if strict
splits
end
#
# Returns the words in +str+ as an Array.
#
# strict - include *only* words, no boundary characters (like spaces, etc.)
#
def self.to_words( str, strict = false )
splits = str.split( /\b/ )
splits.reject! { |w| !(w =~ /\w/) } if strict
splits
end
#
# Removes noise from 2 Strings and return a refined String version.
#
def self.refine( str1, str2 )
return str1 if str1 == str2
#
# Removes noise from 2 Strings and return a refined String version.
#
def self.refine( str1, str2 )
return str1 if str1 == str2
# get the words of the first str in an array
s_words = to_words( str1 )
# get the words of the first str in an array
s_words = to_words( str1 )
# get the words of the second str in an array
o_words = to_words( str2 )
# get the words of the second str in an array
o_words = to_words( str2 )
# get what hasn't changed (the rdiff, so to speak) as a string
(s_words - (s_words - o_words)).join
end
# get what hasn't changed (the rdiff, so to speak) as a string
(s_words - (s_words - o_words)).join
end
#
# Returns a unicode escaped string for Javascript