Module: Msf::DBManager::Vuln

Included in:
Msf::DBManager
Defined in:
lib/msf/core/db_manager/vuln.rb

Instance Method Summary collapse

Instance Method Details

#delete_vuln(opts) ⇒ Array

Deletes Vuln entries based on the IDs passed in.

Parameters:

  • opts (:ids)
    Array

    Array containing Integers corresponding to the IDs of the Vuln entries to delete.

Returns:

  • (Array)

    Array containing the Mdm::Vuln objects that were successfully deleted.

Raises:

  • (ArgumentError)


289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
# File 'lib/msf/core/db_manager/vuln.rb', line 289

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) ⇒ Object



47
48
49
50
51
52
53
54
# File 'lib/msf/core/db_manager/vuln.rb', line 47

def find_vuln_by_refs(refs, host, service=nil)
  ref_ids = refs.find_all { |ref| ref.name.starts_with? 'CVE-'}
  relation = host.vulns.joins(:refs)
  if !service.try(:id).nil?
    return relation.where(service_id: service.try(:id), refs: { id: ref_ids}).first
  end
  return relation.where(refs: { id: ref_ids}).first
end

#get_vuln(wspace, host, service, name, data = '') ⇒ Object

Raises:

  • (RuntimeError)


56
57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/msf/core/db_manager/vuln.rb', line 56

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

Returns:

  • (Boolean)


73
74
75
76
77
# File 'lib/msf/core/db_manager/vuln.rb', line 73

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)

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.

Raises:

  • (ArgumentError)


90
91
92
93
94
95
96
97
98
99
100
101
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
# File 'lib/msf/core/db_manager/vuln.rb', line 90

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 = opts.delete(:service)

  # 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

      services = host.services.where(port: opts[:port].to_i, proto: proto)
      services = services.where(name: sname) if sname.present?
      service = services.first_or_create
    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
      vuln = find_vuln_by_refs(rids, host, service)
      vuln.service = service if vuln
    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
      vuln = service.vulns.find_by_name(name)
    else
      vuln = host.vulns.find_by_name(name)
    end

    unless vuln

      vinf = {
        :host_id => host.id,
        :name    => name,
        :info    => info
      }

      vinf[:service_id] = service.id if service
      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

  # Merge the references
  if rids
    vuln.refs << (rids - vuln.refs)
  end

  # Finalize
  if vuln.changed?
    msf_assign_timestamps(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.

Parameters:

  • opts (Hash)

    Hash containing the updated values. Key should match the attribute to update. Must contain :id of record to update.

Returns:

  • (Mdm::Vuln)

    The updated Mdm::Vuln object.



273
274
275
276
277
278
279
280
281
282
283
# File 'lib/msf/core/db_manager/vuln.rb', line 273

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



247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/msf/core/db_manager/vuln.rb', line 247

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