netapi32 enhancements and new post module, add_user #680

Closed
wants to merge 3 commits into
from
View
120 lib/msf/core/post/windows/accounts.rb
@@ -58,6 +58,97 @@ def delete_user(username, server_name = nil)
return nil
end
+ ##
+ # add_user(username, password = nil, comment = nil, dont_expire_pwd = false, server_name = nil)
+ #
+ # Summary:
+ # Adds a user account to the given server (or local, if none specified)
+ #
+ # Parameters
+ # username - The username of the account to add (not-qualified, e.g. BOB)
+ # password - The password to be assigned to the new user account
+ # comment - A comment or description of the user account
+ # dont_expire_pwd - toggles the "Password never expires" flag on the account
+ # server_name - DNS or NetBIOS name of remote server on which to add user
+ #
+ # Returns:
+ # One of the following:
+ # :success - Account was created successfully
+ # :access_denied - You do not have permission to add the account
+ # :user_exists - A user account with +username+ already exists
+ # :group_exists - Group exists (unclear why NetUserAdd would return this, but it does)
+ # :invalid_server - The server name provided was invalid
+ # :not_on_primary - Operation allowed only on domain controller
+ # :invalid_password - Password violates password policy somehow (complexity, length, etc.)
+ #
+ # OR nil if there was an exceptional windows error (example: ran out of memory)
+ #
+ # Caveats:
+ # nil is returned if there is an *exceptional* windows error. That error is printed.
+ # Everything other than ':success' signifies failure
+ ##
+ def add_user(username, password = nil, comment = nil, dont_expire_pwd = false, server_name = nil)
@jlee-r7
jlee-r7 added a line comment Oct 22, 2012

Should check for existence of railgun before attempting to use it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ addr_username = set_value(username)
+ addr_password = set_value(password)
+ addr_comment = set_value(comment)
+
+ if addr_username.nil? || addr_password.nil? || addr_comment.nil?
+ return nil
+ end
+
+ acct_flags = 'UF_SCRIPT | UF_NORMAL_ACCOUNT'
+ if dont_expire_pwd
+ acct_flags << ' | UF_DONT_EXPIRE_PASSWD'
+ end
+
+ usri1 = [ # struct USER_INFO_1
+ addr_username, # usri1_name
+ addr_password, # usri1_password
+ 0, # usri1_password_age
+ 1, # usri1_priv = USER_PRIV_USER
+ 0, # usri1_home_dir
+ addr_comment, # usri1_comment
+ client.railgun.const(acct_flags), # usri1_flags
+ 0 # usri1_script_path
+ ].pack("VVVVVVVV")
+
+ result = client.railgun.netapi32.NetUserAdd(server_name, 1, usri1, 4)
+
+ client.railgun.multi([
+ ["kernel32", "VirtualFree", [addr_username, 0, MEM_RELEASE]],
+ ["kernel32", "VirtualFree", [addr_password, 0, MEM_RELEASE]],
+ ["kernel32", "VirtualFree", [addr_comment, 0, MEM_RELEASE]]
+ ])
+
+ case result['return']
+ when 0
+ return :success
+ when client.railgun.const('ERROR_ACCESS_DENIED')
+ return :access_denied
+ when 2224 # NERR_UserExists
+ return :user_exists
+ when 2223 # NERR_GroupExists
+ return :group_exists
+ when 2351 # NERR_InvalidComputer
+ return :invalid_server
+ when 2226 # NERR_NotPrimary
+ return :not_on_primary
+ when 2245 # NERR_PasswordTooShort
+ return :invalid_password
+ else
+ error = result['GetLastError']
+ if error != 0
+ print_error "Unexpected Windows System Error #{error}"
+ else
+ # Uh... we shouldn't be here
+ print_error "add_user unexpectedly returned #{result['return']}"
+ end
+ end
+
+ # If we got here, then something above failed
+ return nil
+ end
+
##
# resolve_sid(sid, system_name = nil)
@@ -177,6 +268,35 @@ def lookup_SID_NAME_USE(enum_value)
:integrity_label
][enum_value - 1]
end
+
+ ##
+ # Writes a ruby string as null-terminated Unicode string to host's memory.
+ # Returns:
+ # The affected memory address, if all goes well
+ # Or 0 if value argument is nil
+ # Or nil if an error occurs
+ ##
+ def set_value(value)
@jlee-r7
jlee-r7 added a line comment Sep 4, 2012

