modules/auxiliary/fuzzers: Resolve RuboCop violations

This commit is contained in:
bcoles
2025-05-10 14:09:40 +10:00
parent b5129fe198
commit 75c2104625
21 changed files with 1569 additions and 1410 deletions
+268 -265
View File
@@ -13,35 +13,40 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'DNS and DNSSEC Fuzzer',
'Description' => %q{
'Name' => 'DNS and DNSSEC Fuzzer',
'Description' => %q{
This module will connect to a DNS server and perform DNS and
DNSSEC protocol-level fuzzing. Note that this module may inadvertently
crash the target server.
},
'Author' => [ 'pello <fropert[at]packetfault.org>' ],
'License' => MSF_LICENSE
'Author' => [ 'pello <fropert[at]packetfault.org>' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
register_options([
Opt::RPORT(53),
OptInt.new('STARTSIZE', [ false, "Fuzzing string startsize.",0]),
OptInt.new('ENDSIZE', [ false, "Max Fuzzing string size. (L2 Frame size)",500]),
OptInt.new('STEPSIZE', [ false, "Increment fuzzing string each attempt.",100]),
OptInt.new('ERRORHDR', [ false, "Introduces byte error in the DNS header.", 0]),
OptBool.new('CYCLIC', [ false, "Use Cyclic pattern instead of A's (fuzzing payload).",true]),
OptInt.new("ITERATIONS", [true, "Number of iterations to run by test case", 5]),
OptString.new('DOMAIN', [ false, "Force DNS zone domain name."]),
OptString.new('IMPORTENUM', [ false, "Import dns_enum database output and automatically use existing RR."]),
OptInt.new('STARTSIZE', [ false, 'Fuzzing string startsize.', 0]),
OptInt.new('ENDSIZE', [ false, 'Max Fuzzing string size. (L2 Frame size)', 500]),
OptInt.new('STEPSIZE', [ false, 'Increment fuzzing string each attempt.', 100]),
OptInt.new('ERRORHDR', [ false, 'Introduces byte error in the DNS header.', 0]),
OptBool.new('CYCLIC', [ false, "Use Cyclic pattern instead of A's (fuzzing payload).", true]),
OptInt.new('ITERATIONS', [true, 'Number of iterations to run by test case', 5]),
OptString.new('DOMAIN', [ false, 'Force DNS zone domain name.']),
OptString.new('IMPORTENUM', [ false, 'Import dns_enum database output and automatically use existing RR.']),
OptEnum.new('METHOD', [false, 'Underlayer protocol to use', 'UDP', ['UDP', 'TCP', 'AUTO']]),
OptBool.new('DNSSEC', [ false, "Add DNSsec to each question (UDP payload size, EDNS0, ...)",false]),
OptBool.new('TRAILINGNUL', [ false, "NUL byte terminate DNS names",true]),
OptBool.new('RAWPADDING', [ false, "Generate totally random data from STARTSIZE to ENDSIZE",false]),
OptString.new('OPCODE', [ false, "Comma separated list of opcodes to fuzz. Leave empty to fuzz all fields.",'' ]),
OptBool.new('DNSSEC', [ false, 'Add DNSsec to each question (UDP payload size, EDNS0, ...)', false]),
OptBool.new('TRAILINGNUL', [ false, 'NUL byte terminate DNS names', true]),
OptBool.new('RAWPADDING', [ false, 'Generate totally random data from STARTSIZE to ENDSIZE', false]),
OptString.new('OPCODE', [ false, 'Comma separated list of opcodes to fuzz. Leave empty to fuzz all fields.', '' ]),
# OPCODE accepted values: QUERY,IQUERY,STATUS,UNASSIGNED,NOTIFY,UPDATE
OptString.new('CLASS', [ false, "Comma separated list of classes to fuzz. Leave empty to fuzz all fields.",'' ]),
OptString.new('CLASS', [ false, 'Comma separated list of classes to fuzz. Leave empty to fuzz all fields.', '' ]),
# CLASS accepted values: IN,CH,HS,NONE,ANY
OptString.new('RR', [ false, "Comma separated list of requests to fuzz. Leave empty to fuzz all fields.",'' ])
OptString.new('RR', [ false, 'Comma separated list of requests to fuzz. Leave empty to fuzz all fields.', '' ])
# RR accepted values: A,CNAME,MX,PTR,TXT,AAAA,HINFO,SOA,NS,WKS,RRSIG,DNSKEY,DS,NSEC,NSEC3,NSEC3PARAM
# RR accepted values: AFSDB,ISDN,RP,RT,X25,PX,SRV,NAPTR,MD,MF,MB,MG,MR,NULL,MINFO,NSAP,NSAP-PTR,SIG
# RR accepted values: KEY,GPOS,LOC,NXT,EID,NIMLOC,ATMA,KX,CERT,A6,DNAME,SINK,OPT,APL,SSHFP,IPSECKEY
@@ -50,7 +55,7 @@ class MetasploitModule < Msf::Auxiliary
])
end
class Dns_header < BinData::Record
class DnsHeader < BinData::Record
endian :big
uint16 :txid, initial_value: rand(0xffff)
bit1 :qr
@@ -68,7 +73,7 @@ class MetasploitModule < Msf::Auxiliary
rest :payload
end
class Dns_add_rr < BinData::Record
class DnsAddRr < BinData::Record
endian :big
uint8 :name
uint16 :rr_type, initial_value: 0x0029
@@ -89,72 +94,70 @@ class MetasploitModule < Msf::Auxiliary
if pkt[4].to_i >= 0x17 || (pkt[4].to_i >= 0x0b && pkt[4].to_i <= 0x0f)
print_error("#{msg} Server replied incorrectly to the following request:\n#{@lastdata.unpack('H*')}")
return false
else
return true
end
return true
end
def dns_alive(method)
connect_udp if method == "UDP" || method == "AUTO"
connect if method == "TCP"
payload = ""
domain = ""
if @domain == nil
domain << Rex::Text.rand_text_alphanumeric(rand(2)+2)
domain << "."
domain << Rex::Text.rand_text_alphanumeric(rand(6)+3)
domain << "."
connect_udp if method == 'UDP' || method == 'AUTO'
connect if method == 'TCP'
domain = ''
domain << Rex::Text.rand_text_alphanumeric(2..3)
domain << '.'
if @domain.nil?
domain << Rex::Text.rand_text_alphanumeric(3..8)
domain << '.'
domain << Rex::Text.rand_text_alphanumeric(2)
else
domain << Rex::Text.rand_text_alphanumeric(rand(2)+2)
domain << "."
domain << @domain
end
splitFQDN = domain.split('.')
payload = splitFQDN.inject("") { |a,x| a + [x.length,x].pack("CA*") }
pkt = Dns_header.new
split_fqdn = domain.split('.')
payload = split_fqdn.inject('') { |a, x| a + [x.length, x].pack('CA*') }
pkt = DnsHeader.new
pkt.txid = rand(0xffff)
pkt.opcode = 0x0000
pkt.payload = payload + "\x00" + "\x00\x01" + "\x00\x01"
testingPkt = pkt.to_binary_s
testing_pkt = pkt.to_binary_s
if method == "UDP"
udp_sock.put(testingPkt)
res, addr = udp_sock.recvfrom(65535)
if method == 'UDP'
udp_sock.put(testing_pkt)
res, = udp_sock.recvfrom(65535)
disconnect_udp
elsif method == "TCP"
sock.put(testingPkt)
res, addr = sock.get_once(-1, 20)
elsif method == 'TCP'
sock.put(testing_pkt)
res, = sock.get_once(-1, 20)
disconnect
end
if res && res.empty?
print_error("#{msg} The remote server is not responding to DNS requests.")
return false
else
return true
end
return true
end
def fuzz_padding(payload, size)
padding = size - payload.length
if padding <= 0 then return payload end
return payload if padding <= 0
if datastore['CYCLIC']
@fuzzdata = Rex::Text.rand_text_alphanumeric(padding)
else
@fuzzdata = 'A' * padding
end
payload = payload.ljust(padding, @fuzzdata)
return payload
return payload.ljust(padding, @fuzzdata)
end
def corrupt_header(pkt,nb)
def corrupt_header(pkt, nb)
len = pkt.length - 1
for i in 0..nb - 1
selectByte = rand(len)
pkt[selectByte] = [rand(255).to_s].pack('H')
for _ in 0..nb - 1
select_byte = rand(len)
pkt[select_byte] = [rand(255).to_s].pack('H')
end
return pkt
end
@@ -167,130 +170,130 @@ class MetasploitModule < Msf::Auxiliary
return pkt
end
def setup_fqdn(domain,entry)
if domain == nil
domain = ""
domain << Rex::Text.rand_text_alphanumeric(rand(62)+2)
domain << "."
domain << Rex::Text.rand_text_alphanumeric(rand(61)+3)
domain << "."
domain << Rex::Text.rand_text_alphanumeric(rand(62)+2)
def setup_fqdn(domain, entry)
if domain.nil?
domain = ''
domain << Rex::Text.rand_text_alphanumeric(2..63)
domain << '.'
domain << Rex::Text.rand_text_alphanumeric(3..63)
domain << '.'
domain << Rex::Text.rand_text_alphanumeric(2..63)
elsif @dnsfile
domain = entry + "." + domain
domain = entry + '.' + domain
else
domain = Rex::Text.rand_text_alphanumeric(rand(62)+2) + "." + domain
domain = Rex::Text.rand_text_alphanumeric(2..63) + '.' + domain
end
return domain
end
def import_enum_data(dnsfile)
enumdata = Array.new(count = File.foreach(dnsfile).inject(0) {|c, line| c+1}, 0)
enumdata = Array.new(File.foreach(dnsfile).inject(0) { |c, _line| c + 1 }, 0)
idx = 0
File.open(dnsfile,"rb").each_line do |line|
line = line.split(",")
File.open(dnsfile, 'rb').each_line do |line|
line = line.split(',')
enumdata[idx] = Hash.new
enumdata[idx][:name] = line[0].strip
enumdata[idx][:rr] = line[1].strip
enumdata[idx][:class] = line[2].strip
idx = idx + 1
idx += 1
end
return enumdata
end
def setup_nsclass(nsclass)
classns = ""
classns = ''
for idx in nsclass
classns << {
"IN" => 0x0001, "CH" => 0x0003, "HS" => 0x0004,
"NONE" => 0x00fd, "ANY" => 0x00ff
}.values_at(idx).pack("n")
'IN' => 0x0001, 'CH' => 0x0003, 'HS' => 0x0004,
'NONE' => 0x00fd, 'ANY' => 0x00ff
}.values_at(idx).pack('n')
end
return classns
end
def setup_opcode(nsopcode)
opcode = ""
opcode = ''
for idx in nsopcode
opcode << {
"QUERY" => 0x0000, "IQUERY" => 0x0001, "STATUS" => 0x0002,
"UNASSIGNED" => 0x0003, "NOTIFY" => 0x0004, "UPDATE" => 0x0005
}.values_at(idx).pack("n")
'QUERY' => 0x0000, 'IQUERY' => 0x0001, 'STATUS' => 0x0002,
'UNASSIGNED' => 0x0003, 'NOTIFY' => 0x0004, 'UPDATE' => 0x0005
}.values_at(idx).pack('n')
end
return opcode
end
def setup_reqns(nsreq)
reqns= ""
reqns = ''
for idx in nsreq
reqns << {
"A" => 0x0001, "NS" => 0x0002, "MD" => 0x0003, "MF" => 0x0004,
"CNAME" => 0x0005, "SOA" => 0x0006, "MB" => 0x0007, "MG" => 0x0008,
"MR" => 0x0009, "NULL" => 0x000a, "WKS" => 0x000b, "PTR" => 0x000c,
"HINFO" => 0x000d, "MINFO" => 0x000e, "MX" => 0x000f, "TXT" => 0x0010,
"RP" => 0x0011, "AFSDB" => 0x0012, "X25" => 0x0013, "ISDN" => 0x0014,
"RT" => 0x0015, "NSAP" => 0x0016, "NSAP-PTR" => 0x0017, "SIG" => 0x0018,
"KEY" => 0x0019, "PX" => 0x001a, "GPOS" => 0x001b, "AAAA" => 0x001c,
"LOC" => 0x001d, "NXT" => 0x001e, "EID" => 0x001f, "NIMLOC" => 0x0020,
"SRV" => 0x0021, "ATMA" => 0x0022, "NAPTR" => 0x0023, "KX" => 0x0024,
"CERT" => 0x0025, "A6" => 0x0026, "DNAME" => 0x0027, "SINK" => 0x0028,
"OPT" => 0x0029, "APL" => 0x002a, "DS" => 0x002b, "SSHFP" => 0x002c,
"IPSECKEY" => 0x002d, "RRSIG" => 0x002e, "NSEC" => 0x002f, "DNSKEY" => 0x0030,
"DHCID" => 0x0031, "NSEC3" => 0x0032, "NSEC3PARAM" => 0x0033, "HIP" => 0x0037,
"NINFO" => 0x0038, "RKEY" => 0x0039, "TALINK" => 0x003a, "SPF" => 0x0063,
"UINFO" => 0x0064, "UID" => 0x0065, "GID" => 0x0066, "UNSPEC" => 0x0067,
"TKEY" => 0x00f9, "TSIG" => 0x00fa, "IXFR" => 0x00fb, "AXFR" => 0x00fc,
"MAILA" => 0x00fd, "MAILB" => 0x00fe, "*" => 0x00ff, "TA" => 0x8000,
"DLV" => 0x8001, "RESERVED" => 0xffff
}.values_at(idx).pack("n")
'A' => 0x0001, 'NS' => 0x0002, 'MD' => 0x0003, 'MF' => 0x0004,
'CNAME' => 0x0005, 'SOA' => 0x0006, 'MB' => 0x0007, 'MG' => 0x0008,
'MR' => 0x0009, 'NULL' => 0x000a, 'WKS' => 0x000b, 'PTR' => 0x000c,
'HINFO' => 0x000d, 'MINFO' => 0x000e, 'MX' => 0x000f, 'TXT' => 0x0010,
'RP' => 0x0011, 'AFSDB' => 0x0012, 'X25' => 0x0013, 'ISDN' => 0x0014,
'RT' => 0x0015, 'NSAP' => 0x0016, 'NSAP-PTR' => 0x0017, 'SIG' => 0x0018,
'KEY' => 0x0019, 'PX' => 0x001a, 'GPOS' => 0x001b, 'AAAA' => 0x001c,
'LOC' => 0x001d, 'NXT' => 0x001e, 'EID' => 0x001f, 'NIMLOC' => 0x0020,
'SRV' => 0x0021, 'ATMA' => 0x0022, 'NAPTR' => 0x0023, 'KX' => 0x0024,
'CERT' => 0x0025, 'A6' => 0x0026, 'DNAME' => 0x0027, 'SINK' => 0x0028,
'OPT' => 0x0029, 'APL' => 0x002a, 'DS' => 0x002b, 'SSHFP' => 0x002c,
'IPSECKEY' => 0x002d, 'RRSIG' => 0x002e, 'NSEC' => 0x002f, 'DNSKEY' => 0x0030,
'DHCID' => 0x0031, 'NSEC3' => 0x0032, 'NSEC3PARAM' => 0x0033, 'HIP' => 0x0037,
'NINFO' => 0x0038, 'RKEY' => 0x0039, 'TALINK' => 0x003a, 'SPF' => 0x0063,
'UINFO' => 0x0064, 'UID' => 0x0065, 'GID' => 0x0066, 'UNSPEC' => 0x0067,
'TKEY' => 0x00f9, 'TSIG' => 0x00fa, 'IXFR' => 0x00fb, 'AXFR' => 0x00fc,
'MAILA' => 0x00fd, 'MAILB' => 0x00fe, '*' => 0x00ff, 'TA' => 0x8000,
'DLV' => 0x8001, 'RESERVED' => 0xffff
}.values_at(idx).pack('n')
end
return reqns
end
def build_packet(dnsOpcode,dnssec,trailingnul,reqns,classns,payload)
pkt = Dns_header.new
pkt.opcode = dnsOpcode
def build_packet(dns_opcode, dnssec, trailingnul, reqns, classns, payload)
pkt = DnsHeader.new
pkt.opcode = dns_opcode
if trailingnul
if @dnsfile
pkt.payload = payload + "\x00" + reqns + classns
else
pkt.payload = payload + "\x00" + [reqns].pack("n") + [classns].pack("n")
pkt.payload = payload + "\x00" + [reqns].pack('n') + [classns].pack('n')
end
elsif @dnsfile
pkt.payload = payload + [rand(1..255).to_s].pack('H') + reqns + classns
else
if @dnsfile
pkt.payload = payload + [(rand(255) + 1).to_s].pack('H') + reqns + classns
else
pkt.payload = payload + [(rand(255) + 1).to_s].pack('H') + [dnsReq].pack("n") + [dnsClass].pack("n")
end
pkt.payload = payload + [rand(1..255).to_s].pack('H') + [dns_req].pack('n') + [dns_class].pack('n')
end
if dnssec
dnssecpkt = Dns_add_rr.new
dnssecpkt = DnsAddRr.new
pkt.additionalRR = 1
pkt.payload = dnssecpkt.to_binary_s
end
return pkt.to_binary_s
pkt.to_binary_s
end
def dns_send(data,method)
method = "UDP" if (method == "AUTO" && data.length < 512)
method = "TCP" if (method == "AUTO" && data.length >= 512)
def dns_send(data, method)
method = 'UDP' if method == 'AUTO' && data.length < 512
method = 'TCP' if method == 'AUTO' && data.length >= 512
connect_udp if method == "UDP"
connect if method == "TCP"
udp_sock.put(data) if method == "UDP"
sock.put(data) if method == "TCP"
connect_udp if method == 'UDP'
connect if method == 'TCP'
udp_sock.put(data) if method == 'UDP'
sock.put(data) if method == 'TCP'
res, addr = udp_sock.recvfrom(65535,1) if method == "UDP"
res, addr = sock.get_once(-1,1) if method == "TCP"
res, = udp_sock.recvfrom(65535, 1) if method == 'UDP'
res, = sock.get_once(-1, 1) if method == 'TCP'
disconnect_udp if method == "UDP"
disconnect if method == "TCP"
disconnect_udp if method == 'UDP'
disconnect if method == 'TCP'
if res && res.length == 0
@failCount += 1
if @failCount == 1
@probablyVuln = @lastdata if @lastdata != nil
return true
elsif @failCount >= 3
if res && res.empty?
@fail_count += 1
if @fail_count == 1
@probably_vuln = @lastdata if !@lastdata.nil?
elsif @fail_count >= 3
if dns_alive(method) == false
if @lastdata
print_error("#{msg} DNS is DOWN since the request:")
@@ -299,186 +302,186 @@ class MetasploitModule < Msf::Auxiliary
print_error("#{msg} DNS is DOWN")
end
return false
else
return true
end
else
return true
end
elsif res && res.length > 0
return true
elsif res && !res.empty?
@lastdata = data
if res[3].to_i >= 0x8000 # ignore server response as a query
@failCount = 0
@fail_count = 0
return true
end
if @rawpadding
@failCount = 0
@fail_count = 0
return true
end
if check_response_construction(res)
@failCount = 0
@fail_count = 0
return true
else
return false
end
return false
end
end
def fix_variables
@fuzz_opcode = datastore['OPCODE'].blank? ? "QUERY,IQUERY,STATUS,UNASSIGNED,NOTIFY,UPDATE" : datastore['OPCODE']
@fuzz_class = datastore['CLASS'].blank? ? "IN,CH,HS,NONE,ANY" : datastore['CLASS']
fuzz_rr_queries = "A,NS,MD,MF,CNAME,SOA,MB,MG,MR,NULL,WKS,PTR," <<
"HINFO,MINFO,MX,TXT,RP,AFSDB,X25,ISDN,RT," <<
"NSAP,NSAP-PTR,SIG,KEY,PX,GPOS,AAAA,LOC,NXT," <<
"EID,NIMLOC,SRV,ATMA,NAPTR,KX,CERT,A6,DNAME," <<
"SINK,OPT,APL,DS,SSHFP,IPSECKEY,RRSIG,NSEC," <<
"DNSKEY,DHCID,NSEC3,NSEC3PARAM,HIP,NINFO,RKEY," <<
"TALINK,SPF,UINFO,UID,GID,UNSPEC,TKEY,TSIG," <<
"IXFR,AXFR,MAILA,MAILB,*,TA,DLV,RESERVED"
@fuzz_rr = datastore['RR'].blank? ? fuzz_rr_queries : datastore['RR']
@fuzz_opcode = datastore['OPCODE'].blank? ? 'QUERY,IQUERY,STATUS,UNASSIGNED,NOTIFY,UPDATE' : datastore['OPCODE']
@fuzz_class = datastore['CLASS'].blank? ? 'IN,CH,HS,NONE,ANY' : datastore['CLASS']
fuzz_rr_queries = 'A,NS,MD,MF,CNAME,SOA,MB,MG,MR,NULL,WKS,PTR,' \
'HINFO,MINFO,MX,TXT,RP,AFSDB,X25,ISDN,RT,' \
'NSAP,NSAP-PTR,SIG,KEY,PX,GPOS,AAAA,LOC,NXT,' \
'EID,NIMLOC,SRV,ATMA,NAPTR,KX,CERT,A6,DNAME,' \
'SINK,OPT,APL,DS,SSHFP,IPSECKEY,RRSIG,NSEC,' \
'DNSKEY,DHCID,NSEC3,NSEC3PARAM,HIP,NINFO,RKEY,' \
'TALINK,SPF,UINFO,UID,GID,UNSPEC,TKEY,TSIG,' \
'IXFR,AXFR,MAILA,MAILB,*,TA,DLV,RESERVED'
@fuzz_rr = datastore['RR'].blank? ? fuzz_rr_queries : datastore['RR']
end
def run_host(ip)
msg = "#{ip}:#{rhost} - DNS -"
begin
@lastdata = nil
@probablyVuln = nil
@startsize = datastore['STARTSIZE']
@stepsize = datastore['STEPSIZE']
@endsize = datastore['ENDSIZE']
@underlayerProtocol = datastore['METHOD']
@failCount = 0
@domain = datastore['DOMAIN']
@dnsfile = datastore['IMPORTENUM']
@rawpadding = datastore['RAWPADDING']
iter = datastore['ITERATIONS']
dnssec = datastore['DNSSEC']
errorhdr = datastore['ERRORHDR']
trailingnul = datastore['TRAILINGNUL']
@lastdata = nil
@probably_vuln = nil
@startsize = datastore['STARTSIZE']
@stepsize = datastore['STEPSIZE']
@endsize = datastore['ENDSIZE']
@underlayer_protocol = datastore['METHOD']
@fail_count = 0
@domain = datastore['DOMAIN']
@dnsfile = datastore['IMPORTENUM']
@rawpadding = datastore['RAWPADDING']
iter = datastore['ITERATIONS']
dnssec = datastore['DNSSEC']
errorhdr = datastore['ERRORHDR']
trailingnul = datastore['TRAILINGNUL']
fix_variables
fix_variables
if !dns_alive(@underlayerProtocol) then return false end
return false if !dns_alive(@underlayer_protocol)
print_status("#{msg} Fuzzing DNS server, this may take a while.")
print_status("#{msg} Fuzzing DNS server, this may take a while.")
if @startsize < 12 && @startsize > 0
print_status("#{msg} STARTSIZE must be at least 12. STARTSIZE value has been modified.")
@startsize = 12
if @startsize < 12 && @startsize > 0
print_status("#{msg} STARTSIZE must be at least 12. STARTSIZE value has been modified.")
@startsize = 12
end
if @rawpadding
if @domain.nil?
print_status('DNS Fuzzer: DOMAIN could be set for health check but not mandatory.')
end
nsopcode = @fuzz_opcode.split(',')
opcode = setup_opcode(nsopcode)
opcode.unpack('n*').each do |dns_opcode|
1.upto(iter) do
while @startsize <= @endsize
data = random_payload(@startsize).to_s
data[2] = 0x0
data[3] = dns_opcode
return false if !dns_send(data, @underlayer_protocol)
@lastdata = data
@startsize += @stepsize
end
@startsize = datastore['STARTSIZE']
end
end
return
end
if @dnsfile
if @domain.nil?
print_error('DNS Fuzzer: Domain variable must be set.')
return
end
if @rawpadding
if @domain == nil
print_status("DNS Fuzzer: DOMAIN could be set for health check but not mandatory.")
end
nsopcode=@fuzz_opcode.split(",")
dnsenumdata = import_enum_data(@dnsfile)
nsreq = []
nsclass = []
nsentry = []
for req, _ in dnsenumdata
nsreq << req[:rr]
nsclass << req[:class]
nsentry << req[:name]
end
nsopcode = @fuzz_opcode.split(',')
else
nsreq = @fuzz_rr.split(',')
nsopcode = @fuzz_opcode.split(',')
nsclass = @fuzz_class.split(',')
begin
classns = setup_nsclass(nsclass)
raise ArgumentError, "Invalid CLASS: #{nsclass.inspect}" unless classns
opcode = setup_opcode(nsopcode)
opcode.unpack("n*").each do |dnsOpcode|
raise ArgumentError, "Invalid OPCODE: #{opcode.inspect}" unless nsopcode
reqns = setup_reqns(nsreq)
raise ArgumentError, "Invalid RR: #{nsreq.inspect}" unless nsreq
rescue StandardError => e
print_error("DNS Fuzzer error, aborting: #{e}")
return
end
end
for question in nsreq
case question
when 'RRSIG', 'DNSKEY', 'DS', 'NSEC', 'NSEC3', 'NSEC3PARAM'
dnssec = true
end
end
if @dnsfile
classns = setup_nsclass(nsclass)
reqns = setup_reqns(nsreq)
opcode = setup_opcode(nsopcode)
opcode.unpack('n*').each do |dns_opcode|
for i in 0..nsentry.length - 1
reqns = setup_reqns(nsreq[i])
classns = setup_nsclass(nsclass[i])
1.upto(iter) do
nsdomain = setup_fqdn(@domain, nsentry[i])
split_fqdn = nsdomain.split('.')
payload = split_fqdn.inject('') { |a, x| a + [x.length, x].pack('CA*') }
pkt = build_packet(dns_opcode, dnssec, trailingnul, reqns, classns, payload)
pkt = corrupt_header(pkt, errorhdr) if errorhdr > 0
if @startsize == 0 && !dns_send(pkt, @underlayer_protocol)
break
end
while @startsize <= @endsize
data = random_payload(@startsize).to_s
data[2] = 0x0
data[3] = dnsOpcode
if !dns_send(data,@underlayerProtocol) then return false end
@lastdata = data
pkt = fuzz_padding(pkt, @startsize)
break if !dns_send(pkt, @underlayer_protocol)
@startsize += @stepsize
end
@startsize = datastore['STARTSIZE']
end
end
return
end
if @dnsfile
if @domain == nil
print_error("DNS Fuzzer: Domain variable must be set.")
return
end
dnsenumdata = import_enum_data(@dnsfile)
nsreq = []
nsclass = []
nsentry = []
for req, value in dnsenumdata
nsreq << req[:rr]
nsclass << req[:class]
nsentry << req[:name]
end
nsopcode=@fuzz_opcode.split(",")
else
nsreq=@fuzz_rr.split(",")
nsopcode=@fuzz_opcode.split(",")
nsclass=@fuzz_class.split(",")
begin
classns = setup_nsclass(nsclass)
raise ArgumentError, "Invalid CLASS: #{nsclass.inspect}" unless classns
opcode = setup_opcode(nsopcode)
raise ArgumentError, "Invalid OPCODE: #{opcode.inspect}" unless nsopcode
reqns = setup_reqns(nsreq)
raise ArgumentError, "Invalid RR: #{nsreq.inspect}" unless nsreq
rescue ::Exception => e
print_error("DNS Fuzzer error, aborting: #{e}")
return
end
end
for question in nsreq
case question
when "RRSIG", "DNSKEY", "DS", "NSEC", "NSEC3", "NSEC3PARAM"
dnssec = true
end
end
if @dnsfile
classns = setup_nsclass(nsclass)
reqns = setup_reqns(nsreq)
opcode = setup_opcode(nsopcode)
opcode.unpack("n*").each do |dnsOpcode|
for i in 0..nsentry.length - 1
reqns = setup_reqns(nsreq[i])
classns = setup_nsclass(nsclass[i])
else
classns.unpack('n*').each do |dns_class|
opcode.unpack('n*').each do |dns_opcode|
reqns.unpack('n*').each do |dns_req|
1.upto(iter) do
payload = ""
nsdomain = setup_fqdn(@domain,nsentry[i])
splitFQDN = nsdomain.split('.')
payload = splitFQDN.inject("") { |a,x| a + [x.length,x].pack("CA*") }
pkt = build_packet(dnsOpcode,dnssec,trailingnul,reqns,classns,payload)
pkt = corrupt_header(pkt,errorhdr) if errorhdr > 0
if @startsize == 0
if !dns_send(pkt,@underlayerProtocol) then return end
else
while @startsize <= @endsize
pkt = fuzz_padding(pkt, @startsize)
if !dns_send(pkt,@underlayerProtocol) then return end
@startsize += @stepsize
end
@startsize = datastore['STARTSIZE']
nsdomain = setup_fqdn(@domain, '')
split_fqdn = nsdomain.split('.')
payload = split_fqdn.inject('') { |a, x| a + [x.length, x].pack('CA*') }
pkt = build_packet(dns_opcode, dnssec, trailingnul, dns_req, dns_class, payload)
pkt = corrupt_header(pkt, errorhdr) if errorhdr > 0
if @startsize == 0 && !dns_send(pkt, @underlayer_protocol)
break
end
end
end
end
else
classns.unpack("n*").each do |dnsClass|
opcode.unpack("n*").each do |dnsOpcode|
reqns.unpack("n*").each do |dnsReq|
1.upto(iter) do
payload = ""
nsdomain = setup_fqdn(@domain,"")
splitFQDN = nsdomain.split('.')
payload = splitFQDN.inject("") { |a,x| a + [x.length,x].pack("CA*") }
pkt = build_packet(dnsOpcode,dnssec,trailingnul,dnsReq,dnsClass,payload)
pkt = corrupt_header(pkt,errorhdr) if errorhdr > 0
if @startsize == 0
if !dns_send(pkt,@underlayerProtocol) then return end # If then return end?
else
while @startsize <= @endsize
pkt = fuzz_padding(pkt, @startsize)
if !dns_send(pkt,@underlayerProtocol) then return end
@startsize += @stepsize
end
@startsize = datastore['STARTSIZE']
end
while @startsize <= @endsize
pkt = fuzz_padding(pkt, @startsize)
break if !dns_send(pkt, @underlayer_protocol)
@startsize += @stepsize
end
@startsize = datastore['STARTSIZE']
end
end
end
+173 -164
View File
@@ -9,39 +9,42 @@
#
##
class MetasploitModule < Msf::Auxiliary
include Exploit::Remote::TcpServer
def initialize()
def initialize
super(
'Name' => 'Simple FTP Client Fuzzer',
'Description' => %q{
'Name' => 'Simple FTP Client Fuzzer',
'Description' => %q{
This module will serve an FTP server and perform FTP client interaction fuzzing
},
'Author' => [ 'corelanc0d3r <peter.ve[at]corelan.be>' ],
'License' => MSF_LICENSE,
'References' =>
[
[ 'URL', 'http://www.corelan.be:8800/index.php/2010/10/12/death-of-an-ftp-client/' ],
]
)
'Author' => [ 'corelanc0d3r <peter.ve[at]corelan.be>' ],
'License' => MSF_LICENSE,
'References' => [
[ 'URL', 'http://www.corelan.be:8800/index.php/2010/10/12/death-of-an-ftp-client/' ],
],
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
OptPort.new('SRVPORT', [ true, "The local port to listen on.", 21 ]),
OptString.new('FUZZCMDS', [ true, "Comma separated list of commands to fuzz (Uppercase).", "LIST,NLST,LS,RETR", nil, /(?:[A-Z]+,?)+/ ]),
OptInt.new('STARTSIZE', [ true, "Fuzzing string startsize.",1000]),
OptInt.new('ENDSIZE', [ true, "Max Fuzzing string size.",200000]),
OptInt.new('STEPSIZE', [ true, "Increment fuzzing string each attempt.",1000]),
OptBool.new('RESET', [ true, "Reset fuzzing values after client disconnects with QUIT cmd.",true]),
OptString.new('WELCOME', [ true, "FTP Server welcome message.","Evil FTP Server Ready"]),
OptBool.new('CYCLIC', [ true, "Use Cyclic pattern instead of A's (fuzzing payload).",true]),
OptBool.new('ERROR', [ true, "Reply with error codes only",false]),
OptBool.new('EXTRALINE', [ true, "Add extra CRLF's in response to LIST",true])
])
OptPort.new('SRVPORT', [ true, 'The local port to listen on.', 21 ]),
OptString.new('FUZZCMDS', [ true, 'Comma separated list of commands to fuzz (Uppercase).', 'LIST,NLST,LS,RETR', nil, /(?:[A-Z]+,?)+/ ]),
OptInt.new('STARTSIZE', [ true, 'Fuzzing string startsize.', 1000]),
OptInt.new('ENDSIZE', [ true, 'Max Fuzzing string size.', 200000]),
OptInt.new('STEPSIZE', [ true, 'Increment fuzzing string each attempt.', 1000]),
OptBool.new('RESET', [ true, 'Reset fuzzing values after client disconnects with QUIT cmd.', true]),
OptString.new('WELCOME', [ true, 'FTP Server welcome message.', 'Evil FTP Server Ready']),
OptBool.new('CYCLIC', [ true, "Use Cyclic pattern instead of A's (fuzzing payload).", true]),
OptBool.new('ERROR', [ true, 'Reply with error codes only', false]),
OptBool.new('EXTRALINE', [ true, "Add extra CRLF's in response to LIST", true])
]
)
end
# Not compatible today
def support_ipv6?
false
@@ -53,287 +56,288 @@ class MetasploitModule < Msf::Auxiliary
end
def run
@fuzzsize=datastore['STARTSIZE'].to_i
exploit()
@fuzzsize = datastore['STARTSIZE'].to_i
exploit
end
# Handler for new FTP client connections
def on_client_connect(c)
@state[c] = {
:name => "#{c.peerhost}:#{c.peerport}",
:ip => c.peerhost,
:port => c.peerport,
:user => nil,
:pass => nil
def on_client_connect(client)
@state[client] = {
name: "#{client.peerhost}:#{client.peerport}",
ip: client.peerhost,
port: client.peerport,
user: nil,
pass: nil
}
# set up an active data port on port 20
print_status("Client connected : " + c.peerhost)
active_data_port_for_client(c, 20)
send_response(c,"","WELCOME",220," "+datastore['WELCOME'])
print_status("Client connected : #{client.peerhost}")
active_data_port_for_client(client, 20)
send_response(client, '', 'WELCOME', 220, ' ' + datastore['WELCOME'])
# from this point forward, on_client_data() will take over
end
def on_client_close(c)
@state.delete(c)
def on_client_close(client)
@state.delete(client)
end
# Active and Passive data connections
def passive_data_port_for_client(c)
@state[c][:mode] = :passive
if(not @state[c][:passive_sock])
def passive_data_port_for_client(client)
@state[client][:mode] = :passive
if !(@state[client][:passive_sock])
s = Rex::Socket::TcpServer.create(
'LocalHost' => '0.0.0.0',
'LocalPort' => 0,
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
)
dport = s.getsockname[2]
@state[c][:passive_sock] = s
@state[c][:passive_port] = dport
@state[client][:passive_sock] = s
@state[client][:passive_port] = dport
print_status(" - Set up passive data port #{dport}")
end
@state[c][:passive_port]
@state[client][:passive_port]
end
def active_data_port_for_client(c,port)
@state[c][:mode] = :active
connector = Proc.new {
host = c.peerhost.dup
sock = Rex::Socket::Tcp.create(
def active_data_port_for_client(client, port)
@state[client][:mode] = :active
connector = proc do
host = client.peerhost.dup
Rex::Socket::Tcp.create(
'PeerHost' => host,
'PeerPort' => port,
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
'Context' => { 'Msf' => framework, 'MsfExploit' => self }
)
}
@state[c][:active_connector] = connector
@state[c][:active_port] = port
end
@state[client][:active_connector] = connector
@state[client][:active_port] = port
print_status(" - Set up active data port #{port}")
end
def establish_data_connection(c)
print_status(" - Establishing #{@state[c][:mode]} data connection")
def establish_data_connection(client)
print_status(" - Establishing #{@state[client][:mode]} data connection")
begin
Timeout.timeout(20) do
if(@state[c][:mode] == :active)
return @state[c][:active_connector].call()
Timeout.timeout(20) do
if (@state[client][:mode] == :active)
return @state[client][:active_connector].call
end
if (@state[client][:mode] == :passive)
return @state[client][:passive_sock].accept
end
end
if(@state[c][:mode] == :passive)
return @state[c][:passive_sock].accept
end
end
print_status(" - Data connection active")
rescue ::Exception => e
print_status(' - Data connection active')
rescue StandardError => e
print_error("Failed to establish data connection: #{e.class} #{e}")
end
nil
end
# FTP Client-to-Server Command handlers
def on_client_data(c)
def on_client_data(client)
# get the client data
data = c.get_once
return if not data
# split data into command and arguments
cmd,arg = data.strip.split(/\s+/, 2)
arg ||= ""
data = client.get_once
return if !data
# split data into command and arguments
cmd, arg = data.strip.split(/\s+/, 2)
arg ||= ''
return if !cmd
return if not cmd
# convert commands to uppercase and strip spaces
case cmd.upcase.strip
when 'USER'
@state[c][:user] = arg
send_response(c,arg,"USER",331," User name okay, need password")
@state[client][:user] = arg
send_response(client, arg, 'USER', 331, ' User name okay, need password')
return
when 'PASS'
@state[c][:pass] = arg
send_response(c,arg,"PASS",230,"-Password accepted.\r\n230 User logged in.")
@state[client][:pass] = arg
send_response(client, arg, 'PASS', 230, "-Password accepted.\r\n230 User logged in.")
return
when 'QUIT'
if (datastore['RESET'])
print_status("Resetting fuzz settings")
if datastore['RESET']
print_status('Resetting fuzz settings')
@fuzzsize = datastore['STARTSIZE']
@stepsize = datastore['STEPSIZE']
end
print_status("** Client disconnected **")
send_response(c,arg,"QUIT",221," User logged out")
print_status('** Client disconnected **')
send_response(client, arg, 'QUIT', 221, ' User logged out')
return
when 'SYST'
send_response(c,arg,"SYST",215," UNIX Type: L8")
send_response(client, arg, 'SYST', 215, ' UNIX Type: L8')
return
when 'TYPE'
send_response(c,arg,"TYPE",200," Type set to #{arg}")
send_response(client, arg, 'TYPE', 200, " Type set to #{arg}")
return
when 'CWD'
send_response(c,arg,"CWD",250," CWD Command successful")
send_response(client, arg, 'CWD', 250, ' CWD Command successful')
return
when 'PWD'
send_response(c,arg,"PWD",257," \"/\" is current directory.")
send_response(client, arg, 'PWD', 257, ' "/" is current directory.')
return
when 'REST'
send_response(c,arg,"REST",200," OK")
send_response(client, arg, 'REST', 200, ' OK')
return
when 'XPWD'
send_response(c,arg,"PWD",257," \"/\" is current directory")
send_response(client, arg, 'PWD', 257, ' "/" is current directory')
return
when 'SIZE'
send_response(c,arg,"SIZE",213," 1")
send_response(client, arg, 'SIZE', 213, ' 1')
return
when 'MDTM'
send_response(c,arg,"MDTM",213," #{Time.now.strftime("%Y%m%d%H%M%S")}")
send_response(client, arg, 'MDTM', 213, " #{Time.now.strftime('%Y%m%d%H%M%S')}")
return
when 'CDUP'
send_response(c,arg,"CDUP",257," \"/\" is current directory")
send_response(client, arg, 'CDUP', 257, ' "/" is current directory')
return
when 'PORT'
port = arg.split(',')[4,2]
if(not port and port.length == 2)
c.put("500 Illegal PORT command.\r\n")
port = arg.split(',')[4, 2]
if !port && (port.length == 2)
client.put("500 Illegal PORT command.\r\n")
return
end
port = port.map{|x| x.to_i}.pack('C*').unpack('n')[0]
active_data_port_for_client(c, port)
send_response(c,arg,"PORT",200," PORT command successful")
port = port.map(&:to_i).pack('C*').unpack('n')[0]
active_data_port_for_client(client, port)
send_response(client, arg, 'PORT', 200, ' PORT command successful')
return
when 'PASV'
print_status("Handling #{cmd.upcase} command")
daddr = Rex::Socket.source_address(c.peerhost)
dport = passive_data_port_for_client(c)
@state[c][:daddr] = daddr
@state[c][:dport] = dport
pasv = (daddr.split('.') + [dport].pack('n').unpack('CC')).join(',')
dofuzz = fuzz_this_cmd("PASV")
daddr = Rex::Socket.source_address(client.peerhost)
dport = passive_data_port_for_client(client)
@state[client][:daddr] = daddr
@state[client][:dport] = dport
pasv = (daddr.split('.') + [dport].pack('n').unpack('CC')).join(',')
dofuzz = fuzz_this_cmd('PASV')
code = 227
if datastore['ERROR']
code = 557
end
if (dofuzz==1)
if (dofuzz == 1)
print_status(" * Fuzzing response for PASV, payload length #{@fuzzdata.length}")
send_response(c,arg,"PASV",code," Entering Passive Mode (#{@fuzzdata},1,1,1,1,1)\r\n")
incr_fuzzsize()
send_response(client, arg, 'PASV', code, " Entering Passive Mode (#{@fuzzdata},1,1,1,1,1)\r\n")
incr_fuzzsize
else
send_response(c,arg,"PASV",code," Entering Passive Mode (#{pasv})")
send_response(client, arg, 'PASV', code, " Entering Passive Mode (#{pasv})")
end
return
when /^(LIST|NLST|LS)$/
# special case - requires active/passive connection
print_status("Handling #{cmd.upcase} command")
conn = establish_data_connection(c)
if(not conn)
c.put("425 Can't build data connection\r\n")
conn = establish_data_connection(client)
if !conn
client.put("425 Can't build data connection\r\n")
return
end
print_status(" - Data connection set up")
print_status(' - Data connection set up')
code = 150
if datastore['ERROR']
code = 550
end
c.put("#{code} Here comes the directory listing.\r\n")
client.put("#{code} Here comes the directory listing.\r\n")
code = 226
if datastore['ERROR']
code = 550
end
c.put("#{code} Directory send ok.\r\n")
strfile = "passwords.txt"
strfolder = "Secret files"
dofuzz = fuzz_this_cmd("LIST")
if (dofuzz==1)
strfile = @fuzzdata + ".txt"
client.put("#{code} Directory send ok.\r\n")
strfile = 'passwords.txt'
strfolder = 'Secret files'
dofuzz = fuzz_this_cmd('LIST')
if (dofuzz == 1)
strfile = @fuzzdata + '.txt'
strfolder = @fuzzdata
paylen = @fuzzdata.length
print_status("* Fuzzing response for LIST, payload length #{paylen}")
incr_fuzzsize()
incr_fuzzsize
end
print_status(" - Sending directory list via data connection")
dirlist = ""
print_status(' - Sending directory list via data connection')
if datastore['EXTRALINE']
extra = "\r\n"
else
extra = ""
extra = ''
end
dirlist = "drwxrwxrwx 1 100 0 11111 Jun 11 21:10 #{strfolder}\r\n" + extra
dirlist << "-rw-rw-r-- 1 1176 1176 1060 Aug 16 22:22 #{strfile}\r\n" + extra
conn.put("total 2\r\n"+dirlist)
conn.put("total 2\r\n" + dirlist)
conn.close
return
when 'RETR'
# special case - requires active/passive connection
print_status("Handling #{cmd.upcase} command")
conn = establish_data_connection(c)
if(not conn)
c.put("425 Can't build data connection\r\n")
conn = establish_data_connection(client)
if !conn
client.put("425 Can't build data connection\r\n")
return
end
print_status(" - Data connection set up")
strcontent = "blahblahblah"
dofuzz = fuzz_this_cmd("LIST")
if (dofuzz==1)
print_status(' - Data connection set up')
strcontent = 'blahblahblah'
dofuzz = fuzz_this_cmd('LIST')
if (dofuzz == 1)
strcontent = @fuzzdata
paylen = @fuzzdata.length
print_status("* Fuzzing response for RETR, payload length #{paylen}")
incr_fuzzsize()
incr_fuzzsize
end
c.put("150 Opening BINARY mode data connection #{strcontent}\r\n")
print_status(" - Sending data via data connection")
client.put("150 Opening BINARY mode data connection #{strcontent}\r\n")
print_status(' - Sending data via data connection')
conn.put(strcontent)
c.put("226 Transfer complete\r\n")
client.put("226 Transfer complete\r\n")
conn.close
return
when /^(STOR|MKD|REM|DEL|RMD)$/
send_response(c,arg,cmd.upcase,500," Access denied")
send_response(client, arg, cmd.upcase, 500, ' Access denied')
return
when 'FEAT'
send_response(c,arg,"FEAT","","211-Features:\r\n211 End")
send_response(client, arg, 'FEAT', '', "211-Features:\r\n211 End")
return
when 'HELP'
send_response(c,arg,"HELP",214," Syntax: #{arg} - (#{arg}-specific commands)")
send_response(client, arg, 'HELP', 214, " Syntax: #{arg} - (#{arg}-specific commands)")
when 'SITE'
send_response(c,arg,"SITE",200," OK")
send_response(client, arg, 'SITE', 200, ' OK')
return
when 'NOOP'
send_response(c,arg,"NOOP",200," OK")
send_response(client, arg, 'NOOP', 200, ' OK')
return
when 'ABOR'
send_response(c,arg,"ABOR",225," Abor command successful")
send_response(client, arg, 'ABOR', 225, ' Abor command successful')
return
when 'ACCT'
send_response(c,arg,"ACCT",200," OK")
send_response(client, arg, 'ACCT', 200, ' OK')
return
when 'RNFR'
send_response(c,arg,"RNRF",350," File.exist")
send_response(client, arg, 'RNRF', 350, ' File.exist')
return
when 'RNTO'
send_response(c,arg,"RNTO",350," File.exist")
send_response(client, arg, 'RNTO', 350, ' File.exist')
return
else
send_response(c,arg,cmd.upcase,200," Command not understood")
send_response(client, arg, cmd.upcase, 200, ' Command not understood')
return
end
return
end
@@ -341,71 +345,76 @@ class MetasploitModule < Msf::Auxiliary
# Do we need to fuzz this command ?
def fuzz_this_cmd(cmd)
@fuzzcommands = datastore['FUZZCMDS'].split(",")
@fuzzcommands = datastore['FUZZCMDS'].split(',')
fuzzme = 0
@fuzzcommands.each do |thiscmd|
if ((cmd.upcase == thiscmd.upcase) || (thiscmd=="*")) && (fuzzme==0)
if ((cmd.upcase == thiscmd.upcase) || (thiscmd == '*')) && (fuzzme == 0)
fuzzme = 1
break
end
end
if fuzzme==1
if fuzzme == 1
# should we use a cyclic pattern, or just A's ?
if datastore['CYCLIC']
@fuzzdata = Rex::Text.pattern_create(@fuzzsize)
else
@fuzzdata = "A" * @fuzzsize
@fuzzdata = 'A' * @fuzzsize
end
end
return fuzzme
end
def incr_fuzzsize
@stepsize = datastore['STEPSIZE'].to_i
@fuzzsize = @fuzzsize + @stepsize
@fuzzsize += @stepsize
print_status("(i) Setting next payload size to #{@fuzzsize}")
if (@fuzzsize > datastore['ENDSIZE'].to_i)
@fuzzsize = datastore['ENDSIZE'].to_i
end
end
# Send data back to the server
def send_response(c,arg,cmd,code,msg)
def send_response(client, arg, cmd, code, msg)
if arg.length > 40
showarg = arg[0,40] + "..."
showarg = arg[0, 40] + '...'
else
showarg = arg
end
if cmd.length > 40
showcmd = cmd[0,40] + "..."
showcmd = cmd[0, 40] + '...'
else
showcmd = cmd
end
print_status("Sending response for '#{showcmd}' command, arg #{showarg}")
dofuzz = fuzz_this_cmd(cmd)
## Fuzz this command ? (excluding PASV, which is handled in the command handler)
if (dofuzz==1) && (cmd.upcase != "PASV")
if (dofuzz == 1) && (cmd.upcase != 'PASV')
paylen = @fuzzdata.length
print_status("* Fuzzing response for #{cmd.upcase}, payload length #{paylen}")
if datastore['ERROR']
code = "550 "
code = '550 '
end
if cmd=="FEAT"
@fuzzdata = "211-Features:\r\n "+@fuzzdata+"\r\n211 End"
if cmd == 'FEAT'
@fuzzdata = "211-Features:\r\n " + @fuzzdata + "\r\n211 End"
end
if cmd=="PWD"
@fuzzdata = " \"/"+@fuzzdata+"\" is current directory"
if cmd == 'PWD'
@fuzzdata = ' "/' + @fuzzdata + '" is current directory'
end
cmsg = code.to_s + " " + @fuzzdata
c.put("#{cmsg}\r\n")
print_status("* Fuzz data sent")
incr_fuzzsize()
cmsg = code.to_s + ' ' + @fuzzdata
client.put("#{cmsg}\r\n")
print_status('* Fuzz data sent')
incr_fuzzsize
else
# Do not fuzz
cmsg = code.to_s + msg
cmsg = cmsg.strip
c.put("#{cmsg}\r\n")
client.put("#{cmsg}\r\n")
end
return
end
end
+154 -149
View File
@@ -9,134 +9,137 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'Simple FTP Fuzzer',
'Description' => %q{
'Name' => 'Simple FTP Fuzzer',
'Description' => %q{
This module will connect to a FTP server and perform pre- and post-authentication fuzzing
},
'Author' => [ 'corelanc0d3r <peter.ve[at]corelan.be>', 'jduck' ],
'License' => MSF_LICENSE
)
'Author' => [ 'corelanc0d3r <peter.ve[at]corelan.be>', 'jduck' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
[
Opt::RPORT(21),
OptInt.new('STARTATSTAGE', [ false, "Start at this test stage",1]),
OptInt.new('STEPSIZE', [ false, "Increase string size each iteration with this number of chars",10]),
OptInt.new('DELAY', [ false, "Delay between connections in seconds",1]),
OptInt.new('STARTSIZE', [ false, "Fuzzing string startsize",10]),
OptInt.new('ENDSIZE', [ false, "Fuzzing string endsize",20000]),
OptInt.new('STOPAFTER', [ false, "Stop after x number of consecutive errors",2]),
OptString.new('USER', [ false, "Username",'anonymous']),
OptString.new('PASS', [ false, "Password",'mozilla@example.com']),
OptBool.new('FASTFUZZ', [ false, "Only fuzz with cyclic pattern",true]),
OptBool.new('CONNRESET', [ false, "Break on CONNRESET error",true]),
])
OptInt.new('STARTATSTAGE', [ false, 'Start at this test stage', 1]),
OptInt.new('STEPSIZE', [ false, 'Increase string size each iteration with this number of chars', 10]),
OptInt.new('DELAY', [ false, 'Delay between connections in seconds', 1]),
OptInt.new('STARTSIZE', [ false, 'Fuzzing string startsize', 10]),
OptInt.new('ENDSIZE', [ false, 'Fuzzing string endsize', 20000]),
OptInt.new('STOPAFTER', [ false, 'Stop after x number of consecutive errors', 2]),
OptString.new('USER', [ false, 'Username', 'anonymous']),
OptString.new('PASS', [ false, 'Password', 'mozilla@example.com']),
OptBool.new('FASTFUZZ', [ false, 'Only fuzz with cyclic pattern', true]),
OptBool.new('CONNRESET', [ false, 'Break on CONNRESET error', true]),
]
)
@evilchars = [
'A','a','%s','%d','%n','%x','%p','-1','0','0xfffffffe','0xffffffff','A/','//','/..','//..',
'A%20','./A','.A',',A','A:','!A','&A','?A','\A','../A/','..?','//A:','\\A','{A','$A','A*',
'cmd','A@a.com','#A','A/../','~','~A','~A/','A`/','>A','<A','A%n','A../','.././','A../',
'....//','~?*/','.\../','\.//A','-%A','%Y','%H','/1','!','@','%','&','/?(*','*','(',')',
'`',',','~/','/.','\$:','/A~%n','=','=:;)}','1.2.','41414141','-1234','999999,','%00','+A',
'+123','..\'','??.','..\.\'','.../','1234123+',
'%Y%%Y%/','%FC%80%80%80%80%AE%FC%80%80%80%80%AE/','????/','\uff0e/','%%32%65%%32%65/',
'+B./','%%32%65%%32%65/','..%c0%af','..%e0%80%af','..%c1%9c'
'A', 'a', '%s', '%d', '%n', '%x', '%p', '-1', '0', '0xfffffffe', '0xffffffff', 'A/', '//', '/..', '//..',
'A%20', './A', '.A', ',A', 'A:', '!A', '&A', '?A', '\A', '../A/', '..?', '//A:', '\\A', '{A', '$A', 'A*',
'cmd', 'A@a.com', '#A', 'A/../', '~', '~A', '~A/', 'A`/', '>A', '<A', 'A%n', 'A../', '.././', 'A../',
'....//', '~?*/', '.\../', '\.//A', '-%A', '%Y', '%H', '/1', '!', '@', '%', '&', '/?(*', '*', '(', ')',
'`', ',', '~/', '/.', '\$:', '/A~%n', '=', '=:;)}', '1.2.', '41414141', '-1234', '999999,', '%00', '+A',
'+123', '..\'', '??.', '..\.\'', '.../', '1234123+',
'%Y%%Y%/', '%FC%80%80%80%80%AE%FC%80%80%80%80%AE/', '????/', '\uff0e/', '%%32%65%%32%65/',
'+B./', '%%32%65%%32%65/', '..%c0%af', '..%e0%80%af', '..%c1%9c'
]
@commands = [
'ABOR','ACCT','ALLO','APPE','AUTH','CWD','CDUP','DELE','FEAT','HELP','HOST','LANG','LIST',
'MDTM','MKD','MLST','MODE','NLST','NLST -al','NOOP','OPTS','PASV','PORT','PROT','PWD','REIN',
'REST','RETR','RMD','RNFR','RNTO','SIZE','SITE','SITE CHMOD','SITE CHOWN','SITE EXEC','SITE MSG',
'SITE PSWD','SITE ZONE','SITE WHO','SMNT','STAT','STOR','STOU','STRU','SYST','TYPE','XCUP',
'XCRC','XCWD','XMKD','XPWD','XRMD'
'ABOR', 'ACCT', 'ALLO', 'APPE', 'AUTH', 'CWD', 'CDUP', 'DELE', 'FEAT', 'HELP', 'HOST', 'LANG', 'LIST',
'MDTM', 'MKD', 'MLST', 'MODE', 'NLST', 'NLST -al', 'NOOP', 'OPTS', 'PASV', 'PORT', 'PROT', 'PWD', 'REIN',
'REST', 'RETR', 'RMD', 'RNFR', 'RNTO', 'SIZE', 'SITE', 'SITE CHMOD', 'SITE CHOWN', 'SITE EXEC', 'SITE MSG',
'SITE PSWD', 'SITE ZONE', 'SITE WHO', 'SMNT', 'STAT', 'STOR', 'STOU', 'STRU', 'SYST', 'TYPE', 'XCUP',
'XCRC', 'XCWD', 'XMKD', 'XPWD', 'XRMD'
]
@emax = @evilchars.length
register_advanced_options(
[
OptString.new('FtpCommands', [ false, "Commands to fuzz at stages 4 and 5",@commands.join(" ")]),
OptBool.new('ExpandCrash', [ false, "Expand any crash strings",false]),
])
OptString.new('FtpCommands', [ false, 'Commands to fuzz at stages 4 and 5', @commands.join(' ')]),
OptBool.new('ExpandCrash', [ false, 'Expand any crash strings', false]),
]
)
end
def get_pkt
buf = sock.get_once(-1, 10)
vprint_status("[in ] #{buf.inspect}")
buf
end
def send_pkt(pkt, get_resp = false)
def send_pkt(pkt, get_resp: true)
vprint_status("[out] #{pkt.inspect}")
sock.put(pkt)
get_pkt if get_resp
end
def process_phase(phase_num, phase_name, prepend = '', initial_cmds = [])
print_status("[Phase #{phase_num}] #{phase_name} - #{Time.now.localtime}")
ecount = 1
@evilchars.each do |evilstr|
if datastore['FASTFUZZ']
evilstr = "Cyclic"
evilstr = 'Cyclic'
@emax = 1
end
if (@stopprocess == false)
count = datastore['STARTSIZE']
print_status(" Character : #{evilstr} (#{ecount}/#{@emax})")
ecount += 1
while count <= datastore['ENDSIZE']
begin
connect
if datastore['FASTFUZZ']
evil = Rex::Text.pattern_create(count)
next unless (@stopprocess == false)
count = datastore['STARTSIZE']
print_status(" Character : #{evilstr} (#{ecount}/#{@emax})")
ecount += 1
while count <= datastore['ENDSIZE']
begin
connect
if datastore['FASTFUZZ']
evil = Rex::Text.pattern_create(count)
else
evil = evilstr * count
end
print_status(" -> Fuzzing size set to #{count} (#{prepend}#{evilstr})")
initial_cmds.each do |cmd|
send_pkt(cmd)
end
pkt = prepend + evil + "\r\n"
send_pkt(pkt)
sock.put("QUIT\r\n")
select(nil, nil, nil, datastore['DELAY'])
disconnect
count += datastore['STEPSIZE']
rescue StandardError => e
@error_cnt += 1
print_status("Exception #{@error_cnt} of #{@nr_errors}")
if e.instance_of?(::Rex::ConnectionRefused) || e.instance_of?(::EOFError) || (e.instance_of?(::Errno::ECONNRESET) && datastore['CONNRESET']) || e.instance_of?(::Errno::EPIPE)
if datastore['ExpandCrash']
print_status("Crash string : #{prepend}#{evil}")
else
evil = evilstr * count
end
print_status(" -> Fuzzing size set to #{count} (#{prepend}#{evilstr})")
initial_cmds.each do |cmd|
send_pkt(cmd, true)
end
pkt = prepend + evil + "\r\n"
send_pkt(pkt, true)
sock.put("QUIT\r\n")
select(nil, nil, nil, datastore['DELAY'])
disconnect
count += datastore['STEPSIZE']
rescue ::Exception => e
@error_cnt += 1
print_status("Exception #{@error_cnt} of #{@nr_errors}")
if (e.class.name == 'Rex::ConnectionRefused') or (e.class.name == 'EOFError') or (e.class.name == 'Errno::ECONNRESET' and datastore['CONNRESET']) or (e.class.name == 'Errno::EPIPE')
if datastore['ExpandCrash']
print_status("Crash string : #{prepend}#{evil}")
else
print_status("Crash string : #{prepend}#{evilstr} x #{count}")
end
if @error_cnt >= @nr_errors
print_status("System does not respond - exiting now\n")
@stopprocess = true
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
return
else
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
select(nil,nil,nil,3) #wait 3 seconds
end
print_status("Crash string : #{prepend}#{evilstr} x #{count}")
end
if @error_cnt >= @nr_errors
count += datastore['STEPSIZE']
@error_cnt = 0
print_status("System does not respond - exiting now\n")
@stopprocess = true
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
break
else
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
select(nil, nil, nil, 3) # wait 3 seconds
end
end
if @error_cnt >= @nr_errors
count += datastore['STEPSIZE']
@error_cnt = 0
end
end
end
end
end
def ftp_commands
if datastore['FtpCommands'].to_s.upcase == "DEFAULT"
if datastore['FtpCommands'].to_s.upcase == 'DEFAULT'
@commands
else
datastore['FtpCommands'].split(/[\s,]+/)
@@ -144,7 +147,6 @@ class MetasploitModule < Msf::Auxiliary
end
def run_host(ip)
startstage = datastore['STARTATSTAGE']
@nr_errors = datastore['STOPAFTER']
@@ -155,97 +157,100 @@ class MetasploitModule < Msf::Auxiliary
@evilchars = ['']
end
print_status("Connecting to host " + ip + " on port " + datastore['RPORT'].to_s)
print_status('Connecting to host ' + ip + ' on port ' + datastore['RPORT'].to_s)
if (startstage == 1)
process_phase(1, "Fuzzing without command")
process_phase(1, 'Fuzzing without command')
startstage += 1
end
if (startstage == 2) and (@stopprocess == false)
process_phase(2, "Fuzzing USER", 'USER ')
if (startstage == 2) && (@stopprocess == false)
process_phase(2, 'Fuzzing USER', 'USER ')
startstage += 1
end
if (startstage == 3) and (@stopprocess == false)
process_phase(3, "Fuzzing PASS", 'PASS ',
[ "USER " + datastore['USER'] + "\r\n" ])
if (startstage == 3) && (@stopprocess == false)
process_phase(3, 'Fuzzing PASS', 'PASS ',
[ 'USER ' + datastore['USER'] + "\r\n" ])
startstage += 1
end
if (startstage == 4)
print_status "[Phase 4] Fuzzing commands: #{ftp_commands.join(", ")}"
ftp_commands().each do |cmd|
if (@stopprocess == false)
process_phase(4, "Fuzzing command: #{cmd}", "#{cmd} ",
[
"USER " + datastore['USER'] + "\r\n",
"PASS " + datastore['PASS'] + "\r\n"
])
end
print_status "[Phase 4] Fuzzing commands: #{ftp_commands.join(', ')}"
ftp_commands.each do |cmd|
next unless (@stopprocess == false)
process_phase(
4,
"Fuzzing command: #{cmd}", "#{cmd} ",
[
'USER ' + datastore['USER'] + "\r\n",
'PASS ' + datastore['PASS'] + "\r\n"
]
)
end
# Don't progress into stage 5, it must be selected manually.
#startstage += 1
# startstage += 1
end
# Fuzz other commands, all command combinations in one session
if (startstage == 5)
print_status("[Phase 5] Fuzzing other commands (Part 2, #{Time.now.localtime}): #{ftp_commands.join(", ")}")
ftp_commands().each do |cmd|
if (@stopprocess == false)
ecount = 1
count = datastore['STARTSIZE']
print_status("Fuzzing command #{cmd} - #{Time.now.localtime}" )
print_status("[Phase 5] Fuzzing other commands (Part 2, #{Time.now.localtime}): #{ftp_commands.join(', ')}")
ftp_commands.each do |cmd|
next unless (@stopprocess == false)
connect
pkt = "USER " + datastore['USER'] + "\r\n"
send_pkt(pkt, true)
pkt = "PASS " + datastore['PASS'] + "\r\n"
send_pkt(pkt, true)
ecount = 1
count = datastore['STARTSIZE']
print_status("Fuzzing command #{cmd} - #{Time.now.localtime}")
while count <= datastore['ENDSIZE']
print_status(" -> Fuzzing size set to #{count}")
begin
@evilchars.each do |evilstr|
if datastore['FASTFUZZ']
evilstr = "Cyclic"
evil = Rex::Text.pattern_create(count)
@emax = 1
ecount = 1
else
evil = evilstr * count
end
print_status(" Command : #{cmd}, Character : #{evilstr} (#{ecount}/#{@emax})")
ecount += 1
pkt = cmd + " " + evil + "\r\n"
send_pkt(pkt, true)
select(nil, nil, nil, datastore['DELAY'])
@error_cnt = 0
end
rescue ::Exception => e
@error_cnt += 1
print_status("Exception #{@error_cnt} of #{@nr_errors}")
if (e.class.name == 'Rex::ConnectionRefused') or (e.class.name == 'EOFError') or (e.class.name == 'Errno::ECONNRESET' and datastore['CONNRESET']) or (e.class.name == 'Errno::EPIPE')
if @error_cnt >= @nr_errors
print_status("System does not respond - exiting now\n")
@stopprocess = true
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
return
else
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
select(nil,nil,nil,3) #wait 3 seconds
end
end
if @error_cnt >= @nr_errors
@error_cnt = 0
connect
pkt = 'USER ' + datastore['USER'] + "\r\n"
send_pkt(pkt)
pkt = 'PASS ' + datastore['PASS'] + "\r\n"
send_pkt(pkt)
while count <= datastore['ENDSIZE']
print_status(" -> Fuzzing size set to #{count}")
begin
@evilchars.each do |evilstr|
if datastore['FASTFUZZ']
evilstr = 'Cyclic'
evil = Rex::Text.pattern_create(count)
@emax = 1
ecount = 1
else
evil = evilstr * count
end
print_status(" Command : #{cmd}, Character : #{evilstr} (#{ecount}/#{@emax})")
ecount += 1
pkt = cmd + ' ' + evil + "\r\n"
send_pkt(pkt)
select(nil, nil, nil, datastore['DELAY'])
@error_cnt = 0
end
rescue StandardError => e
@error_cnt += 1
print_status("Exception #{@error_cnt} of #{@nr_errors}")
if e.instance_of?(::Rex::ConnectionRefused) || e.instance_of?(::EOFError) || (e.instance_of?(::Errno::ECONNRESET) && datastore['CONNRESET']) || e.instance_of?(::Errno::EPIPE)
if @error_cnt >= @nr_errors
print_status("System does not respond - exiting now\n")
@stopprocess = true
print_error("Error: #{e.class} #{e} #{e.backtrace}\n")
break
end
print_status("Exception triggered, need #{@nr_errors - @error_cnt} more exception(s) before interrupting process")
select(nil, nil, nil, 3) # wait 3 seconds
end
if @error_cnt >= @nr_errors
@error_cnt = 0
end
count += datastore['STEPSIZE']
end
sock.put("QUIT\r\n")
select(nil, nil, nil, datastore['DELAY'])
disconnect
count += datastore['STEPSIZE']
end
sock.put("QUIT\r\n")
select(nil, nil, nil, datastore['DELAY'])
disconnect
end
end
end
+290 -283
View File
@@ -7,136 +7,144 @@ class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'HTTP Form Field Fuzzer',
'Description' => %q{
This module will grab all fields from a form,
and launch a series of POST actions, fuzzing the contents
of the form fields. You can optionally fuzz headers too
(option is enabled by default)
},
'Author' => [
'corelanc0d3r',
'Paulino Calderon <calderon[at]websec.mx>' #Added cookie handling
super(
update_info(
info,
'Name' => 'HTTP Form Field Fuzzer',
'Description' => %q{
This module will grab all fields from a form,
and launch a series of POST actions, fuzzing the contents
of the form fields. You can optionally fuzz headers too
(option is enabled by default)
},
'Author' => [
'corelanc0d3r',
'Paulino Calderon <calderon[at]websec.mx>' # Added cookie handling
],
'License' => MSF_LICENSE,
'References' =>
[
['URL','http://www.corelan.be:8800/index.php/2010/11/12/metasploit-module-http-form-field-fuzzer'],
]
))
'License' => MSF_LICENSE,
'References' => [
['URL', 'http://www.corelan.be:8800/index.php/2010/11/12/metasploit-module-http-form-field-fuzzer'],
],
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options(
[
OptString.new('URL', [ false, "The URL that contains the form", "/"]),
OptString.new('FORM', [ false, "The name of the form to use. Leave empty to fuzz all forms","" ] ),
OptString.new('FIELDS', [ false, "Name of the fields to fuzz. Leave empty to fuzz all fields","" ] ),
OptString.new('ACTION', [ false, "Form action full URI. Leave empty to autodetect","" ] ),
OptInt.new('STARTSIZE', [ true, "Fuzzing string startsize.",1000]),
OptInt.new('ENDSIZE', [ true, "Max Fuzzing string size.",40000]),
OptInt.new('STEPSIZE', [ true, "Increment fuzzing string each attempt.",1000]),
OptInt.new('TIMEOUT', [ true, "Number of seconds to wait for response on GET or POST",15]),
OptInt.new('DELAY', [ true, "Number of seconds to wait between 2 actions",0]),
OptInt.new('STOPAFTER', [ false, "Stop after x number of consecutive errors",2]),
OptBool.new('CYCLIC', [ true, "Use Cyclic pattern instead of A's (fuzzing payload).",true]),
OptBool.new('FUZZHEADERS', [ true, "Fuzz headers",true]),
OptString.new('HEADERFIELDS', [ false, "Name of the headerfields to fuzz. Leave empty to fuzz all fields","" ] ),
OptString.new('TYPES', [ true, "Field types to fuzz","text,password,inputtextbox"]),
OptString.new('CODE', [ true, "Response code(s) indicating OK", "200,301,302,303" ] ),
OptBool.new('HANDLECOOKIES', [ true, "Appends cookies with every request.",false])
])
OptString.new('URL', [ false, 'The URL that contains the form', '/']),
OptString.new('FORM', [ false, 'The name of the form to use. Leave empty to fuzz all forms', '' ]),
OptString.new('FIELDS', [ false, 'Name of the fields to fuzz. Leave empty to fuzz all fields', '' ]),
OptString.new('ACTION', [ false, 'Form action full URI. Leave empty to autodetect', '' ]),
OptInt.new('STARTSIZE', [ true, 'Fuzzing string startsize.', 1000]),
OptInt.new('ENDSIZE', [ true, 'Max Fuzzing string size.', 40000]),
OptInt.new('STEPSIZE', [ true, 'Increment fuzzing string each attempt.', 1000]),
OptInt.new('TIMEOUT', [ true, 'Number of seconds to wait for response on GET or POST', 15]),
OptInt.new('DELAY', [ true, 'Number of seconds to wait between 2 actions', 0]),
OptInt.new('STOPAFTER', [ false, 'Stop after x number of consecutive errors', 2]),
OptBool.new('CYCLIC', [ true, "Use Cyclic pattern instead of A's (fuzzing payload).", true]),
OptBool.new('FUZZHEADERS', [ true, 'Fuzz headers', true]),
OptString.new('HEADERFIELDS', [ false, 'Name of the headerfields to fuzz. Leave empty to fuzz all fields', '' ]),
OptString.new('TYPES', [ true, 'Field types to fuzz', 'text,password,inputtextbox']),
OptString.new('CODE', [ true, 'Response code(s) indicating OK', '200,301,302,303' ]),
OptBool.new('HANDLECOOKIES', [ true, 'Appends cookies with every request.', false])
]
)
end
def init_vars
proto = "http://"
proto = 'http://'
if datastore['SSL']
proto = "https://"
proto = 'https://'
end
@send_data = {
:uri => '',
:version => '1.1',
:method => 'POST',
:headers => {
'Content-Length' => 100,
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' => 'en-us,en;q=0.5',
'Accept-Encoding' => 'gzip,deflate',
'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Keep-Alive' => '300',
'Connection' => 'keep-alive',
'Referer' => proto + datastore['RHOST'] + ":" + datastore['RPORT'].to_s,
'Content-Type' => 'application/x-www-form-urlencoded'
}
uri: '',
version: '1.1',
method: 'POST',
headers: {
'Content-Length' => 100,
'Accept' => 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language' => 'en-us,en;q=0.5',
'Accept-Encoding' => 'gzip,deflate',
'Accept-Charset' => 'ISO-8859-1,utf-8;q=0.7,*;q=0.7',
'Keep-Alive' => '300',
'Connection' => 'keep-alive',
'Referer' => proto + datastore['RHOST'] + ':' + datastore['RPORT'].to_s,
'Content-Type' => 'application/x-www-form-urlencoded'
}
}
@get_data_headers = {
'Referer' => proto + datastore['RHOST'] + ":" + datastore['RPORT'].to_s,
}
'Referer' => proto + datastore['RHOST'] + ':' + datastore['RPORT'].to_s
}
end
def init_fuzzdata
@fuzzsize = datastore['STARTSIZE']
@endsize = datastore['ENDSIZE']
set_fuzz_payload()
set_fuzz_payload
@nrerrors = 0
end
def incr_fuzzsize
@stepsize = datastore['STEPSIZE'].to_i
@fuzzsize = @fuzzsize + @stepsize
@fuzzsize += @stepsize
end
def set_fuzz_payload
if datastore['CYCLIC']
@fuzzdata = Rex::Text.pattern_create(@fuzzsize)
else
@fuzzdata = "A" * @fuzzsize
@fuzzdata = 'A' * @fuzzsize
end
end
def is_error_code(code)
okcode = false
checkcodes = datastore['CODE'].split(",")
checkcodes.each do | testcode |
testcode = testcode.upcase.gsub(" ","")
if testcode == code.to_s().upcase.gsub(" ","")
checkcodes = datastore['CODE'].split(',')
checkcodes.each do |testcode|
testcode = testcode.upcase.gsub(' ', '')
if testcode == code.to_s.upcase.gsub(' ', '')
okcode = true
end
end
return okcode
end
def fuzz_this_field(fieldname,fieldtype)
fuzzcommands = datastore['FIELDS'].split(",")
def fuzz_this_field(fieldname, fieldtype)
fuzzcommands = datastore['FIELDS'].split(',')
fuzzme = 0
if fuzzcommands.size > 0
if !fuzzcommands.empty?
fuzzcommands.each do |thiscmd|
thiscmd = thiscmd.strip
if ((fieldname.upcase == thiscmd.upcase) || (thiscmd == "")) && (fuzzme == 0)
if ((fieldname.upcase == thiscmd.upcase) || (thiscmd == '')) && (fuzzme == 0)
fuzzme = 1
end
end
else
fuzztypes = datastore['TYPES'].split(",")
fuzztypes.each do | thistype |
fuzztypes = datastore['TYPES'].split(',')
fuzztypes.each do |thistype|
if (fieldtype.upcase.strip == thistype.upcase.strip)
fuzzme = 1
end
end
end
if fuzzme == 1
set_fuzz_payload()
set_fuzz_payload
end
return fuzzme
end
def fuzz_this_headerfield(fieldname)
fuzzheaderfields = datastore['HEADERFIELDS'].split(",")
fuzzheaderfields = datastore['HEADERFIELDS'].split(',')
fuzzme = 0
if fuzzheaderfields.size > 0
if !fuzzheaderfields.empty?
fuzzheaderfields.each do |thisfield|
thisfield = thisfield.strip
if ((fieldname.upcase == thisfield.upcase) || (thisfield == "")) && (fuzzme == 0)
if ((fieldname.upcase == thisfield.upcase) || (thisfield == '')) && (fuzzme == 0)
fuzzme = 1
end
end
@@ -144,25 +152,23 @@ class MetasploitModule < Msf::Auxiliary
fuzzme = 1
end
if fuzzme == 1
set_fuzz_payload()
set_fuzz_payload
end
return fuzzme
end
def do_fuzz_headers(form,headers)
def do_fuzz_headers(form, headers)
headercnt = 0
datastr = ""
form[:fields].each do | thisfield |
normaldata = "blah&"
if thisfield[:value]
if thisfield[:value] != ""
normaldata = thisfield[:value].strip + "&"
end
datastr = ''
form[:fields].each do |thisfield|
normaldata = 'blah&'
if thisfield[:value] && thisfield[:value] != ('')
normaldata = thisfield[:value].strip + '&'
end
datastr << thisfield[:name].downcase.strip + "=" + normaldata
datastr << thisfield[:name].downcase.strip + '=' + normaldata
end
if datastr.length > 0
datastr=datastr[0,datastr.length-1] + "\r\n"
if !datastr.empty?
datastr = datastr[0, datastr.length - 1] + "\r\n"
else
datastr = "\r\n"
end
@@ -171,141 +177,142 @@ class MetasploitModule < Msf::Auxiliary
mysendheaders = @send_data[:headers].dup
# get or post ?
mysendheaders[:method] = form[:method].upcase
myheaders.each do | thisheader |
if not headers[thisheader[0]]
myheaders.each do |thisheader|
if !(headers[thisheader[0]])
# add header if needed
mysendheaders[thisheader[0]]= thisheader[1]
mysendheaders[thisheader[0]] = thisheader[1]
end
end
nrheaderstofuzz = mysendheaders.size
mysendheaders.each do | thisheader|
mysendheaders.each do |thisheader|
@fuzzheader = mysendheaders.dup
@nrerrors = 0
fuzzpacket = @send_data.dup
fuzzpacket[:method] = mysendheaders[:method]
headername = thisheader[0]
if fuzz_this_headerfield(headername.to_s().upcase) == 1
print_status(" - Fuzzing header '#{headername}' (#{headercnt+1}/#{nrheaderstofuzz})")
init_fuzzdata()
while @fuzzsize <= @endsize+1
if fuzz_this_headerfield(headername.to_s.upcase) == 1
print_status(" - Fuzzing header '#{headername}' (#{headercnt + 1}/#{nrheaderstofuzz})")
init_fuzzdata
while @fuzzsize <= @endsize + 1
@fuzzheader[headername] = @fuzzdata
fuzzpacket[:headers] = @fuzzheader
response = send_fuzz(fuzzpacket,datastr)
if not process_response(response,headername,"header")
@fuzzsize = @endsize+2
response = send_fuzz(fuzzpacket, datastr)
if !process_response(response, headername, 'header')
@fuzzsize = @endsize + 2
end
if datastore['DELAY'] > 0
print_status(" (Sleeping for #{datastore['DELAY']} seconds...)")
select(nil,nil,nil,datastore['DELAY'])
select(nil, nil, nil, datastore['DELAY'])
end
incr_fuzzsize()
incr_fuzzsize
end
else
print_status(" - Skipping header '#{headername}' (#{headercnt+1}/#{nrheaderstofuzz})")
print_status(" - Skipping header '#{headername}' (#{headercnt + 1}/#{nrheaderstofuzz})")
end
headercnt += 1
end
end
def do_fuzz_field(form,field)
fieldstofuzz = field.downcase.strip.split(",")
def do_fuzz_field(form, field)
fieldstofuzz = field.downcase.strip.split(',')
@nrerrors = 0
while @fuzzsize <= @endsize+1
while @fuzzsize <= @endsize + 1
allfields = form[:fields]
datastr = ""
normaldata = ""
allfields.each do | thisfield |
datastr = ''
normaldata = ''
allfields.each do |thisfield|
dofuzzthis = false
if thisfield[:name]
fieldstofuzz.each do | fuzzthis |
if fuzzthis
if (thisfield[:name].downcase.strip == fuzzthis.downcase.strip)
dofuzzthis = true
end
end
end
if thisfield[:value]
normaldata = thisfield[:value].strip
else
normaldata = ""
end
if (dofuzzthis)
datastr << thisfield[:name].downcase.strip + "=" + @fuzzdata + "&"
else
datastr << thisfield[:name].downcase.strip + "=" + normaldata + "&"
next unless thisfield[:name]
fieldstofuzz.each do |fuzzthis|
next unless fuzzthis
if (thisfield[:name].downcase.strip == fuzzthis.downcase.strip)
dofuzzthis = true
end
end
if thisfield[:value]
normaldata = thisfield[:value].strip
else
normaldata = ''
end
if dofuzzthis
datastr << thisfield[:name].downcase.strip + '=' + @fuzzdata + '&'
else
datastr << thisfield[:name].downcase.strip + '=' + normaldata + '&'
end
end
datastr=datastr[0,datastr.length-1]
datastr = datastr[0, datastr.length - 1]
@send_data[:uri] = form[:action]
@send_data[:uri] = "/#{form[:action]}" if @send_data[:uri][0,1] != '/'
@send_data[:uri] = "/#{form[:action]}" if @send_data[:uri][0, 1] != '/'
@send_data[:method] = form[:method].upcase
response = send_fuzz(@send_data,datastr)
if not process_response(response,field,"field")
response = send_fuzz(@send_data, datastr)
if !process_response(response, field, 'field')
return
end
if datastore['DELAY'] > 0
print_status(" (Sleeping for #{datastore['DELAY']} seconds...)")
select(nil,nil,nil,datastore['DELAY'])
select(nil, nil, nil, datastore['DELAY'])
end
end
end
def process_response(response,field,type)
if response == nil
print_error(" No response - #{@nrerrors+1} / #{datastore['STOPAFTER']} - fuzzdata length : #{@fuzzsize}")
if @nrerrors+1 >= datastore['STOPAFTER']
def process_response(response, field, type)
if response.nil?
print_error(" No response - #{@nrerrors + 1} / #{datastore['STOPAFTER']} - fuzzdata length : #{@fuzzsize}")
if @nrerrors + 1 >= datastore['STOPAFTER']
print_status(" *!* No response : #{type} #{field} | fuzzdata length : #{@fuzzsize}")
return false
else
@nrerrors = @nrerrors + 1
@nrerrors += 1
end
else
okcode = is_error_code(response.code)
if okcode
@nrerrors = 0
incr_fuzzsize()
@nrerrors = 0
incr_fuzzsize
end
if not okcode and @nrerrors+1 >= datastore['STOPAFTER']
if !okcode && (@nrerrors + 1 >= datastore['STOPAFTER'])
print_status(" *!* Error response code #{response.code} | #{type} #{field} | fuzzdata length #{@fuzzsize}")
return false
else
@nrerrors = @nrerrors + 1
@nrerrors += 1
end
end
return true
end
def send_fuzz(postdata,data)
def send_fuzz(postdata, data)
header = postdata[:headers]
response = send_request_raw({
'uri' => postdata[:uri],
'version' => postdata[:version],
'method' => postdata[:method],
'headers' => header,
'data' => data
}, datastore['TIMEOUT'])
'uri' => postdata[:uri],
'version' => postdata[:version],
'method' => postdata[:method],
'headers' => header,
'data' => data
}, datastore['TIMEOUT'])
return response
end
def get_field_val(input)
tmp = input.split(/\=/)
tmp = input.split(/=/)
# get delimiter
tmp2 = tmp[1].strip
delim = tmp2[0,1]
delim = tmp2[0, 1]
if delim != "'" && delim != '"'
delim = ""
delim = ''
end
tmp3 = tmp[1].split(/>/)
tmp4 = tmp3[0].gsub(delim,"")
tmp4 = tmp3[0].gsub(delim, '')
return tmp4
end
def get_form_data(body)
print_status("Enumerating form data")
body = body.gsub("\r","")
body = body.gsub("\n","")
print_status('Enumerating form data')
body = body.gsub("\r", '')
body = body.gsub("\n", '')
bodydata = body.downcase.split(/<form/)
# we need part after <form
totalforms = bodydata.size - 1
@@ -315,51 +322,51 @@ class MetasploitModule < Msf::Auxiliary
forms = []
while formcnt < totalforms
fdata = bodydata[formidx]
print_status(" - Enumerating form ##{formcnt+1}")
data = fdata.downcase.split(/<\/form>/)
print_status(" - Enumerating form ##{formcnt + 1}")
data = fdata.downcase.split(%r{</form>})
# first, get action and name
formdata = data[0].downcase.split(/>/)
subdata = formdata[0].downcase.split(/ /)
namefound = false
actionfound = false
idfound = false
actionname = ""
formname = ""
formid = ""
formmethod = "post"
subdata.each do | thisfield |
if thisfield.match(/^name=/) and not namefound
actionname = ''
formname = ''
formid = ''
formmethod = 'post'
subdata.each do |thisfield|
if thisfield.match(/^name=/) && !namefound
formname = get_field_val(thisfield)
namefound = true
end
if thisfield.match(/^id=/) and not idfound
if thisfield.match(/^id=/) && !idfound
formid = get_field_val(thisfield)
idfound = true
end
if thisfield.match(/^method=/)
formmethod = get_field_val(thisfield)
end
if thisfield.match(/^action=/) and not actionfound
actionname = get_field_val(thisfield)
if (actionname.length < datastore['URL'].length) and (datastore['URL'].downcase.index(actionname.downcase).to_i() > -1)
actionname = datastore['URL']
end
actionfound = true
next unless thisfield.match(/^action=/) && !actionfound
actionname = get_field_val(thisfield)
if (actionname.length < datastore['URL'].length) && (datastore['URL'].downcase.index(actionname.downcase).to_i > -1)
actionname = datastore['URL']
end
actionfound = true
end
if datastore['ACTION'].length > 0
if !datastore['ACTION'].empty?
actionname = datastore['ACTION']
actionfound = true
end
if formname == "" and formid != ""
if (formname == '') && (formid != '')
formname = formid
end
if formid == "" and formname != ""
if (formid == '') && (formname != '')
formid = formname
end
if formid == "" and formname == ""
formid = "noname_" + (formcnt+1).to_s()
if (formid == '') && (formname == '')
formid = 'noname_' + (formcnt + 1).to_s
formname = formid
end
idfound = true
@@ -368,90 +375,89 @@ class MetasploitModule < Msf::Auxiliary
formfields = []
# input boxes
fieldtypemarks = [ '<input', '<select' ]
fieldtypemarks.each do | currfieldmark |
formfieldcnt=0
if (namefound or idfound) and actionfound
# get fields in current form - data[0]
subdata = data[0].downcase.split(currfieldmark)
skipflag=0
if subdata.size > 1
subdata.each do | thisinput |
if skipflag == 1
# first, find the delimiter
fielddata = thisinput.downcase.split(/>/)
fields = fielddata[0].split(/ /)
fieldname = ""
fieldtype = ""
fieldvalue = ""
fieldmethod = "post"
fieldid = ""
fields.each do | thisfield |
if thisfield.match(/^type=/)
fieldtype = get_field_val(thisfield)
end
if currfieldmark == "<select" and thisfield.match(/^class=/)
fieldtype = get_field_val(thisfield)
end
if thisfield.match(/^name=/)
fieldname = get_field_val(thisfield)
end
if thisfield.match(/^id=/)
fieldid = get_field_val(thisfield)
end
if thisfield.match(/^value=/)
# special case
location = fielddata[0].index(thisfield)
delta = fielddata[0].size - location
remaining = fielddata[0][location,delta]
tmp = remaining.strip.split(/\=/)
if tmp.size > 1
delim = tmp[1][0,1]
tmp2 = tmp[1].split(delim)
fieldvalue = tmp2[1]
end
end
end
if fieldname == "" and fieldid != ""
fieldname = fieldid
end
if fieldid == "" and fieldname != ""
fieldid = fieldname
end
print_status(" Field : #{fieldname}, type #{fieldtype}")
if fieldid != ""
formfields << {
:id => fieldid,
:name => fieldname,
:type => fieldtype,
:value => fieldvalue
}
formfieldcnt += 1
end
else
skipflag += 1
fieldtypemarks.each do |currfieldmark|
formfieldcnt = 0
next unless (namefound || idfound) && actionfound
# get fields in current form - data[0]
subdata = data[0].downcase.split(currfieldmark)
skipflag = 0
next unless subdata.size > 1
subdata.each do |thisinput|
if skipflag == 1
# first, find the delimiter
fielddata = thisinput.downcase.split(/>/)
fields = fielddata[0].split(/ /)
fieldname = ''
fieldtype = ''
fieldvalue = ''
fieldid = ''
fields.each do |thisfield|
if thisfield.match(/^type=/)
fieldtype = get_field_val(thisfield)
end
if (currfieldmark == '<select') && thisfield.match(/^class=/)
fieldtype = get_field_val(thisfield)
end
if thisfield.match(/^name=/)
fieldname = get_field_val(thisfield)
end
if thisfield.match(/^id=/)
fieldid = get_field_val(thisfield)
end
next unless thisfield.match(/^value=/)
# special case
location = fielddata[0].index(thisfield)
delta = fielddata[0].size - location
remaining = fielddata[0][location, delta]
tmp = remaining.strip.split(/=/)
next unless tmp.size > 1
delim = tmp[1][0, 1]
tmp2 = tmp[1].split(delim)
fieldvalue = tmp2[1]
end
if (fieldname == '') && (fieldid != '')
fieldname = fieldid
end
if (fieldid == '') && (fieldname != '')
fieldid = fieldname
end
print_status(" Field : #{fieldname}, type #{fieldtype}")
if fieldid != ''
formfields << {
id: fieldid,
name: fieldname,
type: fieldtype,
value: fieldvalue
}
formfieldcnt += 1
end
else
skipflag += 1
end
end
end
print_status(" Nr of fields in form '#{formname}' : #{formfields.size}")
# store in multidimensional array
forms << {
:name => formname,
:id => formid,
:action => actionname,
:method => formmethod,
:fields => formfields
name: formname,
id: formid,
action: actionname,
method: formmethod,
fields: formfields
}
formidx = formidx + 1
formidx += 1
formcnt += 1
end
if forms.size > 0
print_status(" Forms : ")
if !forms.empty?
print_status(' Forms : ')
end
forms.each do | thisform |
forms.each do |thisform|
print_status(" - Name : #{thisform[:name]}, ID : #{thisform[:id]}, Action : #{thisform[:action]}, Method : #{thisform[:method]}")
end
@@ -459,25 +465,26 @@ class MetasploitModule < Msf::Auxiliary
end
def set_cookie(cookie)
@get_data_headers["Cookie"]=cookie
@send_data[:headers]["Cookie"]=cookie
@get_data_headers['Cookie'] = cookie
@send_data[:headers]['Cookie'] = cookie
end
def run
init_fuzzdata()
init_vars()
init_fuzzdata
init_vars
print_status("Grabbing webpage #{datastore['URL']} from #{datastore['RHOST']}")
response = send_request_raw(
{
'uri' => normalize_uri(datastore['URL']),
'version' => '1.1',
'method' => 'GET',
'headers' => @get_data_headers
{
'uri' => normalize_uri(datastore['URL']),
'version' => '1.1',
'method' => 'GET',
'headers' => @get_data_headers
}, datastore['TIMEOUT'])
if response == nil
print_error("No response")
}, datastore['TIMEOUT']
)
if response.nil?
print_error('No response')
return
end
@@ -488,62 +495,62 @@ class MetasploitModule < Msf::Auxiliary
print_status("Grabbing webpage #{datastore['URL']} from #{datastore['RHOST']} using cookies")
response = send_request_raw(
{
'uri' => normalize_uri(datastore['URL']),
'version' => '1.1',
'method' => 'GET',
'headers' => @get_data_headers
}, datastore['TIMEOUT'])
{
'uri' => normalize_uri(datastore['URL']),
'version' => '1.1',
'method' => 'GET',
'headers' => @get_data_headers
}, datastore['TIMEOUT']
)
end
if response == nil
print_error("No response")
if response.nil?
print_error('No response')
return
end
print_status("Code : #{response.code}")
okcode = is_error_code(response.code)
if not okcode
print_error("Server replied with error code. Check URL or set CODE to another value, and try again.")
if !okcode
print_error('Server replied with error code. Check URL or set CODE to another value, and try again.')
return
end
if response.body
formfound = response.body.downcase.index("<form")
formfound = response.body.downcase.index('<form')
if formfound
formdata = get_form_data(response.body)
# fuzz !
# for each form that needs to be fuzzed
formdata.each do | thisform |
if thisform[:name].length > 0
if ((datastore['FORM'].strip == "") || (datastore['FORM'].upcase.strip == thisform[:name].upcase.strip)) && (thisform[:fields].size > 0)
print_status("Fuzzing fields in form #{thisform[:name].upcase.strip}")
# for each field in this form, fuzz one field at a time
formfields = thisform[:fields]
formfields.each do | thisfield |
if thisfield[:name]
if fuzz_this_field(thisfield[:name],thisfield[:type]) == 1
print_status(" - Fuzzing field #{thisfield[:name]}")
do_fuzz_field(thisform,thisfield[:name])
init_fuzzdata()
end
end
end
print_status("Done fuzzing fields in form #{thisform[:name].upcase.strip}")
end
# fuzz headers ?
if datastore['FUZZHEADERS']
print_status("Fuzzing header fields")
do_fuzz_headers(thisform,response.headers)
formdata.each do |thisform|
next if thisform[:name].empty?
if ((datastore['FORM'].strip == '') || (datastore['FORM'].upcase.strip == thisform[:name].upcase.strip)) && !thisform[:fields].empty?
print_status("Fuzzing fields in form #{thisform[:name].upcase.strip}")
# for each field in this form, fuzz one field at a time
formfields = thisform[:fields]
formfields.each do |thisfield|
next unless thisfield[:name]
next unless fuzz_this_field(thisfield[:name], thisfield[:type]) == 1
print_status(" - Fuzzing field #{thisfield[:name]}")
do_fuzz_field(thisform, thisfield[:name])
init_fuzzdata
end
print_status("Done fuzzing fields in form #{thisform[:name].upcase.strip}")
end
# fuzz headers ?
if datastore['FUZZHEADERS']
print_status('Fuzzing header fields')
do_fuzz_headers(thisform, response.headers)
end
end
else
print_error("No form found in response body")
print_error('No form found in response body')
print_status(response.body)
return
end
else
print_error("No response data")
print_error('No response data')
end
end
end
@@ -3,28 +3,37 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'HTTP GET Request URI Fuzzer (Incrementing Lengths)',
'Description' => %q{
This module sends a series of HTTP GET request with incrementing URL lengths.
},
'Author' => [ 'nullthreat' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'HTTP GET Request URI Fuzzer (Incrementing Lengths)',
'Description' => %q{
This module sends a series of HTTP GET request with incrementing URL lengths.
},
'Author' => [ 'nullthreat' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(80),
OptInt.new("MAXLENGTH", [true, "The longest string length to try", 16384] ),
OptString.new("URIBASE", [true, "The base URL to use for the request fuzzer", "/"]),
OptString.new("VHOST", [false, "The virtual host name to use in requests"])
OptInt.new('MAXLENGTH', [true, 'The longest string length to try', 16384]),
OptString.new('URIBASE', [true, 'The base URL to use for the request fuzzer', '/']),
OptString.new('VHOST', [false, 'The virtual host name to use in requests'])
])
end
def do_http_get(uri='',opts={})
def do_http_get(uri = '', opts = {})
@connected = false
connect
@connected = true
@@ -49,28 +58,28 @@ class MetasploitModule < Msf::Auxiliary
# XXX: Encode the string or leave it raw? Best to make a new boolean option to enable/disable this
uri = pre + str
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using string length #{len}")
end
begin
r = do_http_get(uri,:timeout => 0.25)
do_http_get(uri, timeout: 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using string length #{len}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} len=#{len} uri=''#{last_str}'' error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} len=#{len} uri=''#{last_str}'' error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
return
break
end
last_str = str
@@ -3,27 +3,36 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'HTTP GET Request URI Fuzzer (Fuzzer Strings)',
'Description' => %q{
This module sends a series of HTTP GET request with malicious URIs.
},
'Author' => [ 'nullthreat' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'HTTP GET Request URI Fuzzer (Fuzzer Strings)',
'Description' => %q{
This module sends a series of HTTP GET request with malicious URIs.
},
'Author' => [ 'nullthreat' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(80),
OptString.new("VHOST", [false, "The virtual host name to use in requests"]),
OptString.new("URIBASE", [true, "The base URL to use for the request fuzzer", "/"])
OptString.new('VHOST', [false, 'The virtual host name to use in requests']),
OptString.new('URIBASE', [true, 'The base URL to use for the request fuzzer', '/'])
])
end
def do_http_get(uri='',opts={})
def do_http_get(uri = '', opts = {})
@connected = false
connect
@connected = true
@@ -46,24 +55,24 @@ class MetasploitModule < Msf::Auxiliary
# XXX: Encode the string or leave it raw? Best to make a new boolean option to enable/disable this
uri = pre + str
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
r = do_http_get(uri,:timeout => 0.50)
do_http_get(uri, timeout: 0.50)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} uri=''#{last_str}'' error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} uri=''#{last_str}'' error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -12,8 +12,8 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'NTP Protocol Fuzzer',
'Description' => %q(
'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:
@@ -35,9 +35,14 @@ class MetasploitModule < Msf::Auxiliary
* 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
},
'Author' => 'Jon Hart <jon_hart[at]rapid7.com>',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
register_options(
@@ -45,7 +50,8 @@ class MetasploitModule < Msf::Auxiliary
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])
])
]
)
register_advanced_options(
[
@@ -54,7 +60,8 @@ class MetasploitModule < Msf::Auxiliary
OptString.new('MODE_6_OPERATIONS', [false, 'Mode 6 operations to fuzz (csv)']),
OptString.new('MODE_7_IMPLEMENTATIONS', [false, 'Mode 7 implementations to fuzz (csv)']),
OptString.new('MODE_7_REQUEST_CODES', [false, 'Mode 7 request codes to fuzz (csv)'])
])
]
)
end
def sleep_time
@@ -66,9 +73,9 @@ class MetasploitModule < Msf::Auxiliary
const_name = thing.to_sym
var_name = thing.downcase
if datastore[thing]
instance_variable_set("@#{var_name}", datastore[thing].split(/[^\d]/).select { |v| !v.empty? }.map { |v| v.to_i })
instance_variable_set("@#{var_name}", datastore[thing].split(/[^\d]/).reject(&:empty?).map(&: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?
raise "Unsupported #{thing}: #{unsupported_things}" unless unsupported_things.empty?
else
instance_variable_set("@#{var_name}", Rex::Proto::NTP.const_get(const_name))
end
@@ -196,6 +203,7 @@ class MetasploitModule < Msf::Auxiliary
request = request.to_binary_s if request.respond_to?('to_binary_s')
responses.select! { |r| r[1] }
return if responses.empty?
responses.each do |response|
data = response[0]
descriptions << Rex::Proto::NTP.describe(data)
@@ -3,27 +3,36 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Negotiate SMB2 Dialect Corruption',
'Description' => %q{
This module sends a series of SMB negotiate requests that advertise a
SMB2 dialect with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Negotiate SMB2 Dialect Corruption',
'Description' => %q{
This module sends a series of SMB negotiate requests that advertise a
SMB2 dialect with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(445),
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test'])
])
end
def do_smb_negotiate(pkt,opts={})
def do_smb_negotiate(pkt, opts = {})
@connected = false
connect
@connected = true
@@ -41,30 +50,30 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SMB negotiate packet with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_smb_negotiate(str, 0.25)
do_smb_negotiate(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -79,13 +88,13 @@ class MetasploitModule < Msf::Auxiliary
def make_smb_negotiate
# The SMB 2 dialect must be there
dialects = ['PC NETWORK PROGRAM 1.0', 'LANMAN1.0', 'Windows for Workgroups 3.1a', 'LM1.2X002', 'LANMAN2.1', 'NT LM 0.12', 'SMB 2.002']
data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
pkt = Rex::Proto::SMB::Constants::SMB_NEG_PKT.make_struct
pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_NEGOTIATE
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0xc853
pkt['Payload'].v['Payload'] = data
pkt['Payload'].v['Payload'] = data
pkt.to_s
end
end
@@ -3,28 +3,37 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Create Pipe Request Fuzzer',
'Description' => %q{
This module sends a series of SMB create pipe
requests using malicious strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Create Pipe Request Fuzzer',
'Description' => %q{
This module sends a series of SMB create pipe
requests using malicious strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
end
def do_smb_create(pkt,opts={})
def do_smb_create(pkt, _opts = {})
@connected = false
connect
smb_login
@connected = true
smb_create("\\" + pkt)
smb_create('\\' + pkt)
end
def run
@@ -37,7 +46,7 @@ class MetasploitModule < Msf::Auxiliary
fuzz_strings do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
@@ -45,16 +54,16 @@ class MetasploitModule < Msf::Auxiliary
do_smb_create(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -3,28 +3,37 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Create Pipe Request Corruption',
'Description' => %q{
This module sends a series of SMB create pipe requests with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Create Pipe Request Corruption',
'Description' => %q{
This module sends a series of SMB create pipe requests with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test']),
OptString.new('SMBPIPE', [true, 'Specify the pipe name to corrupt', "\\BROWSER"])
OptString.new('SMBPIPE', [true, 'Specify the pipe name to corrupt', '\\BROWSER'])
])
deregister_options('SMB::ProtocolVersion')
end
def do_smb_login(pkt,opts={})
def do_smb_login(pkt, opts = {})
@connected = false
connect(versions: [1])
smb_login
@@ -35,7 +44,6 @@ class MetasploitModule < Msf::Auxiliary
end
def run
# Connect in order to get the server-assigned user-id/tree-id
connect(versions: [1])
smb_login
@@ -50,30 +58,30 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SMB create pipe with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_smb_login(str, 0.25)
do_smb_login(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -86,15 +94,14 @@ class MetasploitModule < Msf::Auxiliary
end
def make_smb_create
filename = datastore['SMBPIPE']
disposition = 1
impersonation = 2
pkt = Rex::Proto::SMB::Constants::SMB_CREATE_PKT.make_struct
self.simple.client.smb_defaults(pkt['Payload']['SMB'])
simple.client.smb_defaults(pkt['Payload']['SMB'])
pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_NT_CREATE_ANDX
pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_NT_CREATE_ANDX
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0x2001
pkt['Payload']['SMB'].v['WordCount'] = 24
@@ -3,26 +3,35 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Negotiate Dialect Corruption',
'Description' => %q{
This module sends a series of SMB negotiate requests with corrupted bytes
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Negotiate Dialect Corruption',
'Description' => %q{
This module sends a series of SMB negotiate requests with corrupted bytes
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(445),
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test'])
])
end
def do_smb_negotiate(pkt,opts={})
def do_smb_negotiate(pkt, opts = {})
@connected = false
connect
@connected = true
@@ -40,30 +49,30 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SMB negotiate packet with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_smb_negotiate(str, 0.25)
do_smb_negotiate(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -78,13 +87,13 @@ class MetasploitModule < Msf::Auxiliary
def make_smb_negotiate
# The SMB 2 dialect must be there
dialects = ['PC NETWORK PROGRAM 1.0', 'LANMAN1.0', 'Windows for Workgroups 3.1a', 'LM1.2X002', 'LANMAN2.1', 'NT LM 0.12']
data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
data = dialects.collect { |dialect| "\x02" + dialect + "\x00" }.join('')
pkt = Rex::Proto::SMB::Constants::SMB_NEG_PKT.make_struct
pkt['Payload']['SMB'].v['Command'] = Rex::Proto::SMB::Constants::SMB_COM_NEGOTIATE
pkt['Payload']['SMB'].v['Flags1'] = 0x18
pkt['Payload']['SMB'].v['Flags2'] = 0xc853
pkt['Payload'].v['Payload'] = data
pkt['Payload'].v['Payload'] = data
pkt.to_s
end
end
@@ -3,20 +3,29 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB NTLMv1 Login Request Corruption',
'Description' => %q{
This module sends a series of SMB login requests using
the NTLMv1 protocol with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB NTLMv1 Login Request Corruption',
'Description' => %q{
This module sends a series of SMB login requests using
the NTLMv1 protocol with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(445),
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test'])
@@ -24,7 +33,7 @@ class MetasploitModule < Msf::Auxiliary
deregister_options('SMB::ProtocolVersion')
end
def do_smb_login(pkt,opts={})
def do_smb_login(pkt, opts = {})
@connected = false
connect(versions: [1])
simple.client.negotiate(false)
@@ -44,30 +53,30 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SMB login with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_smb_login(str, 0.25)
do_smb_login(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -80,11 +89,10 @@ class MetasploitModule < Msf::Auxiliary
end
def make_smb_login
user = "USER"
domain = "DOMAIN"
hash_lm = Rex::Proto::NTLM::Crypt.lanman_des("X", "X" * 8)
hash_nt = Rex::Proto::NTLM::Crypt.ntlm_md4("X", "X" * 8)
user = 'USER'
domain = 'DOMAIN'
hash_lm = Rex::Proto::NTLM::Crypt.lanman_des('X', 'X' * 8)
hash_nt = Rex::Proto::NTLM::Crypt.ntlm_md4('X', 'X' * 8)
data = ''
data << hash_lm
@@ -3,23 +3,32 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Tree Connect Request Fuzzer',
'Description' => %q{
This module sends a series of SMB tree connect
requests using malicious strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Tree Connect Request Fuzzer',
'Description' => %q{
This module sends a series of SMB tree connect
requests using malicious strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
end
def do_smb_connect(pkt,opts={})
def do_smb_connect(pkt, _opts = {})
@connected = false
connect
simple.login(
@@ -43,7 +52,7 @@ class MetasploitModule < Msf::Auxiliary
fuzz_strings do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
@@ -51,16 +60,16 @@ class MetasploitModule < Msf::Auxiliary
do_smb_connect(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -3,28 +3,37 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::SMB::Client
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SMB Tree Connect Request Corruption',
'Description' => %q{
This module sends a series of SMB tree connect requests with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SMB Tree Connect Request Corruption',
'Description' => %q{
This module sends a series of SMB tree connect requests with corrupted bytes.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test']),
OptString.new('SMBTREE', [true, 'Specify the tree name to corrupt', "\\\\SERVER\\IPC$"])
OptString.new('SMBTREE', [true, 'Specify the tree name to corrupt', '\\\\SERVER\\IPC$'])
])
deregister_options('SMB::ProtocolVersion')
end
def do_smb_tree(pkt,opts={})
def do_smb_tree(pkt, opts = {})
@connected = false
connect(versions: [1])
simple.login(
@@ -40,7 +49,6 @@ class MetasploitModule < Msf::Auxiliary
end
def run
# Connect in order to get the server-assigned user-id
connect(versions: [1])
smb_login
@@ -55,30 +63,30 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SMB tree connect with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_smb_tree(str, 0.25)
do_smb_tree(str, 0.25)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
@@ -93,7 +101,7 @@ class MetasploitModule < Msf::Auxiliary
def make_smb_tree
share = datastore['SMBTREE']
pass = ''
data = [ pass, share, '?????' ].collect{ |a| a + "\x00" }.join('');
data = [ pass, share, '?????' ].collect { |a| a + "\x00" }.join('')
pkt = Rex::Proto::SMB::Constants::SMB_TREE_CONN_PKT.make_struct
simple.client.smb_defaults(pkt['Payload']['SMB'])
+81 -80
View File
@@ -8,6 +8,7 @@
# It allows to respect the order or just throw everything at it....
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Smtp
include Msf::Auxiliary::Fuzzer
@@ -15,22 +16,27 @@ class MetasploitModule < Msf::Auxiliary
def initialize
super(
'Name' => 'SMTP Simple Fuzzer',
'Name' => 'SMTP Simple Fuzzer',
'Description' => 'SMTP Simple Fuzzer',
'References' =>
[
['URL', 'http://www.ietf.org/rfc/rfc2821.txt'],
],
'Author' => 'justme',
'License' => MSF_LICENSE
'References' => [
['URL', 'http://www.ietf.org/rfc/rfc2821.txt'],
],
'Author' => 'justme',
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
register_options([
Opt::RPORT(25),
OptInt.new("STARTLEN", [true, "Length of the string - start number", 100] ),
OptInt.new("INTERACTIONS", [false, "Number of interactions to run", 100] ),
OptBool.new("RESPECTORDER", [false, "Respect order of commands", true] ),
OptEnum.new("CMD", [true,"Command to fuzzer",'EHLO',
OptInt.new('STARTLEN', [true, 'Length of the string - start number', 100]),
OptInt.new('INTERACTIONS', [false, 'Number of interactions to run', 100]),
OptBool.new('RESPECTORDER', [false, 'Respect order of commands', true]),
OptEnum.new('CMD', [
true, 'Command to fuzzer', 'EHLO',
[
'EHLO',
'HELO',
@@ -39,29 +45,27 @@ class MetasploitModule < Msf::Auxiliary
'DATA',
'VRFY',
'EXPN'
], 'EHLO'])
], 'EHLO'
])
])
end
def smtp_send(data='', con=true)
begin
@result=''
@coderesult=''
if (con)
@connected=false
connect
end
@connected=true
sock.put(data)
@result=sock.get_once
@codresult=@result[0..2]
rescue ::Exception => e
print_error(e.to_s)
def smtp_send(data = '', con: true)
@result = ''
@coderesult = ''
if con
@connected = false
connect
end
@connected = true
sock.put(data)
@result = sock.get_once
@codresult = @result[0..2]
rescue StandardError => e
print_error(e.to_s)
end
def run_host(ip)
begin
def run_host(_ip)
last_str = nil
last_inp = nil
last_err = nil
@@ -72,84 +76,81 @@ class MetasploitModule < Msf::Auxiliary
cnt += 1
str = fuzzer_gen_string(cnt)
cmd=datastore['CMD']
cmd = datastore['CMD']
begin
if (datastore['RESPECTORDER'])
if datastore['RESPECTORDER']
case cmd
when "HELO", "EHLO", "VRFY", "EXPN"
c = datastore['CMD'] + " " + str + "\r\n"
smtp_send(c,true)
#print_status(c)
when 'HELO', 'EHLO', 'VRFY', 'EXPN'
c = datastore['CMD'] + ' ' + str + "\r\n"
smtp_send(c)
# print_status(c)
disconnect
when "MAILFROM"
c ="EHLO localhost\r\n"
smtp_send(c,true)
#print_status(c)
c="MAIL FROM:<" + str + ">\r\n"
smtp_send(c,false)
when 'MAILFROM'
c = "EHLO localhost\r\n"
smtp_send(c)
# print_status(c)
c = 'MAIL FROM:<' + str + ">\r\n"
smtp_send(c)
disconnect
#print_status(c)
when "RCPTTO"
c ="EHLO localhost\r\n"
smtp_send(c,true)
#print_status(c)
c="MAIL FROM:<" + datastore['MAILFROM'] + ">\r\n"
smtp_send(c,false)
#print_status(c)
c="RCPT TO:<" + str + ">\r\n"
smtp_send(c,false)
#print_status(c)
# print_status(c)
when 'RCPTTO'
c = "EHLO localhost\r\n"
smtp_send(c)
# print_status(c)
c = 'MAIL FROM:<' + datastore['MAILFROM'] + ">\r\n"
smtp_send(c, con: false)
# print_status(c)
c = 'RCPT TO:<' + str + ">\r\n"
smtp_send(c, con: false)
# print_status(c)
disconnect
when "DATA"
c ="EHLO localhost\r\n"
smtp_send(c,true)
#print_status(c)
c="MAIL FROM:<" + datastore['MAILFROM'] + ">\r\n"
smtp_send(c,false)
#print_status(c)
c="RCPT TO:<" + datastore['MAILTO'] + ">\r\n"
smtp_send(c,false)
#print_status(c)
c="DATA \r\n"
smtp_send(c,false)
c= str + "\r\n.\r\n"
smtp_send(c,false)
#print_status(c)
when 'DATA'
c = "EHLO localhost\r\n"
smtp_send(c)
# print_status(c)
c = 'MAIL FROM:<' + datastore['MAILFROM'] + ">\r\n"
smtp_send(c, con: false)
# print_status(c)
c = 'RCPT TO:<' + datastore['MAILTO'] + ">\r\n"
smtp_send(c, con: false)
# print_status(c)
c = "DATA \r\n"
smtp_send(c, con: false)
c = str + "\r\n.\r\n"
smtp_send(c, con: false)
# print_status(c)
disconnect
end
else
c = datastore['CMD'] + " " + str + "\r\n"
smtp_send(c,true)
#print_status(c)
c = datastore['CMD'] + ' ' + str + "\r\n"
smtp_send(c)
# print_status(c)
disconnect
end
print_status("Fuzzing with iteration #{interaction}\n #{@result}")
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{interaction} using string #{str}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
#ensure
#disconnect
# ensure
# disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{interection-1} String=''#{last_str}'' error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{interection - 1} String=''#{last_str}'' error=#{last_err}")
else
print_status("Could not connect to the service: #{last_err}")
end
return
break
end
last_str = str
last_inp = @last_fuzzer_input
end
end
end
end
@@ -3,36 +3,45 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SSH Key Exchange Init Corruption',
'Description' => %q{
This module sends a series of SSH requests with a corrupted initial key exchange payload.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SSH Key Exchange Init Corruption',
'Description' => %q{
This module sends a series of SSH requests with a corrupted initial key exchange payload.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(22),
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test'])
])
end
def do_ssh_kexinit(pkt,opts={})
def do_ssh_kexinit(pkt, opts = {})
@connected = false
connect
@connected = true
@banner = sock.get_once(-1,opts[:banner_timeout])
return if not @banner
@banner = sock.get_once(-1, opts[:banner_timeout])
return if !@banner
sock.put("SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1\r\n")
sock.put(pkt)
sock.get_once(-1,opts[:kex_timeout])
sock.get_once(-1, opts[:kex_timeout])
end
def run
@@ -45,38 +54,38 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SSH initial key exchange with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_ssh_kexinit(str,:banner_timeout => 5, :kex_timeout => 0.5)
do_ssh_kexinit(str, banner_timeout: 5, kex_timeout: 0.5)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} 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
if(not @banner)
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.to_s.unpack("H*")[0]} ")
if !@banner
print_status("The service may have crashed (no banner): iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.to_s.unpack('H*')[0]} ")
return
end
@@ -86,104 +95,106 @@ class MetasploitModule < Msf::Auxiliary
end
def make_kex_init
[0x00, 0x00, 0x03, 0x14, 0x08, 0x14, 0xff, 0x9f,
0xde, 0x5d, 0x5f, 0xb3, 0x07, 0x8f, 0x49, 0xa7,
0x79, 0x6a, 0x03, 0x3d, 0xaf, 0x55, 0x00, 0x00,
0x00, 0x7e, 0x64, 0x69, 0x66, 0x66, 0x69, 0x65,
0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e,
0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x65,
0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x2c, 0x64,
0x69, 0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65,
0x6c, 0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x2d, 0x65, 0x78, 0x63, 0x68,
0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, 0x68, 0x61,
0x31, 0x2c, 0x64, 0x69, 0x66, 0x66, 0x69, 0x65,
0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e,
0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x31, 0x34,
0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c, 0x64, 0x69,
0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c,
0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f,
0x75, 0x70, 0x31, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x00, 0x00, 0x00, 0x0f, 0x73, 0x73, 0x68, 0x2d,
0x72, 0x73, 0x61, 0x2c, 0x73, 0x73, 0x68, 0x2d,
0x64, 0x73, 0x73, 0x00, 0x00, 0x00, 0x9d, 0x61,
0x65, 0x73, 0x31, 0x32, 0x38, 0x2d, 0x63, 0x62,
0x63, 0x2c, 0x33, 0x64, 0x65, 0x73, 0x2d, 0x63,
0x62, 0x63, 0x2c, 0x62, 0x6c, 0x6f, 0x77, 0x66,
0x69, 0x73, 0x68, 0x2d, 0x63, 0x62, 0x63, 0x2c,
0x63, 0x61, 0x73, 0x74, 0x31, 0x32, 0x38, 0x2d,
0x63, 0x62, 0x63, 0x2c, 0x61, 0x72, 0x63, 0x66,
0x6f, 0x75, 0x72, 0x31, 0x32, 0x38, 0x2c, 0x61,
0x72, 0x63, 0x66, 0x6f, 0x75, 0x72, 0x32, 0x35,
0x36, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f, 0x75,
0x72, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x39, 0x32,
0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73,
0x32, 0x35, 0x36, 0x2d, 0x63, 0x62, 0x63, 0x2c,
0x72, 0x69, 0x6a, 0x6e, 0x64, 0x61, 0x65, 0x6c,
0x2d, 0x63, 0x62, 0x63, 0x40, 0x6c, 0x79, 0x73,
0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x75,
0x2e, 0x73, 0x65, 0x2c, 0x61, 0x65, 0x73, 0x31,
0x32, 0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61,
0x65, 0x73, 0x31, 0x39, 0x32, 0x2d, 0x63, 0x74,
0x72, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35, 0x36,
0x2d, 0x63, 0x74, 0x72, 0x00, 0x00, 0x00, 0x9d,
0x61, 0x65, 0x73, 0x31, 0x32, 0x38, 0x2d, 0x63,
0x62, 0x63, 0x2c, 0x33, 0x64, 0x65, 0x73, 0x2d,
0x63, 0x62, 0x63, 0x2c, 0x62, 0x6c, 0x6f, 0x77,
0x66, 0x69, 0x73, 0x68, 0x2d, 0x63, 0x62, 0x63,
0x2c, 0x63, 0x61, 0x73, 0x74, 0x31, 0x32, 0x38,
0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x72, 0x63,
0x66, 0x6f, 0x75, 0x72, 0x31, 0x32, 0x38, 0x2c,
0x61, 0x72, 0x63, 0x66, 0x6f, 0x75, 0x72, 0x32,
0x35, 0x36, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f,
0x75, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x39,
0x32, 0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65,
0x73, 0x32, 0x35, 0x36, 0x2d, 0x63, 0x62, 0x63,
0x2c, 0x72, 0x69, 0x6a, 0x6e, 0x64, 0x61, 0x65,
0x6c, 0x2d, 0x63, 0x62, 0x63, 0x40, 0x6c, 0x79,
0x73, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x69,
0x75, 0x2e, 0x73, 0x65, 0x2c, 0x61, 0x65, 0x73,
0x31, 0x32, 0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c,
0x61, 0x65, 0x73, 0x31, 0x39, 0x32, 0x2d, 0x63,
0x74, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35,
0x36, 0x2d, 0x63, 0x74, 0x72, 0x00, 0x00, 0x00,
0x69, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64,
0x35, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73,
0x68, 0x61, 0x31, 0x2c, 0x75, 0x6d, 0x61, 0x63,
0x2d, 0x36, 0x34, 0x40, 0x6f, 0x70, 0x65, 0x6e,
0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
0x68, 0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70,
0x65, 0x6d, 0x64, 0x31, 0x36, 0x30, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70, 0x65,
0x6d, 0x64, 0x31, 0x36, 0x30, 0x40, 0x6f, 0x70,
0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73,
0x68, 0x61, 0x31, 0x2d, 0x39, 0x36, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2d,
0x39, 0x36, 0x00, 0x00, 0x00, 0x69, 0x68, 0x6d,
0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36, 0x34,
0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61,
0x63, 0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64,
0x31, 0x36, 0x30, 0x2c, 0x68, 0x6d, 0x61, 0x63,
0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64, 0x31,
0x36, 0x30, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x2d, 0x39, 0x36, 0x2c, 0x68, 0x6d, 0x61, 0x63,
0x2d, 0x6d, 0x64, 0x35, 0x2d, 0x39, 0x36, 0x00,
0x00, 0x00, 0x1a, 0x7a, 0x6c, 0x69, 0x62, 0x40,
0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
0x63, 0x6f, 0x6d, 0x2c, 0x7a, 0x6c, 0x69, 0x62,
0x2c, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00,
0x1a, 0x7a, 0x6c, 0x69, 0x62, 0x40, 0x6f, 0x70,
0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
0x6d, 0x2c, 0x7a, 0x6c, 0x69, 0x62, 0x2c, 0x6e,
0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00].pack("C*")
[
0x00, 0x00, 0x03, 0x14, 0x08, 0x14, 0xff, 0x9f,
0xde, 0x5d, 0x5f, 0xb3, 0x07, 0x8f, 0x49, 0xa7,
0x79, 0x6a, 0x03, 0x3d, 0xaf, 0x55, 0x00, 0x00,
0x00, 0x7e, 0x64, 0x69, 0x66, 0x66, 0x69, 0x65,
0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e,
0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x2d, 0x65,
0x78, 0x63, 0x68, 0x61, 0x6e, 0x67, 0x65, 0x2d,
0x73, 0x68, 0x61, 0x32, 0x35, 0x36, 0x2c, 0x64,
0x69, 0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65,
0x6c, 0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72,
0x6f, 0x75, 0x70, 0x2d, 0x65, 0x78, 0x63, 0x68,
0x61, 0x6e, 0x67, 0x65, 0x2d, 0x73, 0x68, 0x61,
0x31, 0x2c, 0x64, 0x69, 0x66, 0x66, 0x69, 0x65,
0x2d, 0x68, 0x65, 0x6c, 0x6c, 0x6d, 0x61, 0x6e,
0x2d, 0x67, 0x72, 0x6f, 0x75, 0x70, 0x31, 0x34,
0x2d, 0x73, 0x68, 0x61, 0x31, 0x2c, 0x64, 0x69,
0x66, 0x66, 0x69, 0x65, 0x2d, 0x68, 0x65, 0x6c,
0x6c, 0x6d, 0x61, 0x6e, 0x2d, 0x67, 0x72, 0x6f,
0x75, 0x70, 0x31, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x00, 0x00, 0x00, 0x0f, 0x73, 0x73, 0x68, 0x2d,
0x72, 0x73, 0x61, 0x2c, 0x73, 0x73, 0x68, 0x2d,
0x64, 0x73, 0x73, 0x00, 0x00, 0x00, 0x9d, 0x61,
0x65, 0x73, 0x31, 0x32, 0x38, 0x2d, 0x63, 0x62,
0x63, 0x2c, 0x33, 0x64, 0x65, 0x73, 0x2d, 0x63,
0x62, 0x63, 0x2c, 0x62, 0x6c, 0x6f, 0x77, 0x66,
0x69, 0x73, 0x68, 0x2d, 0x63, 0x62, 0x63, 0x2c,
0x63, 0x61, 0x73, 0x74, 0x31, 0x32, 0x38, 0x2d,
0x63, 0x62, 0x63, 0x2c, 0x61, 0x72, 0x63, 0x66,
0x6f, 0x75, 0x72, 0x31, 0x32, 0x38, 0x2c, 0x61,
0x72, 0x63, 0x66, 0x6f, 0x75, 0x72, 0x32, 0x35,
0x36, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f, 0x75,
0x72, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x39, 0x32,
0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65, 0x73,
0x32, 0x35, 0x36, 0x2d, 0x63, 0x62, 0x63, 0x2c,
0x72, 0x69, 0x6a, 0x6e, 0x64, 0x61, 0x65, 0x6c,
0x2d, 0x63, 0x62, 0x63, 0x40, 0x6c, 0x79, 0x73,
0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x69, 0x75,
0x2e, 0x73, 0x65, 0x2c, 0x61, 0x65, 0x73, 0x31,
0x32, 0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c, 0x61,
0x65, 0x73, 0x31, 0x39, 0x32, 0x2d, 0x63, 0x74,
0x72, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35, 0x36,
0x2d, 0x63, 0x74, 0x72, 0x00, 0x00, 0x00, 0x9d,
0x61, 0x65, 0x73, 0x31, 0x32, 0x38, 0x2d, 0x63,
0x62, 0x63, 0x2c, 0x33, 0x64, 0x65, 0x73, 0x2d,
0x63, 0x62, 0x63, 0x2c, 0x62, 0x6c, 0x6f, 0x77,
0x66, 0x69, 0x73, 0x68, 0x2d, 0x63, 0x62, 0x63,
0x2c, 0x63, 0x61, 0x73, 0x74, 0x31, 0x32, 0x38,
0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x72, 0x63,
0x66, 0x6f, 0x75, 0x72, 0x31, 0x32, 0x38, 0x2c,
0x61, 0x72, 0x63, 0x66, 0x6f, 0x75, 0x72, 0x32,
0x35, 0x36, 0x2c, 0x61, 0x72, 0x63, 0x66, 0x6f,
0x75, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x31, 0x39,
0x32, 0x2d, 0x63, 0x62, 0x63, 0x2c, 0x61, 0x65,
0x73, 0x32, 0x35, 0x36, 0x2d, 0x63, 0x62, 0x63,
0x2c, 0x72, 0x69, 0x6a, 0x6e, 0x64, 0x61, 0x65,
0x6c, 0x2d, 0x63, 0x62, 0x63, 0x40, 0x6c, 0x79,
0x73, 0x61, 0x74, 0x6f, 0x72, 0x2e, 0x6c, 0x69,
0x75, 0x2e, 0x73, 0x65, 0x2c, 0x61, 0x65, 0x73,
0x31, 0x32, 0x38, 0x2d, 0x63, 0x74, 0x72, 0x2c,
0x61, 0x65, 0x73, 0x31, 0x39, 0x32, 0x2d, 0x63,
0x74, 0x72, 0x2c, 0x61, 0x65, 0x73, 0x32, 0x35,
0x36, 0x2d, 0x63, 0x74, 0x72, 0x00, 0x00, 0x00,
0x69, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64,
0x35, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73,
0x68, 0x61, 0x31, 0x2c, 0x75, 0x6d, 0x61, 0x63,
0x2d, 0x36, 0x34, 0x40, 0x6f, 0x70, 0x65, 0x6e,
0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c,
0x68, 0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70,
0x65, 0x6d, 0x64, 0x31, 0x36, 0x30, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x72, 0x69, 0x70, 0x65,
0x6d, 0x64, 0x31, 0x36, 0x30, 0x40, 0x6f, 0x70,
0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
0x6d, 0x2c, 0x68, 0x6d, 0x61, 0x63, 0x2d, 0x73,
0x68, 0x61, 0x31, 0x2d, 0x39, 0x36, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2d,
0x39, 0x36, 0x00, 0x00, 0x00, 0x69, 0x68, 0x6d,
0x61, 0x63, 0x2d, 0x6d, 0x64, 0x35, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x2c, 0x75, 0x6d, 0x61, 0x63, 0x2d, 0x36, 0x34,
0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68,
0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68, 0x6d, 0x61,
0x63, 0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64,
0x31, 0x36, 0x30, 0x2c, 0x68, 0x6d, 0x61, 0x63,
0x2d, 0x72, 0x69, 0x70, 0x65, 0x6d, 0x64, 0x31,
0x36, 0x30, 0x40, 0x6f, 0x70, 0x65, 0x6e, 0x73,
0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x2c, 0x68,
0x6d, 0x61, 0x63, 0x2d, 0x73, 0x68, 0x61, 0x31,
0x2d, 0x39, 0x36, 0x2c, 0x68, 0x6d, 0x61, 0x63,
0x2d, 0x6d, 0x64, 0x35, 0x2d, 0x39, 0x36, 0x00,
0x00, 0x00, 0x1a, 0x7a, 0x6c, 0x69, 0x62, 0x40,
0x6f, 0x70, 0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e,
0x63, 0x6f, 0x6d, 0x2c, 0x7a, 0x6c, 0x69, 0x62,
0x2c, 0x6e, 0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00,
0x1a, 0x7a, 0x6c, 0x69, 0x62, 0x40, 0x6f, 0x70,
0x65, 0x6e, 0x73, 0x73, 0x68, 0x2e, 0x63, 0x6f,
0x6d, 0x2c, 0x7a, 0x6c, 0x69, 0x62, 0x2c, 0x6e,
0x6f, 0x6e, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
].pack('C*')
end
end
+32 -24
View File
@@ -3,31 +3,41 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SSH 1.5 Version Fuzzer',
'Description' => %q{
This module sends a series of SSH requests with malicious version strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SSH 1.5 Version Fuzzer',
'Description' => %q{
This module sends a series of SSH requests with malicious version strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(22)
])
end
def do_ssh_version(pkt,opts={})
def do_ssh_version(pkt, opts = {})
@connected = false
connect
@connected = true
@banner = sock.get_once(-1,opts[:banner_timeout])
return if not @banner
@banner = sock.get_once(-1, opts[:banner_timeout])
return if !@banner
sock.put("#{pkt}\r\n")
end
@@ -36,40 +46,38 @@ class MetasploitModule < Msf::Auxiliary
last_inp = nil
last_err = nil
ver = make_ssh_version_base
make_ssh_version_base
cnt = 0
fuzz_strings do |str|
cnt += 1
pkt = ver + str
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
r = do_ssh_version(str,:banner_timeout => 5)
do_ssh_version(str, banner_timeout: 5)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} 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
if(not @banner)
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} ")
if !@banner
print_status("The service may have crashed (no banner): iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} ")
return
end
@@ -79,6 +87,6 @@ class MetasploitModule < Msf::Auxiliary
end
def make_ssh_version_base
"SSH-1.5-"
'SSH-1.5-'
end
end
+32 -24
View File
@@ -3,31 +3,41 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SSH 2.0 Version Fuzzer',
'Description' => %q{
This module sends a series of SSH requests with malicious version strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SSH 2.0 Version Fuzzer',
'Description' => %q{
This module sends a series of SSH requests with malicious version strings.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(22)
])
end
def do_ssh_version(pkt,opts={})
def do_ssh_version(pkt, opts = {})
@connected = false
connect
@connected = true
@banner = sock.get_once(-1,opts[:banner_timeout])
return if not @banner
@banner = sock.get_once(-1, opts[:banner_timeout])
return if !@banner
sock.put("#{pkt}\r\n")
end
@@ -36,40 +46,38 @@ class MetasploitModule < Msf::Auxiliary
last_inp = nil
last_err = nil
ver = make_ssh_version_base
make_ssh_version_base
cnt = 0
fuzz_strings do |str|
cnt += 1
pkt = ver + str
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
r = do_ssh_version(str,:banner_timeout => 5)
do_ssh_version(str, banner_timeout: 5)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} 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
if(not @banner)
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} ")
if !@banner
print_status("The service may have crashed (no banner): iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} ")
return
end
@@ -79,6 +87,6 @@ class MetasploitModule < Msf::Auxiliary
end
def make_ssh_version_base
"SSH-2.0-"
'SSH-2.0-'
end
end
@@ -3,32 +3,42 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::Tcp
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'SSH Version Corruption',
'Description' => %q{
This module sends a series of SSH requests with a corrupted version string
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'SSH Version Corruption',
'Description' => %q{
This module sends a series of SSH requests with a corrupted version string
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
register_options([
Opt::RPORT(22),
OptInt.new('MAXDEPTH', [false, 'Specify a maximum byte depth to test'])
])
end
def do_ssh_version(pkt,opts={})
def do_ssh_version(pkt, opts = {})
@connected = false
connect
@connected = true
@banner = sock.get_once(-1,opts[:banner_timeout])
return if not @banner
@banner = sock.get_once(-1, opts[:banner_timeout])
return if !@banner
sock.put("#{pkt}\r\n")
end
@@ -42,38 +52,38 @@ class MetasploitModule < Msf::Auxiliary
max = datastore['MAXDEPTH'].to_i
max = nil if max == 0
tot = ( max ? [max,pkt.length].min : pkt.length) * 256
tot = (max ? [max, pkt.length].min : pkt.length) * 256
print_status("Fuzzing SSH version string with #{tot} requests")
fuzz_string_corrupt_byte_reverse(pkt,max) do |str|
fuzz_string_corrupt_byte_reverse(pkt, max) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt}/#{tot} using #{@last_fuzzer_input}")
end
begin
r = do_ssh_version(str,:banner_timeout => 5)
do_ssh_version(str, banner_timeout: 5)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => e
last_err = e
ensure
disconnect
end
if(not @connected)
if(last_str)
print_status("The service may have crashed: iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} error=#{last_err}")
if !@connected
if last_str
print_status("The service may have crashed: iteration:#{cnt - 1} 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
if(not @banner)
print_status("The service may have crashed (no banner): iteration:#{cnt-1} method=#{last_inp} string=#{last_str.unpack("H*")[0]} ")
if !@banner
print_status("The service may have crashed (no banner): iteration:#{cnt - 1} method=#{last_inp} string=#{last_str.unpack('H*')[0]} ")
return
end
@@ -83,6 +93,6 @@ class MetasploitModule < Msf::Auxiliary
end
def make_ssh_version
"SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1"
'SSH-2.0-OpenSSH_5.1p1 Debian-5ubuntu1'
end
end
@@ -3,50 +3,56 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MSSQL
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'TDS Protocol Login Request Corruption Fuzzer',
'Description' => %q{
This module sends a series of malformed TDS login requests.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'TDS Protocol Login Request Corruption Fuzzer',
'Description' => %q{
This module sends a series of malformed TDS login requests.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
end
# A copy of the mssql_login method with the ability to overload each option
def make_login(opts={})
pkt = ""
idx = 0
db = ""
def make_login(opts = {})
pkt = ''
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
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
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 )
cname = Rex::Text.to_unicode(opts[:cname] || Rex::Text.rand_text_alpha(1..8))
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(1..8))
sname = Rex::Text.to_unicode(opts[:sname] || rhost)
dname = Rex::Text.to_unicode(opts[:dname] || db)
idx = pkt.size + 50 # lengths below
@@ -73,7 +79,7 @@ class MetasploitModule < Msf::Auxiliary
pkt << [idx, 0].pack('vv')
pkt << [idx, dname.length / 2].pack('vv')
idx += dname.length
dname.length
# The total length has to be embedded twice more here
pkt << [
@@ -92,7 +98,7 @@ class MetasploitModule < Msf::Auxiliary
pkt << dname
# Total packet length
pkt[0,4] = [pkt.length].pack('V')
pkt[0, 4] = [pkt.length].pack('V')
# Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
@@ -103,16 +109,16 @@ class MetasploitModule < Msf::Auxiliary
pkt
end
def do_login(pkt,opts={})
def do_login(pkt, opts = {})
@connected = false
disconnect if self.sock
disconnect if sock
connect
@connected = true
resp = mssql_send_recv(pkt,opts[:timeout])
resp = mssql_send_recv(pkt, opts[:timeout])
info = {:errors => []}
info = mssql_parse_reply(resp,info)
info = { errors: [] }
info = mssql_parse_reply(resp, info)
info
end
@@ -126,24 +132,24 @@ class MetasploitModule < Msf::Auxiliary
fuzz_string_corrupt_byte_reverse(pkt) do |str|
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
do_login(str,:timeout => 0.50)
do_login(str, timeout: 0.50)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => 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}")
if !@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
@@ -3,55 +3,61 @@
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'English'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::MSSQL
include Msf::Auxiliary::Fuzzer
def initialize(info = {})
super(update_info(info,
'Name' => 'TDS Protocol Login Request Username Fuzzer',
'Description' => %q{
This module sends a series of malformed TDS login requests.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE
))
super(
update_info(
info,
'Name' => 'TDS Protocol Login Request Username Fuzzer',
'Description' => %q{
This module sends a series of malformed TDS login requests.
},
'Author' => [ 'hdm' ],
'License' => MSF_LICENSE,
'Notes' => {
'Stability' => [CRASH_SERVICE_DOWN],
'SideEffects' => [],
'Reliability' => []
}
)
)
end
# A copy of the mssql_login method with the ability to overload each option
def do_login(opts={})
def do_login(opts = {})
@connected = false
disconnect if self.sock
disconnect if sock
connect
@connected = true
pkt = ""
idx = 0
db = ""
pkt = ''
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
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
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 )
cname = Rex::Text.to_unicode(opts[:cname] || Rex::Text.rand_text_alpha(1..8))
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(1..8))
sname = Rex::Text.to_unicode(opts[:sname] || rhost)
dname = Rex::Text.to_unicode(opts[:dname] || db)
idx = pkt.size + 50 # lengths below
@@ -78,7 +84,6 @@ class MetasploitModule < Msf::Auxiliary
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 << [
@@ -97,7 +102,7 @@ class MetasploitModule < Msf::Auxiliary
pkt << dname
# Total packet length
pkt[0,4] = [pkt.length].pack('V')
pkt[0, 4] = [pkt.length].pack('V')
# Embedded packet lengths
pkt[pkt.index([0x12345678].pack('V')), 8] = [pkt.length].pack('V') * 2
@@ -105,10 +110,10 @@ class MetasploitModule < Msf::Auxiliary
# 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])
resp = mssql_send_recv(pkt, opts[:timeout])
info = {:errors => []}
info = mssql_parse_reply(resp,info)
info = { errors: [] }
info = mssql_parse_reply(resp, info)
info
end
@@ -121,26 +126,27 @@ class MetasploitModule < Msf::Auxiliary
fuzz_strings do |str|
# capped at 16-bit lengths
next if str.length > 65535
cnt += 1
if(cnt % 100 == 0)
if (cnt % 100 == 0)
print_status("Fuzzing with iteration #{cnt} using #{@last_fuzzer_input}")
end
begin
do_login(:uname => str, :timeout => 0.50)
do_login(uname: str, timeout: 0.50)
rescue ::Interrupt
print_status("Exiting on interrupt: iteration #{cnt} using #{@last_fuzzer_input}")
raise $!
rescue ::Exception => e
raise $ERROR_INFO
rescue StandardError => 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}")
if !@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