Adds a basic fuzzing mixin, as well as a TDS username fuzzer to demonstrate usage

git-svn-id: file:///home/svn/framework3/trunk@7241 4d416f70-5f16-0410-b530-b9f4589650da
unstable
HD Moore 2009-10-25 01:40:27 +00:00
parent acf1b2eda7
commit cae0d61491
4 changed files with 461 additions and 8 deletions

View File

@ -12,14 +12,7 @@ module Msf
###
class Auxiliary < Msf::Module
#
# Auxiliary mixins
#
require 'msf/core/auxiliary/scanner'
require 'msf/core/auxiliary/report'
require 'msf/core/auxiliary/dos'
require 'msf/core/auxiliary/timed'
require 'msf/core/auxiliary/wmapmodule'
require 'msf/core/auxiliary/mixins'
#
# Returns MODULE_AUX to indicate that this is an auxiliary module.

View File

@ -0,0 +1,285 @@
module Msf
###
#
# This module provides methods useful for developing fuzzers
#
###
module Auxiliary::Fuzzer
#
# Creates an instance of a fuzzer module
#
def initialize(info = {})
super
register_advanced_options([
OptString.new('FuzzTracer', [ true, 'Sets the magic string to embed into fuzzer string inputs', 'MSFROCKS']),
OptString.new('FuzzChar', [ true, 'Sets the character to use for generating long strings', 'X'])
], Msf::Auxiliary::Fuzzer)
end
#
# Self-reflective iterators
#
def fuzz_numbers
res = []
self.methods.grep(/^fuzzer_number/).each do |m|
@last_fuzzer_input = m
block_given? ? self.send(m) {|x| yield(x) } : (res << self.send(m))
end
res
end
def fuzz_strings
res = []
self.methods.grep(/^fuzzer_string/).each do |m|
@last_fuzzer_input = m
block_given? ? self.send(m) {|x| yield(x) } : (res << self.send(m))
end
res
end
#
# General input mangling routines
#
def fuzz_string_corrupt_byte(str)
res = []
0.upto(str.length - 1) do |offset|
0.upto(255) do |val|
buf = str.dup
buf[offset,1] = [val].pack('C')
block_given? ? yield(buf) : (res << buf)
end
end
res
end
#
# Useful generators (many derived from AxMan)
#
def fuzzer_string_format
res = %W{ %s %p %n %x %@ %.257d %.65537d %.2147483648d %.257f %.65537f %.2147483648f}
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_string_filepath_dos
res = %W{ aux con nul com1 com2 com3 com4 lpt1 lpt2 lp3 lpt4 prn }
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_number_power2
res = [
0x100000000,
0x80000000,
0x40000000,
0x20000000,
0x10000000,
0x01000000,
0x00100000,
0x00010000,
0x00001000,
0x00000100,
0x00000010,
0x00000001
]
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_number_power2_plus
res = []
fuzzer_number_power2 do |num|
res << num + 1
res << num + 2
res << num - 1
res << num - 2
res << num * -1
res << (num + 1) * -1
res << (num + 2) * -1
end
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_gen_string(len)
datastore['FuzzChar'][0,1] * len
end
def fuzzer_string_small
res = []
1.upto(512) do |len|
buf = fuzzer_gen_string(len)
block_given? ? yield(buf) : (res << buf)
end
res
end
def fuzzer_string_long
res = []
64.step(8192,64) do |len|
buf = fuzzer_gen_string(len)
buf[len / 2, datastore['FuzzTracer'].length] = datastore['FuzzTracer']
block_given? ? yield(buf) : (res << buf)
end
res
end
def fuzzer_string_giant
res = []
512.step(65532 * 2, 512) do |len|
buf = fuzzer_gen_string(len)
buf[len / 2, datastore['FuzzTracer'].length] = datastore['FuzzTracer']
block_given? ? yield(buf) : (res << buf)
end
res
end
def fuzzer_string_uri_types
res = %W{
aaa aaas about acap adiumxtra afp aim apt aw bolo callto cap chrome cid
content crid cvs data dav designates dict disk dns doi ed2k example examples
fax feed file finger fish ftp gg gizmoproject go gopher h323 hcp http https
iax2 icap im imap info ipp irc ircs iris iris.beep iris.lws iris.xpc iris.xpcs
itms jar javascript keyparc lastfm ldap ldaps lsid magnet mailto mid mms modem
ms-help msnim msrp msrps mtqp mupdate mvn news nfs nntp notes opaquelocktoken
over pop pres prospero psyc res rlogin rmi rsync rtsp secondlife service sftp
sgn shell shttp sip sips skype smb sms snews snmp soap.beep soap.beeps soldat
ssh steam svn tag teamspeak tel telephone telnet tftp thismessage tip tv unreal
urn ut2004 vbscript vemmi ventrilo view-source wais webcal worldwind wtai wyciwyg
wysiwyg xfire xmlrpc.beep xmpp xri ymsgr z39.50r z39.50s
}
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_string_uri_dividers
res = %W{ : :/ :// }
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_string_path_prefixes
res = %W{ C:\\ \\\\localhost\\ / }
block_given? ? res.each { |n| yield(n) } : res
end
def fuzzer_string_uris_small
res = []
fuzzer_string_uri_types do |proto|
fuzzer_string_uri_dividers do |div|
fuzzer_string_small do |str|
buf = proto + div + str
block_given? ? yield(buf) : (res << buf)
end
end
end
res
end
def fuzzer_string_uris_long
res = []
fuzzer_string_uri_types do |proto|
fuzzer_string_uri_dividers do |div|
fuzzer_string_long do |str|
buf = proto + div + str
block_given? ? yield(buf) : (res << buf)
end
end
end
res
end
def fuzzer_string_uris_giant
res = []
fuzzer_string_uri_types do |proto|
fuzzer_string_uri_dividers do |div|
fuzzer_string_giant do |str|
buf = proto + div + str
block_given? ? yield(buf) : (res << buf)
end
end
end
res
end
def fuzzer_string_uris_format
res = []
fuzzer_string_uri_types do |proto|
fuzzer_string_uri_dividers do |div|
fuzzer_string_format do |str|
buf = proto + div + str
block_given? ? yield(buf) : (res << buf)
end
end
end
res
end
def fuzzer_string_uris_dos
res = []
fuzzer_string_uri_types do |proto|
fuzzer_string_uri_dividers do |div|
fuzzer_string_filepath_dos do |str|
buf = proto + div + str
block_given? ? yield(buf) : (res << buf)
end
end
end
res
end
def fuzzer_string_paths_small
res = []
fuzzer_string_path_prefixes do |pre|
fuzzer_string_small do |str|
buf = pre + str
block_given? ? yield(buf) : (res << buf)
end
end
res
end
def fuzzer_string_paths_long
res = []
fuzzer_string_path_prefixes do |pre|
fuzzer_string_long do |str|
buf = pre + str
block_given? ? yield(buf) : (res << buf)
end
end
res
end
def fuzzer_string_paths_giant
res = []
fuzzer_string_path_prefixes do |pre|
fuzzer_string_giant do |str|
buf = pre + str
block_given? ? yield(buf) : (res << buf)
end
end
res
end
def fuzzer_string_paths_format
res = []
fuzzer_string_path_prefixes do |pre|
fuzzer_string_format do |str|
buf = pre + str
block_given? ? yield(buf) : (res << buf)
end
end
res
end
def fuzzer_string_paths_dos
res = []
fuzzer_string_path_prefixes do |pre|
fuzzer_string_filepath_dos do |str|
buf = pre + str
block_given? ? yield(buf) : (res << buf)
end
end
res
end
end
end

View File

@ -0,0 +1,10 @@
#
# Auxiliary mixins
#
require 'msf/core/auxiliary/scanner'
require 'msf/core/auxiliary/report'
require 'msf/core/auxiliary/dos'
require 'msf/core/auxiliary/fuzzer'
require 'msf/core/auxiliary/timed'
require 'msf/core/auxiliary/wmapmodule'

View File

@ -0,0 +1,165 @@
##
# $Id$
##
##
# This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# Framework web site for more information on licensing and terms of use.
# http://metasploit.com/framework/
##
require 'msf/core'
class Metasploit3 < Msf::Auxiliary
include Msf::Exploit::Remote::MSSQL
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'TDS Protocol Login Request Fuzzer',
'Description' => %q{
This module sends a series of malformed TDS login requests.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Version' => '$Revision$'
))
end
# A copy of the mssql_login method with the ability to overload each option
def do_login(opts={})
@connected = false
disconnect if self.sock
connect
@connected = true
pkt = ""
idx = 0
db = ""
pkt << [
0x00000000, # Dummy size
opts[:tds_version] || 0x71000001, # TDS Version
opts[:size] || 0x00000000, # Size
opts[:version] || 0x00000007, # Version
opts[:pid] || rand(1024+1), # PID
opts[:connection_id] || 0x00000000, # ConnectionID
opts[:flags_opt1] || 0xe0, # Option Flags 1
opts[:flags_opt2] || 0x03, # Option Flags 2
opts[:flags_sql_type] || 0x00, # SQL Type Flags
opts[:flags_reserved] || 0x00, # Reserved Flags
opts[:timezone] || 0x00000000, # Time Zone
opts[:collation] || 0x00000000 # Collation
].pack('VVVVVVCCCCVV')
cname = Rex::Text.to_unicode( opts[:cname] || Rex::Text.rand_text_alpha(rand(8)+1) )
uname = Rex::Text.to_unicode( opts[:uname] || "sa" )
pname = opts[:pname_raw] || mssql_tds_encrypt( opts[:pname] || "" )
aname = Rex::Text.to_unicode(opts[:aname] || Rex::Text.rand_text_alpha(rand(8)+1) )
sname = Rex::Text.to_unicode( opts[:sname] || rhost )
dname = Rex::Text.to_unicode( opts[:dname] || db )
idx = pkt.size + 50 # lengths below
pkt << [idx, cname.length / 2].pack('vv')
idx += cname.length
pkt << [idx, uname.length / 2].pack('vv')
idx += uname.length
pkt << [idx, pname.length / 2].pack('vv')
idx += pname.length
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, sname.length / 2].pack('vv')
idx += sname.length
pkt << [0, 0].pack('vv')
pkt << [idx, aname.length / 2].pack('vv')
idx += aname.length
pkt << [idx, 0].pack('vv')
pkt << [idx, dname.length / 2].pack('vv')
idx += dname.length
# The total length has to be embedded twice more here
pkt << [
0,
0,
0x12345678,
0x12345678
].pack('vVVV')
pkt << cname
pkt << uname
pkt << pname
pkt << aname
pkt << sname
pkt << aname
pkt << dname
# Total packet length
pkt[0,4] = [pkt.length].pack('V')
# Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
# Packet header and total length including header
pkt = "\x10\x01" + [pkt.length + 8].pack('n') + [0].pack('n') + [1].pack('C') + "\x00" + pkt
resp = mssql_send_recv(pkt,opts[:timeout])
info = {:errors => []}
info = mssql_parse_reply(resp,info)
info
end
def run
last_str = nil
last_inp = nil
last_err = nil
cnt = 0
fuzz_strings do |str|
# capped at 16-bit lengths
next if str.length > 65535
cnt += 1
if(cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
do_login(:uname => str, :timeout => 0.50)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
return
end
last_str = str
last_inp = @last_fuzzer_input
end
end
end