modules/auxiliary/fuzzers: Resolve RuboCop violations
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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'])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user