dap 0.0.4 → 0.0.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +1 -2
- data/Gemfile.lock +2 -3
- data/data/vulndb.rb +33 -0
- data/lib/dap/filter/http.rb +62 -34
- data/lib/dap/filter/vulnmatch.rb +140 -0
- data/lib/dap/version.rb +1 -1
- metadata +3 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: a5979d47a46ba9175ad7041e9ae9e27281c0bbae
         | 
| 4 | 
            +
              data.tar.gz: ce96da3ec587d1174890396b7e8cc0ec0d9e8789
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: f5ae71d2855a533c4d06060c416686e691fb5e93439a456b1b68c6c3b92443ba5790c3b5baf4e2dc2c04f51c69623dcfebc59a87bec13955fb319fc4bd5f736f
         | 
| 7 | 
            +
              data.tar.gz: 39a3e52480b4970ca5f95df73090172243a857dc2902a251e913fbf3fc03250f6483baf52f5b8fb3e53701020adcc8531fbf73b0a8a214c5ad6abfbf72b4dad2
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    | @@ -28,7 +28,7 @@ GEM | |
| 28 28 | 
             
                nokogiri (1.6.3.1)
         | 
| 29 29 | 
             
                  mini_portile (= 0.6.0)
         | 
| 30 30 | 
             
                oj (2.10.2)
         | 
| 31 | 
            -
                recog ( | 
| 31 | 
            +
                recog (2.0.2)
         | 
| 32 32 | 
             
                  nokogiri
         | 
| 33 33 | 
             
                rspec (3.1.0)
         | 
| 34 34 | 
             
                  rspec-core (~> 3.1.0)
         | 
| @@ -53,7 +53,6 @@ DEPENDENCIES | |
| 53 53 | 
             
              geoip-c
         | 
| 54 54 | 
             
              htmlentities
         | 
| 55 55 | 
             
              net-dns
         | 
| 56 | 
            -
              nokogiri
         | 
| 57 56 | 
             
              oj
         | 
| 58 | 
            -
              recog (>=  | 
| 57 | 
            +
              recog (>= 2.0)
         | 
| 59 58 | 
             
              rspec (~> 3.1.0)
         | 
    
        data/data/vulndb.rb
    CHANGED
    
    | @@ -80,4 +80,37 @@ SEARCHES = { | |
| 80 80 | 
             
                        ['7.0', 'sp4'] => ['CVE-2003-0230', 'CVE-2003-0231', 'CVE-2003-0232', 'CVE-2004-1560', 'CVE-2008-0085', 'CVE-2008-0086', 'CVE-2008-0106', 'CVE-2008-0107'],
         | 
| 81 81 | 
             
                    }
         | 
| 82 82 | 
             
                }],
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                :http => [
         | 
| 85 | 
            +
                #### ELASTICSEARCH RCE
         | 
| 86 | 
            +
                {
         | 
| 87 | 
            +
                    # direct shellcommand elastic rce
         | 
| 88 | 
            +
                    :match => [
         | 
| 89 | 
            +
                        ['http.path', '/_search'],
         | 
| 90 | 
            +
                        ['http.body', 'script_fields'],
         | 
| 91 | 
            +
                        ['http.body', 'java.lang.Runtime'],
         | 
| 92 | 
            +
                        ['http.body', 'getRuntime()'],
         | 
| 93 | 
            +
                    ],
         | 
| 94 | 
            +
                    :cve => ['VULN-ELASTICSEARCH-RCE', 'CVE-2014-3120']
         | 
| 95 | 
            +
                },{
         | 
| 96 | 
            +
                    # this just adds another tag as it's most likely done with metasploit
         | 
| 97 | 
            +
                    :match => [
         | 
| 98 | 
            +
                        ['http.path', '/_search'],
         | 
| 99 | 
            +
                        ['http.body', 'script_fields'],
         | 
| 100 | 
            +
                        ['http.body', 'metasploit.Payload'],
         | 
| 101 | 
            +
                    ],
         | 
| 102 | 
            +
                    :cve => ['VULN-ELASTICSEARCH-RCE', 'METASPLOIT']
         | 
| 103 | 
            +
                }] + [
         | 
| 104 | 
            +
                #### PHP CGI
         | 
| 105 | 
            +
                {
         | 
| 106 | 
            +
                    :match => [
         | 
| 107 | 
            +
                        ['http.path', '/cgi-bin/php'],
         | 
| 108 | 
            +
                    ],
         | 
| 109 | 
            +
                    :cve => ['VULN-PHPCGI']
         | 
| 110 | 
            +
                },{
         | 
| 111 | 
            +
                    :match => [
         | 
| 112 | 
            +
                        ['http.path', '/cgi-bin/authLogin.cgi'],
         | 
| 113 | 
            +
                    ],
         | 
| 114 | 
            +
                    :cve => ['VULN-QNAP-SHELLSHOCK']
         | 
| 115 | 
            +
                }],
         | 
