Module: Msf::DBManager::Note

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

Instance Method Summary collapse

Instance Method Details

#delete_note(opts) ⇒ Array

Deletes note entries based on the IDs passed in.

Parameters:

  • opts (:ids)
    Array

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

Returns:

  • (Array)

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

Raises:

  • (ArgumentError)


223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'lib/msf/core/db_manager/note.rb', line 223

def delete_note(opts)
  raise ArgumentError.new("The following options are required: :ids") if opts[:ids].nil?

  ::ApplicationRecord.connection_pool.with_connection {
    deleted = []
    opts[:ids].each do |note_id|
      note = Mdm::Note.find(note_id)
      begin
        deleted << note.destroy
      rescue # refs suck
        elog("Forcibly deleting #{note}")
        deleted << note.delete
      end
    end

    return deleted
  }
end

#each_note(wspace = framework.db.workspace, &block) ⇒ Object

This method iterates the notes table calling the supplied block with the note instance of each entry.



6
7
8
9
10
11
12
# File 'lib/msf/core/db_manager/note.rb', line 6

def each_note(wspace=framework.db.workspace, &block)
::ApplicationRecord.connection_pool.with_connection {
  wspace.notes.each do |note|
    block.call(note)
  end
}
end

#find_or_create_note(opts) ⇒ Object

Find or create a note matching this type/data



17
18
19
# File 'lib/msf/core/db_manager/note.rb', line 17

def find_or_create_note(opts)
  report_note(opts)
end

#notes(opts) ⇒ Object

This methods returns a list of all notes in the database



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
# File 'lib/msf/core/db_manager/note.rb', line 24

def notes(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::Note.find(opts[:id]))
    end

    wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
    opts = opts.clone()
    opts.delete(:workspace)

    data = opts.delete(:data)
    search_term = opts.delete(:search_term)
    results = wspace.notes.includes(:host).where(opts)

    # Compare the deserialized data from the DB to the search data since the column is serialized.
    unless data.nil?
      results = results.select { |note| note.data == data }
    end

    if search_term && !search_term.empty?
      re_search_term = /#{search_term}/mi
      results = results.select { |note|
        note.attribute_names.any? { |a| note[a.intern].to_s.match(re_search_term) }
      }
    end
    results
  }
end

#report_note(opts) ⇒ Object

Report a Note to the database. Notes can be tied to a ::Mdm::Workspace, Host, or Service.

opts MUST contain

:type

The type of note, e.g. smb_peer_os

opts can contain

:workspace

the workspace to associate with this Note

:host

an IP address or a Host object to associate with this Note

:service

a Service object to associate with this Note

:data

whatever it is you’re making a note of

:port

along with :host and :proto, a service to associate with this Note

:proto

along with :host and :port, a service to associate with this Note

:update

what to do in case a similar Note exists, see below

The :update option can have the following values:

:unique

allow only a single Note per :host/:type pair

:unique_data

like :unique, but also compare :data

:insert

always insert a new Note even if one with identical values exists

If the provided :host is an IP address and does not exist in the database, it will be created. If :workspace, :host and :service are all omitted, the new Note will be associated with the current workspace.



79
80
81
82
83
84
85
86
87
88
89
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
# File 'lib/msf/core/db_manager/note.rb', line 79

def report_note(opts)
  return if not active
::ApplicationRecord.connection_pool.with_connection {
  wspace = Msf::Util::DBManager.process_opts_workspace(opts, framework)
  opts = opts.clone()
  opts.delete(:workspace)
  seen = opts.delete(:seen) || false
  crit = opts.delete(:critical) || false
  host = nil
  addr = nil
  # Report the host so it's there for the Proc to use below
  if opts[:host]
    if opts[:host].kind_of? ::Mdm::Host
      host = opts[:host]
    else
      addr = Msf::Util::Host.normalize_host(opts[:host])
      host = report_host({:workspace => wspace, :host => addr})
    end
    # Do the same for a service if that's also included.
    if (opts[:port])
      proto = nil
      sname = nil
      proto_lower = opts[:proto].to_s.downcase # Catch incorrect usages
      case proto_lower
      when 'tcp','udp'
        proto = proto_lower
        sname = opts[:sname] if opts[:sname]
      # XXX: These normalizations are lazy af
      when 'http', 'smb'
        proto = 'tcp'
        sname = proto_lower
      when 'dns','snmp','dhcp'
        proto = 'udp'
        sname = proto_lower
      else
        proto = 'tcp'
        sname = proto_lower
      end
      sopts = {
        :workspace => wspace,
        :host  => host,
        :port  => opts[:port],
        :proto => proto
      }
      sopts[:name] = sname if sname
      report_service(sopts)
    end
  end
  # Update Modes can be :unique, :unique_data, :insert
  mode = opts[:update] || :unique

  ret = {}

  if addr and not host
    host = get_host(:workspace => wspace, :host => addr)
  end
  if host and (opts[:port] and proto)
    # only one result can be returned, as the +port+ field restricts potential results to a single service
    service = services(:workspace => wspace,
                       :hosts => {address: host.address},
                       :proto => proto,
                       :port => opts[:port]).first
  elsif opts[:service] and opts[:service].kind_of? ::Mdm::Service
    service = opts[:service]
  end

  ntype  = opts.delete(:type) || opts.delete(:ntype) || (raise RuntimeError, "A note :type or :ntype is required")
  data   = opts[:data]
  note   = nil

  conditions = { :ntype => ntype }
  conditions[:host_id] = host[:id] if host
  conditions[:service_id] = service[:id] if service
  conditions[:vuln_id] = opts[:vuln_id]

  case mode.to_sym
  when :unique
    note      = wspace.notes.where(conditions).first_or_initialize
    note.data = data
  when :unique_data
    notes = wspace.notes.where(conditions)

    # Don't make a new Note with the same data as one that already
    # exists for the given: type and (host or service)
    notes.each do |n|
      # Compare the deserialized data from the table to the raw
      # data we're looking for.  Because of the serialization we
      # can't do this easily or reliably in SQL.
      if n.data == data
        note = n
        break
      end
    end
    if not note
      # We didn't find one with the data we're looking for, make
      # a new one.
      note = wspace.notes.new(conditions.merge(:data => data))
    end
  else
    # Otherwise, assume :insert, which means always make a new one
    note = wspace.notes.new
    if host
      note.host_id = host[:id]
    end
    if opts[:service] and opts[:service].kind_of? ::Mdm::Service
      note.service_id = opts[:service][:id]
    end
    note.seen     = seen
    note.critical = crit
    note.ntype    = ntype
    note.data     = data
  end
  if opts[:vuln_id]
    note.vuln_id = opts[:vuln_id]
  end
  msf_import_timestamps(opts,note)
  note.save!
  ret[:note] = note
}
end

#update_note(opts) ⇒ Mdm::Note

Update the attributes of a note 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::Note)

    The updated Mdm::Note object.



205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/msf/core/db_manager/note.rb', line 205

def update_note(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

    id = opts.delete(:id)
    note = Mdm::Note.find(id)
    note.update!(opts)
    return note
  }
end