Module: ResponseDataHelper

Overview

HTTP response helper class

Instance Method Summary collapse

Instance Method Details

#json_to_hash(response_wrapper) ⇒ Hash

Converts an HTTP response to a Hash

Parameters:

  • response_wrapper (ResponseWrapper)

    A wrapped HTTP response containing a JSON body.

Returns:

  • (Hash)

    A Hash interpretation of the JSON body.

Raises:

  • (RuntimeError)

    response_wrapper is a Metasploit::Framework::DataService::RemoteHTTPDataService::ErrorResponse

  • (RuntimeError)

    response_wrapper is a Metasploit::Framework::DataService::RemoteHTTPDataService::FailedResponse

  • (RuntimeError)

    response_wrapper contains an empty response


15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/metasploit/framework/data_service/remote/http/response_data_helper.rb', line 15

def json_to_hash(response_wrapper)
  body = response_wrapper.response_body
  if !body.nil? && !body.empty?
    parsed_body = JSON.parse(body, symbolize_names: true)

    if response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::SuccessResponse)
      parsed_body[:data]
    elsif response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::ErrorResponse)
      # raise a local exception with the message of the server-side error
      raise parsed_body[:error][:message]
    end
  elsif response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::FailedResponse)
    raise response_wrapper.to_s
  else
    raise 'empty response'
  end
end

#json_to_mdm_object(response_wrapper, mdm_class) ⇒ ApplicationRecord

Converts an HTTP response to an Mdm Object

Parameters:

  • response_wrapper (ResponseWrapper)

    A wrapped HTTP response containing a JSON body.

  • mdm_class (String)

    The Mdm class name the JSON will be converted to.

Returns:

  • (ApplicationRecord)

    An object of type mdm_class, which inherits from ApplicationRecord

Raises:

  • (RuntimeError)

    response_wrapper is a Metasploit::Framework::DataService::RemoteHTTPDataService::ErrorResponse

  • (RuntimeError)

    response_wrapper is a Metasploit::Framework::DataService::RemoteHTTPDataService::FailedResponse

  • (RuntimeError)

    response_wrapper contains an empty response


42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/metasploit/framework/data_service/remote/http/response_data_helper.rb', line 42

def json_to_mdm_object(response_wrapper, mdm_class)
  body = response_wrapper.response_body
  if !body.nil? && !body.empty?
    parsed_body = JSON.parse(body, symbolize_names: true)

    if response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::SuccessResponse)
      begin
        data = Array.wrap(parsed_body[:data])
        rv = []
        data.each do |json_object|
          rv << to_ar(mdm_class.constantize, json_object)
        end
        return rv
      rescue => e
        raise "Mdm Object conversion failed #{e.message}"
      end
    elsif response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::ErrorResponse)
      # raise a local exception with the message of the server-side error
      raise parsed_body[:error][:message]
    end
  elsif response_wrapper.is_a?(Metasploit::Framework::DataService::RemoteHTTPDataService::FailedResponse)
    raise response_wrapper.to_s
  else
    raise 'empty response'
  end
end

#process_file(base64_file, save_path) ⇒ String

Processes a Base64 encoded file included in a JSON request. Saves the file in the location specified in the parameter.

Parameters:

  • base64_file (String)

    The Base64 encoded file.

  • save_path (String)

    The location to store the file. This should include the file's name.

Returns:

  • (String)

    The location where the file was successfully stored.


75
76
77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/metasploit/framework/data_service/remote/http/response_data_helper.rb', line 75

def process_file(base64_file, save_path)
  decoded_file = Base64.urlsafe_decode64(base64_file)
  begin
    # If we are running the data service on the same box this will ensure we only write
    # the file if it is somehow not there already.
    unless File.exists?(save_path) && File.read(save_path) == decoded_file
      File.open(save_path, 'w+') { |file| file.write(decoded_file) }
    end
  rescue => e
    elog "There was an error writing the file: #{e}"
    e.backtrace.each { |line| elog "#{line}\n"}
  end
  save_path
end

#to_ar(klass, val, base_object = nil) ⇒ ApplicationRecord

Converts a Hash or JSON string to an ActiveRecord object. Importantly, this retains associated objects if they are in the JSON string.

Modified from github.com/swdyh/toar/ Credit to github.com/swdyh

Parameters:

  • klass (String)

    The ActiveRecord class to convert the JSON/Hash to.

  • val (String)

    The JSON string, or Hash, to convert.

  • base_class (Class)

    The base class to build back to. Used for recursion.

Returns:


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
# File 'lib/metasploit/framework/data_service/remote/http/response_data_helper.rb', line 100

def to_ar(klass, val, base_object = nil)
  return nil unless val
  data = val.class == Hash ? val.dup : JSON.parse(val, symbolize_names: true)
  obj = base_object || klass.new

  obj_associations = klass.reflect_on_all_associations(:has_many).reduce({}) do |reflection, i|
    reflection[i.options[:through]] = i if i.options[:through]
    reflection
  end

  obj_attribute_names = obj.attributes.transform_keys(&:to_sym).keys

  data.except(*obj_attribute_names).each do |k, v|
    association = klass.reflect_on_association(k)
    next unless association

    case association.macro
      when :belongs_to
        data.delete(:"#{k}_id")
        # Polymorphic associations do not auto-create the 'build_model' method
        next if association.options[:polymorphic]
        to_ar(association.klass, v, obj.send("build_#{k}"))
        obj.class_eval do
          define_method("#{k}_id") { obj.send(k).id }
        end
      when :has_one
        to_ar(association.klass, v, obj.send("build_#{k}"))
      when :has_many
        obj.send(k).proxy_association.target =
            v.map { |i| to_ar(association.klass, i) }

        as_th = obj_associations[k.to_sym]
        if as_th
          obj.send(as_th.name).proxy_association.target =
              v.map { |i| to_ar(as_th.klass, i[as_th.source_reflection_name.to_s]) }
        end
    end
  end
  obj.assign_attributes(data.slice(*obj_attribute_names))

  obj.instance_eval do
    # prevent save
    def valid?(_context = nil)
      false
    end
  end
  obj
end