| 83 116 | 
             
            }
         | 
    
        data/lib/dap/filter/http.rb
    CHANGED
    
    | @@ -2,11 +2,62 @@ module Dap | |
| 2 2 | 
             
            module Filter
         | 
| 3 3 |  | 
| 4 4 | 
             
            require 'htmlentities'
         | 
| 5 | 
            -
            require ' | 
| 5 | 
            +
            require 'shellwords'
         | 
| 6 6 | 
             
            require 'uri'
         | 
| 7 7 |  | 
| 8 | 
            +
            # Dirty element extractor, works around memory issues with Nokogiri
         | 
| 9 | 
            +
            module HTMLGhetto
         | 
| 10 | 
            +
              def extract_elements(data)
         | 
| 11 | 
            +
                @coder ||= HTMLEntities.new
         | 
| 12 | 
            +
                res = []
         | 
| 13 | 
            +
                data.
         | 
| 14 | 
            +
                  to_s.
         | 
| 15 | 
            +
                  encode('UTF-8', invalid: :replace, undef: :replace, replace: '').
         | 
| 16 | 
            +
                  scan(/<([^>]+)>/m).each do |e|
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                  e = e.first
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Skip closing tags
         | 
| 21 | 
            +
                  next if e[0,1] == "/"
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  # Get the name vs attributes
         | 
| 24 | 
            +
                  name, astr = e.split(/\s+/, 2).map{|x| x.to_s }
         | 
| 25 | 
            +
                  astr ||= ''
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  # Skip non-alpha elements
         | 
| 28 | 
            +
                  next unless name =~ /^[a-zA-Z]/
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  # Convert newlines to spaces & strip trailing />
         | 
| 31 | 
            +
                  astr = astr.gsub(/\n/, ' ').sub(/\/$/, '')
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                  o = { name: name }
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                  begin
         | 
| 36 | 
            +
                   Shellwords.shellwords(astr).each do |attr_str|
         | 
| 37 | 
            +
                      aname, avalue = attr_str.split('=', 2).map{|x| x.to_s.strip }
         | 
| 38 | 
            +
                      avalue = avalue.to_s.gsub(/^\"|"$/, '')
         | 
| 39 | 
            +
                      o[aname] = @coder.decode(avalue)
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
                  rescue ::Interrupt
         | 
| 42 | 
            +
                    raise $!
         | 
| 43 | 
            +
                  rescue ::Exception
         | 
| 44 | 
            +
                    # If shellwords couldn't parse it, split on space instead
         | 
| 45 | 
            +
                    astr.to_s.split(/\s+/).each do |attr_str|
         | 
| 46 | 
            +
                      aname, avalue = attr_str.split('=', 2).map{|x| x.to_s.strip }
         | 
| 47 | 
            +
                      avalue = avalue.to_s.gsub(/^\"|"$/, '')
         | 
| 48 | 
            +
                      o[aname] = @coder.decode(avalue)
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                  res << o
         | 
| 52 | 
            +
                end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                res
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
            end
         | 
| 57 | 
            +
             | 
| 8 58 | 
             
            class FilterHTMLIframes
         | 
| 9 59 | 
             
              include Base
         | 
| 60 | 
            +
              include HTMLGhetto
         | 
| 10 61 |  | 
| 11 62 | 
             
              def process(doc)
         | 
| 12 63 | 
             
                out = []
         | 
| @@ -20,25 +71,11 @@ class FilterHTMLIframes | |
| 20 71 | 
             
              end
         | 
| 21 72 |  | 
| 22 73 | 
             
              def extract(data)
         | 
| 23 | 
            -
                 | 
| 24 | 
            -
                urls = []
         | 
