Module: Msf::DBManager::Vuln
- Included in:
- Msf::DBManager
- Defined in:
- lib/msf/core/db_manager/vuln.rb
Instance Method Summary collapse
-
#delete_vuln(opts) ⇒ Array
Deletes Vuln entries based on the IDs passed in.
-
#each_vuln(wspace = framework.db.workspace, &block) ⇒ Object
This method iterates the vulns table calling the supplied block with the vuln instance of each entry.
-
#find_or_create_vuln(opts) ⇒ Object
Find or create a vuln matching this service/name.
- #find_vuln_by_details(details_map, host, service = nil) ⇒ Object
- #find_vuln_by_refs(refs, host, service = nil, cve_only = true, resource = nil) ⇒ Object
- #get_vuln(wspace, host, service, name, data = '') ⇒ Object
-
#has_vuln?(name) ⇒ Boolean
Find a vulnerability matching this name.
-
#report_vuln(opts) ⇒ Object
- opts MUST contain
:host - the host where this vulnerability resides
:name - the friendly name for this vulnerability (title)
:workspace -
the workspace to report this vulnerability in.
- the friendly name for this vulnerability (title)
- the host where this vulnerability resides
- opts MUST contain
-
#update_vuln(opts) ⇒ Mdm::Vuln
Update the attributes of a Vuln entry with the values in opts.
-
#vulns(opts) ⇒ Object
This methods returns a list of all vulnerabilities in the database.
Instance Method Details
#delete_vuln(opts) ⇒ Array
Deletes Vuln entries based on the IDs passed in.
335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 |
# File 'lib/msf/core/db_manager/vuln.rb', line 335 def delete_vuln(opts) raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil? ::ApplicationRecord.connection_pool.with_connection { deleted = [] opts[:ids].each do |vuln_id| vuln = Mdm::Vuln.find(vuln_id) begin deleted << vuln.destroy rescue # refs suck elog("Forcibly deleting #{vuln}") deleted << vuln.delete end end return deleted } end |
#each_vuln(wspace = framework.db.workspace, &block) ⇒ Object
This method iterates the vulns table calling the supplied block with the vuln instance of each entry.
6 7 8 9 10 11 12 |
# File 'lib/msf/core/db_manager/vuln.rb', line 6 def each_vuln(wspace=framework.db.workspace, &block) ::ApplicationRecord.connection_pool.with_connection { wspace.vulns.each do |vulns| block.call(vulns) end } end |
#find_or_create_vuln(opts) ⇒ Object
Find or create a vuln matching this service/name
17 18 19 |
# File 'lib/msf/core/db_manager/vuln.rb', line 17 def find_or_create_vuln(opts) report_vuln(opts) end |
#find_vuln_by_details(details_map, host, service = nil) ⇒ Object
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'lib/msf/core/db_manager/vuln.rb', line 21 def find_vuln_by_details(details_map, host, service=nil) # Create a modified version of the criteria in order to match against # the joined version of the fields crit = {} details_map.each_pair do |k,v| crit[ "vuln_details.#{k}" ] = v end vuln = nil if service other_vulns = service.vulns.includes(:vuln_details).where(crit).to_a vuln = other_vulns.empty? ? nil : other_vulns.first end # Return if we matched based on service return vuln if vuln # Prevent matches against other services crit["vulns.service_id"] = nil if service other_vulns = host.vulns.includes(:vuln_details).where(crit).to_a other_vulns.empty? ? nil : other_vulns.first end |
#find_vuln_by_refs(refs, host, service = nil, cve_only = true, resource = nil) ⇒ Object
47 48 49 50 51 52 53 54 55 56 57 58 |
# File 'lib/msf/core/db_manager/vuln.rb', line 47 def find_vuln_by_refs(refs, host, service = nil, cve_only = true, resource = nil) ref_ids = cve_only ? refs.find_all { |ref| ref.name.starts_with? 'CVE-'} : refs relation = host.vulns.joins(:refs) if !service.try(:id).nil? if resource return relation.where(service_id: service.try(:id), refs: { id: ref_ids}, resource: resource).first else return relation.where(service_id: service.try(:id), refs: { id: ref_ids}).first end end return relation.where(refs: { id: ref_ids}).first end |
#get_vuln(wspace, host, service, name, data = '') ⇒ Object
60 61 62 63 64 65 66 67 68 69 70 71 72 |
# File 'lib/msf/core/db_manager/vuln.rb', line 60 def get_vuln(wspace, host, service, name, data='') raise RuntimeError, "Not workspace safe: #{caller.inspect}" ::ApplicationRecord.connection_pool.with_connection { vuln = nil if (service) vuln = ::Mdm::Vuln.find.where("name = ? and service_id = ? and host_id = ?", name, service.id, host.id).order("vulns.id DESC").first() else vuln = ::Mdm::Vuln.find.where("name = ? and host_id = ?", name, host.id).first() end return vuln } end |
#has_vuln?(name) ⇒ Boolean
Find a vulnerability matching this name
77 78 79 80 81 |
# File 'lib/msf/core/db_manager/vuln.rb', line 77 def has_vuln?(name) ::ApplicationRecord.connection_pool.with_connection { Mdm::Vuln.find_by_name(name) } end |
#report_vuln(opts) ⇒ Object
opts MUST contain
:host-
the host where this vulnerability resides
:name-
the friendly name for this vulnerability (title)
:workspace-
the workspace to report this vulnerability in
opts can contain
:info-
a human readable description of the vuln, free-form text
:refs-
an array of Ref objects or string names of references
:details-
a hash with :key pointed to a find criteria hash and the rest containing VulnDetail fields
:sname-
the name of the service this vulnerability relates to, used to associate it or create it.
:exploited_at-
a timestamp indicating when this vulnerability was exploited, if applicable
:ref_ids-
an array of reference IDs to associate with this vulnerability
:service-
a Mdm::Service object or a Hash with service attributes to associate this vulnerability with
:port-
the port number of the service this vulnerability relates to, if applicable
:proto-
the transport layer protocol of the service this vulnerability relates to, if applicable
:details_match-
a Mdm:VulnDetail with details related to this vulnerability
:resource-
a resource hash to associate with this vulnerability, such as a URI or pipe name
102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 |
# File 'lib/msf/core/db_manager/vuln.rb', line 102 def report_vuln(opts) return if not active raise ArgumentError.new("Missing required option :host") if opts[:host].nil? raise ArgumentError.new("Deprecated data column for vuln, use .info instead") if opts[:data] name = opts[:name] || return info = opts[:info] ::ApplicationRecord.connection_pool.with_connection { wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) opts = opts.clone() opts.delete(:workspace) exploited_at = opts[:exploited_at] || opts["exploited_at"] details = opts.delete(:details) rids = opts.delete(:ref_ids) if opts[:refs] rids ||= [] opts[:refs].each do |r| if r.instance_of?(Mdm::Module::Ref) str = r.name elsif (r.respond_to?(:ctx_id)) and (r.respond_to?(:ctx_val)) str = "#{r.ctx_id}-#{r.ctx_val}" elsif (r.is_a?(Hash) and r[:ctx_id] and r[:ctx_val]) str = "#{r[:ctx_id]}-#{r[:ctx_val]}" elsif r.is_a?(String) str = r end rids << find_or_create_ref(:name => str) unless str.nil? end end host = nil addr = nil if opts[:host].kind_of? ::Mdm::Host host = opts[:host] else host = report_host({:workspace => wspace, :host => opts[:host]}) addr = Msf::Util::Host.normalize_host(opts[:host]) end ret = {} # Truncate the info field at the maximum field length if info info = info[0,65535] end # Truncate the name field at the maximum field length name = name[0,255] # Placeholder for the vuln object vuln = nil # Identify the associated service service_opt = opts.delete(:service) case service_opt when Mdm::Service service = service_opt when Hash service = report_service(service_opt.merge(workspace: wspace, host: host)) else dlog("Skipping service since it is not a Hash or Mdm::Service: #{service.class}") service = nil end # Treat port zero as no service if service or opts[:port].to_i > 0 if not service proto = nil case opts[:proto].to_s.downcase # Catch incorrect usages, as in report_note when 'tcp','udp' proto = opts[:proto] sname = opts[:sname] when 'dns','snmp','dhcp' proto = 'udp' sname = opts[:proto] else proto = 'tcp' sname = opts[:proto] end # If sname and proto are not provided, this will assign the first service # registered in the database for this host with the given port and proto. # This is likely to be the TCP service. sopts = { workspace: wspace, host: host, port: opts[:port].to_i, proto: proto } sopts[:name] = sname if sname.present? service = report_service(sopts) end # Try to find an existing vulnerability with the same service & references # If there are multiple matches, choose the one with the most matches # If a match is found on a vulnerability with no associated service, # update that vulnerability with our service information. This helps # prevent dupes of the same vuln found by both local patch and # service detection. if rids and rids.length > 0 if opts[:resource] vuln = find_vuln_by_refs(rids, host, service, nil, opts[:resource]) else vuln = find_vuln_by_refs(rids, host, service) end vuln.service = service if vuln && !vuln.service_id? end else # Try to find an existing vulnerability with the same host & references # If there are multiple matches, choose the one with the most matches if rids and rids.length > 0 vuln = find_vuln_by_refs(rids, host) end end # Try to match based on vuln_details records if not vuln and opts[:details_match] vuln = find_vuln_by_details(opts[:details_match], host, service) if vuln && service && vuln.service.nil? vuln.service = service end end # No matches, so create a new vuln record unless vuln if service if opts[:resource] vuln = service.vulns.find_by(name: name, resource: opts[:resource]) else vuln = service.vulns.find_by_name(name) end else if opts[:resource] vuln = host.vulns.find_by(name: name, resource: opts[:resource]) else vuln = host.vulns.find_by_name(name) end end unless vuln vinf = { :host_id => host.id, :name => name, :info => info } vinf[:service_id] = service.id if service vinf[:resource] = opts[:resource] if opts[:resource] vuln = Mdm::Vuln.create(vinf) begin framework.events.on_db_vuln(vuln) if vuln rescue ::Exception => e wlog("Exception in on_db_vuln event handler: #{e.class}: #{e}") wlog("Call Stack\n#{e.backtrace.join("\n")}") end end end # Set the exploited_at value if provided vuln.exploited_at = exploited_at if exploited_at # Vuln origin ignored, rationale: # https://github.com/rapid7/metasploit-framework/pull/19817#issuecomment-2615656036 # vuln.origin = opts[:origin] if opts[:origin] # Merge the references if rids vuln.refs << (rids - vuln.refs) end # Finalize if vuln.changed? (opts, vuln) vuln.save! end # Handle vuln_details parameters report_vuln_details(vuln, details) if details vuln } end |
#update_vuln(opts) ⇒ Mdm::Vuln
Update the attributes of a Vuln entry with the values in opts. The values in opts should match the attributes to update.
319 320 321 322 323 324 325 326 327 328 329 |
# File 'lib/msf/core/db_manager/vuln.rb', line 319 def update_vuln(opts) ::ApplicationRecord.connection_pool.with_connection { wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework, false) opts = opts.clone() opts.delete(:workspace) opts[:workspace] = wspace if wspace v = Mdm::Vuln.find(opts.delete(:id)) v.update!(opts) v } end |
#vulns(opts) ⇒ Object
This methods returns a list of all vulnerabilities in the database
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 |
# File 'lib/msf/core/db_manager/vuln.rb', line 293 def vulns(opts) ::ApplicationRecord.connection_pool.with_connection { # If we have the ID, there is no point in creating a complex query. if opts[:id] && !opts[:id].to_s.empty? return Array.wrap(Mdm::Vuln.find(opts[:id])) end wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework) opts = opts.clone() opts.delete(:workspace) search_term = opts.delete(:search_term) if search_term && !search_term.empty? column_search_conditions = Msf::Util::DBManager.create_all_column_search_conditions(Mdm::Vuln, search_term) wspace.vulns.includes(:host).where(opts).where(column_search_conditions) else wspace.vulns.includes(:host).where(opts) end } end |