Poorly named method. Should be something more descriptive, like alloc_and_write_str. Also has nothing to do with Windows accounts and therefore belongs in a different mixin, probably Windows::Railgun

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if value == nil
+ return 0
+ else
+ data = client.railgun.util.str_to_uni_z(value)
+ result = client.railgun.kernel32.VirtualAlloc(nil, data.length, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE)
+
+ if result['return'].nil?
+ print_error "Failed to allocate memory on the host."
+ return nil
+ end
+
+ addr = result['return']
+ if client.railgun.memwrite(addr, data, data.length)
+ return addr
+ else
+ print_error "Failed to write to memory on the host."
+ return nil
+ end
+ end
+ end
end # Accounts
end # Windows
end # Post
View
156 lib/rex/post/meterpreter/extensions/stdapi/railgun/def/def_netapi32.rb
@@ -9,24 +9,102 @@ module Def
class Def_netapi32
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa371109.aspx
+ USER_INFO_1 = [
+ [:usri1_name, :LPWSTR],
+ [:usri1_password, :LPWSTR],
+ [:usri1_password_age, :DWORD],
+ [:usri1_priv, :DWORD],
+ [:usri1_home_dir, :LPWSTR],
+ [:usri1_comment, :LPWSTR],
+ [:usri1_flags, :DWORD],
+ [:usri1_script_path, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370897.aspx
+ SERVER_INFO_100 = [
+ [:sv100_platform_id, :DWORD],
+ [:sv100_name, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370903.aspx
+ SERVER_INFO_101 = [
+ [:sv101_platform_id, :DWORD],
+ [:sv101_name, :LPWSTR],
+ [:sv101_version_major, :DWORD],
+ [:sv101_version_minor, :DWORD],
+ [:sv101_type, :DWORD],
+ [:sv101_comment, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370247.aspx
+ AT_ENUM = [
+ [:JobId, :DWORD],
+ [:JobTime, :PDWORD_PTR],
+ [:DaysOfMonth, :DWORD],
+ [:DaysOfWeek, :UCHAR],
+ [:Flags, :UCHAR],
+ [:Command, :LPWSTR]
+ ]
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370248.aspx
+ AT_INFO = [
+ [:JobTime, :PDWORD_PTR],
+ [:DaysOfMonth, :DWORD],
+ [:DaysOfWeek, :UCHAR],
+ [:Flags, :UCHAR],
+ [:Command, :LPWSTR]
+ ]
+
def self.create_dll(dll_path = 'netapi32')
dll = DLL.new(dll_path, ApiConstants.manager)
- dll.add_function('NetUserDel', 'DWORD',[
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370649.aspx
+ dll.add_function('NetUserAdd', 'DWORD',[
["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 1, 2, 3, 4
+ ["PBLOB","buf","in"], # ptr to USER_INFO_x structure where x = +level+
+ ["PDWORD","parm_err","out"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370650.aspx
+ dll.add_function('NetUserChangePassword', 'DWORD',[
+ ["PWCHAR","domainname","in"],
["PWCHAR","username","in"],
+ ["PWCHAR","oldpassword","in"],
+ ["PWCHAR","newpassword","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370651.aspx
+ dll.add_function('NetUserDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","username","in"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370652.aspx
+ dll.add_function('NetUserEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1, 2, 3, 4, 10, 11, 20, 23
+ ["DWORD","filter","in"],
+ ["PDWORD","bufptr","out"], # ptr to array of USER_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resume_handle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370423.aspx
dll.add_function('NetGetJoinInformation', 'DWORD',[
["PWCHAR","lpServer","in"],
["PDWORD","lpNameBuffer","out"],
["PDWORD","BufferType","out"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370623.aspx
dll.add_function('NetServerEnum', 'DWORD',[
["PWCHAR","servername","in"],
- ["DWORD","level","in"],
- ["PDWORD","bufptr","out"],
+ ["DWORD","level","in"], # 100, 101
+ ["PDWORD","bufptr","out"], # ptr to array of SERVER_INFO_x structures where x = +level+
["DWORD","prefmaxlen","in"],
["PDWORD","entriesread","out"],
["PDWORD","totalentries","out"],
@@ -35,11 +113,79 @@ def self.create_dll(dll_path = 'netapi32')
["DWORD","resume_handle","inout"]
])
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370304.aspx
+ dll.add_function('NetApiBufferFree', 'DWORD',[
+ ["LPVOID","Buffer","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370426.aspx
+ dll.add_function('NetGroupDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","groupname","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370428.aspx
+ dll.add_function('NetGroupEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1, 2, 3
+ ["PDWORD","bufptr","out"], # ptr to array of GROUP_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resume_handle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370437.aspx
+ dll.add_function('NetLocalGroupDel', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["PWCHAR","groupname","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370440.aspx
+ dll.add_function('NetLocalGroupEnum', 'DWORD',[
+ ["PWCHAR","servername","in"],
+ ["DWORD","level","in"], # 0, 1
+ ["PDWORD","bufptr","out"], # ptr to array of LOCALGROUP_INFO_x structures where x = +level+
+ ["DWORD","prefmaxlen","in"],
+ ["PDWORD","entriesread","out"],
+ ["PDWORD","totalentries","out"],
+ ["DWORD","resumehandle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370614.aspx
+ dll.add_function('NetScheduleJobAdd', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["PBLOB","Buffer","in"], # ptr to AT_INFO structure
+ ["PDWORD","JobId","out"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370615.aspx
+ dll.add_function('NetScheduleJobDel', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["DWORD","MinJobId","in"],
+ ["DWORD","MaxJobId","in"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370616.aspx
+ dll.add_function('NetScheduleJobEnum', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["PDWORD","PointerToBuffer","out"], # ptr to array of AT_ENUM structures
+ ["DWORD","PreferredMaximumLength","in"],
+ ["PDWORD","EntriesRead","out"],
+ ["PDWORD","TotalEntries","out"],
+ ["DWORD","ResumeHandle","inout"]
+ ])
+
+ # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370617.aspx
+ dll.add_function('NetScheduleJobGetInfo', 'DWORD',[
+ ["PWCHAR","Servername","in"],
+ ["DWORD","JobId","in"],
+ ["PBLOB","PointerToBuffer","out"] # ptr to AT_INFO structure
+ ])
+
return dll
end
end
end; end; end; end; end; end; end
-
-
View
68 modules/post/windows/manage/add_user.rb
@@ -0,0 +1,68 @@
+##
+# 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 'msf/core/post/windows/accounts'
+
+class Metasploit3 < Msf::Post
+
+ include Msf::Post::Windows::Accounts
+
+ def initialize(info={})
+ super( update_info( info,
+ 'Name' => 'Windows Manage Local User Account Addition',
+ 'Description' => %q{
+ This module adds a local user account to the specified server,
+ or the local machine if no server is given.
+ },
+ 'License' => MSF_LICENSE,
+ 'Author' => 'Chris Lennert',
+ 'Platform' => [ 'windows' ],
+ 'SessionTypes' => [ 'meterpreter' ]
+ ))
+
+ register_options(
+ [
+ OptString.new('USERNAME', [ true, 'The username of the user to add (not-qualified, e.g. BOB)' ]),
+ OptString.new('SERVER_NAME', [ false, 'DNS or NetBIOS name of remote server on which to add user' ]),
+ OptString.new('PASSWORD', [ false, 'The password of the user account to be created' ]),
+ OptBool.new( 'DONT_EXPIRE_PWD', [ false, 'Set to true to toggle the "Password never expires" flag on account', false ]),
+ OptString.new('COMMENT', [ false, 'The comment/description to apply to the new account' ]),
+ ], self.class)
+ end
+
+ def run
+ result = add_user(
+ datastore['USERNAME'],
+ datastore['PASSWORD'],
+ datastore['COMMENT'],
+ datastore['DONT_EXPIRE_PWD'],
+ datastore['SERVER_NAME']
+ )
+
+ case result
+ when :success
+ print_status 'User was added!'
+ when :user_exists
+ print_error 'User already exists.'
+ when :group_exists
+ print_error 'Group already exists.'
+ when :access_denied
+ print_error 'Sorry, you do not have permission to add that user.'
+ when :invalid_server
+ print_error 'The server you specified was invalid.'
+ when :not_on_primary
+ print_error 'You must be on the primary domain controller to do that.'
+ when :invalid_password
+ print_error 'The password does not appear to be valid (too short, too long, too recent, etc.).'
+ when nil
+ print_error 'Something horrible just happened. Sorry.'
+ else
+ print_error 'This module is out of date.'
+ end
+ end
+end