| 25 | 
            -
             | 
| 26 | 
            -
                data = data.encode('UTF-8', invalid: :replace, undef: :replace, replace: '')
         | 
| 27 | 
            -
                html = nil
         | 
| 28 | 
            -
                begin
         | 
| 29 | 
            -
                  html = Nokogiri::HTML(data) do |conf|
         | 
| 30 | 
            -
                    conf.strict.noent
         | 
| 31 | 
            -
                  end
         | 
| 32 | 
            -
                rescue ::Exception
         | 
| 33 | 
            -
                  return urls
         | 
| 34 | 
            -
                end
         | 
| 35 | 
            -
             | 
| 36 | 
            -
                html.xpath('//iframe').each do |e|
         | 
| 74 | 
            +
                extract_elements(data).select{|x| x[:name] == 'iframe'}.each do |e|
         | 
| 37 75 | 
             
                  url = e['src']
         | 
| 38 | 
            -
                  next unless url
         | 
| 76 | 
            +
                  next unless (url && url.length > 0)
         | 
| 39 77 | 
             
                  urls << url
         | 
| 40 78 | 
             
                end
         | 
| 41 | 
            -
             | 
| 42 79 | 
             
                urls
         | 
| 43 80 | 
             
              end
         | 
| 44 81 | 
             
            end
         | 
| @@ -46,6 +83,7 @@ end | |
| 46 83 |  | 
| 47 84 | 
             
            class FilterHTMLLinks
         | 
| 48 85 | 
             
              include Base
         | 
| 86 | 
            +
              include HTMLGhetto
         | 
| 49 87 |  | 
| 50 88 | 
             
              def process(doc)
         | 
| 51 89 | 
             
                out = []
         | 
| @@ -61,20 +99,10 @@ class FilterHTMLLinks | |
| 61 99 | 
             
              def extract(data)
         | 
| 62 100 | 
             
                urls = []
         | 
| 63 101 |  | 
| 64 | 
            -
                data | 
| 65 | 
            -
                html = nil
         | 
| 66 | 
            -
                begin
         | 
| 67 | 
            -
                  html = Nokogiri::HTML(data) do |conf|
         | 
| 68 | 
            -
                    conf.strict.noent
         | 
| 69 | 
            -
                  end
         | 
| 70 | 
            -
                rescue ::Exception
         | 
| 71 | 
            -
                 return urls
         | 
| 72 | 
            -
                end
         | 
| 73 | 
            -
             | 
| 74 | 
            -
                html.xpath('//*').each do |e|
         | 
| 102 | 
            +
                extract_elements(data).each do |e|
         | 
| 75 103 | 
             
                  url = e['href'] || e['src']
         | 
| 76 | 
            -
                  next unless url
         | 
| 77 | 
            -
                  urls << { 'link' => url, 'element' => e | 
| 104 | 
            +
                  next unless (url && url.length > 0)
         | 
| 105 | 
            +
                  urls << { 'link' => url, 'element' => e[:name] }
         | 
| 78 106 | 
             
                end
         | 
| 79 107 |  | 
| 80 108 | 
             
                urls
         | 
| @@ -136,14 +164,14 @@ class FilterDecodeHTTPReply | |
| 136 164 | 
             
                  when /^Date:\s*(.*)/i
         | 
| 137 165 | 
             
                    d = DateTime.parse($1.strip) rescue nil
         | 
| 138 166 | 
             
                    save["http_date"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
         | 
| 139 | 
            -
             | 
| 167 | 
            +
             | 
| 140 168 | 
             
                  when /^Last-modified:\s*(.*)/i
         | 
| 141 169 | 
             
                    d = DateTime.parse($1.strip) rescue nil
         | 
| 142 170 | 
             
                    save["http_modified"] = d.to_time.strftime("%Y%m%dT%H:%M:%S") if d
         | 
| 143 171 |  | 
| 144 172 | 
             
                  when /^Location:\s*(.*)/i
         | 
| 145 | 
            -
                    save["http_location"] = $1.strip | 
| 146 | 
            -
             | 
| 173 | 
            +
                    save["http_location"] = $1.strip
         | 
| 174 | 
            +
             | 
| 147 175 | 
             
                  when /^WWW-Authenticate:\s*(.*)/i
         | 
| 148 176 | 
             
                    save["http_auth"] = $1.strip
         | 
| 149 177 |  | 
| @@ -159,7 +187,7 @@ class FilterDecodeHTTPReply | |
| 159 187 | 
             
                end
         | 
| 160 188 |  | 
| 161 189 | 
             
                head, body = data.split(/\r?\n\r?\n/, 2)
         | 
| 162 | 
            -
             | 
| 190 | 
            +
             | 
| 163 191 | 
             
                # Some buggy systems exclude the header entirely
         | 
| 164 192 | 
             
                body ||= head
         | 
| 165 193 |  | 
    
        data/lib/dap/filter/vulnmatch.rb
    CHANGED
    
    | @@ -64,5 +64,145 @@ class FilterVulnMatchMSSQL | |
| 64 64 | 
             
              end
         | 
| 65 65 | 
             
            end
         | 
| 66 66 |  | 
| 67 | 
            +
            class FilterVulnMatchHTTP
         | 
| 68 | 
            +
              include Base
         | 
| 69 | 
            +
              include BaseVulnMatch
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def check_shellshock(doc)
         | 
| 72 | 
            +
                if not doc["http.headers"]
         | 
| 73 | 
            +
                  return []
         | 
| 74 | 
            +
                end
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                h = doc["http.headers"]
         | 
| 77 | 
            +
                sspattern = /\(\)\s*{\s*:;\s*};/
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                if h["user-agent"] and h["user-agent"] =~ sspattern
         | 
| 80 | 
            +
                  return ['VULN-SHELLSHOCK', 'CVE-2014-6271']
         | 
| 81 | 
            +
                end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                if h["referrer"] and h["referrer"] =~ sspattern
         | 
| 84 | 
            +
                  return ['VULN-SHELLSHOCK', 'CVE-2014-6271']
         | 
| 85 | 
            +
                end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                return []
         | 
| 88 | 
            +
              end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
              def check_elastic(doc)
         | 
| 91 | 
            +
                if not doc['http.path']
         | 
| 92 | 
            +
                  return []
         | 
| 93 | 
            +
                end
         | 
| 94 | 
            +
                if not doc['http.path'] == '/_search'
         | 
| 95 | 
            +
                  return []
         | 
| 96 | 
            +
                end
         | 
| 97 | 
            +
             | 
| 98 | 
            +
                input = doc['http.url']
         | 
| 99 | 
            +
                if doc['http.method'] == "POST"
         | 
| 100 | 
            +
                  input = doc['http.body']
         | 
| 101 | 
            +
                end
         | 
| 102 | 
            +
             | 
| 103 | 
            +
                if not input.match("script_fields")
         | 
| 104 | 
            +
                  return []
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
             | 
| 107 | 
            +
                out = ['VULN-ELASTICSEARCH-RCE', 'CVE-2014-3120']
         | 
| 108 | 
            +
                if input.match("Runtime") and input.match("getRuntime()")
         | 
| 109 | 
            +
                  out += ["EXEC-SHELLCMD"]
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                if input.match("FileOutputStream") and input.match("URLClassLoader")
         | 
| 113 | 
            +
                  out += ["EXEC-JAVA-CLASS"]
         | 
| 114 | 
            +
                end
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                if input.match("getDeclaredConstructor")
         | 
| 117 | 
            +
                  out += ['CVE-2015-1427']
         | 
| 118 | 
            +
                end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                if input.match("metasploit.Payload")
         | 
| 121 | 
            +
                  out += ['METASPLOIT']
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
                return out
         | 
| 125 | 
            +
              end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
              def process(doc)
         | 
| 128 | 
            +
                vulns = []
         | 
| 129 | 
            +
                if doc['vulnerability']
         | 
| 130 | 
            +
                  vulns |= doc['vulnerability']
         | 
| 131 | 
            +
                end
         | 
| 132 | 
            +
             | 
| 133 | 
            +
                vulns |= check_elastic(doc)
         | 
| 134 | 
            +
                vulns |= check_shellshock(doc)
         | 
| 135 | 
            +
             | 
| 136 | 
            +
                # see vulndb.rb, allows for simple matches to be added quickly
         | 
| 137 | 
            +
                SEARCHES[:http].each do | entry |
         | 
| 138 | 
            +
                  success = true
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                  # all matches must go through
         | 
| 141 | 
            +
                  entry[:match].each do | k, v |
         | 
| 142 | 
            +
                    if not doc[k]
         | 
| 143 | 
            +
                      success = false
         | 
| 144 | 
            +
                    else
         | 
| 145 | 
            +
                      m = doc[k].match(v)
         | 
| 146 | 
            +
                      if not m
         | 
| 147 | 
            +
                        success = false
         | 
| 148 | 
            +
                      end
         | 
| 149 | 
            +
                    end
         | 
| 150 | 
            +
             | 
| 151 | 
            +
                    if not success
         | 
| 152 | 
            +
                      break
         | 
| 153 | 
            +
                    end
         | 
| 154 | 
            +
                  end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
                  if success
         | 
| 157 | 
            +
                    vulns |= entry[:cve]
         | 
| 158 | 
            +
                  end
         | 
| 159 | 
            +
                end
         | 
| 160 | 
            +
             | 
| 161 | 
            +
                if vulns != []
         | 
| 162 | 
            +
                  doc['vulnerability'] = vulns
         | 
| 163 | 
            +
                end
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                [ doc ]
         | 
| 166 | 
            +
              end
         | 
| 167 | 
            +
            end
         | 
| 168 | 
            +
             | 
| 169 | 
            +
            class FilterGenericSetMatch
         | 
| 170 | 
            +
              include Base
         | 
| 171 | 
            +
              attr_accessor :matchset
         | 
| 172 | 
            +
             | 
| 173 | 
            +
              def initialize(args)
         | 
| 174 | 
            +
                self.opts = {}
         | 
| 175 | 
            +
                args.each do |arg|
         | 
| 176 | 
            +
                    k,v = arg.split("=", 2)
         | 
| 177 | 
            +
                    self.opts[k] = v
         | 
| 178 | 
            +
                end
         | 
| 179 | 
            +
                self.name = Dap::Factory.name_from_class(self.class)
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                fail "Expected key and set arguments to #{self.name} but got #{self.opts}" unless self.opts.has_key?("key") and self.opts.has_key?("set")
         | 
| 182 | 
            +
             | 
| 183 | 
            +
                self.matchset = {}
         | 
| 184 | 
            +
                File.readlines(self.opts["set"]).each do |line|
         | 
| 185 | 
            +
                  self.matchset[line.chomp] = nil
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
              end
         | 
| 188 | 
            +
             | 
| 189 | 
            +
              def process(doc)
         | 
| 190 | 
            +
                if doc.has_key?(self.opts["key"])
         | 
| 191 | 
            +
                  if doc[self.opts["key"]].kind_of?(Array)
         | 
| 192 | 
            +
                    doc[self.opts["key"]].each do |val|
         | 
| 193 | 
            +
                      if self.matchset.has_key?(val)
         | 
| 194 | 
            +
                        return [ doc ]
         | 
| 195 | 
            +
                      end
         | 
| 196 | 
            +
                    end
         | 
| 197 | 
            +
                  else
         | 
| 198 | 
            +
                    if self.matchset.has_key?(doc[self.opts["key"]])
         | 
| 199 | 
            +
                      return [ doc ]
         | 
| 200 | 
            +
                    end
         | 
| 201 | 
            +
                  end
         | 
| 202 | 
            +
                end
         | 
| 203 | 
            +
                [ ]
         | 
| 204 | 
            +
              end
         | 
| 205 | 
            +
            end
         | 
| 206 | 
            +
             | 
| 67 207 | 
             
            end
         | 
| 68 208 | 
             
            end
         | 
    
        data/lib/dap/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: dap
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0.0. | 
| 4 | 
            +
              version: 0.0.5
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Rapid7 Research
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2015- | 
| 11 | 
            +
            date: 2015-09-29 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rspec
         | 
| @@ -241,7 +241,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 241 241 | 
             
                  version: '0'
         | 
| 242 242 | 
             
            requirements: []
         | 
| 243 243 | 
             
            rubyforge_project: 
         | 
| 244 | 
            -
            rubygems_version: 2.4. | 
| 244 | 
            +
            rubygems_version: 2.4.8
         | 
| 245 245 | 
             
            signing_key: 
         | 
| 246 246 | 
             
            specification_version: 4
         | 
| 247 247 | 
             
            summary: 'DAP: The Data Analysis Pipeline'
         |